搜文章
推荐 原创 视频 Java开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发
Lambda在线 > 云开源 > haproxy代码框架分析

haproxy代码框架分析

云开源 2017-11-01
点击上方 “公众号” 可以订阅哦!

1、概述


haproxy 是用于提供高可用性、负载均衡以及基于四层和七层网络的代理软件。和大多数网络模块一样,看不到haproxy很炫的流程和功能,但是它实时都在默默进行链路管理、报文分发,大部分场景下对其性能要求较高、差错容忍度较低。


关于性能,其官网描述列举了至少8个方向的优化措施,这使得haproxy进程本身的消耗比内核空间消耗低20倍以上。因此,在高端系统上haproxy的7层性能可能会超过硬件负载均衡设备。在健康检查方面除了支持tcp/http基本检查外,还能支持mysql等特殊应用的高级健康检查。


openstack架构里,有以下地方可能会用到haproxy做负载均衡:


1)控制节点的发往各个组件API的报文节点负载均衡;


2)作为neutron-lbaas的底层可选实现之一;


本次我们基于haproxy1.5版本源码及一些网络资料分析下haproxy的代码框架。框架上主要包括:主流程、调度管理、配置读取、session管理、统计管理、基础模块等。重点介绍下调度管理和session管理。


haproxy代码框架分析

1.1  haproxy-systemd-wrapper

haproxy代码框架分析


systemd 管理haproxy程序运行时我们至少会看到有3个相关进程在运行,如果跑N个实例,则会有N+2个相关进程。


root     19744     1  0 Feb15 ?        00:00:00 /usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid


haproxy  19745 19744  0 Feb15 ?        00:00:00 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds


haproxy  19746 19745  0 Feb15 ?        00:04:45 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid –Ds


systemd管理haproxy是通过先启动haproxy-systemd-wrapper进程去启动和管理haproxy程序,warpper进程进行了一次fork操作,后续的haproxy内又会做一次fork。


另外,为了优雅重启haproxy, wrapper中守护SIGUSR2信号进行重启,并读取旧的pids。如果存在旧的pids,则启动时增加"-sf"选项,这样进入新的haproxy程序后会对向所有旧进程发出SIGUSR1信号,旧的harpoxy程序捕获SIGUSR1执行对应回调sig_soft_stop优雅退出。


haproxy代码框架分析

1.2  haproxy代码主线

haproxy代码框架分析


haproxy代码主线如下:


haproxy代码框架分析


2、调度管理

haproxy代码框架分析

2.1  run_poll_loop主循环

haproxy代码框架分析


haproxy 的调度管理主要在run_poll_loop中循环实现。采用事件驱动模型显著降低了上下文切换的开销及内存占用。代码如下:


/* Runs the polling loop */

void run_poll_loop()

{

     int next;

 

     tv_update_date(0,1);

     while (1) {

              /* check if we caught some signals and process them */

              signal_process_queue();

 

              /* Check if we can expire some tasks */

              wake_expired_tasks(&next);

 

              /* Process a few tasks */

              process_runnable_tasks(&next);

 

              /* stop when there's nothing left to do */

              if (jobs == 0)

                       break;

 

              /* The poller will ensure it returns around <next> */

              cur_poller.poll(&cur_poller, next);

              fd_process_spec_events();

     }

}


主循环的结构比较清晰,循环的执行下面的调用: