Network

[gsoap] 웹서버 오버헤드를 줄이기 위한 gsoap 버퍼링 구현

behonestar 2015. 3. 5. 20:17

증상
HTTPS 사용 시 ONVIF 응답 지연 발생


원인

[gsoap] gsoap은 하나의 Response를 위해 여러 번의 send를 한다.

soap_send_raw(soap, "<", 1);
soap_send_raw(soap, "SOAP-ENV:Envelope", ...);
soap_send_raw(soap, ">", 1);
...


[lighttpd] gsoap으로부터 recv한 패킷들을 각각 SSL_write → 오버헤드로 인한 전송 지연

[963760976.648370]: ssl write begin
[963760976.649320]: SSL_write
[963760976.650925]: SSL_write
[963760976.652421]: SSL_write
[963760976.653860]: SSL_write
[963760976.655395]: SSL_write
[963760976.657133]: SSL_write
......
[963760977.318255]: SSL_write
[963760977.320038]: SSL_write
[963760977.321517]: SSL_write
[963760977.324003]: ssl write end // (676ms 소요)


대책

1. gsoap 초기화 시 SOAP_IO_BUFFER 옵션 적용

-# define SOAP_IO_DEFAULT               (SOAP_IO_FLUSH | SOAP_XML_DEFAULTNS)

+# define SOAP_IO_DEFAULT               (SOAP_IO_BUFFER | SOAP_XML_DEFAULTNS)


2. [gsoap] Response 문자열을 버퍼링하여 한 번에 send

/* stdsoap2.c */

...

#ifndef PALM_1
SOAP_FMAC1
int
SOAP_FMAC2
#ifdef KHB_FEATURE_SOAP_BUFFER
soap_send_raw_(struct soap *soap, const char *s, size_t n)
#else
soap_send_raw(struct soap *soap, const char *s, size_t n)
#endif
{ if (!n)
    return SOAP_OK;
#ifndef WITH_LEANER
  if (soap->fpreparesend && (soap->mode & SOAP_IO) != SOAP_IO_STORE && (soap->mode & SOAP_IO_LENGTH) && (soap->error = soap->fpreparesend(soap, s, n)))
    return soap->error;
  if (soap->ffiltersend && (soap->error = soap->ffiltersend(soap, &s, &n)))
    return soap->error;
#endif
  if (soap->mode & SOAP_IO_LENGTH)
    soap->count += n;
  else if (soap->mode & SOAP_IO)
  { register size_t i = SOAP_BUFLEN - soap->bufidx;
    while (n >= i)
    { memcpy(soap->buf + soap->bufidx, s, i);
      soap->bufidx = SOAP_BUFLEN;
      if (soap_flush(soap))
        return soap->error;
      s += i;
      n -= i;
      i = SOAP_BUFLEN;
    }
    memcpy(soap->buf + soap->bufidx, s, n);
    soap->bufidx += n;
  }
  else
    return soap_flush_raw(soap, s, n);
  return SOAP_OK;
}

#ifdef KHB_FEATURE_SOAP_BUFFER
struct _gsoap
{ char buf[SOAP_BUFLEN];
  char *pbuf;
  size_t bufidx;
};
static struct _gsoap gsoap;

SOAP_FMAC1
int
SOAP_FMAC2
soap_send_final(struct soap *soap)
{
  int r = soap_send_raw_(soap, gsoap.buf, gsoap.bufidx);
  memset(&gsoap, 0, sizeof(gsoap));
  gsoap.pbuf = gsoap.buf;
  return r;
}

SOAP_FMAC1
int
SOAP_FMAC2
soap_send_raw(struct soap *soap, const char *s, size_t n)
{ static int init = 0;
  if (!init)
  { memset(&gsoap, 0, sizeof(gsoap));
    gsoap.pbuf = gsoap.buf;
    init = 1;
  }
  if (!n)
    return SOAP_OK;
  if(gsoap.bufidx + n >= SOAP_BUFLEN)
  { int r = soap_send_final(soap);
    if(r != SOAP_OK)
    return r;
  }   
  memcpy(gsoap.pbuf, s, n);
  gsoap.pbuf += n;
  gsoap.bufidx += n;
  return SOAP_OK;
}
#endif
#endif


...

#ifndef PALM_2
SOAP_FMAC1
int
SOAP_FMAC2
soap_envelope_end_out(struct soap *soap)
{ if (soap_element_end_out(soap, "SOAP-ENV:Envelope")
   || soap_send_raw(soap, "\r\n", 2))    /* 2.8: always emit \r\n */
    return soap->error;
#ifdef KHB_FEATURE_SOAP_BUFFER
  if (soap_send_final(soap))
    return soap->error;  
#endif
#ifndef WITH_LEANER
  if ((soap->mode & SOAP_IO_LENGTH) && (soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM))
  { soap->dime.size = soap->count - soap->dime.size;    /* DIME in MIME correction */
    sprintf(soap->id, soap->dime_id_format, 0);
    soap->dime.id = soap->id;
    if (soap->local_namespaces)
    { if (soap->local_namespaces[0].out)
        soap->dime.type = (char*)soap->local_namespaces[0].out;
      else
        soap->dime.type = (char*)soap->local_namespaces[0].ns;
    }
    soap->dime.options = NULL;
    soap->dime.flags = SOAP_DIME_MB | SOAP_DIME_ABSURI;
    if (!soap->dime.first)
      soap->dime.flags |= SOAP_DIME_ME;
    soap->count += 12 + ((strlen(soap->dime.id)+3)&(~3)) + (soap->dime.type ? ((strlen(soap->dime.type)+3)&(~3)) : 0);
  }
  if ((soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM))
    return soap_send_raw(soap, SOAP_STR_PADDING, -(long)soap->dime.size&3);
#endif
  soap->part = SOAP_END_ENVELOPE;
  return SOAP_OK;
}
#endif


결과

[963786468.956911]: ssl write begin
[963786468.957803]: SSL_write // contents-length를 위한 Response
[963786468.964595]: SSL_write // xml response
[963786468.969947]: ssl write end // (13ms 소요)


'Network' 카테고리의 다른 글

tcp_tw_reuse와 SO_REUSEADDR의 차이  (0) 2017.02.08
OAuth2.0  (0) 2017.01.19
HTTP Digest 인증과 토큰 기반 인증  (0) 2015.05.10
[TCP] window size full  (0) 2015.03.18
[gsoap] recv, send 함수 흐름  (0) 2015.02.27