HAProxy 源代码阅读指引

HAProxy 源代码阅读指引

HAProxy 是一款性能优异的高可用proxy 软件,在抽空整理其源代码与文档之后,感叹作者编码水平之高,项目注释之清晰、文档之齐全,阅读代码过程中让人欲罢不能,往往回首已经2、3个小时过去了。特此记录学习路线在此,供其他朋友借鉴。

你值得在HAProxy的源码中徜徉200个小时!

你可以在HAProxy的源代码中学习到如何写一个优秀的高性能软件!

你可以在HAProxy的源代码中学习到各种内存模型数据结构的使用!

HAProxy 是一个巨大的宝藏,值得真对其各种特性反复阅读分析!

准备工作

万事开头难,如果要了解HAProxy的背景知识以及官方文档,可以访问
官方网站 HAProxy 了解官方文档,从
HAProxy git: clone 代码到本地阅读、分析

首先从git上 clone代码到本地之后,只需要关注以下几个目录:

- doc                   //文档目录,值得所有文档读一遍
    - internals         //实现原理
    - design-thoughts    //设计中的一些思路想法

- include
    - proto             //协议相关,包括proto_tcp proto_http proto_udp session等
    - common
    - import
    - types             //对应entites.pdf 中的HAProxy 各种实体定义

- src                   //所有的c文件 

阅读的起点 : HAProxy 初步介绍

建议从 doc/intro.txt开始,intro.txt 主要介绍LoadBanlance的基本原理、HAProxy能干啥不能干啥,HAProxy 总体在各方面的设计思想以及具体的使用指引

management.txt 主要讲如何多节点部署组成集群,如何实现无缝升级

configuration.txt 主要讲解配置文件的使用

architechure LB的最佳架构以及如何与其他第三方产品交互

proxy-proto 讲解什么是proxy 协议以及需要注意的要点

HAProxy的能力:

  • TCP 双向 proxy
  • HTTP 反向代理
  • SSL 解包
  • TCP 异常流量处理,如SYN flood
  • HTTP 流量均衡化
  • HTTP Fix Tool 解决http 头缺失、错误等
  • Content Based Switch ,基于内容端口复用
  • Traffic Regular 流量规则化,限流限速
  • DDOS 防火墙
  • Server Network Traffic
  • Http Compreess offloader http解压缩

HAProxy 支持的负载均衡算法

HAProxy支持的负载均衡算法以及适用的场景如下:

算法 场景 特点
round-robin 短链接 服务器轮流接收
last-conn 长链接 在来源服务比较少的情况下,可能会一直都在一台机器上
source SSL/Terminal 来源ip,在来源较少的或者有上层代理的情况下有可能都压在一台机器上
URI http cache 根据http URI 设置
HDR http header 根据http header请求,适用于蓝绿、金丝雀等测试
first short_lived_virtual_machine 根据第一次访问的分配,适用于快速释放的虚拟机后端

HAProxy 如何实现高可用的一些tip

  1. server 健康检查,仅适用状态健康的server(貌似废话)
  2. 各组件均实现优雅停机
  3. 备用服务要具备在灾难时自动顶上的能力
  4. 如果下游服务大面积报错,则直接全局报错,熔断;主动让请求方转而请求其他节点,避免出现后段服务失败率较高还不从负载均衡中摘除的情况
  5. 无状态设计,便于组集群
  6. 具有监控、自动重启能力,如keeplive VRRP 等

HAProxy 不具备的能力

  • HAProxy 不是能帮你实现浏览器代理的纯粹的http代理
    HAProxy 不是缓存代理,并不缓存代理过程中的流量
    HAProxy 不是web服务器,HAProxy在启动后无权访问文件系统,如果要找优秀的Web服务器,请选择Apache 或者Nginx
    HAProxy 不是基于数据包的负载均衡器,无法当NAT 甚至 DSR

HAProxy 整体设计

HAProxy 是使用纯C实现的面相对象设计的典范~至少我是这么认为的。为什么这么说,因为以下几点:

1、HAProxy 在整体设计中,完全是面相对象的设计,可以看下HAProxy的entities 图设计
2、HAProxy 的代码实现中,所有的所有的entities基本都是头文件中申明 struts中包含callback 函数,然后在.c文件中实现对应的callback函数,对应到一些其他的面向对象语言中,就是class
3、在HAProxy的源代码中,使用了部分g++的特性,比如 attribute(“constructor”)等
4、HAProxy在实现多种网络协议的解析和处理的时候、设计和抽象Task 、Connector等多种组件等时候,基本都是面相对象的设计,所有的组件都设计成了相同的接口,在filter的设计中,更是标准的接口化、插件化设计

内部主要对象

HAProxy 的主要实体见下图 其主要的对象都在 include/types/中声明,具体的用途方法在entities.txt ,主要的对象有:

思路:按照自己对图的理解,按照总分总的结构讲,先总体纵览,然后具体组件分析,最后以事件序列进行汇总

整体分析

HAProxy本质上来说就是一个单线程、事件驱动的流分发器,其中蓝色部分的Session 和Stream是HAProxy 最主要的对象。整体上来说HAProxy主要思想就是流设计,为每个session的前端connection和对应的backend target connection 建立一个双向流,通过poll_event 从前端的connection fd中拉取数据,经过req channel 到判断target 之后选择 backend的connection fd 写出。后端的Response 数据,经过后端的connection、res channel 反馈给前端frontend.如果是http 协议的话另外增加最下方的http 交易 http txn 管理。保证在后端无响应的情况下返回相应的错误代码。

整体流程

HAProxy 在启动的时候读取配置文件中 frontend 下的listener 配置,在启动的时候解析配置文件、实例化 Listener监听,每个HAProxy必须有一个以上Listener。

具体组件

Session

Listener 是HAProxy对于自身启动的Socket监听做的抽象,实现Socket端口的bind、accept,之后的应用层协议握手、session管理、数据处理分发由Session、Stream 完成。

Initiator:当Listener有网络连接进来的时候,创建Initiator,或者由后端主动发起connect 其他Server的后端Task发起,Initiator 的主要作用是链接listener,回调上一层的accept函数,实现session、stream的创建

Connection:是HAProxy对网络链接的抽象,包括监听的端口和作为客户端链接后段而随机打开的端口,都按照UNIX网络抽象为文件描述符fd,为了管理fd ,HAProxy建立了一个fdtab的list进行fd的管理。

Session: Session主要是为了做状态的关联管理。我们常见的session 一般来自于http session,比如区分用户状态等。而HAProxy 中为了实现在向后端分发请求的时候不丢session,也需要对session进行管理。 HAProxy中Session对象包含:stkctr、Listener、frontend、origin 四个主要对象,其中stkctr 主要是session标示字段,一般是http header;frontend 按照配置文件生成,frontend 在启动时会创建具体等listener;网络请求建立之后,生成了 connection、stream对象,session对象通过origin指针,指向具体的connection。

Stream

stream 是HAProxy中的核心对象,简而言之 要构成一个stream ,至少要两点、一线。两点是指一个流的两端,在HAProxy中不管是Listener accept之后创建的链接还是HAproxy 根据配置文件主动connect 创建的链接,HAProxy 都抽象为connection对象以及对应等fd READ|WRITE操作,Stream中,使用两个Stream Interface对象 si[2] ,si[0] 用做指向输入,si[1] 指向输出流,Stream 中创建了两个单向的 channel ,分别处理request 、response 数据,在两个channel之上增加filter 来实现具体的过滤器。

store ?:TODO 待补充

target ?:TODO 待补充

task : 负责处理相关的定时任务,比如server 的connection 的心跳检查、断线重连、超时处理等。

Http txn

http txn是http协议代理中的特有对象,依附与req channel与res channel ,主要是为了保持http request-responses的交易完整性,比如在后段无响应的情况下主动返回404 错误等, 为了保持http txn的跟踪,需要hdridx (http头保存)、http 验证信息状态等,另外为了实现txn的跟踪,还需要保留cookie信息、uri信息等。

HAProxy 的启动与初始化流程

main

HAProxy 的主流程参考haproxy.c 中的 main函数,具体流程:

int main(int argc,char ** argv){
    1. 调用init(),实现配置文件读取、frontend、backend、listener等对象的创建,辅助的内存池、task创建
    2. sigquit、USR1、HUP、PIPE等操作系统信号回调函数设置,其中PIPE信号不做处理
    3. 设置ulimits\maxmem 等内核参数 
    4. while(retry>0): 最多100次重试 StartProxy 启动代理,之后检查是否Listener是否Ready,如果没有则报错退出
    5. proto_bind_all 做proto的accept绑定,之后listener的回调函数指向具体的proto
    6. SIGOUT\TTIN信号处理函数注册、写pid文件
    7. chroot\setgid pid Deamon 处理,关掉不必要的stdinstdout8. proxy 与process unbind ,peers 与process unbind //TODO 待补充
    9. proto_enable_all 启动所有协议的accept
    10. run_poll_loop 进入主事件循环,主要是session_accept_fd创建session\stream 链接握手处理、process_stream 完成数据的stream流转
    11.deinit\exit(0) 一旦退出主事件循环,则整体程序退出
}

init

haproxy 的初始化流程比较冗长,涉及了配置文件读取、日志配置初始化、默认数据结构(task、stream、session、connection)初始化、对象创建、协议对象创建、lua脚本解释器创建、输入参数的解析处理、server state读取、server初始化、filter创建、初始化poller 等,最后清理内存。详见 haproxy.c init() 函数

HAProxy 的I/O 事件处理流程:如何处理一个链接

HAProxy 处理新链接的主要逻辑步骤包括:
1. 接收配置文件里等frentend 配置信息,创建listener监听
2. frontend预处理、包括阻塞设置、修改具体的请求头等
3. 传递给后端等server实体
4. 后端预处理
5. 决定发给后段的哪一个server
6. 后端对response 进行过滤处理
7. 前端对response 数据处理
8. 向监控发送日志log
9. 如果是http则从第二步继续循环(http 有keeplive等情况),否则关闭链接。

具体的函数调用过程:
//todo

HAProxy 的filter 机制与插件开发

HAProxy 与luna脚本

HAProxy 的backend 重试机制

//Todo: 待续

你可能感兴趣的:(proxy,c)