아래글이 원본입니다.


간단히 설명하면 http://prettygom.com 으로 접속해도 KT 에서 prettygom.com + 자신들의 프로그램 을 동시에 보내주는거죠. 자신들의 프로그램을 실행시켜서 내부 IP 정보를 얻는다고 합니다.



http://www.mapoo.kr/entry/%EC%82%AC%EC%95%85%ED%95%9C-KT%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B3%B5%EC%9C%A0%EA%B8%B0%EB%A5%BC-%EA%B2%80%EC%B6%9C%ED%95%98%EB%8A%94%EA%B0%80?category=30


KT 공유기 이용제재 조치 개념도

2005 년 KT는 자사의 인터넷 라인에 공유기를 꼽아 여러 대의 IP를 따서 사용하는 유저를 식별할 수 있는 시스템, 즉 공유기 검출 시스템 개발에 성공했다고

발표

했습니다.

하지만 이것이 기술적으로 완벽하지 않다는 반론과 함께, 기존 사용자들의 거센 반발로 인해 사용 가능 PC를 2대로 제한한다는 정책으로 선회한 뒤 이 문제는 그대로 묻히나 했는데... 작년부터 KT와 하나로 사용자들을 중심으로 인터넷 연결 중 이상한 현상에 대한 보고가 들어오기 시작합니다. 참고1,참고2

이를 정리하면, KT등의 ISP(인터넷 서비스 공급자, Internet Service Provider) 라인을 사용하는 유저들이 웹브라우저로 특정 사이트에 접속할 때, KT에서는 임의로 공유기 검출을 위해 특정 서버로 먼저 요청을 보낸 다음, 사용자가 요청한 페이지로 다시 이동시킨다는 것입니다.참고3

명목상 이런 과정은 "접속 환경 개선을 위한 속도 테스트"나 "불량 사이트 차단"을 위한 것이라는 설명을 많이 하는데, 실제로 앞에서 얘기한 KT 서버에서 하는 일은 사용자가 공유기를 사용하는지를 체크하는 것입니다. 그럼 이런 과정이 어떻게 일어나는지 한 번 추적해 보겠습니다.

로직 추적

보통의 경우라면 바로 www.jinbo.net으로 요청이 전달되어 진보넷 페이지가 브라우저에 뜨게 됩니다. 하지만 공유기 검출 로직이 실행되는 중이라면 요청한 주소는 다음과 같은 HTML로 치환되어 보내집니다.

<HTML>
 <HEAD></HEAD>
  <FRAMESET border=0 frameSpacing=0 rows=0,* frameBorder=0>
  <FRAME name=dolla src="http://221.147.67.202/headseed.asp">
  <FRAME src="http://221.147.67.202/xs_btn/uxcsdo.asp?n=2&u=8825&i=0&y=0&m=0&k=0&t=N&b=78&x=www.jinbo.net&s=600&d=2008-01-01">
</FRAMESET>
</HTML>

화면이 두 개의 프레임으로 나뉘고, 둘 다 특정한 KT 서버에 요청이 갑니다. 이 중 첫 번째 요청(http://221.147.67.202 /headseed.asp)은 서버에서 정확히 어떤 일을 하는지 알 수 없고 단지 빈 HTML이 돌아올 뿐입니다. 여기서 주목해야 할 것은 두 번째 요청입니다.

<html>
<frameset rows='0,*' border='0'>
<frame src='/xs_btn/uxcn.asp?n=2&u=8825&i=0&y=0&m=0&k=0&t=N&b=78&s=600&x=www.jinbo.net&d=2008-01-01'>
<frame src='http://www.jinbo.net/?' name='rosea'>
</frameset>
<body oncontextmenu="return false" onselectstart="return false" ondragstart="return false">
</body>
</html>

응답의 결과로 역시 두 개의 프레임을 나눠서 줍니다. 첫 번째 프레임은 hidden 처리되어 특정 코드를 수행하고, 두 번째 프레임은 요청한 주소의 페이지를 보여줍니다. 결과적으로 화면에는 진보넷 페이지가 보이게 되니까 사용자는 크게 문제점을 인식하지 못할 가능성이 높습니다. 하지만 실제로는 사용자가 눈치채지 못하게 첫 번째 프레임의 동작이 실행되는 셈입니다. 그럼 첫 번째 프레임의 요청을 따라가 봅시다.

<input type='hidden' id='PTe' value='http://www.jinbo.net' target='_top'>

<script language='javascript'>
  var tcheck = document.cookie;
  window.onload = gonogo;

  function gonogo() {
    if (tcheck == ) {
      if (navigator.appName == 'Microsoft Internet Explorer') {
        document.all.PTAe.click();
      }
      if (navigator.appName == 'Netscape') {
        self.location.href=document.getElementById('PTe').value;
      }
    } else {
      today = new Date();
      var YY, MM, DD;
      YY = today.getYear();
      MM = today.getMonth()+1;
      DD = today.getDate();
      self.location.href='uxcn01.asp?mode=SSL3H&n=2&ha='+YY+'&ba='+MM+'&ya='+DD+'&x=www.jinbo.net&i=0&y=0&m=0&k=0&t=N&b=78&u=8825&d=2008-01-01';
    }
  }
</script>
<html>
<head>
  <title></title>
  <meta http-equiv="Content-Type" content="text/html; charset=euc-kr">
</head>
<body oncontextmenu="return false" onselectstart="return false" ondragstart="return false">
</body>
</html>

특정한 스크립트가 응답으로 날아옵니다. 페이지가 로드되면서 스크립트를 실행시켜 쿠키가 있는 경우 날짜와 기타 정보들을 파라미터로 심고 특정 사이트로 다시 보내버립니다. 다시 페이지로 따라가 보겠습니다.

<SCRIPT LANGUAGE="JavaScript">
</SCRIPT>
<input type='hidden' id='PT' value='http://www.jinbo.net' target='_top'>

<script language='javascript'>
  if (navigator.appName == 'Microsoft Internet Explorer') {
    window.onload = function() {
      document.all.PTA.click();
    }
  }
  if (navigator.appName == 'Netscape') {
    window.onload = function() {
      self.location.href=document.getElementById('PT').value;
    }
  }
</script>

역시 다른 페이지로 보내버립니다. 또 따라가 봅시다.

<HTML><HEAD><TITLE>Untitled Document</TITLE>
<META http-equiv=Content-Type content="text/html; charset=euc-kr">
<SCRIPT language=javascript>
<!--
//오늘은 더이상 팝업 윈도우를 띄우지 않습니다 
function GetCookie( name )
{
  var nameOfCookie = name + "=";
  var x = 0;
  while ( x <= document.cookie.length )
  {
    var y = (x+nameOfCookie.length);
    if ( document.cookie.substring( x, y ) == nameOfCookie ) {
      if ( (endOfCookie=document.cookie.indexOf( ";", y )) == -1 )
        endOfCookie = document.cookie.length;
      return unescape( document.cookie.substring( y, endOfCookie ) );
    }
    x = document.cookie.indexOf( " ", x ) + 1;
    if ( x == 0 )
      break;
  }
  return "";
}

//**********************************************************************************************************************

popup();

function popup()
{
  var g1 = "78";
  var n_type = "2";
  var u_idx = "8825";
  var cnt_g = "0";
  var cnt_i = "0";
  var msgUrl = "";
  var width = "";
  var height = "";		
  var msgUrl2 = "";
  var width2 = "";
  var height2 = "";
  var total = "0";
  var xt = "www.google.com";
  var dsa = "2008-01-01";
  var mainsvIP = ""; //임시 (공유기 서비스가입)

  var tmpwin = "";
  var tmpwin2 = "";
  var param = "";
  param = "g1="+g1+"&u_idx="+u_idx+"&cnt_g="+cnt_g+"&cnt_i="+cnt_i+"&total="+total+"&xt="+xt+"&mainsvIP="+mainsvIP+"&dsa="+dsa;

  if (n_type != 2 && msgUrl != "") // 유도
  {
    url = "evsol.asp?n_type="+n_type+"&"+param+"&msgUrl="+msgUrl;
    tmpwin=window.open(url,"pop1","width="+width+",height="+height+",top=0,left=0");
  }

  if (msgUrl2 != "") // 일반
  {
    //오늘은 더이상 팝업 윈도우를 띄우지 않습니다 if 문
    var erer = GetCookie("Notice");
    if ( erer != "done" )
    {
      url2 = "evsol.asp?n_type=1&"+param+"&msgUrl="+msgUrl2;
      tmpwin2=window.open(url2,"pop2","width="+width2+",height="+height2+",top=0,left=370");
      //tmpwin2.focus();
    }
  }

  if (n_type == 2)
  {
    var strHTML = "<APPLET CODE='fnqlxmfhvl.class' WIDTH='1' HEIGHT='1' name='Internet_Loading'>";
    strHTML += "<PARAM NAME='URL' VALUE='dotori_app.asp?b=78&n=2&u=8825&i=0&y=0&t=0&x=www.daum.net&re_ip='>";
    strHTML += "<PARAM NAME='TARGET' VALUE='_self'>";
    strHTML += "</APPLET>";	
    document.write (strHTML);
  }
} // popup() end

//-->
</SCRIPT>
</HEAD>
<BODY oncontextmenu="return false" onselectstart="return false" ondragstart="return false"></BODY></HTML>

드디어 정말 하고 싶었던 일을 하는 페이지가 나왔습니다. 이 복잡한 과정의 목적은 결국 공유기 사용자에게 경고 팝업을 띄우고, fnqlxmfhvl.class란 java 애플릿을 실행시키는 것입니다. 일단 fnqlxmfhvl.class가 무엇을 하는지 다운받아 리버스 컴파일해 보았습니다.

  • fnqlxmfhvl.java
import java.applet.Applet;
import java.applet.AppletContext;
import java.net.*;

public class fnqlxmfhvl extends Applet
{
  public fnqlxmfhvl()
  {
    flema = "";
    mkbro = "unknown";
    fx0073 = "";
  }

  public void init()
  {
    mkbro = rusianxx();
    if(getParameter("URL") != null)
    {
      fx0073 = getParameter("URL");
      fx0073 = fx0073 + mkbro;
      if(getParameter("TARGET") != null)
        flema = getParameter("TARGET");
    }
  }

  public void start()
  {
    try
    {
      URL url = new URL(getDocumentBase(), fx0073);
      getAppletContext().showDocument(url, flema);
    }
    catch(Exception exception) { }
  }

  private String rusianxx()
  {
    String s = "";
    String s1 = getDocumentBase().getHost();
    int i = 80;
    if(getDocumentBase().getPort() != -1)
      i = getDocumentBase().getPort();
    try
    {
      s = (new Socket(s1, i)).getLocalAddress().getHostAddress();
    }
    catch(Exception exception) { }
    return s;
  }

  String flema;
  String mkbro;
  String fx0073;
}

이 프로그램이 하는 일은 매우 간단합니다. 사용자의 내부 IP를 알아낸 다음, 이 IP를 dotori_app.asp로 전송해 줍니다. dotori_app.asp로 요청을 보냈을 때 특별한 응답은 날아오지 않았지만, 짐작으로는 아마 dotori_app.asp에서 사용자의 내부 IP를 받아 공유기를 사용한 IP인지, ISP에서 정상적으로 발급한 IP인지를 판단할 것입니다.

로직 설명

일반적으로 KT나 하나로텔레콤 등의 ISP는 사용자에게 유동 IP를 발급합니다. 유동 IP는 모뎀이 ISP에 접속할 때마다 일정 범위 내에 있는 IP를 발급해 주는 IP입니다. 사용자가 ISP에서 제공하는 라인을 컴퓨터에 바로 꽂아 쓰는 경우에는 사용자 PC의 IP는 외부에서나 내부에서나 ISP가 발급한 유동 IP로 동일하게 됩니다.

하지만 공유기를 사용하게 되면 약간 다른 상황이 발생합니다. 공유기는 하나의 외부 IP를 여러 개의 내부 IP로 분할해 사용 가능하게 해 줍니다. 이 경우 유동 IP는 공유기가 갖게 되고 사용자의 PC는 공유기가 발급한 내부 IP를 사용하게 됩니다. 따라서 사용자 PC의 IP는 10.0.0.2나 192.168.0.2 같은 형태의 내부 IP가 됩니다. 하지만 외부에서 볼 때 사용자 PC의 IP는 공유기가 가지고 있는 IP, 즉 ISP가 발급한 외부 IP로 보이게 되죠. 따라서 이 경우는 외부 IP와 내부 IP가 달라지는 현상이 발생합니다.

어쨌든 ISP는 사용자들이 공유기를 사용하여 여러 대의 PC가 한 개의 라인에 물려있는지 판단할 수 있는 작업을 사용자 몰래 하고 있는 셈입니다. 현실적으로 집에서 2~3개의 PC를 공유기로 사용하는 경우는 크게 문제를 삼고 있지 않지만, 사무실 등에서 10개 이상의 PC가 한 개의 공유기를 통해 사용하는 경우는 적발하여 추가적으로 라인을 구매하도록 유도하고 있다고 합니다. 이런 경우를 알 수 있는 것도 이러한 공유기 검출 시스템이 있기 때문이죠. 아마 사용자의 약관 어딘가에 사용자 동의 없이 하는 이런 작업을 정당화 하는 문구도 넣어놨으리라 생각됩니다.

결론

현실적으로 ISP에서 제공하는 망을 사용하는 이상, 개인 PC에 침입하여 벌어지는 이런 일들을 막기가 매우 어렵습니다. 어떻게 보면 인터넷 세상의 진정한 지배자는 ISP가 아닐까요...

(글이 너무 길어져서 공유기가 어떻게 IP를 분배하는지는 다른 글에서 다루도록 하겠습니다.)

댓글을 달아 주세요


1. CKEditor, FCKEditor

사이트 :: http://ckeditor.com/

데모 : http://ckeditor.com/demo

현존현존 하는 위지윅 에디터 중 가장 많이 사용되고 있으며 강력한 기능을 가진가진 CKEditor와 FCKEditor입니다. 이름에서 알 수 있듯이 CKEditor는 FCKEditor의 업데이트 버전으로 웹표준과웹표준과 접근성향상 그리고 디자인적인 발전등 날로 발전해 나가고 있는 프로그램입니다.

특징 : 툴바, 스킨등 개인에 맞게 설정 가능. 크로스 브라우징 가능.
언어 : 기본 영어, 한글 가능.
라이센스 : 오픈소스 라이센스 및 상업용 라이센스.
최종버전 : ckeditor - 3.0.1, fckeditor - 2.6.5
추천도 : ★★★★★

2. TinyMCE

CKEditor와 함께 가장 많이 사용되고 있는 위지윅 에디터  하나로 개인설정과 플러그인 설정으로

아주 강력한 기능을 가진 에디터입니다. jQuery버전도jQuery버전도 따로 존재하며 wiki도 존재하여 개인설정과 사용이 아주 간편한 편입니다.

사이트 : http://tinymce.moxiecode.com

데모 : http://tinymce.moxiecode.com/examples/full.php

특징 : 툴바, 스킨,플러그인등 개인에 맞게 설정 가능. jQuery 버전 존재. 크로스 브라우징 가능.
언어 : 기본 영어, 한글 가능.
라이센스 : 오픈소스 라이센스
최종버전 : 3.2.7
추천도 : ★★★★★


3. 다음 오픈 에디터에디터

얼마 전 다음이 구글코드를 통해 공개한 공개용공개용 다음 오픈 에디터입니다. 개발자들에 의하면 시멘틱 정보 웹 에디터라는 개념을 도입하여도입하여 사용자가 생산하는 콘텐츠에 데이터간 상호 연결성을 더해주는 웹 정보를 포함시켜 시멘틱시멘틱 검색 기술과 데이터 연결이라는 데이터 생태계를 구현했다고 합니다. 구글 코드 포럼을포럼을 통해 사용자들의 의견을 수렴하여 계속해서 업데이트되고 있어 미래가 기대되는 에디터  하나입니다.

사이트 : http://code.google.com/p/daumopeneditor/

데모 :: http://uie.daum.net/openeditor/sample/5.2.0/editor.html

특징 : 국내 최대 포탈사이트중 하나인 다음에서 공개한 오픈 에디터. 초보자가 사용하기 용이함.
언어 : 한글
라이센스 : 오픈소스 라이센스
최종버전 : 5.3.0 
추천도 : ★★★★☆

4. 스마트 에디터

네이버에서 공개한 위지윅 에디터인 스마트 에디터입니다. 웹폰트 지원, 초보 사용자가 사용하기 용이한 점등이 특징이지만 웹표준 및 브라우저 접근성에서 떨어져 원성을 사고 있기도 합니다.

사이트사이트 : http://dev.naver.com/projects/smarteditor

미리보기 : http://dev.naver.com/projects/smarteditor/wiki/SmartEditorBasicUserInterfacePreview

특징 : 웹폰트 사용가능(IE전용). 초보자가 사용하기 용이함.
언어 : 한글
라이센스 : 오픈소스 라이센스
최종버전 : 0.3.17
추천도 : ★★★☆


5. 알디터

사이트 : http://www.alik.info/

데모 : http://www.alik.info/alditor/sample.html

알릭님께서 제작한 알디터입니다. 간단한 인터페이스와 심플함으로 제로보드 및 그누보스 사용자들이사용자들이 많이 사용하고 있으며 비표준 및 접근성이 떨어지는 점만 제외하면 쓸만한 에디터입니다.에디터입니다. 그러나 따로 소스를 배포하지 않고 업데이트가 끊긴 점을 생각해 볼때 그리그리 추천하지는 않습니다.

특징 : 심플함.
언어 : 한글
라이센스 : 비영리목적 사용가능. 소스 수정/배포 금지. 상업적 목적 사용시 제작자에 연락.
최종버전 : 06년 9월 23일
추천도 : ★★

6. Xinha

사이트 : http://trac.xinha.org/

데모 : http://xinha.raimundmeyer.de/x_examples/ext_example.html

이번에 위지윅 에디터에에디터에 관해 알아보면서 알게된 Xinha 라는 에디터입니다. 한글사용은 안되는듯 보이지만 크로스 브라우징도브라우징도 지원하며 플러그인도 사용가능한듯 보이며 위지윅 에디터로 사용하기엔 좋은듯 싶습니다.

특징 : 위지윅 에디터 기능에 충실, 플러그인 사용가능.
언어 : 영어
라이센스 : 오픈소스 라이센스
최종버전 : 0.95
추천도 : ★★☆

7. openWYSIWYG

사이트 : http://www.openwebware.com/

데모 : http://www.openwebware.com/wysiwyg/demo.shtml

사용이 간편하면서도 다양한 언어를 지원하는 위지윅 에디터입니다.

특징 : PHP, Perl, ASP, ASP.net, Java 등 다양한 언어 지원. 사용이 간편함.
언어 : 영어
라이센스 : 오프소스 라이센스
최종버전 : 1.4.7
추천도 : ★★☆


8. Richtext Editor

사이트 : http://rtef.info

데모 : http://rtef.info/demo.htm

가볍고 쓸만한 위지윅 에디터입니다. 다양한 브라우저에서 사용가능하다는게사용가능하다는게 장점입니다.

특징 : 가벼운 인터페이스. 크로스 브라우징 지원.
언어 : 영어
라이센스 : 오픈소스 라이센스(MIT)
최종버전 : 0.007
추천도 : ★★☆

댓글을 달아 주세요



1. Windows 7 에서는 localhost 를 번역(?)하느라 시간을 많이 잡아먹는다.


localhost 대신 127.0.0.1 을 사용하고

C:\Windows\System32\drivers\etc 안의 host 파일에


127.0.0.1 localhost


부분의 주석을 제거한다.



2. 안티 바이러스 프로그램이 httpd.exe , php 파일 부분을 검색하지 않게 설정한다.


약간 위험한 방법이긴 하지만 요즘의 대부분 바이러스는 exe, php 감염은 적다. 특히 서버에서 잦은 실행 하는 부분은   바이러스 검사에서 빼주자.



3. Log 를 확인한다.


php 버젼이 최신인 경우 xpressengine 과 같이 최신 php 를 지원하지 않는 경우 무수히 많은 warning 을 쏟아낸다. 이는 직접적인 속도 감소로 이어진다. Log 를 확인해서 log 파일이 빠르게 쌓이고 있지 않나 확인해본다.

댓글을 달아 주세요


XAMPP root 패스워드 변경


http://rskarthikmca.blogspot.kr/2011/07/reset-xampp-mysql-root-password-through.html



그누보드 에러 해결

http://sir.co.kr/bbs/board.php?bo_table=g4_qa&wr_id=201405


 short_open_tag = On 

XAMPP Virtual HOST 권한 문제 - apache 2.4 부터 Require all granted 로 변경되었음



 <VirtualHost *:80>
    DocumentRoot "C:\xampp\htdocs-nacaa"
    ServerName nacaa.local
    ErrorLog "logs/nacaa.localhost-error.log"
     <Directory "C:\xampp\htdocs-nacaa">
      Options Indexes FollowSymLinks Includes ExecCGI
      AllowOverride All
      Require all granted
     </Directory>
  </VirtualHost>


아래는 보안상 문제있을수 있음


<Directory />
    AllowOverride none
    Require all granted
</Directory>

댓글을 달아 주세요


한영키가 되도록 고쳐진 realvnc client 로 인터넷에서 획득



vnc.exe


댓글을 달아 주세요


http://with-thai.com/2012/06/%EC%95%84%ED%8C%8C%EC%B9%98-%ED%8A%9C%EB%8B%9D%EC%9C%BC%EB%A1%9C-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8%EB%A5%BC-%EB%B9%A0%EB%A6%BF%EB%B9%A0%EB%A6%BF%ED%95%98%EA%B2%8C/

 

 

이전에 워드프레스 성능 개선팁이라는 게시물을 포스팅했습니다. 비단 워드프레스 블로그에 국한되는 이야기는 아니며 가장 널리 쓰이는 웹서버인 아파치에서 동시접속자를 튜닝하는 방법을 설명하고자 합니다. 여기서 설명하는 것이 절대적인 진리는 아니며 시스템을 꾸준히 모니터링하면서 알맞은 값을 직접 찾으셔야 합니다.

웹 서버를 며칠간 재시작하지 않고 놔둬서 현재 서비스 상태를 파악하는 게 가장 좋을 것 같습니다. 간단히 아파치 서버와 클라이언트 사이의 동시 소켓 연결 개수를 확인하기 위해 아래와 같이 명령합니다.

$ netstat -nta | grep :80.*ESTABLISHED | wc -l

이 연결 개수는 아파치 서버가 현재 처리 중인 소켓 연결 개수이며 앞으로 설명할 MaxClients 수를 넘지 않도록 유의해야 합니다.

그런데 아직 서비스를 시작하기 전이라 웹 서버의 부하를 확인하기가 어렵다면 아파치 벤치마킹 도구(ab)를 이용해볼 수도 있습니다.

$ ab -n 3000 -c 200 http://example.com/

위 명령어는 http://example.com 사이트에 200명의 사용자가 15번씩 총 3000번 요청하는 것입니다. 사이트 주소의 끝에 / (슬래시)를 붙이는 것을 잊지 마세요. 여러 페이지를 계속 접속 시도해 동시접속 부하를 주세요. 그리고 여러 가지 시스템 상황을 모니터링해봅시다.

이렇게 부하를 줘가면서 시스템의 메모리 현황을 측정하겠습니다. 아래 예제에서는 물리 메모리가 2000MB로 확인되었습니다.

$ free -m
             total       used       free     shared    buffers     cached
Mem:          2000        664       1335          0         15         86
-/+ buffers/cache:        562       1437
Swap:          875        344        531
이제 MySQL 메모리 사용량을 측정합니다.
$ ps aux | grep mysql | awk '{print $6}' | awk '{total = total + $1} END {print total/1024}'
9.11328

그리고 아파치의 메모리 사용량도 측정합니다.

$ ps aux | grep apache2 | awk '{print $6}' | awk '{total = total + $1} END {print total/1024}'
1848.07

아파치 프로세스의 개수도 계산합니다.

$ ps aux | grep apache2 | wc -l
183

그럼 아파치 전체 메모리 사용량을 아파치 프로세스 개수로 나누어 계산해서 아파치 프로세스 평균 메모리 사용량은 약 10.09MB라는 것을 알았습니다. 따라서 이론적인 최대동시연결수는 아래와 같이 계산할 수 있습니다.

(전체 메모리 – MySQL 메모리 사용량) / 아파치 프로세스 평균 메모리 사용량

= (2000 – 9.11) / 10.09

= 197.31

램이 2G인 서버에서 동시연결소켓수가 197.31이면 이미 최대라는 이야기입니다. 실제 서버에는 bind, ssh, postfix, dovecot 다른 데몬 등이 돌아가고 있을 수 있으므로 이 값은 더 줄어듭니다.

아파치에서 동시접속을 처리하는 방법을 이해해야 합니다. 동시접속 처리방법에는 프로세스를 이용하는 법(prefork 방식)과 스레드를 이용하는 법(worker 방식)이 있습니다. 아파치 서버의 기본 동작은 prefork 방식입니다.

prefork 방식은 위 그림처럼 하나의 연결은 스레드가 하나인 자식 프로세스가 처리합니다. 프로세스를 만드는 비용은 스레드를 만드는 비용보다 더 크고 메모리를 더 많이 차지합니다. 하지만 프로세스간 메모리를 공유하지 않아 서로 독립적이기 때문에 문제가 있는 연결이 다른 연결을 간섭하지 않습니다.

반면에 worker 방식은 위 그림처럼 자식 프로세스 안에 여러 스레드가 있고 이 스레드는 하나의 연결을 처리합니다. 스레드를 만드는 것이 프로세스를 만드는 것보다 속도가 더 빠르고 메모리를 덜 차지하지만 문제가 생긴 스레드는 자식 프로세스 안의 다른 스레드까지 망가뜨릴 위험이 있습니다. 또한 경쟁상태(race condition)도 조심해야합니다.

여기서는 가장 널리 쓰이는 prefork 방식이며 이제 본론으로 들어가 최대동시접속자수를 어떻게 튜닝할지 이야기하겠습니다.

앞서 계산해서 얻은 값을 토대로 튜닝한다면 아파치의 prefork 지시어 설정은 아래와 같이 할 수 있습니다.


여기서 기억해둘 사항은 캐싱을 적극적으로 활용하면 아파치 프로세스가 10MB 정도 차지하지만 캐싱하지 않으면 프로세스당 메모리가 30MB까지도 잡힙니다. 즉, PHP 코딩이 아파치 프로세스의 메모리 사용량에 영향을 주고 최대동시접속연결수도 영향을 줍니다. 그리고 MaxClients 값을 비롯해 StartServers 등의 값을 너무 크게 잡으면 오히려 아파치 프로세스의 점유 메모리가 더 커지는 현상이 있으므로 적당한 값을 지정해야 합니다.

ServerLimit 값은 최대 자식 프로세스의 수인데 MaxClients 값보다 높게 설정하지 않아야 합니다. 구글링하다보면 MaxClients 값을 256 이상 지정하기 위해서 아파치 소스를 다시 컴파일해야 한다는 게시물들이 있는데 그럴 필요가 없습니다. 최대값이 2000이라 이미 충분히 큽니다. MaxClients 값이 2000을 넘길 필요가 없다면 여러분은 ServerLimit와 MaxClients 값을 동일한 값으로 지정해주면 됩니다.

StartServer 값은 아파치 데몬을 처음 시작할 때 프로세스 개수이며 이 값은 MinSpareServer 또는 MaxSpareServer의 값과 일치시켜주면 됩니다.

MinSpareServers와 MaxSpareServers 값은 요청을 기다리는 몇 개의 자식 프로세스를 여분으로 둘 지 범위를 지정합니다. 아파치 데몬이 프로세스의 개수를 이 범위 안으로 두기 위해 노력하지만 이를 보장하지는 않습니다. 초당 4개 이상의 자식 프로세스를 생성(spawn)하지 않도록 설정해야 합니다. 보통은 MinSpareServers 값은 MaxClients의 10~25% 정도 크기의 값으로 MaxSpareServers 값은 25~50% 크기의 값으로 지정합니다. 그러나 이 값이 커지면 아파치 프로세스의 점유 메모리도 함께 커지는 현상이 있으니 크다고 능사는 아니므로 유념하세요.

아파치 웹 서버는 오픈소스로서 끊임 없이 패치하고 있지만 C로 작성되어 있어 메모리 누수(leak)나 무한루프 등의 서버 부하(load) 발생할 수도 있습니다. 그래서 일정 횟수 동안 클라이언트 요청을 처리하고 프로세스가 죽도록 되어 있습니다. 아파치 매뉴얼에서는 디폴트로 클라이언트 요청을 10,000번 처리하고 죽는다고 합니다. 이 클라이언트 요청을 몇 번 처리하고 프로세스가 죽을지는 MaxRequestPerChild 값의 변경해 지정할 수 있으며 시스템 사양이 좋다면 이 값을 넉넉히 할 수 있으며 프로세스가 절대 죽지 않도록 하려면 0으로 합니다. 3000에서 4000 정도 주면 적당하지 싶습니다.

이외에도 Timeout, KeepAlive, MaxKeepAliveRequests, MaxKeepAliveTimeout 같은 옵션도 성능에 영향을 미칩니다.

Timeout 10

기본적으로 소켓 연결 유지 시간입니다. 클라이언트가 요청하여 아파치와 연결된 이후로 이 시간 동안 아무런 메시지가 없으면 오류로 처리됩니다. 네트워크 회선 상태가 좋지 않으면 이 값을 크게 할 수 있습니다. 국내 사용자만 고려한다면 10초 정도 국외 사용자까지 고려하면 20초가 좋습니다.

KeepAlive를 On 해으면 첫 요청시에 열어 놓은 연결을 KeepAliveTimeout에서 지정한 시간 동안 끊지 않습니다. 그래서 img src=”http://~” 태그로 이미지를 많이 링크하고 있으면 매번 소켓을 열지 않기 때문에 접속 속도가 향상됩니다. 하지만 동시접속자수는 줄어들 수 있습니다.이 블로그의 경우에는 동시 접속자가 많지 않으므로 빠른 접속 속도를 위해 켰습니다.

KeepAliveTimeout 2

국내 접속자가 많으면 3초 이하 국외 접속자가 많으면 10초까지 지정할 수 있다고 합니다. 10초 이상은 메모리 점유로 동시접속자가 크게 감소할 수 있다고 판단됩니다.

MaxKeepAliveRequests 100

KeepAlive 상태에서 처리할 최대 요청 수로 보통 웹 사이트에서는 100 정도 지정하면 충분합니다.

참고로 개인 블로그가 아닌 정말 접속자가 많은 사이트를 운영할 경우에는 관리상 웹 어플리케이션 서버와 이미지 서버를 분리해 운영하는 것이 맞습니다. 특히, 웹 어플리케이션 서버의 경우에는 KeepAlive Off로 하고 이미지 서버는 연결 유지를 위해 KeepAlive On으로 설정해두기도 합니다.

워드프레스뿐만 아니라 아파치를 쓰는 어떤 웹서비스나 적용 가능한 팁입니다. 이제 쌩쌩 달리는 아파치를 경험하세요.

댓글을 달아 주세요



윈도우에서 cron 을 사용하는 방법은 wincron, cronw, nncron 등이 있는데 이중에서 nncron 이 가장 사용하기 편하다.


설치 폴더에 cron.tab 파일을 수정하면 된다.



nncronlt117.exe


http://www.nncron.ru/download.shtml

댓글을 달아 주세요


윈도우에서 백그라운드에서 console 을 실행하려면 hstart 란 프로그램이 도움이 된다.


유료버전도 있는데 현재 사용해본바로는 무료버전도 잘 동작한다.


예제)

hstart64 /noconsole /d="e:/" "e:/top10.bat"



Hstart_4.1-bin.zip


http://www.ntwind.com/software/hstart.html

댓글을 달아 주세요


1) phpmyadmin 에서 SQL 퀘리로 아래를 실행시킨다.


SELECT CONCAT('ALTER TABLE `', t.`TABLE_SCHEMA`, '`.`', t.`TABLE_NAME`, '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;') as stmt  FROM `information_schema`.`TABLES` t WHERE 1 AND t.`TABLE_SCHEMA` = '바꾸고 싶은 데이터베이스' ORDER BY 1;


2) 나오는 결과를 drag & drop 해서 메모장에 옮긴다.


3) 다시 이것을 SQL 퀘리에서 실행한다.


4) 마지막으로 SQL 퀘리에서 아래를 실횅한다.


ALTER DATABASE 바꾸고싶은데이터베이스 CHARACTER SET utf8;



모든 Table 과 Column 의 내용이 UTF-8 으로 변환되어 있다.

댓글을 달아 주세요


XP Home 의 5개 제한은 컴퓨터 개수가 아니라. 연결 session 의 개수이다.

따라서 프린터 공유 1개, 다른 프린터 공유 1개, \\ 로 공유 폴더를 1개 열면 3개 사용한 것이 된다.

따라서 필요없는 공유 프린터 드라이버는 제거하는것이 좋다.



1. 세션 시간을 줄이기


시작->실행->cmd 치고

net config server /autodisconnect:1


2. TCP session 패치


첨부한 파일을 다운 받아서 실행 / 참고로 virus 검사시 경고가 뜬다.


EvID4226Patch223d-en.zip


http://www.lvllord.de



참고로 2 번 방법으로 하면 \\ 로 공유 폴더 제한은 풀린다. 하지만 프린터 공유 제한 갯수 5개는 풀리지 않는다.



댓글을 달아 주세요