いわむぶろぐ

Webエンジニア@スタートアップ@のんびり綴ってます。

TCPコネクションを切断するstubを作成して、Nginx configの検証を行う

f:id:kohei_iwamura:20180217195023j:plain

状況

  • Nginxでserverにproxyして通信する際、 一定確率でNginxとserverでTCPコネクションが確立されず、NginxがHTTP_statuscode502を返す事象が発生していた。

今回行ったこと

  • TCPコネクションを切断するstubを作成して再現させ、対処法を考えた

検証

stub_server

nginx config

# 自身のserverとproxyするため、private_ipを設定
# max_fails=0で、Nginx側からはコネクションを張ったままにする
upstream stub {
    server 172.31.27.174:8000 max_fails=0;
    server 172.31.27.174:8000 max_fails=0;
    server 172.31.27.174:8000 max_fails=0;
}

server {
    listen  80;
    server_name 172.31.27.174;
    charset     utf-8;

    # proxyの設定
    location / {
      proxy_pass http://stub/;
      # HTTP_statu_code500の場合のみ、次のserverとproxyするように設定
      proxy_next_upstream http_500;
    }
}

stub program

  • stubの処理を3種類にわけ、randomで処理を行うようにした
    • HTTP_response(status_code=200)を返す
    • HTTP_response(status_code=500)を返す
    • TCPコネクション切断。
# -*- coding:utf-8 -*-
import socket
import random

STUB_PORT = 8000

print "waiting now"
# TCPで通信を行うためのsocketを作成する
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# hostsとportを設定してbindする
serversock.bind(('', STUB_PORT))
# 接続の待ち受けをする(キューの最大数を指定)
serversock.listen(100)
while True:
    # #接続された時にデータを格納する
    soc, addr = serversock.accept()
    # クライアントからのリクエストを受信する(50000byteまで。適当に設定した)
    soc.recv(50000)
    method = random.choice(["200","500","error"])
    if method == "200":
        # HTTP_responseを追加し、返している
        soc.send("HTTP/1.1 200 OK\r\nCache-Control: no-cache, private\r\nContent-Length: 0\r\nDate: Fri, 17 Feb 2018 10:21:21 GMT\r\n\r\n")
    elif method == "500":
        # HTTP_responseを追加し、返している
        soc.send("HTTP/1.1 500 InternalServerError\r\nCache-Control: no-cache, private\r\nContent-Length: 0\r\nDate: Fri, 17 Feb 2018 10:21:21 GMT\r\n\r\n")
    else:
        # コネクションを切断する
        soc.close()

response集計用script

import requests

status_code200 = 0
status_code500 = 0
error_response = 0
for i in range(100):
    r = requests.get('http://54.250.242.76/stub/')

    if r.status_code == 200:
        status_code200 += 1
    elif r.status_code == 500:
        status_code500 += 1
    else:
        error_response += 1


print "status_code200 : " + str(status_code200)
print "status_code500 : " + str(status_code500)
print "error_response : " + str(error_response)

集計結果

status_code200 : 48 # <--そのまま返すので、clientに返される割合は多くなる
status_code500 : 4 # <-- 一定回数retryするので、clientに返される割合は少なくなる
error_response : 48 # <--そのままresponseするので、clientに返される割合は多くなる

↓POINT↓

改修方法(nginx)

  • proxy_next_upstream に errorを追加する
    • TCPコネクションでerrorが起こってもretryしコネクションを確立させようとする
upstream stub {
    server 172.31.27.174:8000 max_fails=0;
    server 172.31.27.174:8000 max_fails=0;
    server 172.31.27.174:8000 max_fails=0;
}

server {
    listen  80;
    server_name 172.31.27.174;
    charset     utf-8;

    location / {
      proxy_pass http://stub/;
      proxy_next_upstream http_500 error;
    }
}

改修後の集計結果

status_code200 : 69 # <--そのまま返すので、clientに返される割合は多くなる
status_code500 : 16 # <-- 一定回数retryするので、clientに返される割合は少なくなる
error_response : 15 # <-- 一定回数retryするので、clientに返される割合は少なくなる