2008. 9. 5. 23:29

<%
   urlS     = "http://static.naver.com/kin/img"
   FILENAME = "/tmp13.gif" '/ 표시 주의!
   SAVEpath = Server.MapPath("/")

'   Set http= CreateObject("Microsoft.XMLHTTP") '엮인글의 사유로 변경함

   Set http= CreateObject("MSXML2.ServerXMLHTTP")
       Http.Open "GET", urls&filename, false
       Http.Send()
       binData = Http.ResponseBody
   Set http = Nothing

   Set Stream = CreateObject("ADODB.Stream")
   Stream.open()
   Stream.type = 1
   Stream.Write binData
   Stream.SaveToFile savepath & filename,2
   Set Stream = Nothing

   '--- 화면에 보기
   '   Response.BinaryWrite binData
%>


Posted by ToTb
2008. 9. 5. 23:27

첫번째 프레임 페이지 입니다.
<frameset rows='100%,*' cols='*' framespacing='0' frameborder='NO' border='0'>
<frame src='SlideShow.asp?<%IF Trim(request.querystring) = '' Then Response.Write(request.Form) ELSE Response.Write(request.QueryString)%>' name='mainFrame' >
<frame src='about:blank' scrolling='NO' noresize name='hiddenFrame' >
</frameset>

SlideShow.asp라는 파일에서 임의의 이벤트가 발생하면 이 파일의 특정 콤보에 값을 채우는 방법입니다
이 방식을 사용하면 새로고침의 한계를 뛰어 넘을 수 있습니다.

parent.document.frames['hiddenFrame'].document.location='top.asp?mode=CSSpec&ProductCode=' + document.frmSlide.ProductCode.value; 이런 방식으로 숨겨진 프레임 top.asp파일을 호출합니다. 이런식으로 사이트에 hidden 프레임을 만들어 두면 그렇게 멀게만 느껴졌던 디비호출을 마음대로 할 수 있습니다
=>Top.asp만 잘 정의 하면 대부분의 요청을 asp리프레쉬 없이 처리 할 수 있습니다.

// 여기는 Top.asp에 해당하는 내용입니다
mode에 따라서

<script language='javascript>

var TargetDoc= parent.document.frames['mainFrame'].document;
var oOption;
IF Request('mode') = 'CSSpec' THEN
Set adocmd = Server.CreateObject('ADODB.Command')
WITH adocmd
.ActiveConnection = DbConn
.CommandType = adCmdStoredProc
.CommandText = 'IS_CSEnterOrderCounts'
.Parameters.Refresh
.Parameters.Item('@ProductCode').Value= Cint(Request('ProductCode'))
Set RS = .Execute
END WITH
%>
for( key in TargetDoc.frmSlide.CSSpec)
{
TargetDoc.frmSlide.CSSpec.remove(key);
}
<%
Do While Not RS.EOF
%>
oOption = TargetDoc.createElement('OPTION');
oOption.value='<%=Trim(RS('ColorCode')) %>;<%=Trim(RS('SizeCode')) %>';
oOption.text ='<%=Trim(RS('Name') )%>-<%=Trim(RS('Size'))%>';
TargetDoc.frmSlide.CSSpec.add(oOption);
<%
RS.MoveNext
Loop
RS.close()
END IF%>

</script>


Posted by ToTb
2008. 9. 5. 23:26
# setInterval()과 XMLHTTP 를 이용하여 페이지 Refresh

* 장점
1) 브라우저 진행율 표시 안나타남
2) Refresh 소리 없음

--> 이 소스는 정해진 시간마다 회원이 로그아웃 했는지를 체크하기 위해 만들었습니다.
로그인상태의 회원은 LOGON 값을 현재시간으로 업데이트하고,
로그아웃상태인 회원은 LOGON 값이 널(null)이 됩니다.

* logon.asp 는 로그인한 회원정보를 보여주는 파일입니다.
* logon_update.asp 는 logon_time.asp 파일을 일정간격으로 refresh한 효과를 냅니다.
* logon_time.asp 파일은 현재시간을 LOGON 값에 넣어줍니다.



(1) XMLHTTP 설명

1. XMLHTTP 메서드

Abort 현재 HTTP 요청을 취소합니다.
GetAllResponseHeaders 응답 메시지에서 모든 헤더 필드를 검색합니다.
GetResponseHeader 응답 본문에서 HTTP 헤더의 값을 검색합니다.
Open HTTP서버에 대한 연결을 엽니다.
SetRequestHeader 요청 헤더 필드 중 하나를 설정합니다.
Send HTTP 서버로 요청을 보냅니다. 본문이 포함될 수 있습니다.


2. XMLHT TP 속성 : 속성을 사용하여 요청 확인
--> XMLHTTP 속성을 사용하면 요청을 확인할 수 있을 뿐만 아니라 서버로부터
반환된 값을 검색하여 요청으로 발생한 모든 오류를 확인할 수 있습니다.

=========================================================================
속성 값 설명
=========================================================================
OnReadyStateChange 이벤트 처리기 참조 비동기 작업에서만 사용합니다.
이 속성은 데이터가 서버에서 반환되는 것과 같은
대기 상태가 변경될 때 이벤트 처리기 호출을 지정합니다.

ReadyState Integer 비동기 작업의 상태, uninitialized (0), loading (1),
loaded (2), interactive (3), completed (4)를 나타냅니다.

ResponseBody Variant 배열 응답의 본문을 배열로 반환합니다.
ResponseStream IStream 응답의 본문을 ADO Stream 개체로 반환합니다.
ResponseText String 응답의 본문을 텍스트 문자열로 반환합니다.
ResponseXML XMLDocument 개체 응답의 본문을 MSXML XMLDOM 파서로 분석된 것으로
반환합니다.
Status Long 서버가 반환한 HTTP 상태 코드
StatusText String HTTP 응답 라인 상태
=========================================================================


(2) 소스 설명

'----------logon.asp 시작------------------

' member_uid : 회원 고유번호

<iframe width="0" height="0" border="0" frameborder="0" src="/logon_update.asp?member_uid=<%=member_uid%
>"></iframe>

'----------logon.asp 끝 ------------------




'----------logon_update.asp 시작------------------

<%
Dim member_uid
member_uid = Trim(Request.QueryString("member_uid")) ' 회원 고유번호
%>

<html>
<head>
<script language="javascript">
<!--

// logon_update.asp 파일을 로드한다.
function pageLoad() {

// (1) XMLHTTP 객체를 생성합니다.
var xmlHTTP = new ActiveXObject("Microsoft.XMLHTTP")

// (2) HTTP서버에 대한 연결을 엽니다.
xmlHTTP.open("get","logon_time.asp?member_uid=<%=member_uid%>",false);

// (3) HTTP 서버로 요청을 보냅니다.
xmlHTTP.send();

}

function intervalCall() {

setInterval("pageLoad()", 5000); // 5초간격으로 pageLoad()함수 호출

}

//-->
</script>
</head>
<body onload="intervalCall()">
</body>
</html>

'----------logon_update.asp 끝------------------



'----------logon_time.asp 시작------------------

<!--#include virtual="/dbconnect.asp"-->
<%
On Error Resume Next

Dim member_uid
member_uid = Trim(Request.QueryString("member_uid")) ' 회원 고유번호

Dim Dbcon, Rs, sqlQuery

' dbconnect.asp 에 정의된 DbOpen()함수를 통해 DB Connection 객체 생성
'(@@ 이부분은 직접 정의하여 사용하세요 ^^;)
Set Dbcon = DbOpen()

' 로그인한지 10초가 초과한 회원의 logon필드값을 널(NULL)로 업데이트
strSQL = ""
strSQL = "UPDATE Q_MEMBER"
strSQL = strSQL & " SET LOGON = NULL"
strSQL = strSQL & " WHERE DATEDIFF(SECOND, LOGON, getdate()) > 10"
Dbcon.Execute(strSQL)


sqlQuery = ""
sqlQuery = "SELECT * FROM MEMBER_INFO WHERE MEMBER_UID="& member_uid

' 회원 로그온 시간 업데이트
Set Rs = Server.CreateObject("ADODB.Recordset")
With Rs
.Source = sqlQuery
.ActiveConnection = Dbcon
.CursorType = adOpenStatic
.LockType = adLockPessimistic
.Open , , , ,adCmdText
.Fields("LOGON_TIME") = Now
.Update
End With

' Error 체크 (@@ ErrorCheck() 함수도 dbconnect.asp 에 정의됨.)
Call ErrorCheck("회원 로그온 시간 UPDATE 처리")

Rs.Close
Set Rs = Nothing

' DB Connection 객체 Close
DbClose(Dbcon)
%>


'----------logon_time.asp 끝------------------


Posted by ToTb
2008. 9. 5. 23:24

성인광고 등록 방지할수 있는 간단한 예방책을 소개합니다.

1. 게시판 폼페이지에서 아래의 항목을 추가 한다.
<input type="hidden" name="sessionid" value="<%=session.SessionID%>">

- 대부분의 성인광고는 처리페이지에 값을 일괄적으로 전송하기때문에 폼페이지에서 입력된 값인지를 확인해야한다.
- 세션아이디값을 처리페이지로 넘겨 현세션과 동일한지를 비교한다. 1 코드처럼 폼페이지에서 세션아이디값을 같이 처리페이지로 전송한다.

2. 처리페이지에 아래코드를 추가 한다.
<%
if not request("sessionid") = session.SessionID then
    Response.Write    "<script language=javascript>"& vbCrLf &_
                    "    alert('올바른접근이아닙니다.');"& vbCrLf &_
                    "    history.back();"& vbCrLf &_
                    "</script>"
    Response.End
end if
%>

 - 폼페이지에서 넘어온 세션아이디값과 현 세션아이디값을 비교하요 폼페이지를 통해서 넘어온 값인지를 확인한다.


Posted by ToTb
2008. 9. 5. 23:20
세션은 어떻게 할까...창을 닫아버리면 어쩔까...컴터를 끄면 어쩔까...고민하다가...
QnA에서 힌트를 얻어서...만들었습니다...
필요하신 분은 참고하세요~

우선 두개의 테이블을 만들었습니다...
checklog Table
    - ip(접속 ip)
    - id(사용자 id)
    - loginTime(로그인 시간)

duplicatelog Table (중복접속이 일어났을 경우 로그기록을 남기기 위해서 존재)
    - id(사용자 id)
    - date(날짜)
    - ip(접속 ip)

자주 쓰는 테이블은 checklog Table 하나면 됩니다. 중복체크를 로그기록으로 남기지 않으시면, duplicatelog 테이블은 필요 없습니다.

그래서, 로그인 할때 마다
'로그인 중복 방지#################################################################
        ' 현재날짜 구하기
        strYear = Year(now)
        strMonth = cint(Month(now))
        strDay   = cint(Day(now))
        if cint(strMonth) < 10 then
            strMonth = "0" & strMonth
        end if  
        if cint(strDay) < 10 then
            strDay = "0" & strDay
        end if
        cur_date = strYear & "/" & strMonth & "/" & strDay
        ' 현재날짜 구하기 끝
        ip = Request.Servervariables("REMOTE_ADDR")
        Set dblog=Server.CreateObject("ADODB.Connection")
        dblog.open("logEvent")
        sql = "select * from checklog where id='" & id & "'"
        set rsLog = dblog.execute(sql)

        if rsLog.EOF or rsLog.BOF then '중복 로그인이 아님
            sql = "insert into checklog (id, ip, loginTime) values ('"&id&"', '"&ip&"', '"&cur_date&"')"
            dblog.execute sql
        else    '중복 로그인
            sql = "update checklog set id='"&id&"', ip='"&ip&"', loginTime='"&cur_date&"'"           
            dblog.execute sql
       end if
'       사용자 id로 된 데이터가 없으면 insert를 id로 된 데이터가 있으면 update를 시킵니다.
        dblog.close
        set dblog = nothing
'##############################################################################

그리고, 현재 id와 ip가 맞는지 검사 해주면 되겠죠.
중복 방지가 필요한 페이지에서

'로그인 중복 방지#################################################################  
    ip = Request.Servervariables("REMOTE_ADDR")
    Set dblog=Server.CreateObject("ADODB.Connection")
    dblog.open("logEvent")
    sql = "select * from checklog where id='" & session("mem_id") & "'"
    set rsLog = dblog.execute(sql)    
    if rsLog.EOF or rsLog.BOF then '로그온 안되거나 update 안됨
    else
        if rsLog("ip") <> ip then
            sql = "insert into duplicatelog (id, ip, date) values ('" & session("mem_id") & "', '" & ip & "', '" &
sLog("loginTime") & "')"
            dblog.execute sql
            %>
            <script>
                alert("동일 아이디의 사용자가 접속하여 세션이 종료됩니다.");
                location.class='MIME' href="include/login_ok.asp?sw=logout&returnUrl=<%
Request.ServerVariables("URL")&"?"&Request.ServerVariables("QUERY_STRING")%>";
                // 강제로 로그아웃
            </script>
            <%          
        end if
    end if
    dblog.close
    set dblog = nothing

'로그인 중복 방지#################################################################

저장된 ip와 클라이언트의 ip가 다르면 duplicatelog Table에 기록을 하고, 경고창을 내보내면서...강제로 로그아웃 시킵니다. 그러면 새로 접속된 세션은 살아있으면서 기존에 있던 세션이 끊어지게 되겠죠...기존에 세션이 있다면요... 그리고, 별 필요는 없지만...깔끔하게 정리하기 위해

로그아웃 버튼이 눌렸을때
'로그인 중복 방지#################################################################
Set dblog=Server.CreateObject("ADODB.Connection")
dblog.open("logEvent")
sql = "delete from checklog where id='" & session("mem_id") & "'"
dblog.execute sql

'#################################################################################

만들어진 레코드를 지워놓습니다.
물론, 안 지워도 상관은 없구요~
그럼, 도움이 되셨길...^-^;;;


Posted by ToTb
2008. 9. 5. 23:18

웹사이트를 운영하다 보면 이미지 파일을 함부로 다운로드 받지 못하도록 이미지의 경로를 노출시키고 싶지 않은 경우가 있다. 여기서는 ASP를 이용한 간단한 방법을 살펴보도록 하겠다. 우선 간단한 예제부터 살펴보도록 하자. 아래 샘플 이미지가 있는데 이 이미지는 여기서 살펴볼 방법에 의해 이미지를 로드한 것이다.

<img src="/etc/codeexample/asp/19444.asp?FName=020129p_01.jpg">
 
이미지의 경로가 .gif나 .jpg가 아니라 .asp인 asp파일로 되어 있다. 즉, asp 파일 안에서 적절한 이미지를 불러 오는 것이다. 그렇다면 /etc/codeexample/asp/19444.asp의 내용은 어떻게 되어 있을까?

<%
Option Explicit
 
'Referer를 먼저 구한다.
Dim strBuffer, FilePath
strBuffer = Request.ServerVariables("HTTP_REFERER")
'만일 referer가 http://korea.internet.com/channel/content.asp였다면
strBuffer = mid(strBuffer, InStr(strBuffer,".") + 1)
'이 상태에서의 strBuffer = internet.com/channel/content.asp가 됨
strBuffer = left(strBuffer, InStr(strBuffer, "/") - 1)
'이 상태에서의 strBuffer =  internet.com이 됨
 
'실제 이미지가 들어있는 디렉토리를 지정.
'다른 웹사이트일 수도 있고 다른 디렉토리일 수도 있다.
'이 값은 자신의 환경에 맞게 수정하기 바란다.
'사람들이 예측할 수 없는 이름을 사용하는 것이 좋다.
FilePath = "/images/photoshop/"
 
'만일 referer에 internet.com이 포함되어 있으면...
'referer도 자신의 환경에 맞게 수정하기 바란다.
If strBuffer = "internet.com" then
        '이미지 경로 완성
        FilePath = FilePath + Request.QueryString("FName")
Else
        '에러 이미지 경로!!
        FilePath = "/images/error.gif"
End If
'원하는 이미지 불러옴
Response.Redirect(FilePath)
%>
 

여기서 살펴본 내용은 사실 완벽한 것이 아니다. 여러 허점이 보이는 그런 코드이다. 페이지에 나타난 이미지를 캡쳐하거나 복사하는 등 여러 가지 막기 어려운 부분이 여전히 남아 있다. 여기서 사용한 방법과 자바스크립트를 이용하면 이미지를 불법으로 가져가는 것을 조금 더 귀찮게 만들 수는 있다. 자바스크립트를 이용한 방법은 다음 글을 참조하기 바란다.



Posted by ToTb
2008. 9. 5. 23:17
- ASP syntax -

ASP가 여러 사람 먹여 살리는 것 같습니다. 1996년 당시, CGI가 판을 칠 때 슬그머니 나타난 ASP. 전 아직도 그 때 당시를 잊지 못합니다. 이렇게 쉬운 것도 있구나... DOS에서 C와 C++을 하다가 윈도상에서 DB를 해야 했을 때 VB, 델파이 등 여러 가지를 시도하다 MS-Access를 만났을 때와 마찬가지로 그 감동은 엄청난 것이었습니다. MS에서 제공해주는 기본적인 예제와 메뉴얼로 이렇게 저렇게 해보다 결국 하나하나 화면이 만들어지는 그 감동. 프로그래머라면 누구나 느끼시는 것일 겁니다. IIS가 웹서버 시장의 절반정도를 차지하고 있는 작금의 현실은 그대로 ASP에 대입된다고 볼 수 있겠죠. 그만큼 사용자나, 개발자가 많다는 얘깁니다. 작설하고.. 본론으로 들어가죠.

자, ASP는 자바스크립트와 자바의 관계처럼 VB와 연관이 있습니다. 즉, VB 스크립트를 주로 사용하기 때문이죠.
이제부터 간단하게나마 syntax들을 훑어봅시다.

1. <%로 시작해서 %>로 끝난다. 문장은 엔터를 만나면 종결됩니다.
2. 변수는 선언해줘도 되고(Dim 구문), 그렇지 않아도 됩니다. 하지만, 시스템이 커질수록 선언해 주는 것이 디버깅 등에 도움이 됩니다. Option Explicit 명령을 써서 강제로 변수를 선언하게 해 줄 수 있습니다. 또한, 자바스크립트와 마찬가지로 형을 선언하지 않습니다.
3. 주석은 ' 뒤에
4. include로 필요한 스크립트 파일을 불러올 수 있습니다.
<!--#Include File="xxx.asp"--> 처럼 파일을 지정할 수도 있고,
<!--#Include Virtual="/inc/xxx.asp"--> 처럼, 가상경로의 파일을 지정할 수 있습니다.
5. 반복문
For Next
     eg. For i=1 to 10
           Next
For Each
    eg. arr=Array("A","B","C")
          For Each item in Arr
          Next
Do Until
    eg. Do Until RS.EOF
          Loop
6. 조건문
If Then
Select Case
    eg. Select Case i
          Case 1:
          Case 2:
          End Select
7. 함수 
여러 군데서 반복해서 쓰여지는 구문일 경우 Sub 프로시져를 사용합니다.(Sub ~ End Sub)
값을 되돌려 받아야 할 경우 Function 프로시져를 사용합니다.(Function ~ End Function)
Exit문을 쓰면 sub, function, 반복문 등을 빠져나올 수 있습니다.

8. 가장 많이 사용되는 DB에 관련된 오브젝트, 프라퍼티, 메쏘드는 다음과 같습니다.

Select Statement
Set Conn=Server.CreateObject("ADODB.Connection")
Set RS=Server.CreateObject("ADODB.RecordSet")
Conn.Open "DSN=DSN_Name;UID=UserID;PWD=Password"
Rs.Open "SQL Statement",conn,1,1
RS.PageSize=15 ' 한 페이지 크기
RS.AbsolutePage=현재 페이지
AllPage=RS.PageCount ' PageCount=전체 페이지
AllRecord=RS.RecordCount

...statement

RS.Close
Conn.Close
Set RS=Nothing
Set Conn=Nothing

Execute Statement
Set Conn=Server.CreateObject("ADODB.Connection")
Conn.Open "DSN=DSN_Name;UID=UserID;PWD=Password"
Set RS = Conn.Execute("Execute SQL Statement")
Set Rs=Nothing
Conn.Close
Set Conn=Nothing



Posted by ToTb
2008. 9. 5. 23:15

앞서 말한 4가지 중 어떤 것이 주된 원인이 되는지 정형화시키기는 사실 매우 어렵다. 그러나, 일반적으로 분류해 보면, 컴포넌트의 오류와 데이터베이스의 오류가 대부분이다. 혹은 이 둘의 복합적인 양상을 띠게 된다.

일반적으로 컴포넌트 오류로 인한 문제는, 주기적으로 이 컴포넌트와 관계된 오류 메시지가 웹 로그에 나타나므로 그 문제점의 발생 및 수정은 비교적 쉽다. 간혹, 컴포넌트 문제가 아니라 웹 서버 자체의 설치 문제와 관계된 사항들에 의해 시스템 에러를 받기도 한다.

이러한 문제들은 디버거를 사용하여 해결하는 경향이 크다. 웹 사이트 자체를 디버깅 모드로 운영하면서 생기는 여러 결함들을 잡아 낼 것을 일반적으로 권하고 있다.

컴포넌트 오류가 아닌 경우에는 정말 어딘가의 병목이 있어서이다. 이 경우가 가장 잡기 어려운 에러이며, 보통 데이터베이스 응답 지연으로 인해 문제가 야기된다. 단순한 예로, 데이터베이스 커넥션을 열기 위한 라이선스가 충분하지 않아 문제를 야기하기도 하며, 소통을 위한 프로토콜 정보가 맞지 않아(즉 보안 프로토콜을 사용할 때), 느려지면서 서버가 죽어버리기도 한다. 이런 문제로 인해 죽는 서비스는 CPU 사용량도, 메모리 사용량도, 심지어 웬만한 변수도 다 정상이다. ASP Queue만이 증가할 뿐, 서버는 아무런 이상 증상을 보이지 않는다.

이렇게 특히 ASP 병목이 있을 때, 쉽게 그 문제를 확인하는 방법은 HTML 파일에 대해서는 응답 지연이 오지 않으면서 ASP 파일만이 응답 지연이 발생하는지 여부를 알아보는 것이 가장 빠르다. 물론 최소한 풀링으로 동작하는 서로게이트를 사용하여야 하고(가급적 독립이 좋긴 하지만, 성능상의 희생이 따르기 때문에) Inetinfo.exe 자체의 상태와 dllhost.exe의 상태를 파악하는 것이다.


DLLHOST.EXE의 과부하와 시스템 문제.

DLLHOST.EXE는 COM(+) 서로게이트를 위한 파일이다. 서로게이트란 DLL을 사용할 수 있도록 베이스 프로세스를 형성해 주는 역할 정도로 이해하면 크게 다르지 않다.

서로게이트를 이용하는 까닭은, 동시에 여러 개의 웹 서버가 운영되는 시스템에서, 하나의 가상 서버에 과부하가 걸렸을 경우, 다른 웹 서버 시스템으로 이런 문제가 전이되지 않도록 하기 위해서이다.

일반적으로, 죽는 웹 서버들 중 일부는 DLLHOST의 과부하(CPU 50%이상 독점 점유)로 인해 발생되며, 이는 서로게이트되고 있는 웹 서비스를 중지한다고 해서 회복되지 않는다. 웹 서비스 전체를 다시 시작해도 그대로 CPU를 점유하는 경우가 많으며, 대부분의 해결책은 재부팅이다.

DLLHOST의 CPU 점유 원인은 생각보다 많다. 일반적으로 ASP Requests Queue 값과 정비례하는 수가 많은데, 이 역시 두 가지 부류로 나뉘게 된다. 가령 일시적 병목 현상이 걷잡을 수 없는 상황으로 발전하는 예를 들 수 있다. 즉, 어떤 이유에서 서버가 잠시 느려진 상황에, 처리용량보다 많은 요청이 들어와서, 큐잉이 생기는 경우 점점 처리용량은 줄어들고 큐잉은 길어지는 상황이 발생하는 것이다. 최악의 경우 Server Too Busy 메시지를 끝으로 죽어버린다. 그런데, 일반적인 경우는 프로그램 외적인 측면에서 찾는 것이 좋다.

최근 DLLHOST CPU 점유율을 가장 높인 일등공신은 웜이다. 웹 사이트 프로그램을 가장하여 해당 대역을 스캔하는 악성 코드 등이 CPU 점유율을 무지막지하게 높이는 수가 많으며, 주기적인 스캔 등으로 악성 코드의 잠입을 막는 방법을 생각해 볼 수 있다. 특히 주기적으로 시스템에 부하가 걸리거나, 네트워크 모니터 결과 ARP 신호가 엄청나게 증가하고 있으면 이는 필시 웜이 동작하는 것으로 볼 수 있다.

웜이 아니라면, DLLHOST CPU 점유율이 올라가는 까닭은 서버사이드의 과중한 프로그램 부하 때문일 수 있다. 가령, 암호화/복호화를 지속적으로 반복하거나, COM(+) 기반 서버 컴포넌트가 많은 경우 이런 문제를 생각해 볼 수 있다. 굳이 이 뿐만 아니라, ASP 등의 과중한 코드 등도 이러한 문제를 야기하게 된다. 이에 대한 해결책은 의외로 간단하다. 서버를 증설하면 된다. 혹은, 비즈니스 로직을 바꾸던가 프로그램을 재설계하는 것을 권한다.

만일 풀링이나 격리 모드로 시스템을 동작시킨다면 DLLHOST의 과부하는 원격에서도 탐지할 수 있다. HTML 파일의 응답 속도는 무척 빠르지만 ASP, ActiveX Server Component 의 호출은 무척 느려지는 현상이 발생하기 때문이다.


Posted by ToTb
2008. 9. 5. 23:14

튜닝 포인트라는 개념은, 실제로 웹 서버의 아주 특수한 부분에 의해 생긴 문제가 전체적으로 확대되어 발생하다는 것이다. 이것은 고속도로에서 흔히 보는 병목 현상과 마찬가지다. 도로 중간에서 사고가 났을 경우, 해당 도로는 마비되지만, 실제로 도로 전 구간이 마비된 것은 아니다.

이런 병목 지점에서 외부의 급격한 리퀘스트를 받게 될 때, 웹 서버는 자기가 처리할 수 있는 양 이상의 값을 받게 된다. 일단 시스템 구조상 이 값은 큐에 삽입된다. 그리고, 큐보다 많이 들어오는 값들은 튕겨 나간다. 이 튕겨 나갈때 나타나는 에러는 Server Too Busy 라는 메시지이다.

그런데, 이 병목 지점이 형성되는 것은 사용 상황과 보조 서버들의 상황, 혹은 여타 외부적인 변수를 매우 많이 받는다. 따라서, ASP Tuning 을 한다고 해서 무조건 커넥션 스트링의 건전성과 CreateObject 구문의 단속만 하는 것은 경우에 따라서 별다른 도움이 되지 못할 수 있다. 물론, CreateObject 구문을 단속하면, 개체의 효율성이 증가하기 때문에 다소간 빠르게 전체적인 처리를 할 수 있겠지만, 웹 서버가 죽을 정도의 병목을 해결할 만한 근원적인 해결책은 되지 못한다.

큐를 넘어서서 서버가 응답 불능으로 들어가는 것은, 보편적으로 입출력을 받는 프로그램이면 동일하게 나타나는 것이다. 가령, 포그라운드에 돌아가는 프로그램이 입력값을 받아 처리하는 것인데, 무작위로 계속 입력값을 주면 결국 프로그램이 이상하게 작동하는 가능성이 높아짐을 볼 수 있다. 멀티쓰레딩을 완벽하게 쓰는 프로그램들은 이런 문제로 인해 중단될 가능성이 더욱 높다.

따라서, 튜닝의 가장 근본적인 해결책은, 전체 프로그램의 플로우차트나 비즈니스 로직이 된다.

자 그렇다면 튜닝의 목적은 병목을 잡는 것이며, 병목은 큐가 쌓이는 지점이라고 이야기했다. 그리고 추가로 이야기하는 몇 가지 팁은 큐가 쌓이더라도 곧바로 시스템이 응답없음 상황으로 빠지는 것은 아니다. 시스템에 따라서 약간의 지연 효과가 발생되며(즉 큐를 처리하기 위해서 시스템이 좀 고생을 하며) 이후 시스템이 다운된다. 이런 메커니즘은 일반적인 OS와 시스템들이 모두 겪는 현상이기는 하지만, 이론적으로 왜 그런 문제들이 발생하는지 체계적으로 설명되지는 않는다.

여기서부터는 실제로 이론적 측면보다는 관리자의 노련함이 필요하다. 이단 관리자는 다음 중 무엇이 이러한 병목을 일으키는지 원인을 파악해 보아야 한다.

1. 정말 접속이 많아서
솔직히, 이 해결책은 돈 들이라는 신호라고 생각할 수밖에 없다.
하드웨어적 한계를 벗어난 경우가 많다. 특정 시점에만.
예를 들어, 밤 10시마다 이벤트를 하는 사이트를 생각할 수 있다.

2. 메모리 누수, 혹은 메모리 침범 에러
Component 의 문제이다. Third-Party Component등이 이 문제를 종종 발생하게 한다.

3. 데이터베이스 병목
쿼리하러 간 쓰레드들이 돌아오지 않을 때 발생한다.

4. 네트워크 병목
혼잡시간 때 DB로 들어가는 선을 끊어 보면 웹서버는 곧 다운된다.

5. 기타.
관계 컴포넌트들의 응답 없음 등이 문제가 될수도 있고, 악의적인 DDOS 공격일 수 있다. 만일 사이트가 죽는 원인이 사이트가 주기적으로 과도한 패킷에 의한 공격이라면, 이는 적절한 보안 대책을 수립하여야 한다. 해당 시간 전후로의 네트워크 모니터링으로 확인이 가능하다.

'Website 세상 > Web Program' 카테고리의 다른 글

ASP syntax  (0) 2008.09.05
ASP Tuning Point와 죽는 Web Server(3)  (0) 2008.09.05
ASP Tuning Point와 죽는 Web Server(1)  (0) 2008.09.05
서버기반의 스크립트 언어  (0) 2008.09.05
도대체 ASP는 왜 나왔을까욤?  (0) 2008.09.05
Posted by ToTb
2008. 9. 5. 23:13

Server.CreateObject 구문을 단속하지 않으면 대용량 시스템에 문제가 많다는 사실은 오래 전부터 있어 온 이야기이다. 그러나, 실제 저 문제에 의해 죽는 시스템은 생각보다 많지 않다.

웹 사이트 성능을 측정하면서 많은 부분 간과하는 것이 바로 CPU와 메모리에 지나칠 정도로 집착하는 것이다. 실제로 죽는 웹 서버는 메모리 소요량이 많아서도 아니며, CPU 점유율이 많아서도 아니다. 간혹 CPU 점유율이 많은 서버가 존재할 수는 있겠지만, 이는 데이터베이스 등에서 병목을 일으키는 예가 된다.

따라서, 죽는 웹 서버는 일반적으로 성능 모니터링을 하지 않아도 죽는 것을 감을 잡는다. 노련한 엔지니어라면, 시스템이 돌아가는 상황을 조금만 보더라도 "이 시스템은 언제쯤 죽을 것인지" 판단이 서기 마련이다. 사실상, 시스템 성능 모니터를 필자도 거의 이용해 보지 않았으며, 시스템 성능 모니터가 제시하는 값이 사실이라 믿기도 어렵다. 일단 성능을 재는 것은 불확정성의 원리를 따르기 때문이다. 물론, 아주 미션크리티컬한 서버를 특수한 환경에서 시스템 성능을 측정하여, 해당 값을 항상 유지하도록 관리자에게 권고하려면 그나마 좋은 방편일 수 있겠다.

성능상의 문제로 죽는 웹 서버는 일반적으로 다음과 같은 현상을 지닌다.

1. 평소에는 멀쩡하다.
2. 어느날 좀 이상하다.
3. 새벽에 갑자기 죽어버린다.

혹은,

1. 아침에는 멀쩡했다.
2. 그러다 갑자기 죽었다.
3. 재부팅하니 몇시간 간다.
4. 그러다 또 죽었다.
5. CPU/메모리 점유율은 거의 없다.
6. 혹시 해킹이 아닐까? 하고 생각하기 시작한다.
7. 죽는 시간을 대충 감을 잡는다.
8. 죽기 전 재빨리 재시작하는 프로그램을 짜고 입 씻는다.

와 같은 프로세스를 처리하는 것이 일반적이다.

자 그럼 성능 문제로 인해 죽는 웹 서버가 왜 발생할까? 왜 이 서버들이 어느 순간 갑자기 죽게 되는 것일까.

실제로, 이러한 서버군은 평소에 아무리 값을 잰다고 해도 재어지지 않는다. 그러면 이런 서버들을 어떻게 다루어야 할까? 지금부터 말하는 튜닝 포인트라는 개념이 여기서 등장하게 된다.

Posted by ToTb