首页 > 其他 > 详细

lvs

时间:2018-11-19 13:23:11      阅读:180      评论:0      收藏:0      [点我收藏+]

LVS


基于工作的协议层次划分:
传输层:
lvs,haproxy(mode tcp)
应用层:
haproxy,nginx,ats,perlbal

章文嵩
lvs:Linux Virtual Server
14:四层交换,四层路由:
根据请求报文的目标IP和PORT将其转发至后端主机集群中的某一台主机(根据挑选算法):
netfilter:
PREROUTING-->INPUT
PREROUTING-->FORWARD-->POSTROUTING
OUTPUT-->POSTROUTING

ipvsadm/ipvs
ipvsadm:用户空间的命令行工具,用于管理集群服务;
ipvs:工作内核中netfilter INPUT钩子上:

支持TCP,UDP,AH,EST,AH_EST,SCTP等诸多协议:

LVS术语
Director Server:调试服务器,将负载分发到 Real Server 的服务器
Real Server:真实服务器,真正提供应用服务的服务器
VIP:虚拟 IP 地址,公布给用户访问的 IP 地址
RIP:真实 IP 地址,集群节点上使用的 IP 地址
DIP:Director 连到 Real Server 的 IP 地址

client IP:CIP
Director Virutal IP:VIP
Director IP:DIP
Real Server IP:RIP

lvs type:

  • lvs-nat MASQUERADE 地址伪装

通过网络地址转换实现的虚拟服务器
Director 将用户请求报文的目的地址改成待定的 Real Server 地址后,转发给 Real Server;大并发访问时,调试器的性能成为瓶颈
优点:节省 IP 缺点:转发效率低

  • lvs-dr(direct routing) GATEWAY 直接使用路由技术实现虚拟服务器

通过改写请求报文的 MAC 地址,将请求发至 Real Server,Real Server 直接响应客户端

  • lvs-tun(ip tunneling) IPIP 通过隧道方式实现虚拟服务器

Director 采用隧道技术将请求发至 Real Server 后,Real Server 直接响应客户端

  • lvs-fullnat 完全NAT 既修改请求报文源地址,又修改请求报文目标地址

lvs-nat:多目标的DNAT(iptables):通过修改请求报文的目标IP地址(同时可能会修改目标端口)至挑选出某RS的RIP地址实现转发;

(1)RS应该和DIP应该使用私网地址,且RS的网关要指向DIP:
(2)请求和响应报文都要经由director转发:极高负载的场景中,director可能会成为系统瓶颈:
(3)支持端口映射:
(4)RS可以使用任意OS;
(5)RS的RIP和Director的DIP必须在同一IP网络:

lvs-dr direct routing

它通过修改请求报文的目标MAC地址进行转发:
Director:VIP,DIP RS:RIP,VIP

(1)保证前端路由器将目标IP为VIP的请求报文发送给director;
解决方案:

  • 静态绑定
  • arptables
  • 修改RS主机内核的参数
    (2)RS的RIP可以使用私有地址:但也可以使用公网地址:
    (3)RS跟Director必须在同一物理网络中:
    (4)请求报文经由Director调度,但响应报文一定不能经由Director;
    (5)不支持端口映射:
    (6)RS可以大多数OS:
    (7)RS的网关不能指向DIP:

lvs-tun:

不修改请求报文的ip首部,而是通过在原有的ip首部(cip<-->vip)之外,再封装一个ip首部(dip<-->rip):
(1)RIP,DIP,VIP全得是公网地址:
(2)RS的网关的不能指向DIP:
(3)请求报文必须经由director调度,但响应报文必须不能经由director;
(4)不支持端口映射:
(5)RS的OS必须支持隧道功能;

lvs-fullnat:

director通过同时修改请求报文的目标地址和源地址进行转发:
(1)VIP是公网地址:RIP和DIP是私网地址,二者无须在同一网络中;
(2)RS接收到的请求报文的源地址为DIP,因此要响应给DIP;
(3)请求报文和响应报文都必须经由Director;
(4)支持端口映射机制:
(5)RS可以使用任意OS;

http:stateless
session保持:
session绑定:
source ip hash
cookie
session集群:
session服务器:

lvs 算法:

静态方法:仅根据算法本身进行调度;

RR:round robin,轮调 将客户端请求平均分发到 Real Server

WRR:weighted rr, 加权轮调 根据 Real Server 的性能设置权重,再进行轮询调度

SH:source hash,源地址散列
调度算法根据请求的源 IP 地址,作为散列键(HashKey)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,否则返回空

DH:destination hash,目标地址散列
调度算法根据请求的目标 IP 地址,作为散列键(HashKey)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,否则返回空。

动态方法:根据算法及各RS的当前负载状态进行调度:

overhead= 当前的负载
Active活动链接数 Inactive非活动链接数

LC:Least Connection 动态地将网络请求调试到已建立的连接数最好少的服务器上
Overhead=Active*256+Inactive
活动链接数 X 256 + 非活动连接数  (Active*256+Inactive) 谁的值小谁则选择谁

WLC:Weighted LC 根据 Real Server 的性能设置权重,再将网络请求调度到已建立的连接数最好少的服务器上
Overhead=(Active*256+Inactive)/weight
活动链接数 X 256 + 非活动连接数)/ 权重
wlc 是默认的调度算法,说明对于 lvs 集群来讲,wlc 是负载均衡最好的调度算法,但是 wlc 存在问题,所有有了 sed 算法

SED:Shortest Expection Delay 
Overhead=(Active+1)*256/weight
基于 WLC 算法
A、B、C 三台机器分别权重 1、2、3,连接数也分别是 1、2、3. 如果使用 WLC 算法的话一个新请求进入时它可能会分给 ABC 中的的任意一个
使用 sed 算法后会进行这样一个运算:A(1+1/1,B(1+2)/2,C(1+3)/3
根据运算结果,把连接交给 C

NQ:Never Queue
SED算法改进;如果有台Real Server的连接数为0就直接分配过去,无需SED运算

LBLC:Locality-Based LC 基于局部性的最少链接
调度算法是针对目标 IP 地址的负载均衡,目前主要用于 Cache 集群系统。该算法根据请求的目标 IP 地址找出该目标IP地址最近使用的服务器,若该服务器是可用的且没有超载,将请求发送到该服务器;若服务器不存在,或者该服务器超载且有服务器处于一半的工作负载,则用 “最少链接” 的原则选出一个可用的服务器,将请求发送到该服务器。

LBLCR:Locality-Based Least-Connection with Replication,带复制的基于局部性最少链接
调度算法也是针对目标 IP 地址的负载均衡,目前主要用于 Cache 集群系统。它与 LBLC 算法的不同之处是它要维护从一个目标 IP 地址到一组服务器的映射,而 LBLC 算法维护从一个目标 IP 地址到一台服务器的映射。该算法根据请求的目标 IP 地址找出该目标 IP 地址对应的服务器组,按 "最小连接"原则从服务器组中选出一台服务器,若服务器没有超载,将请求发送到该服务器,若服务器超载;则按 "最小连接" 原则从这个集群中选出一台服务器,将该服务器加入到服务器组中,将请求发送到该服务器。同时,当该服务器组有一段时间没有被修改,将最忙的服务器从服务器组中删除,以降低复制的

程度。

ipvs的集群服务:
tcp,udp,ah,esp,ah_esp,sctp ipvsadm的用法:

(1)一个ipvs主机可以同时定义多个cluster service:
tcp,udp
(2)一个cluster service上至少应该一个real server;定义时:指明lvs-type,以及lvs scheduler;

LVS 的 IP 负载均衡技术是通过 IPVS 模块实现的
IPVS 是 LVS 集群的核心软件
已成为 Linux 组成部分
grep -i -A 10 ‘IPVS‘ /boot/config-3.10.0-327.el7.x86_64

安装ipvsadm

yum install ipvsadm -y
rpm -ivh ipvsadm-xxx.rpm
ipvsadm -v
rpm -ql ipvsadm

ipvsadm的用法:
管理集群服务
ipvsadm -A|E -t|u|f service-address [-s scheduler]
ipvsadm -D -t|u|f service-address
ipvsadm -C
ipvsadm -L|l [options]

service-address:
tcp: -t ip:port
udp: -u ip:port
fwm: -f mark

-s scheculer:默认为WLC

管理集群服务中的RS
ipvsadm -a|e -t|u|f service-address -r server-address [ -g|i|m ][-w weight]
ipvsadm -d -t|u|f service-address -r server-address

server-address:ip[:port]

lvs-type:
-g:gateway,dr
-i:ipip,tun
-m:masquerade,nat

清空和查看:
ipvsadm -C
ipvsadm -L|l [options]
-n:numeric,基于数字格式显示地址和端口:
-c:connection,显示ipvs连接:
-stats:统计数据
--rate:速率
-exact:精确值

保存和重载:
ipvsadm -R
ipvsadm -S [-n]

置零计数器:
ipvsadm -Z [-t|u|f service-address]

LVS-NAT

技术分享图片
lvs-nat

CIP>INTERNET>VIP>DIP>RIP1>DIP>VIP>INTERNET>CIP
配置IP
direct

VIP ip 192.168.200.100  netmask 255.255.255.0
DIP ip 192.168.2.254  netmask 255.255.255.0

web1 ip 192.168.2.100  netmask 255.255.255.0 gateway 192.168.2.254
web2 ip 192.168.2.101  netmask 255.255.255.0 gateway 192.168.2.254

direct 打开转发

cp /etc/sysctl.conf{,.bak}
echo net.ipv4.ip_forward = 1 >> /etc/sysctl.conf
sysctl -p

开启WEB服务

web1 
echo web1> /var/www/html/index.html
systemctl restart httpd

web2
echo web2> /var/www/html/index.html
systemctl restart httpd

配置ipvsadm
direct
ipvsadm 动作 协议 VIP:端口 指定调度算法 调度算法
ipvsadm 动作 协议 VIP:端口 添加RIP RIP 类型

ipvsadm -A -t 192.168.200.100:80 -s rr
ipvsadm -a -t 192.168.200.100:80 -r 192.168.2.100 -m
ipvsadm -a -t 192.168.200.100:80 -r 192.168.2.101 -m
ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.200.100:80 rr
  -> 192.168.2.100:80             Masq    1      0          0         
  -> 192.168.2.101:80             Masq    1      0          0 

测试

[root@localhost ~]# curl 192.168.200.100
web1
[root@localhost ~]# curl 192.168.200.100
web2
[root@localhost ~]# ab -c 10 -n 1000 http://192.168.200.100/

[root@direct ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.200.100:80 rr
  -> 192.168.2.100:80             Masq    1      0          501       
  -> 192.168.2.101:80             Masq    1      0          501 

保存,清空,导入ipvsadm规则

direct

rpm -ql ipvsadm
/usr/lib/systemd/system/ipvsadm.service

grep ipvsadm /usr/lib/systemd/system/ipvsadm.service
ExecStart=/bin/bash -c "exec /sbin/ipvsadm-restore < /etc/sysconfig/ipvsadm"
ExecStop=/bin/bash -c "exec /sbin/ipvsadm-save -n > /etc/sysconfig/ipvsadm"
ExecStop=/sbin/ipvsadm -C

保存ipvsadm规则
ipvsadm -S > /etc/sysconfig/ipvsadm

cat /etc/sysconfig/ipvsadm
-A -t 192.168.200.100:http -s rr
-a -t 192.168.200.100:http -r 192.168.2.100:http -m -w 1
-a -t 192.168.200.100:http -r 192.168.2.101:http -m -w 1

清空ipvsadm规则
ipvsadm -C

ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn

导入ipvsadm规则
ipvsadm -R < /etc/sysconfig/ipvsadm

ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.200.100:80 rr
  -> 192.168.2.100:80             Masq    1      0          0         
  -> 192.168.2.101:80             Masq    1      0          0    

显示IPVS已经建立的连接
ipvsadm -Lc
统计数据
ipvsadm -Ln --stats
统计速率
ipvsadm -Ln --rate
统计精确值,不进行单位换算
ipvsadm -Ln --exact

LVA-DR

技术分享图片
lvs-dr

CIP>INTERNET>ROUTE1>VIP>DIP>RIP1>ROUTE1>INTERNET>CIP
配置IP 调整内核参数
arp_ignore 用于定义通告限制级别
共有 3 个值:

  • 0:默认值,表示默认情况下只要主机接入至网络中,它会把每个接口以及接口 IP 和 mac 对应关系向本地网络通告
  • 1:尽可能避免不将地址通告本地网络
  • 2:只通告直连接口到本地网络
    一般模式选择 2

arp_annouce 用于定义响应级别
arp_ignore
共有 8 个值,重点是 1

  • 0:只要本地网络有此 ip 则直接通告
  • 1:仅接收在本地接口的 ip 地址

禁止 RS 上的 vip 直接跟前端路由通信的三种方案

  1. 修改路由,使用静态 arp
  2. 在 RS 上使用 arptables 禁止响应对 vip 的广播请求
  3. 在 RS 上修改其内核参数,并将 vip 配置在与 RIP 不同的接口的别名上比如 lo:0
    通常代价比较低,用的比较广泛的是第三种方案,直接绑定 lo 环回地址

linux 还有一种特性,尽管配置了 vip 也禁止了 vip 的响应请求,当请求报文到达以后,realserver 要对其封装响应,响应直接发送至客户端,这时我们需要考虑两个问题:
1.响应报文从哪个接口出去,则将哪个接口的地址当做源地址,报文响应出去的时候其源地址是 RIP,但 CIP 一定是期望 VIP 响应的
(过程中会通过一条特定的路由设定来实现),

2.如果 rip vip dip 都在同一网段内,那么可以轻松实现,直接将网关指向路由即可
但如果 rip、dip 不跟 vip 在同一网络内,无论如何各 realserver 的网关一定不能指向 directory,必须要准备另一个路由设备,比如将网关指向与 RIP 在同一网段内
最终外网接口有可能通过其他路由到达互联网也有可能到达出口路由器,前提是必须能与 RIP 进行通信才可以
如果在生产环境内只有一个公网 ip 其余的使用私有 ip,所以只能用公网 ip 当做 vip,既然只有一个公网 ip,那么 rip 和 dip 都是私有地址,与 vip 不在同一网段,所以这时候必须要配置一个本地网关,通过地址转换(路由)到互联网中去

direct

ifconfig eno33554968 192.168.2.254/24 up
ifconfig eno33554968:0 192.168.2.253/32 broadcast 192.168.2.253 up
route add -host 192.168.2.253 dev eno33554968:0

web1 web2

echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
ifconfig lo:0 192.168.2.253/32 broadcast 192.168.2.253 up
route add -host 192.168.2.253 dev lo:0

配置ipvsadm

ipvsadm -A -t 192.168.2.253:80 -s rr
ipvsadm -a -t 192.168.2.253:80 -r 192.168.2.100 -g
ipvsadm -a -t 192.168.2.253:80 -r 192.168.2.101 -g

测试

[root@localhost ~]# curl 192.168.2.253
web1
[root@localhost ~]# curl 192.168.2.253
web2
[root@localhost ~]# curl 192.168.2.253
web1
[root@localhost ~]# curl 192.168.2.253
web2

RIP 与 VIP 不在同一网段

但如果 rip、dip 不跟 vip 在同一网络内,无论如何各 realserver 的网关一定不能指向 directory,必须要准备另一个路由设备,比如将网关指向与 RIP 在同一网段内
最终外网接口有可能通过其他路由到达互联网也有可能到达出口路由器,前提是必须能与 RIP 进行通信才可以
如果在生产环境内只有一个公网 ip 其余的使用私有 ip,所以只能用公网 ip 当做 vip,既然只有一个公网 ip,那么 rip 和 dip 都是私有地址,与 vip 不在同一网段,所以这时候必须要配置一个本地网关,通过地址转换(路由)到互联网中去

技术分享图片
lvs-dr-2

CIP>INTERNET>ROUTE1>VIP>DIP>RIP1>ROUTE2>INTERNET>CIP

direct

ifconfig eno33554968 192.168.2.254/24 up
ifconfig eno33554968:1 192.168.3.254/24 up
ifconfig eno33554968:0 192.168.2.253/32 broadcast 192.168.2.253 up
route add -host 192.168.2.253 dev eno33554968:0

web1

echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
ifconfig eno16777728:0 192.168.3.100/24 
route add default gw 192.168.3.254
ifconfig lo:0 192.168.2.253/32 broadcast 192.168.2.253 up
route add -host 192.168.2.253 dev lo:0

web2

echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
ifconfig eno16777728:0 192.168.3.100/24 
route add default gw 192.168.3.254
ifconfig lo:0 192.168.2.253/32 broadcast 192.168.2.253 up
route add -host 192.168.2.253 dev lo:0

防火墙标记

通过FWM定义集群的方式:
(1)在director上netfilter的mang1e表的PREROUTING定义用于“打标”的规则
iptables -t mangle -A PREROUTING -d $vip -p $protocol --dports $port -j MARK--set-mark #
$vip:VIP地址
$protocol:协议
$port:协议端口
(2)基于FMWM定义集群服务:
ipvsadm -A -f # -s scheduler
ipvsadm -a -f # -r RIP [ -g|i|m ]
ipvsadm -a -f # -r RIP [ -g|i|m ]
功用:将共享一组RS的集群服务统一进行定义;

iptables -t mangle -A PREROUTING -d 192.168.2.253 -p tcp --dport 80 -j MARK --set-mark 10
[root@localhost ~]# ipvsadm -A -f 10 -s rr
[root@localhost ~]# ipvsadm -a -f 10 -r 192.168.3.100 -g
[root@localhost ~]# ipvsadm -a -f 10 -r 192.168.3.101 -g
[root@localhost ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
FWM  10 rr
  -> 192.168.3.100:0              Route   1      0          0         
  -> 192.168.3.101:0              Route   1      0          0   

session保持:

  • session绑定
  • session复制
  • session服务器

session绑定:lvs sh算法
对某一特定服务

lvs 持久链接

功能:无论ipvs使用何种调度方法,其都能实现将来自于同一个client的请求始终定向至第一次调度时挑选出的RS;
\持久连接主要是将同一用户的不同请求分发至一台服务器
使用其关联性无论使用什么方法都可以在外围捆绑机制上实现持久功能

如何去实现 lvs 持久链接
持久连接必须对其进行连接追踪,所以要实现端口关联性的持久性,lvs 在内部引进了机制叫做 lvs 持久链接模板(lvs persistent template)
简单来讲就是一内存存储空间,是一段保存在内存中的哈希表,保存内容无非就是键值对(键:CIP;值:realserver)
启动这种功能的时候 lvs 调度的过程为:

  1. 用户请求 80 服务于是根据算法选择了 realserver1 并且将这对应关系保存在哈希表里
  2. 于是第二次同一个请求不管是否是同一客户端的请求到达后,不是将通过算法调度,而是先去查找内存哈希表,查看是否曾经被分配至某个 realserver 不管使用的哪个服务都需要查找内存空间的哈希表
  3. 如果此前曾经被分配过某个 realserver 那么哪怕其访问的其他服务,都统统被分发到此 realserver
    所以在实现 lvs 持久链接的时候,先去查内存空间哈希表,只要表中匹配则进行分配

那么对于非常繁忙的 lvs 来说 持久链接就存在瓶颈,如果有 N 个客户端那么就意味着要建立 N 个条目
也就意味着内存空间需要充足能容纳我们的记录的条目才可以,所以在必要的时候要去调整模板的空间大小

对多个共享同一组RS的服务,统一进行绑定

定义持久连接机制
持久多长时间超时需要自己按需定义
在添加 ipvs 规则的时候加入参数 -p:
ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask]

如果不去设置其值那么其默认值为 360 秒
ipvsadm -p timeout_number

持久性跟算法没有关系,就是说无论使用哪种算法都可以实现持久连接
那么疑问来了:使用持久链接是否就没有意义了?
在客户端发送第一次连接时进行调度,在实现持久功能之前的选择,算法依旧有用的,因此需要挑选一个最合理的算法
持久链接的功能主要目的是实现将多个端口绑定的,在定义一个服务的时候加 -p 只对当前服务有效,那如果对其定义 2 个不同的服务,那么他们 2 个各自持久链接是有效果的,但是彼此之间仍然没关系,那么我们的目的是将 2 个服务绑定在一起
因此我们需要人为的将 2 个集群定制为一个集群服务:

持久连接的实现方式
1.PPC :持久端口连接 每端口持久 单服务持久调度

ipvsadm -A -t $VIP:80 -s rr -p
ipvsadm -a -t $VIP:80 -s -r $RIP1 -g
ipvsadm -a -t $VIP:80 -s -r $RIP2 -g

2.PFMC : 每FWM持久 单FWM持久调度 将多个服务做一个标记,调度时加-p
只需要将某几个服务的请求刚到达 directory 服务器的时候,在 perouting 链上做防火墙标记,并用 ipvs 调用其标记

iptables -t mangle -A PREROUTING -d 192.168.2.253 -p tcp --dport 80 -j MARK--set-mark 10
iptables -t mangle -A PREROUTING -d 192.168.2.253 -p tcp --dport 443 -j MARK--set-mark 10
ipvsadm -A -f 10 -s rr -p
ipvsadm -a -f 10 -r $RIP1 -g
ipvsadm -a -f 10 -r $RIP1 -g

3.PCC :每客户端持久 持久客户端连接 单客户端持久调度
director会将用户的任务请求都识别为集群服务,并向RS进行调度
TCP/UDP 1-65535

ipvsadm -A -t $VIP:0 -s rr -p 
ipvsadm -a -t $VIP:0 -s -r RIP -g
ipvsadm -a -t $VIP:0 -s -r RIP -g

自动监控脚本

#!/bin/bash

VIP=192.168.2.253
RIP1=192.168.2.100
RIP2=192.168.2.101

while [ 1 ]
do
    for IP in $RIP1 $RIP2
    do
        WebStatus=$(nmap -n -sT $IP -p 80 |grep open)
        IPVSIP=$(ipvsadm -Ln |grep $IP)
        if [ -z "$WebStatus" ];then
            if [ -n "$IPVSIP" ];then
                ipvsadm -d -t ${VIP}:80 -r $IP
            fi
        else
            if [ -z "$IPVSIP" ];then
                if [  "$IP"=="$RIP1" ];then
                    ipvsadm -at ${VIP}:80 -r $IP
                else
                    ipvsadm -at ${VIP}:80 -r $IP -g -w 2
                fi
            fi
        fi
    done
    sleep 2
done

lvs

原文:https://www.cnblogs.com/fina/p/9982529.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!