热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linuxrabbit集群部署+ha

http:www.cnblogs.comtelwanggsp7124832.htmlhttp:www.ywnds.com?p4741https:blog.csdn.netu013

    http://www.cnblogs.com/telwanggs/p/7124832.html

http://www.ywnds.com/?p=4741

   https://blog.csdn.net/u013256816/article/details/77150922

集群理论知识:

由于RabbitMQ是用erlang开发的,RabbitMQ完全依赖Erlang的Cluster,因为erlang天生就是一门分布式语言,集群非常方便,但其本身并不支持负载均衡。Erlang的集群中各节点是经由过程一个magic COOKIE来实现的,这个COOKIE存放在 $home/.erlang.COOKIE 中(像我的root用户安装的就是放在我的root/.erlang.COOKIE中),文件是400的权限。所以必须包管各节点COOKIE对峙一致,不然节点之间就无法通信。

RabbitMQ的集群节点包括内存节点、磁盘节点。顾名思义内存节点就是将所有数据放在内存,磁盘节点将数据放在磁盘。不过,如果在投递消息时,打开了消息的持久化,那么即使是内存节点,数据还是安全的放在磁盘。

 Rabbitmq集群大概分为二种方式:

(1)普通模式:默认的集群模式。

(2)镜像模式:把需要的队列做成镜像队列。

一个rabbitmq集 群中可以共享 user,vhost,queue,exchange等,所有的数据和状态都是必须在所有节点上复制的,一个例外是,那些当前只属于创建它的节点的消息队列,尽管它们可见且可被所有节点读取。rabbitmq节点可以动态的加入到集群中,一个节点它可以加入到集群中,也可以从集群环境中移除。
   集群中有两种节点:
1 内存节点:只保存状态到内存(一个例外的情况是:持久的queue的持久内容将被保存到disk)
2 磁盘节点:保存状态到内存和磁盘。
内存节点虽然不写入磁盘,但是它执行比磁盘节点要好。集群中,只需要一个磁盘节点来保存状态 就足够了如果集群中只有内存节点,那么不能停止它们,否则所有的状态,消息等都会丢失。

良好的设计架构可以如下:在一个集群里,有3台以上机器,其中1台使用磁盘模式,其它使用内存模式。其它几台为内存模式的节点,无疑速度更快,因此客户端(consumer、producer)连接访问它们。而磁盘模式的节点,由于磁盘IO相对较慢,因此仅作数据备份使用。

一、普通模式:默认的集群模式

默认的集群模式,queue创建之后,如果没有其它policy,则queue就会按照普通模式集群。对于Queue来说,消息实体只存在于其中一个节点,A、B两个节点仅有相同的元数据,即队列结构,但队列的元数据仅保存有一份,即创建该队列的rabbitmq节点(A节点),当A节点宕机,你可以去其B节点查看,./rabbitmqctl list_queues 发现该队列已经丢失,但声明的exchange还存在。当消息进入A节点的Queue中后,consumer从B节点拉取时,RabbitMQ会临时在A、B间进行消息传输,把A中的消息实体取出并经过B发送给consumer,所以consumer应平均连接每一个节点,从中取消息。该模式存在一个问题就是当A节点故障后,B节点无法取到A节点中还未消费的消息实体。如果做了队列持久化或消息持久化,那么得等A节点恢复,然后才可被消费,并且在A节点恢复之前其它节点不能再创建A节点已经创建过的持久队列;如果没有持久化的话,消息就会失丢。这种模式更适合非持久化队列,只有该队列是非持久的,客户端才能重新连接到集群里的其他节点,并重新创建队列。假如该队列是持久化的,那么唯一办法是将故障节点恢复起来。
为什么RabbitMQ不将队列复制到集群里每个节点呢?这与它的集群的设计本意相冲突,集群的设计目的就是增加更多节点时,能线性的增加性能(CPU、内存)和容量(内存、磁盘)。理由如下

1。存储空间:如果每个集群节点每个队列的一个完整副本,增加节点需要更多的存储容量。例如,如果一个节点可以存储1 gb的消息,添加两个节点需要两份相同的1 gb的消息
2。性能:发布消息需要将这些信息复制到每个集群节点。对持久消息,要求为每条消息触发磁盘活动在所有节点上。每次添加一个节点都会带来 网络和磁盘的负载。

当然RabbitMQ新版本集群也支持队列复制(有个选项可以配置)。比如在有五个节点的集群里,可以指定某个队列的内容在2个节点上进行存储,从而在性能与高可用性之间取得一个平衡(应该就是指镜像模式)。

二、镜像模式:把需要的队列做成镜像队列,存在于多个节点,属于RabbitMQ的HA方案(镜像模式是在普通模式的基础上,增加一些镜像策略)

该模式解决了上述问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在consumer取数据时临时拉取。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用,一个队列想做成镜像队列,需要先设置policy,然后客户端创建队列的时候,rabbitmq集群根据“队列名称”自动设置是普通集群模式或镜像队列。具体如下:
队列通过策略来使能镜像。策略能在任何时刻改变,rabbitmq队列也近可能的将队列随着策略变化而变化;非镜像队列和镜像队列之间是有区别的,前者缺乏额外的镜像基础设施,没有任何slave,因此会运行得更快。为了使队列称为镜像队列,你将会创建一个策略来匹配队列,设置策略有两个键“ha-mode和 ha-params(可选)”。ha-params根据ha-mode设置不同的值,下面表格说明这些key的选项:

语法讲解:

在cluster中任意节点启用策略,策略会自动同步到集群节点  (hrsystem  一般"/")

rabbitmqctl set_policy -p hrsystem ha-allqueue"^" '{"ha-mode":"all"}'

rabbitmqctl set_policy -p / ha-all "^" '{"ha-mode":"all"}'

这行命令在vhost名称为hrsystem创建了一个策略,策略名称为ha-allqueue,策略模式为 all 即复制到所有节点,包含新增节点,策略正则表达式为 “^” 表示所有匹配所有队列名称。

例如rabbitmqctl set_policy -p hrsystem ha-allqueue "^message" '{"ha-mode":"all"}'

rabbitmqctl set_policy -p / ha-all "^message" '{"ha-mode":"all"}'

注意:"^message" 这个规则要根据自己修改,这个是指同步"message"开头的队列名称,我们配置时使用的应用于所有队列,所以表达式为"^"
官方set_policy说明参见
set_policy [-p vhostpath] {name} {pattern} {definition} [priority]

“nodes”策略和迁移master

需要注意的是设置和修改一个“nodes”策略将不会引起已经存在的master离开,尽管你让其离开。比如:如果一个队列在{A},并且你给它一个节点策略告知它在{B C},它将会在{A B C}。如果节点A那时失败或者停机了,那个节点上的镜像将不回来且队列将继续保持在{B C}(注:当队列已经是镜像队列且同步到其它节点,就算原节点宕机,也不影响其它节点对此队列使用)。

队列名称以“ha.”开头的队列都是镜像队列,镜像到集群内所有节点:

列名称以“two.”开头的队列,其策略镜像到集群内任何两个节点:

队列同步到指rabbitmq 节点  ,rabbitmqctl:

./rabbitmqctl set_policy sa-specify "^sa\.specify\." '{"ha-mode":"nodes","ha-params":["rabbit@is137","rabbit@raxtone"]}' 

切记,需要把队列同步到的节点都写进去。

4台机器  44、45、46,47,前3台部署rabbitmq,后一台部署ha

1. 修改主机名称 zcy44,zcy45,zcy46,zcy47(参考之前的blog)

 hostnamectl --static set-hostname zcy44

重启命令:reboot

2. 修改/etc/hosts (44,45,46 三台集群的hosts文件中添加以下,同步主机的/etc/hosts文件)

172.18.8.44  zcy44
172.18.8.45  zcy45

172.18.8.46  zcy46

3. 3台主机分别安装rabbitmq

    在3台主机上分别安装rabbitmq ,并且启动,并且打开管理界面

  安装参考另一个blog

3. 同步erlang.COOKIE文件 (此文件是隐藏,所以ls -al )

Erlang COOKIE 文件:/var/lib/rabbitmq/.erlang.COOKIE。这里将 node1 的该文件复制到 node2、node3,由于这个文件权限是 400,所以需要先修改 node2、node3 中的该文件权限为 777

然后将 node1 中的该文件拷贝到 node2、node3,最后将权限和所属用户/组修改回来

4. 加入集群

1> zcy44 上启动(命令必须是这样):service rabbitmq-server start

在zcy44节点上查看集群信息,此时集群中应只有自己

rabbitmqctl cluster_status


root@live-mq-01:~ # rabbitmqctl cluster_status

Cluster status of node 'rabbit@zcy44' ...

[

{nodes,[{disc,['rabbit@zcy44']}]},

#集群中的节点,disc表示为磁盘模式,ram表示为内存模式

{running_nodes,['rabbit@zcy44']},

#正在运行的集群节点

{cluster_name,<<"rabbit&#64;zcy44">>},

#集群的名称

{partitions,[]}

]

2> 下面zcy44,45,46 组成集群&#xff1a;

在zcy45上面先用service rabbitmq-server start启动&#xff0c;执行 rabbitmqctl cluster_status 看看&#xff0c;然后执行以下

1
2
3
root&#64;live-mq-02:~ # rabbitmqctl stop_app
root&#64;live-mq-02:~ # rabbitmqctl join_cluster rabbit&#64;zcy44
root&#64;live-mq-02:~ # rabbitmqctl start_app

在zcy46上面先用service rabbitmq-server start启动&#xff0c;执行 rabbitmqctl cluster_status 看看&#xff0c;然后执行以下

1
2
3
root&#64;live-mq-03:~ # rabbitmqctl stop_app
root&#64;live-mq-03:~ # rabbitmqctl join_cluster rabbit&#64;zcy44 --ram
root&#64;live-mq-03:~ # rabbitmqctl start_app


此时 node2 与 node3 也会自动建立连接&#xff1b;如果要使用内存节点&#xff0c;则可以使用

node2 # rabbitmqctl join_cluster --ram rabbit&#64;node1 加入集群。当然可以不用

3> 在任何一台节点上执行 rabbitmqctl cluster_status (结果忽略&#xff0c;贴的别人)&#xff0c;反正就是3台现在集群了


root&#64;live-mq-01:~ # rabbitmqctl cluster_status

Cluster status of node &#39;rabbit&#64;zcy44&#39; ...

[{nodes,[{disc,[&#39;rabbit&#64;live-mq-01&#39;]},

{ram,[&#39;rabbit&#64;live-mq-03&#39;,&#39;rabbit&#64;live-mq-02&#39;]}]},

{running_nodes,[&#39;rabbit&#64;live-mq-02&#39;,&#39;rabbit&#64;live-mq-03&#39;,&#39;rabbit&#64;live-mq-01&#39;]},

{cluster_name,<<"rabbit&#64;live-mq-01">>},

{partitions,[]}]

5. 设置镜像队列策略


在任意一个节点上执行&#xff1a;

rabbitmqctl set_policy -p / ha-all "^" &#39;{"ha-mode":"all"}&#39;

将所有队列设置为镜像队列&#xff0c;即队列会被复制到各个节点&#xff0c;各个节点状态保持一直。

rabbitmqctl list_policies 查看策略

6. 同步队列

rabbitmqctl sync_queue ryttest。 或者在rabbitmq 界面上点击队列&#xff0c;然后同步

6. 安装并配置 HAProxy及配置 (43主机)

https://www.jianshu.com/p/2bee5e32deeb

1>安装

yum install haproxy

&#xff12;&#xff1e;修改 /etc/haproxy/haproxy.cfg &#xff08;注意红色部分&#xff0c;其他不修改&#xff09;

#全局配置
global
        #日志输出配置&#xff0c;所有日志都记录在本机&#xff0c;通过local0输出
        log 127.0.0.1 local0 info
        #最大连接数
        maxconn 4096
        #改变当前的工作目录
        chroot /opt/haproxy-1.7.8
        #以指定的UID运行haproxy进程
        uid 99
        #以指定的GID运行haproxy进程
        gid 99
        #以守护进程方式运行haproxy #debug #quiet
        daemon
        #debug
        #当前进程pid文件
        pidfile /opt/haproxy-1.7.8/haproxy.pid


#默认配置
defaults
        #应用全局的日志配置
        log global
        #默认的模式mode{tcp|http|health}
        #tcp是4层&#xff0c;http是7层&#xff0c;health只返回OK
        mode tcp
        #日志类别tcplog
        option tcplog
        #不记录健康检查日志信息
        option dontlognull
        #3次失败则认为服务不可用
        retries 3
        #每个进程可用的最大连接数
        maxconn 2000
        #连接超时
        timeout connect 5s
        #客户端超时
        timeout client 120s
        #服务端超时
        timeout server 120s


#绑定配置

listen rabbitmq_cluster 

 &#xff42;ind 43主机ip:5672

        #配置TCP模式
        mode tcp
        #简单的轮询
        balance roundrobin
        #RabbitMQ集群节点配置
        server zcy44 192.168.0.2:5672 check inter 5000 rise 2 fall 3 weight 1
        server zcy45 192.168.0.3:5672 check inter 5000 rise 2 fall 3 weight 1
        server zcy46 192.168.0.4:5672 check inter 5000 rise 2 fall 3 weight 1


#haproxy监控页面地址

listen monitor 

 bind 43ip:8100

        mode http
        option httplog
        stats enable
        stats uri /stats
​​​​​​​

        stats refresh 5s

#---------------------------------------------------------------------
#---------------------------------------------------------------------
global# to have these messages end up in /var/log/haproxy.log you will# need to:## 1) configure syslog to accept network log events. This is done# by adding the &#39;-r&#39; option to the SYSLOGD_OPTIONS in# /etc/sysconfig/syslog## 2) configure local2 events to go to the /var/log/haproxy.log# file. A line like the following can be added to# /etc/sysconfig/syslog## local2.* /var/log/haproxy.log#log 127.0.0.1 local2chroot /var/lib/haproxypidfile /var/run/haproxy.pidmaxconn 4000user haproxygroup haproxydaemon# turn on stats unix socketstats socket /var/lib/haproxy/stats#---------------------------------------------------------------------
# common defaults that all the &#39;listen&#39; and &#39;backend&#39; sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaultsmode httplog globaloption httplogoption dontlognulloption http-server-close#option forwardfor except 127.0.0.0/8#注意这行得注释掉&#xff0c;不然报错option redispatchretries 3timeout http-request 10stimeout queue 1mtimeout connect 10stimeout client 1mtimeout server 1mtimeout http-keep-alive 10stimeout check 10smaxconn 3000#---------------------------------------------------------------------
#---------------------------------------------------------------------
#对MQ集群进行监听
listen rabbitmq_clusterbind 0.0.0.0:5673 #通过5673对m1和m2进行映射option tcplog #记录TCP连接状态和时间mode tcp #四层协议代理&#xff0c;即对TCP进行转发option clitcpka #开启TCP的Keep Alive(长连接模式)timeout connect 1s #haproxy与mq建立连接的超时时间timeout client 10s #客户端与haproxy最大空闲时间timeout server 10s #服务器与haproxy最大空闲时间balance roundrobin #采用轮询转发消息#每5秒发送一次心跳包&#xff0c;如果连续两次有响应则代表状态良好#如果连续3次没有响应&#xff0c;则视为服务故障&#xff0c;该节点将被剔除server 85node 192.168.7.85:5672 check inter 5s rise 2 fall 3server 86node 192.168.7.86:5672 check inter 5s rise 2 fall 3server 87node 192.168.7.87:5672 check inter 5s rise 2 fall 3listen rabbitmq_adminbind 0.0.0.0:8100server 85node 192.168.7.85:15672server 86node 192.168.7.86:15672server 87node 192.168.7.87:15672#开启监控服务
listen http_frontbind 0.0.0.0:1080 #监听端口stats refresh 30s #每30秒刷新一次stats uri /haproxy?stats #统计页面uristats auth admin123:admin123 #统计页面用户名和密码设置#---------------------------------------------------------------------
#---------------------------------------------------------------------

 

在上面的配置中“listen rabbitmq_cluster bind 192.168.0.9.5671”这里定义了客户端连接IP地址和端口号。这里配置的负载均衡算法是roundrobin&#xff0c;注意这里的roundrobin是加权轮询。和RabbitMQ最相关的是“ server rmq_node1 192.168.0.2:5672 check inter 5000 rise 2 fall 3 weight 1”这种&#xff0c;它定义了RabbitMQ服务&#xff0c;每个RabbitMQ服务定义指令包含6个部分

server &#xff1a;定义RabbitMQ服务的内部标示&#xff0c;注意这里的“rmq_node”是指包含有含义的字符串名称&#xff0c;不是指RabbitMQ的节点名称。
:&#xff1a;定义RabbitMQ服务的连接的IP地址和端口号。
check inter &#xff1a;定义了每隔多少毫秒检查RabbitMQ服务是否可用。
rise &#xff1a;定义了RabbitMQ服务在发生故障之后&#xff0c;需要多少次健康检查才能被再次确认可用。
fall &#xff1a;定义需要经历多少次失败的健康检查之后&#xff0c;HAProxy才会停止使用此RabbitMQ服务。
weight &#xff1a;定义了当前RabbitMQ服务的权重。

最后一段配置定义的是HAProxy的数据统计页面。数据统计页面包含各个服务节点的状态、连接、负载等信息。在调用

haproxy -f haproxy.cfg

行HAProxy之后可以在浏览器上输入http://192.168.0.9:8100/stats来加载相关的页面

7.其他

1> 磁盘节点改成内存节点

   如果想把live-mq-02由磁盘节点改成内存节点

此时live-mq-02与live-mq-03也会自动建立连接&#xff0c;上面我的两个节点&#xff0c;其中live-mq-02是磁盘节点&#xff0c;live-mq-03是内存节点&#xff0c;但live-mq-01节点默认是磁盘节点&#xff08;一个集群中最少要有一个磁盘节点&#xff09;。如果想把live-mq-02由磁盘节点改成内存节点&#xff0c;使用如下change_cluster_node_type命令修改即可&#xff0c;但要先stop&#xff1a;

1
2
3
4
5
6
7
8
9
root&#64;live-mq-02:~ # rabbitmqctl stop_app
Stopping node &#39;rabbit&#64;live-mq-02&#39; ...
...done.
root&#64;live-mq-02:~ # rabbitmqctl change_cluster_node_type ram
Turning &#39;rabbit&#64;live-mq-02&#39; into a ram node ...
...done.
root&#64;live-mq-02:~ # rabbitmqctl start_app
Starting node &#39;rabbit&#64;live-mq-02&#39; ...
...done.

2> RabbitMQ退出集群

假设要把rabbit&#64;live-mq-02退出集群&#xff0c;在rabbit&#64;live-mq-02上执行&#xff1a;

1
2
3
$ rabbitmqctl stop_app
$ rabbitmqctl reset
$ rabbitmqctl start_app

或者&#xff1a;

在集群主节点上执行

1
$ rabbitmqctl forget_cluster_node rabbit&#64;live-mq-02

3> RabbitMQ集群重启

集群重启时&#xff0c;最后一个挂掉的节点应该第一个重启&#xff0c;如果因特殊原因&#xff08;比如同时断电&#xff09;&#xff0c;而不知道哪个节点最后一个挂掉。可用以下方法重启&#xff1a;

先在一个节点上执行

1
2
$ rabbitmqctl force_boot
$ service rabbitmq-server start

在其他节点上执行

1
$ service rabbitmq-server start

查看cluster状态是否正常&#xff08;要在所有节点上查询&#xff09;。

1
rabbitmqctl cluster_status

如果有节点没加入集群&#xff0c;可以先退出集群&#xff0c;然后再重新加入集群。

队列删除不了&#xff0c;处理方式&#xff1a;

  • 输入命令rabbitmqctl list_queues&#xff0c;有2万多条数据

  • 关闭应用

    rabbitmqctl stop_app

    执行清除命令

  • rabbitmqctl reset

    启动应权用

  • rabbitmqctl start_app

    验证清除结果rabbitmqctl list_queues




推荐阅读
  • 什么是网关服务器初学linux服务器开发时,我们的服务器是很简单的,只需要一个程序完成与客户端的连接,接收客户端数据,数据处理,向客户端发送数据。但是在处理量很大的情况下,一 ... [详细]
  • 在单位的一台4cpu的服务器上部署了esxserver,挂载了6个虚拟机,目前运行正常。在安装部署过程中,得到了cnvz.net论坛精华区 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • Nginxgaodaima.comnginx属于七层架构,支持的是http协议,本身对tcp协议没有支持。所以不能代理mysql等实现负载均衡。但是lvs这个东西不熟悉,主要是公司 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
  • Nginx Buffer 机制引发的下载故障
    Nginx ... [详细]
  • Linux系统高级网络配置:链路聚合
    链路聚合网卡的链路聚合就是将多块网卡连接起来,当一块网卡损坏,网络依旧可以正常运行,可以有效的防止因为网卡损坏带来的损失,同 ... [详细]
  • 服务网关与流量网关
    一、为什么需要服务网关1、什么是服务网关传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关& ... [详细]
  • ZooKeeper 学习
    前言相信大家对ZooKeeper应该不算陌生。但是你真的了解ZooKeeper是个什么东西吗?如果别人面试官让你给他讲讲ZooKeeper是个什么东西, ... [详细]
author-avatar
pz51747pz你
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有