CDN调度器HAProxy、Nginx、Varnish

Stella981
• 阅读 759

CDN功能如下:
1、将全网IP分为若干个IP段组,分组的依据通常是运营商或者地域,目的是让相同网络环境中的用户聚集到相同的组内;
2、依据CDN服务器们的网络和容量,确定哪些CDN服务器适合服务哪些IP段组;
3、根据以上两步得到的结论,让用户去最适合他的服务器得到服务。

说白了,就是根据用户不同的来源IP把用户请求重定向到不同的CDN服务器上去。
那么,如何实现呢?

智能DNS是办法之一,稳定可靠且有效。
但至少在两个环境下它不能完全满足我们:
1、需要特别精细的调度时。由于大多数DNS Server不支持DNS扩展协议,所以拿不到用户的真实IP,只能根据Local DNS来调度。
2、访问特别频繁时。由于每次调度都将触发一次DNS,如果请求变得密集,DNS请求本身带来的开销也会相应变大;
3、需要根据服务器的带宽容量、连接数、负载情况、当机与否来调度时。由于DNS Server没有CDN节点服务器的信息,这种调度会变得困难。

这时候我们可以:
1、将用户先行引导到某一台或几台统一的服务器上去;
2、让它拿到用户的真实IP,计算出服务他的服务器;
3、通过HTTP302或其它方式把用户定位到最终服务器上。

部署在用户先访问到的那几台服务器上,负责定位IP然后重定向用户请求的那个软件,我们叫它“调度器”。

HAProxy实现:
HAProxy不支持形如0.0.0.1-0.8.255.255 cn的IP段表示方法,只支持1.1.4.0/22 “CN”的IP段表示方法。
1、我们需要先把IP段转化成它认识的方式;
a> 下载iprang.c或者iprang.c本地镜像;
b> 编译gcc -s -O3 -o iprange iprange.c;
c> 整理IP段列表geo.txt形如:

# head geo.txt "1.0.0.0","1.0.0.255","AU" "1.0.1.0","1.0.3.255","CN" "1.0.4.0","1.0.7.255","AU" "1.0.8.0","1.0.15.255","CN" "1.0.16.0","1.0.31.255","JP" "1.0.32.0","1.0.63.255","CN" "1.0.64.0","1.0.127.255","JP" "1.0.128.0","1.0.255.255","TH" "1.1.0.0","1.1.0.255","CN" "1.1.1.0","1.1.1.255","AU"

1

2

3

4

5

6

7

8

9

10

11

# head geo.txt

"1.0.0.0","1.0.0.255","AU"

"1.0.1.0","1.0.3.255","CN"

"1.0.4.0","1.0.7.255","AU"

"1.0.8.0","1.0.15.255","CN"

"1.0.16.0","1.0.31.255","JP"

"1.0.32.0","1.0.63.255","CN"

"1.0.64.0","1.0.127.255","JP"

"1.0.128.0","1.0.255.255","TH"

"1.1.0.0","1.1.0.255","CN"

"1.1.1.0","1.1.1.255","AU"

d> 输出HAProxy认识的IP段列表:

# cut -d, -f1,2,5 geo.txt | ./iprange | head 1.0.0.0/24 "AU" 1.0.1.0/24 "CN" 1.0.2.0/23 "CN" 1.0.4.0/22 "AU" 1.0.8.0/21 "CN" 1.0.16.0/20 "JP" 1.0.32.0/19 "CN" 1.0.64.0/18 "JP" 1.0.128.0/17 "TH" 1.1.0.0/24 "CN" 1.1.1.0/24 "AU"

1

2

3

4

5

6

7

8

9

10

11

12

# cut -d, -f1,2,5 geo.txt | ./iprange | head

1.0.0.0/24 "AU"

1.0.1.0/24 "CN"

1.0.2.0/23 "CN"

1.0.4.0/22 "AU"

1.0.8.0/21 "CN"

1.0.16.0/20 "JP"

1.0.32.0/19 "CN"

1.0.64.0/18 "JP"

1.0.128.0/17 "TH"

1.1.0.0/24 "CN"

1.1.1.0/24 "AU"

e> 便于管理的目的,将整合后的IP段归类到同一个文件中:

# cut -d, -f1,2,5 geo.txt | ./iprange | sed 's/"//g' | awk -F' ' '{ print $1 >> $2".subnets" }' # ls *.subnets A1.subnets AX.subnets BW.subnets CX.subnets FJ.subnets GR.subnets IR.subnets LA.subnets ML.subnets NF.subnets PR.subnets SI.subnets TK.subnets VE.subnets # cat AU.subnets 1.0.0.0/24 1.0.4.0/22 1.1.1.0/24

1

2

3

4

5

6

7

# cut -d, -f1,2,5 geo.txt | ./iprange | sed 's/"//g' | awk -F' ' '{ print $1 >> $2".subnets" }'

# ls *.subnets

A1.subnets  AX.subnets  BW.subnets  CX.subnets  FJ.subnets  GR.subnets  IR.subnets  LA.subnets  ML.subnets  NF.subnets  PR.subnets  SI.subnets  TK.subnets  VE.subnets

# cat AU.subnets

1.0.0.0/24

1.0.4.0/22

1.1.1.0/24

f> 把这些文件放到同一个文件夹下,我们以/etc/haproxy/conf/为例。

2、正确配置HAProxy以这些IP段为规则正确调度;
下面是一个haproxy.cfg的例子。配置好后重启Haproxy即可。

global log 127.0.0.1 local2 debug chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 8000 user haproxy group haproxy daemon stats socket /var/lib/haproxy/stats defaults mode http log global option httplog option dontlognull option http-server-close option forwardfor except 127.0.0.0/8 option <a href="http://www.ttlsa.com/redis/" title="redis"target="_blank">redispatch retries 3 timeout http-request 10s timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout http-keep-alive 10s timeout check 10s maxconn 8000 frontend main *:5000 acl geo_A1 src -f /etc/haproxy/conf/A1.subnets acl geo_AX src -f /etc/haproxy/conf/AX.subnets acl geo_BW src -f /etc/haproxy/conf/BW.subnets acl geo_CX src -f /etc/haproxy/conf/CX.subnets acl geo_FJ src -f /etc/haproxy/conf/FJ.subnets ... reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=A1\ HTTP if geo_A1 reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=AX\ HTTP if geo_AX reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=BW\ HTTP if geo_BW reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=CX\ HTTP if geo_CX reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=FJ\ HTTP if geo_FJ ... default_backend static backend static server static 127.0.0.1:6081 check

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

global

log 127.0.0.1 local2 debug

chroot /var/lib/haproxy

pidfile /var/run/haproxy.pid

maxconn 8000

user        haproxy

group       haproxy

daemon

stats socket /var/lib/haproxy/stats

defaults

mode                    http

log                     global

option                  httplog

option                  dontlognull

option http-server-close

option forwardfor       except 127.0.0.0/8

option                  redispatch

retries 3

timeout http-request 10s

timeout queue 1m

timeout connect 10s

timeout client 1m

timeout server 1m

timeout http-keep-alive 10s

timeout check 10s

maxconn 8000

frontend  main *:5000

acl geo_A1 src -f /etc/haproxy/conf/A1.subnets

acl geo_AX src -f /etc/haproxy/conf/AX.subnets

acl geo_BW src -f /etc/haproxy/conf/BW.subnets

acl geo_CX src -f /etc/haproxy/conf/CX.subnets

acl geo_FJ src -f /etc/haproxy/conf/FJ.subnets

...

reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=A1\ HTTP if geo_A1

reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=AX\ HTTP if geo_AX

reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=BW\ HTTP if geo_BW

reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=CX\ HTTP if geo_CX

reqrep ^([^\ ]*)\ /(.*)\ HTTP \1\ /\2&ipfrom=FJ\ HTTP if geo_FJ

...

default_backend             static

backend static

server      static 127.0.0.1:6081 check

Nginx实现:
Nginx可以在核心模块HttpGeoModule(http://wiki.nginx.org/HttpGeoModule)的配合下实现调度:

http{ ... geo $useriprang { ranges; default a; 0.0.0.1-0.8.255.255 a; 0.9.0.0-0.255.255.255 a; 1.0.0.0-1.0.0.255 a; 1.0.1.0-1.0.1.255 b; 1.0.2.0-1.0.3.255 b; 1.0.4.0-1.0.7.255 a; ... 223.255.252.0-223.255.253.255 c; 223.255.254.0-223.255.254.255 a; 223.255.255.0-223.255.255.255 a; } upstream backend { server 127.0.0.1:81; } server { listen 80; client_max_body_size 10240m; location / { proxy_redirect off; proxy_pass http://backend$request\_uri&useriprang=$useriprang; proxy_next_upstream http_502 http_504 error timeout invalid_header; proxy_cache cache_one; proxy_cache_key $host:$server_port$uri$is_args$args; expires 5s; } } ... }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

http{

...

geo $useriprang {

ranges;

default a;

0.0.0.1-0.8.255.255 a;

0.9.0.0-0.255.255.255 a;

1.0.0.0-1.0.0.255 a;

1.0.1.0-1.0.1.255 b;

1.0.2.0-1.0.3.255 b;

1.0.4.0-1.0.7.255 a;

...

223.255.252.0-223.255.253.255 c;

223.255.254.0-223.255.254.255 a;

223.255.255.0-223.255.255.255 a;

}

upstream backend {

server 127.0.0.1:81;

}

server {

listen 80;

client_max_body_size 10240m;

location / {

proxy_redirect off;

proxy_pass http://backend$request\_uri&useriprang=$useriprang;

proxy_next_upstream http_502 http_504 error timeout invalid_header;

proxy_cache cache_one;

proxy_cache_key $host:$server_port$uri$is_args$args;

expires 5s;

}

}

...

}

Varnish实现:
Varnish则有两个插件可以实现调度:
https://github.com/cosimo/varnish-geoip (Last updated: 28/05/2013)
https://github.com/meetup/varnish-geoip-plugin (Last updated: 2010)

性能问题
如上所述,使用Haproxy、Nginx、Varnish都能快速实现这个功能。
其中Nginx和Varnish使用了二分法在IP表中定位用户IP,而Haproxy是逐条过滤。
所以在IP分得较细,IP段组较多(归类后超过1000组)时,Haproxy会出现明显的性能衰减,其余两者没有这个问题。

其它
本文使用的软件版本如下:
HAProxy1.4.22,Nginx1.2.9,Varnish3.0.4。
HAProxy和Varnish都是目前的最新版本。
本文有参考http://blog.exceliance.fr/2012/07/02/use-geoip-database-within-haproxy/
转自:http://blog.yikuyiku.com/?p=3851

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这