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


在上一篇文章中,分析了haproxy的stick table特性和用法,其中特性之一也是很实用的特性是stick table支持在haproxy多个节点之间进行复制(replication)。

本文仅讨论如何配置实现stick table的复制功能,不考虑在什么环境下实现它,以及它的双主模型如何配置。

本文实验环境:

1.stick table复制特性

只要设置好haproxy的节点组,haproxy就会自动将新插入的、刚更新的stickiness记录通过TCP连接推送到节点组中的非本地节点上。这样一来,stickiness记录不会丢失,即使某haproxy节点出现了故障,其他节点也能将客户端按照粘性映射关系引导到正确的后端服务器上。

由于每条stickiness记录占用空间都很小(平均最小50字节,最大166字节,由是否记录额外统计数据以及记录多少来决定占用空间大小),使得即使在非常繁忙的环境下多个节点之间推送都不会出现压力瓶颈和网络阻塞(可以按节点数量、stickiness记录的大小和平均并发量来计算每秒在网络间推送的数据流量)。

它不像被人诟病的session复制(copy),因为session复制的数据量比较大,而且是在各应用程序服务器之间进行的。而一个稍大一点的核心应用,提供服务的应用程序服务器数量都不会小,这样复制起来很容出现网络阻塞。关于stick table的复制效率,可看文章结尾的测试

此外,stick table还可以在haproxy重启时,在新旧两个进程间进行复制,这是本地复制。当haproxy重启时,旧haproxy进程会和新haproxy进程建立TCP连接,将其维护的stick table推送给新进程。这样新进程不会丢失粘性信息,和其他节点也能最大程度地保持同步,使得其他节点只需要推送该节点重启过程中新增加的stickiness记录就能完全保持同步。

如果后端使用了session共享,在大多数情况下没必要在代理层实现会话保持。如果此时使用stick table,一般只是为了收集统计数据进行采样调查,但这样的状态统计数据无需在各节点之间进行复制。

2.如何定义haproxy节点成员

haproxy提供了peers指令,用于定义节点组,peer指令用于定义节点组中的成员。

例如,定义本文测试环境的节点组:

peers my_peers    # 节点组名
peer haproxy1 192.168.100.59:12138 # 定义对端名称,以及和对端建立tcp连接的端口,用于推送stickiness记录
peer haproxy2 192.168.100.54:12138
peer haproxy3 192.168.100.42:12138

然后在stick-table指令中引用节点组。例如:

stick-table type ip size 100k expire 5m peers my_peers

注意,应当让每个haproxy节点的节点组内容一致,然后使用haproxy命令的"-L"选项指定本地节点成员,这样方便管理每个节点和本地节点。

haproxy -D -L haproxy1 -f /etc/haproxy/haproxy.cfg

如果不指定"-L",则在解析配置文件时将默认以主机名作为本地节点名进行解析,但很可能它不会定义在peers中,因此会报错。

因此,如果要实现stick table的复制,还想使用sysV或systemctl管理haproxy服务,需要修改这些服务管理脚本,在启动、重启和语法检查项上加上"-L"选项。例如,systemd的haproxy服务管理脚本改为如下内容:

[root@xuexi ~]# cat /usr/lib/systemd/system/haproxy.service
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target [Service]
EnvironmentFile=/etc/sysconfig/haproxy
ExecStartPre=/usr/sbin/haproxy -L haproxy2 -f /etc/haproxy/haproxy.cfg -c -q
ExecStart=/usr/sbin/haproxy-systemd-wrapper -L haproxy2 -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid $OPTIONS
ExecReload=/usr/sbin/haproxy -L haproxy2 -f /etc/haproxy/haproxy.cfg -c -q
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed [Install]
WantedBy=multi-user.target

另外个人建议,每个节点要么完全使用sysV、systemctl管理haproxy的启动、停止,要么完全使用haproxy命令手动管理服务的启动和停止,否则可能会出现记录不同步的现象。至今没找到会出现这个问题的原因。

3.完成配置

如下,是每个haproxy节点的配置文件内容。

global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 20000
user haproxy
group haproxy
daemon
stats socket /var/run/haproxy.sock mode 600 level admin
spread-checks 2 peers mypeers
peer haproxy1 192.168.100.59:12138
peer haproxy2 192.168.100.54:12138
peer haproxy3 192.168.100.42:12138
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
timeout http-request 2s
timeout queue 3s
timeout connect 1s
timeout client 10s
timeout server 2s
timeout http-keep-alive 10s
timeout check 2s
maxconn 18000 frontend http-in
bind *:80
mode http
log global
default_backend dynamic_group backend dynamic_group
stick-table type string len 32 size 5k expire 5m peers mypeers
stick on req.cook(PHPSESSID)
stick store-response res.cook(PHPSESSID)
balance roundrobin
option http-server-close
option httpchk GET /index.php
option redispatch
http-check expect status 200
server app1 192.168.100.60:80 check rise 1 maxconn 3000
server app2 192.168.100.61:80 check rise 1 maxconn 3000

然后分别在3个节点上启动haproxy,注意加上"-L"选项。

haproxy -D -L haproxy1 -f /etc/haproxy/haproxy.cfg    # exec on haproxy1
haproxy -D -L haproxy2 -f /etc/haproxy/haproxy.cfg # exec on haporxy2
haproxy -D -L haproxy3 -f /etc/haproxy/haproxy.cfg # exec on haproxy3

分别在haproxy1、haproxy2、haproxy3上监控stick table。如果下面命令执行失败,见查看stick table信息

watch -n 1 'echo "show table dynamic_group" | socat unix:/var/run/haprox.sock -'

由于上面的配置文件中,stick table中存储和匹配的记录是响应报文中名为PHPSESSID的cookie,因此在浏览器上测试时,能实现会话粘性。但如果使用curl命令进行测试,由于curl每次执行结束进程就退出,无法缓存cookie,因此curl的每次请求都是新连接。此处正好利用curl这一点特性,来生成多个stickiness记录,方便观察stick table记录推送的情况。

找一台Linux主机,分别对三个haproxy节点进行5次curl请求。

ip1=192.168.100.59
ip2=192.168.100.54
ip3=192.168.100.42
for i in $ip1 $ip2 $ip3;do
for j in `seq 1 5`;do
curl http://$i/index.php &>/dev/null
usleep 500000
done
done

以下是截取自三个节点的stick table内容。

###################### haproxy1 ########################
# table: dynamic_group, type: string, size:5120, used:15
0x1551cc4: key=1t1lr3s5s7pm4tuu3476c40jk3 use=0 exp=286311 server_id=2
0x1551f04: key=3m3o6qkfvk8l4lenk0k8to2bd6 use=0 exp=288500 server_id=2
0x1551904: key=8bg6ej7md7453sp0rrgi5vsiu7 use=0 exp=281350 server_id=1
0x1552144: key=9c4lf1tncd290mjl7vnjafnqe2 use=0 exp=287418 server_id=2
0x1552744: key=b3gqn2i3emi43rfh3i8t0jhln4 use=0 exp=283523 server_id=1
0x15519c4: key=cskoqa8vbb7fbac4eko27hnk70 use=0 exp=281897 server_id=2
0x1550c54: key=ejjgpe1j99nv7hqmnlpib5osq6 use=0 exp=286867 server_id=1
0x1550fc4: key=immqb6ocsq8m5982o28hatk6c2 use=0 exp=285187 server_id=2
0x1550e44: key=lg78n74u0dj51m3cvkn9qhce66 use=0 exp=287953 server_id=1
0x15522c4: key=livn0t62bthpfvp5motknhb934 use=0 exp=284620 server_id=1
0x1551b44: key=mhkh0c4lpisr9kgdb3ggmplpk1 use=0 exp=285745 server_id=1
0x1552384: key=ovfoprd6p91sjbf22qo5jjd4u0 use=0 exp=289035 server_id=1
0x1552204: key=rvt3fnhr51sfrqu1aefjceqlu4 use=0 exp=282978 server_id=2
0x1550d84: key=snkm72pvasr36ut39qopd3jmb1 use=0 exp=282442 server_id=1
0x1552084: key=ucj2pkve527nt39mejp1n7jdu5 use=0 exp=284084 server_id=2 ###################### haproxy2 ########################
# table: dynamic_group, type: string, size:5120, used:15
0x7fa77ec4c8a4: key=1t1lr3s5s7pm4tuu3476c40jk3 use=0 exp=278032 server_id=2
0x7fa77ec4d4e4: key=3m3o6qkfvk8l4lenk0k8to2bd6 use=0 exp=280404 server_id=2
0x7fa77ec4c2a4: key=8bg6ej7md7453sp0rrgi5vsiu7 use=0 exp=272658 server_id=1
0x7fa77ec4c424: key=9c4lf1tncd290mjl7vnjafnqe2 use=0 exp=279232 server_id=2
0x7fa77ec4be24: key=b3gqn2i3emi43rfh3i8t0jhln4 use=0 exp=275012 server_id=1
0x7fa77ec4c4e4: key=cskoqa8vbb7fbac4eko27hnk70 use=0 exp=273251 server_id=2
0x7fa77ec4c724: key=ejjgpe1j99nv7hqmnlpib5osq6 use=0 exp=278635 server_id=1
0x7fa77ec4d2a4: key=immqb6ocsq8m5982o28hatk6c2 use=0 exp=276814 server_id=2
0x7fa77ec4c964: key=lg78n74u0dj51m3cvkn9qhce66 use=0 exp=279812 server_id=1
0x7fa77ec4d664: key=livn0t62bthpfvp5motknhb934 use=0 exp=276201 server_id=1
0x7fa77ec4d7e4: key=mhkh0c4lpisr9kgdb3ggmplpk1 use=0 exp=277418 server_id=1
0x7fa77ec4c1e4: key=ovfoprd6p91sjbf22qo5jjd4u0 use=0 exp=280984 server_id=1
0x7fa77ec4d724: key=rvt3fnhr51sfrqu1aefjceqlu4 use=0 exp=274422 server_id=2
0x7fa77ec4c664: key=snkm72pvasr36ut39qopd3jmb1 use=0 exp=273842 server_id=1
0x7fa77ec4d424: key=ucj2pkve527nt39mejp1n7jdu5 use=0 exp=275620 server_id=2 ###################### haproxy3 ########################
# table: dynamic_group, type: string, size:5120, used:15
0x1372c24: key=1t1lr3s5s7pm4tuu3476c40jk3 use=0 exp=276198 server_id=2
0x1372e64: key=3m3o6qkfvk8l4lenk0k8to2bd6 use=0 exp=278387 server_id=2
0x1372864: key=8bg6ej7md7453sp0rrgi5vsiu7 use=0 exp=271237 server_id=1
0x1373964: key=9c4lf1tncd290mjl7vnjafnqe2 use=0 exp=277304 server_id=2
0x1373f64: key=b3gqn2i3emi43rfh3i8t0jhln4 use=0 exp=273410 server_id=1
0x1372924: key=cskoqa8vbb7fbac4eko27hnk70 use=0 exp=271784 server_id=2
0x13723e4: key=ejjgpe1j99nv7hqmnlpib5osq6 use=0 exp=276753 server_id=1
0x13726e4: key=immqb6ocsq8m5982o28hatk6c2 use=0 exp=275073 server_id=2
0x1372564: key=lg78n74u0dj51m3cvkn9qhce66 use=0 exp=277840 server_id=1
0x1373ae4: key=livn0t62bthpfvp5motknhb934 use=0 exp=274507 server_id=1
0x1372aa4: key=mhkh0c4lpisr9kgdb3ggmplpk1 use=0 exp=275631 server_id=1
0x1373ba4: key=ovfoprd6p91sjbf22qo5jjd4u0 use=0 exp=278922 server_id=1
0x1373a24: key=rvt3fnhr51sfrqu1aefjceqlu4 use=0 exp=272865 server_id=2
0x13724a4: key=snkm72pvasr36ut39qopd3jmb1 use=0 exp=272330 server_id=1
0x13738a4: key=ucj2pkve527nt39mejp1n7jdu5 use=0 exp=273971 server_id=2

结果符合预期目标,每个表都有15条stickiness,且它们的key和对应的server_id完全相同。

4.测试并发请求下的stick table复制效率

再来个大一点的并发测试。环境如下:这个环境并不合理,但不影响根据测试得出结果。

3个haproxy节点进行replication。它们都代理3个php app server。

然后使用ab进行测试其中一个haproxy节点,并发请求数为1000,总共请求50000个请求。

ab -c 1000 -n 50000 -r http://192.168.100.42/index.php

结果如图,没有错误请求的状况下,5W个请求总共花费10.718秒。

监控3个haproxy节点的stick table复制情况,并秒表计时。结果如图:

从结果中看到,3个节点之间完全同步50000个stickiness记录,比请求结束只多花了1.8秒,其中这1.8秒还有我迟钝的反应时间和按下停止计时按钮的时间。也就是说,50000个stickiness记录完全同步完成的时间比请求完成最多也就多花不到1秒的时间(第二次测试时,注意力更集中了些,发现50000个请求才多花了0.16秒的时间就同步完成了,这还要考虑我点下秒表停止按钮的时间,以及 watch - n 'echo "show table" | socat unix:/var/run/haproxy.sock -' 的时候每秒刷新一次的时间差)。

不难看出,stick table的复制效率非常高。事实上,即使在非常繁忙的环境下,多个节点间进行复制,同步完成也不会有多少延迟。可以想象下,haproxy检索、插入一个stickiness并推送(n个节点*50字节)的数据出去,比haproxy处理并转发一个请求给后端要轻松的多的多的多。特别是单独使用一个网卡负责推送stick table时,效率更高。

有了stick table的复制功能,在没有session共享的情况下,为架构设计多提供了几种方案。至于如何选择,需要考虑与其配合的软件特性。特别适用的一种情况是,在haproxy前端使用LVS代理时,由于LVS只能4层负载,很可能将同一客户端调度到不同的haproxy节点上,如果使用stick table复制,就不用担心haproxy不能将客户端引导到同一后端服务器上的问题。另一种使用stick table复制的场景是结合keepalived实现双主模型代理不同服务时,为了会话保持也高可用,可以使用stick table的复制保证粘性记录不丢失。

haproxy的stick table复制的更多相关文章

  1. haproxy实现会话保持(2):stick table

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  2. alter table,复制, 单表查询

    修改表 语法:1. 修改表名      ALTER TABLE 表名                           RENAME 新表名; 2. 增加字段      ALTER TABLE 表名 ...

  3. haproxy反向代理配置示例

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  4. haproxy的丰富特性简介

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  5. haproxy配置示例和需要考虑的问题

    HaProxy系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html haproxy是一个非常优秀的负载均衡工具,它的特性非常丰富,功能也非常非常 ...

  6. haproxy实现会话保持

    HAProxy系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html 1.反向代理为什么需要设置cookie 任何一个七层的http负载均衡器,都 ...

  7. LVS Nginx HAProxy 优缺点

    搭建负载均衡高可用环境相对简单,主要是要理解其中原理.此文描述了三种负载均衡器的优缺点,以便在实际的生产应用中,按需求取舍. 目前,在线上环境中应用较多的负载均衡器硬件有F5 BIG-IP,软件有LV ...

  8. haproxy 2.0 dataplaneapi rest api 转为graphql docker 镜像

    为了方便直接使用haproxy dataplaneapi graphql 格式的查询,制作了一个简单的docker 镜像 基于dotenv 进行配置管理,可以直接通过环境变量传入参数,处理不同hapr ...

  9. haproxy 2.0 dataplaneapi rest api 试用

    我们可以基于haproxy 提供的dataplaneapi 动态进行haproxy 配置的修改,增强haproxy的可编程能力,以下是一个简单 的测试,基于docker-compose运行 环境准备 ...

随机推荐

  1. LCA—倍增法求解

    LCA(Least Common Ancestors) 即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. 常见解法一般有三种 这里讲解一种在线算法-倍增 首先我们定义fa[u][j ...

  2. lamp环境部署脚本

    关于lamp环境的安装脚本,直接复制即可使用 注:apache2.2.X 版本和apache2.4.X版本 本人推荐兼容性版本安装 apache2.4.25 + apr1.5.2 + apr-util ...

  3. PHP实现网页爬虫

    抓取某一个网页中的内容,需要对DOM树进行解析,找到指定节点后,再抓取我们需要的内容,过程有点繁琐.LZ总结了几种常用的.易于实现的网页抓取方式,如果熟悉JQuery选择器,这几种框架会相当简单. 一 ...

  4. uva1471 二叉搜索树

    此题紫书上面有详细分析,关键是运用Set优化实现O(nlgn)复杂度 AC代码: #include<cstdio> #include<set> #include<algo ...

  5. hdu1698 Just a Hook 线段树

    共有Q个更新,每次更新给更新的区间一个标记,表示该区间是在哪一次被更新,最后统计答案是以最近被更新的值为答案. AC代码: #include<cstdio> const int maxn= ...

  6. 关于C语言文件操作

    关于C语言的文件操作之前我也写过一篇博客来介绍,但是当时写的很不全面,只是简单的使用了一下 ,今天再从新学习一下. 1.文件的写 首先还是先看一个简单的例子: include<stdio.h&g ...

  7. 在SpringBoot中配置定时任务

    前言 之前在spring中使用过定时任务,使用注解的方式配置很方便,在SpringBoot中的配置基本相同,只是原来在spring中的xml文件的一些配置需要改变,在SpringBoot中也非常简单. ...

  8. 生活常用类API调用的代码示例合集:邮编查询、今日热门新闻查询、区号查询等

    以下示例代码适用于 www.apishop.net 网站下的API,使用本文提及的接口调用代码示例前,您需要先申请相应的API服务. 邮编查询:通过邮编查询地名:通过地名查询邮编 今日热门新闻查询:提 ...

  9. 在bmp上添加字符2

    void CTextOnbmpDlg::OnButton2() {  // TODO: Add your control notification handler code here  FILE *f ...

  10. linux shell中获取mongodb最大连接数、内存使用情况等

    前两天接到了一个新的需求,需要在linux shell脚本中监控到mongodb最大连接数.内存使用情况等. 但是我对于linux shel很不了解,只是会一些简单常用的linux的操作而已,只要一顿 ...