[질문] preforked server에서, accept 사용시 blocking문제...

decay의 이미지

안녕하세요. 처음 글을 쓰게 됩니다.

accept 주위에 file lock을 사용하는 preforked server를 운용중에 있습니다.

client는 java applet으로 작성되어 있구요.. 서비스 하는데 별 문제는 없는데,

가끔 client에서 NoRouteHostException 이 발생해서 원인을 파악하던중 아

래와 같은 의문이 들었습니다. (네트웍이 불안해서라고 생각했지만, 상사가 문제
를 해결해라라고 해서, 서버 소스를 뒤지고 있습니다. -_-);

먼저 서버 소스의 일부분 입니다.
(server child process는 여러개가 생성되어 있습니다. 각 차일드마다 아래처럼 client의 접속을 기다리고 있습니다.)

        /*lock을 이용하여 대기한다.*/
        g_sc_lock_wait(fd);

        /*client의 접근을 기다린다.*/
        datafd = accept(dsock, (struct sockaddr *)&client, &cli_len);

        /*lock을 해제해 준다*/
        g_sc_lock_release(fd);

java applet이 위의 서버로 connect를 할 경우 accept에 문제가 발생하면(네

트웍 상황이 좋지 않다거나, 여타의 이유로..) accept()함수가 바로 리턴하지 못

하는 상황이 발생할 수 있지 않습니까? 제 생각에는, accept() 함수가 바로 리

턴되지 않고 몇 초간의 딜레이가 발생한다면 lock을 release하지 못하게 되고,

다른 클라이언트들도 서버에 접속하는데 딜레이가 발생하여서 서비스를 원할하

게 할 수 없을 것 같습니다. 혹시 이것이 java client의 NoRouteException과

관련이 있지나 않은지.. 혹은 제 추측이 맞다면 문제를 해결할 수 있는 방안에는

어떤 방법들이 있는지 고견을 듣고 싶습니다. :?

ssehoony의 이미지

추측하신 이유로 그런 에러가 나는건지 자바경험이 적어서 잘 모르겠습니다.
하지만 일단 그 accept 에서 락문제로 다른 클라이언트에서 오래 기다리게 되는 문제의 해결 법은 서버에서 소켓을 block 모드로 하고 accept 위, 아래에 있는 lock 을 제거 하시면 됩니다.
block 모드일때 accept 는 자동으로 accept 할 client 가 있을 때까지 계속 대기 하고 있다가 accept 되면 반환되니깐 별도의 lock 시스템을 만들 필요는 없습니다.
별도의 락을 제거 하게 되면 기존의 accept 를 하나의 프로세스만이 했던것을 동시에 여러 프로세스가 accept 를 하게 되죠.

헌데 이런 방법에 약간의 문제점이 있긴합니다.
저의 경우 레드햇 2.4 커널을 사용하는데요.
만약 prefork 된 프로세스가 10개가 있고 모두 accept 상태로 접속을 기다리고 있는데
짧은 시간에 접속 요청이 5개가 (거의 동시에) 발생하면 프로세스 다섯개에 각각 하나씩 분배되지 않고 한 프로세스에 몰리는 현상이 발생하더군요.
이럴 경우 가장 먼저 사용자를 제외하고 다른 사용자의 해당 프로세스의 back log 에 대기상태로 있게되서 back log 의 뒷쪽에 들어간 client 의 경우 timeout 이 발생하는 경우도 있더군요.
하지만 이런 스케줄링은 커널에서 하기때문에,
prefork 에서 block 모드로 accept 를 하고 있을 땐 이걸 어떻게 해결할 방법을 찾지 못했습니다.
하지만 웹 처럼 동시에 많이 요청하는 경우 문제가 되지만,
ftp 나 telnet 처럼 연결 지향적인 서버라면 이런 방식을 이용하시면 간단히 해결 될 듯 하군요.

저의 경우 업그레이드때 fork 에서 쓰레드로 변경하고,
accept 는 한녀석만 하고 연결되면 응답은 쓰레드로 넘기는 방식으로 구현을 했습니다.

허허.. 이거 당직이라 심심해서 답변을 몇개 썼는데 모두 길게 달았군요. ^^
도움이 되셨길...

indie의 이미지

소켓을 논블럭 모드로 만드시고 accept()
이전에 select나 epoll로 이벤트가 발생할때까지 wait하게 하면 어떨까요..
그리고 select나 epoll이전에 lock을 걸고 accept후에 바로 lock을 풀면
한쪽 프로세스에서 accept이후의 처리를 하는동안 다른 프로세스에서는
lock에서 대기하면서 계속적으로 클라이언트의 접속을 받아들일 수 있을거 같은데요...

집에나 갈까?

ssehoony의 이미지

indie 님께서 말씀하신 방법은 많은 사람들이 생각하는 방법이지만
thundering herd problem 이라고 불리는 문제가 있습니다.
이게 뭔고 하니, 10 개의 프로세스가 select 에서 대기중에 있다가 접속 요청이 들어오면 10개의 프로세스가 모두 깨어서나 lock 부분에서 하나의 프로세스만 통과하고 나머지는 불필요하게 깨어나는 비효율성이 발생합니다.

그리고 처음 질문하신분이 말씀하셨듯이 lock 을 너무 늦게 풀어서 다른 대기 client 에서 문제가 생기는 것 같다고 하셨으니 indie 님 말씀 처럼 구현해서는 이 문제가 해결 되지 않을 듯 하군요.

indie의 이미지

제가 얘기한 방법에서는
lock은 select이전에 걸기 때문에 모든 자식들이 다 깨어날 수가 없습니다.
오로지 하나의 프로세스만 select에서 대기하게 됩니다.
그리고 바로 accept를 호출하고 논블록이기 때문에 delay없이 바로 리턴할거라 생각합니다.
그리고 바로 lock을 풀기때문에 하나의 클라이언트가 접속을 하는동안에 다른
클라이언트의 접속에 문제가 있다고는 보지 않습니다.
단지 서버가 루프를 돌면서 일을 처리해야 하는 구조 라면 문제가 있다고 봅니다.

집에나 갈까?

케인의 이미지

devilhero wrote:
indie 님께서 말씀하신 방법은 많은 사람들이 생각하는 방법이지만
thundering herd problem 이라고 불리는 문제가 있습니다.
이게 뭔고 하니, 10 개의 프로세스가 select 에서 대기중에 있다가 접속 요청이 들어오면 10개의 프로세스가 모두 깨어서나 lock 부분에서 하나의 프로세스만 통과하고 나머지는 불필요하게 깨어나는 비효율성이 발생합니다.

그리고 처음 질문하신분이 말씀하셨듯이 lock 을 너무 늦게 풀어서 다른 대기 client 에서 문제가 생기는 것 같다고 하셨으니 indie 님 말씀 처럼 구현해서는 이 문제가 해결 되지 않을 듯 하군요.

thundering herd problem은 이제 없는 것으로 알고 있습니다만... ^^;; (Linux 2.2.9까지 있던 문제라고 하더군요.)

http://www.citi.umich.edu/projects/linux-scalability/reports/accept.html

FreeBSD 5.x에서 보면 수백개의 아파치 프로세스가 모두 accept 하고 있습니다. 예전에는 하나만 select 하고 나머지는 모두 lock 하고 있었죠. (아파치 코드는 두 가지 모두 가능하도록 되어있습니다. 시스템에 따라서 선택할 수 있지요.)

decay의 이미지

답변 감사합니다.

일단, 서버의 다른 process가 lock을 획득하지 못했기 때문에 client에서 NoRouteHostException 이 발생하는지 테스트 하기 위해서 ,thundering herd problem 를 감수하고 lock부분을 빼 봤습니다.
여전히 NoRouteHostException이 발생하였습니다.

디버깅 하면서 특이한 점이 발견되었는데요.. 일단 아래 코드에서..

        /*lock을 이용하여 대기한다.*/
        /* g_sc_lock_wait(fd); */

        /*client의 접근을 기다린다.*/
        datafd = accept(dsock, (struct sockaddr *)&client, &cli_len);

        /*lock을 해제해 준다*/
        /*g_sc_lock_release(fd); */

        if(datafd< 0)
              printf("accept error \n");
        else
              printf("socket accepted......\n");

client 의 connect 에서는 NoRouteHostException이 발생하였지만, 서버쪽에서는 "socket accepted"라는 메세지가 출력이 되는군요..

위의 결과로 봤을때는 서버쪽에서는 TCP three-way handshake가 정상적으로 수행되었다는 말이고, 클라이언트는 성공적으로 수행하지 못했다는 이야기 인것 같은데, Java Socket쪽에 버그가 있는게 아닌지 생각해 봅니다. ㅠ.ㅠ 도대체 어떻게 결론을 내려야 되는지 원....

일단은 indie님께서 지적하신 데로 nonblock 으로 accept를 해서, lock 문제는 해결해야 겠습니다...

참.. 사용중인 서버의 OS는 HP-UX 11.10 입니다.

ssehoony의 이미지

제가 말씀 드린 thundering herd problem 은 accept 함수 말고 select 함수를 동시에 여러 프로세스가 동일한 fd에 대해 하고 있을 때를 말씀드린거였습니다.

그리고 indie 님께서 말씀하신건 select 이전에 lock 를 거는 것이었군요.
전에 select 이후에 lock 을 거는 걸로 이해했습니다. 죄송합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.