haproxy的丰富特性简介

HaProxy系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.htmlphp


1.简介

官方站点:http://www.haproxy.orghtml

haproxy是一款负载均衡软件,它工做在7层模型上,能够分析数据包中的应用层协议,并按规则进行负载。一般这类7层负载工具也称为反向代理软件,nginx是另外一款著名的反向代理软件。前端

haproxy支持使用splice()系统调用,它能够将数据在两个套接字之间在内核空间直接使用管道进行传递,无需再在kernel buffer-->app buffer-->kernel之间来回复制数据,实现零复制转发(Zero-copy forwarding),还能够实现零复制启动(zero-starting)。haproxy默认对客户端的请求和对服务端的响应数据都开启了splice功能,它自身对数据状态进行判断,决定此数据是否启用splice()进行管道传递,这能极大提升性能。mysql

2.haproxy的特性(1):链接保持和链接关闭

先说明说明HTTP协议事务模型。nginx

http协议是事务驱动的,意味着每一个request产生且仅产生一个response。客户端发送请求时,将创建一个从客户端到服务端的TCP链接,客户端发送的每个request都通过此链接传送给服务端,而后服务端发出response报文。随后这个TCP链接将关闭,下一个request将从新打开一个tcp链接进行传送。git

[conn1][req1]......[resp1][close1][conn2][req2]......[resp2][close2]......

这种模式称为"http close"模式。这种模式下,有多少个http事务就有多少个链接,且每发出一个response就关闭一次tcp链接。这种状况下,客户端不知道response中body的长度。github

若是"http close"能够避免"tcp链接随response而关闭",那么它的性能就能够获得必定程度的提高,由于频繁创建和关闭tcp链接消耗的资源和时间是较大的。正则表达式

那么如何进行提高?在server端发送response时给出content-length的标记,让客户端知道还有多少内容没有接收到,没有接收完则tcp链接不关闭。这种模式称为"keep-alive"redis

[conn][req1]...[resp1][req2]...[resp2][close]

另外一种提高"http close"的方式是"pipelining"模式。它仍然使用"keep-alive"模式,可是客户端不须要等待收到服务端的response后才发送后续的request。这在请求一个含有大量图片的页面时颇有用。这种模式相似于累积报文数量成一批或完成后才一次性发送,能很好的提高性能。算法

[conn][req1][req2]...[resp1][resp2][close]...

不少http代理不支持pipelining,由于它们没法将response和相应的request在一个http协议中联系起来,而haproxy能够在pipelinign模式下对报文进行重组。

默认haproxy操做在keep-alive模式:对于每个tcp链接,它处理每个request和response,而且在发送response后链接两端都处于空闲状态一段时间,若是该链接的客户端发起新的request,则继续使用此链接。

haproxy支持5种链接模式:

  1. keep alive:分析并处理全部的request和response(默认),后端为静态或缓存服务器建议使用此模式
  2. tunnel:仅分析处理第一个request和response,剩余全部内容不进行任何分析直接转发。1.5版本以前此为默认,如今不建议设置为此模式。
  3. passive close:在请求和响应首部加上"connection:close"标记的tunnel,在处理完第一个request和response后尝试关闭两端链接。
  4. server close:处理完第一个response后关闭和server端的链接,但和客户端的链接仍然保持,后端为动态应用程序服务器组建议使用此模式
  5. forced close:传输完一个response后客户端和服务端都关闭链接。

3.haproxy的特性(2):会话保持

任何一个反向代理软件,都必须具有这个基本的功能。这主要针对后端是应用服务器的状况,若是后端是静态服务器或缓存服务器,无需实现会话保持,由于它们是"无状态"的。

若是反向代理的后端提供的是"有状态"的服务或协议时,必须保证请求过一次的客户端能被引导到同义服务端上。只有这样,服务端才能知道这个客户端是它曾经处理过的,能查到并获取到和该客户端对应的上下文环境(session上下文),有了这个session环境才能继续为该客户端提供后续的服务。

若是不太理解,简单举个例子。客户端A向服务端B请求将C商品加入它的帐户购物车,加入成功后,服务端B会在某个缓存中记录下客户端A和它的商品C,这个缓存的内容就是session上下文环境。而识别客户端的方式通常是设置session ID(如PHPSESSID、JSESSIONID),并将其做为cookie的内容交给客户端。客户端A再次请求的时候(好比将购物车中的商品下订单)只要携带这个cookie,服务端B就能够从中获取到session ID并找到属于客户端A的缓存内容,也就能够继续执行下订单部分的代码。

假如这时使用负载均衡软件对客户端的请求进行负载,若是这个负载软件只是简单地进行负载转发,就没法保证将客户端A引导到服务端B,可能会引导到服务端X、服务端Y,可是X、Y上并无缓存和客户端A对应的session内容,固然也没法为客户端A下订单。

所以,反向代理软件必须具有将客户端和服务端"绑定"的功能,也就是所谓的提供会话保持,让客户端A后续的请求必定转发到服务端B上。

3.1 源地址hash算法实现会话保持

做为负载均衡软件,通常都会提供一种称为"源地址hash"的调度算法,将客户端的IP地址结合后端服务器数量和权重作散列计算,每次客户端请求时都会进行一样的hash计算,这样同一客户端总能获得相同的hash值,也就能调度到同一个服务端上。

通常来讲,除非无路可选,都不该该选择相似源地址hash这样的算法。由于只要后端服务器的权重发生任何一点改变,全部源IP地址的hash值几乎都会改变,这是很是大的动荡。

做为反向代理软件,通常还提供一种cookie绑定的功能实现会话保持。反向代理软件为客户端A单独生成一个cookie1,或者直接修改应用服务器为客户端设置的cookie2,最后将cookie经过在响应报文中设置"Set-Cookie"字段响应给客户端。与此同时,反向代理软件会在内存中维持一张cookie表,这张表记录了cookie1或修改后的cookie2对应的服务端。只要客户端请求报文中的"Cookie"字段中携带了cookie1或cookie2属性的请求到达反向代理软件时,反向代理软件根据cookie表就能检索到对应的服务端是谁。

须要注意的是,客户端收到的cookie可能来源有两类:一类是反向代理软件增长的,这时客户端收到的响应报文中将至少有两个"Set-Cookie"字段,其中一个是反代软件的,其余是应用服务器设置的;一类是反向代理软件在应用服务器设置的Cookie基础上修改或增长属性。

例如,当配置haproxy插入cookie时,客户端从第一次请求到第二次请求被后端应用程序处理的过程大体以下图所示:

3.3 stick table实现会话粘性

haproxy还提供另外一种stick table功能实现会话粘性(stickiness)。这张stick-table表很是强大,它能够根据抽取客户端请求报文中的内容或者源IP地址或者抽取响应报文中的内容(例如应用服务器设置的Session ID这个cookie)做为这张表的key,将后端服务器的标识符ID做为key对应的value。只要客户端再次请求,haproxy就能对请求进行匹配(match),不管是源IP仍是cookie亦或是其它字符串做为key,总能匹配到对应的记录,并且匹配速度极快,再根据value转发给对应的后端服务器。

例如,下图是一张最简单的stick table示意图:

该stick-table存储的key是客户端的源IP地址,当客户端第一次请求到达haproxy后,haproxy根据调度算法为其分配一个后端appserver1,当请求转发到达后端后,haproxy当即在stick table表中创建一条ip1和appserver1的粘性(stickiness)记录。以后,不管是否使用cookie,haproxy都能为该客户端找到它对应的后端服务器。

stick table的强大远不止会话粘性。还能够根据须要定制要记录的计数器和速率统计器,例如在一个时间段内总共流入了多少个链接、平均每秒流入多少个链接、流入流出的字节数和平均速率、创建会话的数量和平均速率等。

更强大的是,stick table能够在"主主模型"下进行stick记录复制(replication),它不像session复制(copy),节点一多,不管是节点仍是网络带宽的压力都会暴增。haproxy每次推送的是stick table中的一条记录,而不是整表整表地复制,并且每条记录占用的空间很小(最小时每条记录50字节),使得即便在很是繁忙的状况下,在几十台haproxy节点之间复制都不会占用太多网络带宽。借助stick table的复制,能够完完整整地实现haproxy"主主模型",保证全部粘性信息都不会丢失,从而保证haproxy节点down掉也不会让客户端和对应的服务端失去联系。

3.4 session共享

不管反向代理软件实现的会话保持能力有多强,功能有多多,只要后端是应用服务器,就必定是"有状态"的。有状态对于某些业务逻辑来讲是必不可少的,但对架构的伸缩和高可用带来了不便。咱们没法在架构中随意添加新的代理节点,甚至没法随意添加新的应用服务器,高可用的时候还必须考虑状态或者某些缓存内容是否会丢失。

若是将全部应用服务器的session信息所有存储到一台服务器上(通常放在redis或数据库中)进行共享,每台应用服务器在须要获取上下文的时候从这台服务器上取,那么应用服务器在取session消息以前就是"无状态"的。

例如,下面是一个后端使用session共享的示意图:

使用session share后,调度器不管将请求调度到哪一个后端上,这个后端都能从session share服务器上获取到对应的session上下文。这样无状态的请求彻底能够被任意负载,负载软件无需记住后端服务器,从而达到四层负载的效果。若是没有特殊需求(如处理7层协议),这时可使用LVS替代haproxy,由于在负载性能上,LVS比haproxy高好几个级别。

session共享给架构带来的好处很是多,正如上面所说的,可使用LVS进行极其高效的负载(前提是没有LVS没法实现的需求),不管是负载节点仍是应用服务器节点均可以随意增删服务器。而惟一须要保证的就是session共享服务器的高可用。

4.haproxy特性(3):后端健康情况检查和被检查

任何一个负载均衡软件,都应该提供后端服务器健康情况检查的功能,即便自身没有,也必须可以借助其余第三方工具来实现。只有具有后端健康检查的功能,在后端某服务器down掉的时候,调度器才能将它从后端服务器组中踢出去,保证客户端的请求不会被调度到这台down掉的服务器上。

haproxy为多种协议类型提供了健康情况检查的功能,除了最基本的基于tcp的检查,据我从官方手册上根据关键词的统计,还为如下几种协议提供健康检查:

  1. HTTP
  2. ldap
  3. mysql
  4. pgsql
  5. redis
  6. SPOP
  7. smtp

若是haproxy没有指定基于哪一种协议进行检查,默认会使用tcp协议进行检查,这种检查的健康判断方式就是可否连上后端。例如:

backend static_group
    server staticsrv1 192.168.100.62:80 check rise 1 server staticsrv2 192.168.100.63:80 check rise 1

在server指令中的check设置的是是否开启健康检查功能,以及检查的时间间隔、判断多少次不健康后就认为后端下线了以及成功多少次后认为后端从新上线了。

若是要基于其它协议检查,须要使用协议对应的option指令显式指定要检查的对象。且前提是server中必须指定check,这是控制检查与否的开关。例如,基于http协议检查:

backend dynamic_group
    option httpchk     GET /index.php
    server appsrv1 192.168.100.60:80  check server appsrv2 192.168.100.61:80 check

对于基于http协议的检查,haproxy提供了多种判断健康与否的方式,能够经过返回状态码或拿状态码来进行正则匹配、经过判断响应体是否包含某个字符串或者对响应体进行正则匹配。

例如:

backend dynamic_group1
    option httpchk     GET /index.php
    http-check expect status 200 server appsrv1 192.168.100.60:80 check server appsrv2 192.168.100.61:80 check backend dynamic_group2 option httpchk GET /index.php http-check expect ! string error server appsrv1 192.168.100.60:80 check server appsrv2 192.168.100.61:80 check

上面两个后端组都指定了使用http协议进行检查,并分别使用http-check expect指定了要检查到状态码200、响应体中不包含字符串"error"才认为健康。若是不指定http-check expect指令,那么基于http协议检查的时候,只要状态码为2xx或3xx都认为是健康的。

haproxy除了具有检查后端的能力,还支持被检查,只须要使用monitor类的指令便可。所谓被检查,指的是haproxy能够指定一个检查本身的指标,本身获取检查结果,并将检查状态上报给它的前端或高可用软件,让它们很容易根据上报的结果(200或503状态码)判断haproxy是否健在。

如下是两个被检查的示例:

frontend www
    mode http
    monitor-uri /haproxy_test

frontend www
   mode http
   acl site_dead nbsrv(dynamic) lt 2
   acl site_dead nbsrv(static)  lt 2
   monitor-uri   /site_alive
   monitor fail  if site_dead

第一个示例中,"/haproxy_test"是它的前端指定要检查的路径,此处haproxy对该uri路径进行监控,当该路径正常时,haproxy会告诉前段"HTTP/1.0 200 OK",当不正常时,将"HTTP/1.0 503 Service unavailable"。

第二个示例中,不只监控了"/site_alive",还监控了后端健康节点的数量。当dynamic或static后端组的健康节点数量少于2时,haproxy当即主动告诉前端"HTTP/1.0 503 Service unavailable",不然返回给前端"HTTP/1.0 200 OK"。

5.haproxy的特性(4):处理请求和响应报文

一个合格的反向代理软件,必须可以处理流入的请求报文和流出的响应报文。具有这些能力后,不只能够按照需求改造报文,还能筛选报文,防止被恶意攻击。

haproxy提供了不少处理请求、响应报文的功能性指令,还有一些所谓"函数"。

大多数处理请求报文的函数都以"req"或"capture.req."开头,处理响应报文的函数都以"res."或"capture.res."开头,这样的函数很是多,几乎能够实现任何想达到的功能。完整的指令集见官方手册:https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#7.3.2

如下是几个比较具备表明性的函数或指令:

capture request header:捕获请求报文。  
capture response header:捕获响应报文。  
reqadd:在请求首部添加字段。  
rspadd:在响应首部添加字段。  
req.cook(name):获取Cookie字段中的name属性的值。  
res.cook(name):获取"Set-Cookie"字段中name属性的值。  
。。。。。。

以上函数都是对7层协议进行处理。除此以外,haproxy还有很是多的函数能够分别处理4层、5层、6层协议。

6.haproxy的特性(5):状态查看

做为反向代理,必须具有查看自身和后端服务器的状态信息。

haproxy提供了多种获取状态信息的方法:

  1. 使用stats enable指令启用状态报告功能,这样就能够在浏览器中输入特定的url访问状态信息。
  2. 提供了不少对前段状态和后端节点状态取样调查的函数。例如某指定后端或全部后端有多少个节点存活、某后端或全部后端已创建多少链接、后端还有多少链接槽位能够继续提供链接、前段创建了多少链接等等。指令集合参见官方手册:https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#7.3.2
  3. 提供了套接字状态查看、管理功能。也许不少人都不知道,默认配置文件中的stat socket指令是干嘛用的,其实这就是为系统管理员提供的接口。
    global
     log         127.0.0.1 local2
     chroot      /var/lib/haproxy
     pidfile     /var/run/haproxy.pid
     maxconn     2000
     user        haproxy
     group       haproxy
     daemon
     stats socket /var/lib/haproxy/stats

咱们安装"socat"包(socket cat,在epel源提供该包)后,就能够经过socat命令来查看/var/lib/haproxy/stats这个状态套接字。例如,执行下面的命令能够获取到全部可执行的命令。

echo "help" | socat unix:/var/lib/haproxy/stats -

例如,其中一条命令是"show backend"用来列出全部的backend,能够这样使用:

echo "show backend" | socat unix:/var/lib/haproxy/stats -

或者也能够进入交互式操做模式:

socat readline unix:/var/lib/haproxy/stats

7.haproxy的特性(6):ACL

能够说,支持ACL的软件都是好软件,好比haproxy、varnish。

ACL本意是access control list(访问控制列表),用来定义一组黑名单或白名单。但显然,它毫不仅仅是为了黑白名单而存在的,有了ACL,能够随意按条件定制一组或多组列表。ACL存在的意义,就像是正则表达式存在的意义同样,极大程度上简化了软件在管理上的复杂度。

在haproxy中,只要能在逻辑意义上进行分组的,几乎均可以使用ACL来定制。好比哪些IP属于A组,后端哪些节点是静态组,后端节点少于几个时属于dead状态等等。

8.haproxy的特性(7):链接重用功能

haproxy支持后端链接重用的功能。

在默认状况下(不使用链接重用),当某客户端的请求到来后,haproxy为了将请求转发给后端,会和后端某服务器创建一个TCP链接,并将请求调度到该服务器上,该客户端后续的请求也会经过该TCP链接转发给后端(假设没有采用关闭后端链接的http事务模型)。但在响应后和该客户端的下一个请求到来前,这个链接是空闲的。

其实仔细想一想,和后端创建的TCP链接仅仅只是为了调度转发,免去后续再次创建tcp链接的消耗。彻底能够为其它客户端的请求调度也使用这个TCP链接,保证TCP链接资源不浪费。可使用http-reuse strategy_name指令设置链接重用的策略,而默认策略禁用链接重用。

该指令有4个值:

  • (1).never:这是默认设置。表示禁用链接重用,由于老版本的haproxy认为来源不一样的请求不该该共享同一个后端链接。
  • (2).safe:这是建议使用的策略。"安全"策略下,haproxy为客户端的每一个第一个请求都单独创建一个和后端的TCP链接,可是后续的请求则会重用和该后端的空闲TCP链接。这样的转发不只提升了资源使用率,还保持了keep-alive的功能。所以,safe策略配合http-keep-alive事务模式比http-server-close事务模式更高效,不管后端是静态、缓存仍是动态应用服务器。
  • (3).aggressive:一种激进的策略,该策略的haproxy会重用空闲TCP链接来转发大多数客户端的第一次请求。之因此是大多数而不是全部,是由于haproxy会挑选那些已经被重用过至少一次的链接(即从创建开始转发过至少两次,无论源是不是同一客户端)进行重用,由于haproxy认为只有这样的链接才具备重用能力。
  • (4).always:它将老是为第一个请求重用空闲链接。当后端是缓存服务器时,这种策略比safe策略的性能要高许多,由于这样的请求行为都是同样的,且能够共享同一链接来获取资源。不过不建议使用这种策略,由于大多数状况下,它和aggressive的性能是同样的,可是却带来了不少风险。

所以,为了性能的提高,将它设置为safe或aggressive吧,同时再将http事务模型设置为http-keep-alive,以避免后端链接在响应后当即被关闭。

相关文章
相关标签/搜索