一、问题

haproxy的server health check默认方法是尝试与backend服务器建立一个TCP连接。

如果指定了"option

httpchk"参数的话,则在TCP连接以后会发送给backend服务器一个HTTP请求。

如果backend返回2xx或3xx的响应,则认为backend服务器活着。

如果返回其他响应码则认为backend已经失败,从而会从负载中摘除。

但是对于nginx+php-fpm、nginx+tomcat这类的应用,haproxy默认的方法就不能很好的满足需求。

因为nginx负责静态内容部分,php-fpm或tomcat则负责动态部分(业务逻辑)。

当php-fpm或tomcat出现问题的时候,如果nginx是正常的(绝大部分时候都是如此),那么简单的HTTP请求会被nginx处理。所以haproxy认为backend一直是正常的,会持续分配连接给它,但事实上该backend已经不能正常服务了。

这种场景下我们希望通过判断backend上的php-fpm或tomcat的状态来决定是否将其从负载中摘除。

二、"option httpchk"

在haproxy的官方文档中是这样描述"option httpchk"
option httpchk method uri version
methodis
the optional HTTP method used with the requests. When not
set,
the "OPTIONS" method is used,
as it generally requires low server
processing and is easy to
filter out from the logs. Any method
may be used, though it is not
recommended to invent non-standard
ones.
uriis the URI referenced in the HTTP
requests. It defaults to " / "
which is accessible by
default on almost any server, but may be
changed to any other URI.
Query strings are permitted.
versionis the optional HTTP version string. It
defaults to "HTTP/1.0"
but some servers might behave
incorrectly in HTTP 1.0, so turning
it to HTTP/1.1 may sometimes
help. Note that the Host field is
mandatory in HTTP/1.1, and as
a trick, it is possible to pass it
after "\r\n" following the
version string.

其中的默认方法是"OPTIONS",默认URL是"/",默认Version是"HTTP/1.0"

作为后端检测,我们希望尽量少的影响backend上的正常服务,且传输数据量越少越好。

所以我们选择的尽量选择OPTIONS,GET,HEAD这三种。

我们来补充一下HTTP方法的相关背景知识:

按照RFC2616的排序,HTTP的方法包括:OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE

OPTIONS

使用该方法来获取资源支持的HTTP方法列表,或者ping服务器。

请求:只有header没有body。

响应:默认只有header,但是也可以在body中添加内容,比如描述性文字

示例:

# 测试对应资源所支持的方法

OPTIONS /test-options

HTTP/1.1

Host: localhost

# 响应

HTTP/1.1 204 No

Content

Allow: GET, POST,

OPTIONS

# ping服务器

OPTIONS * HTTP/1.1

Host: localhost

# 响应

HTTP/1.1 204 No

Content

安全:是

幂等:是

显然,它就是一个纯粹的信息读取的操作,不改变资源的状态,同时保证幂等性。

GET

该方法用以获取资源的表述。

请求:只有header,没有body。

响应:对应请求URI的资源表述,通常带有body。

响应header中的Content-Type,Content-Length,Content-Language,Last-Modified,ETag等应该和响应body的表述一致。

# 请求

GET /hello

HTTP/1.1

Host: localhost

# 响应

HTTP/1.1 200 OK

Content-Type: application/xml;

charset=UTF-8

Content-Length: 21

#

此处为空行,由于高亮插件原因,特此注明

tester

安全:是

幂等:是

简单的资源信息读取,不对资源状态造成影响,保证幂等性。

HEAD

使用该方法可以获取与GET响应相同的header,但是响应中没有任何body。

请求:只有header,没有body。

响应:只有header,没有body。服务器不能添加body。

# 请求

GET /hello
HTTP/1.1
Host: localhost
# 响应
HTTP/1.1 200 OK
Content-Type: application/xml;
charset=UTF-8
Content-Length: 21

安全:是

幂等:是

简单的资源信息读取,不对资源状态造成影响,保证幂等性。

三、具体实现

3.1 在backend的nginx配置中加入health check的URL

location = /health_check.php {
access_log off;
fastcgi_pass
127.0.0.1:9000;
fastcgi_index
index.php;
include
/etc/nginx/fastcgi_params;
}

3.2

在backend编写health_check.php(或.jsp),只需要简单的一个输出即可

# vi health_check.php
#< ?php echo "I'm healthy"; ? >
# vi
health_check.jsp
#< %out.print("I'm healthy");% >
3.3 在haproxy上配置option httpchk
backend appservers
mode http
option httpchk HEAD
/health_check.php HTTP/1.1\r\nHost:\ example.com
server web1 x.x.x.x:80
weight 5 check inter 2000
server web2 x.x.x.x:80
weight 5 check inter 2000
server web3 x.x.x.x:80
weight 5 check inter 2000
我们来解释一下这一行:
option httpchk HEAD /health_check.php HTTP/1.1\r\nHost:\
example.com
使用"HEAD"方法发送HTTP请求
"URI"是/health_check.php
"HTTP

version"使用HTTP/1.1,并且设置了主机头"example.com",这是HTTP/1.1强制要求的

这样就可以实现我们的目的啦!

参考文档:

http://iamseanmurphy.com/a-better-haproxy-health-check-for-dynamic-websites/

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

https://serversforhackers.com/load-balancing-with-haproxy

http://blog.lucode.net/protocol/http-method-tutorial.html