Linux源码安装RabbitMQ高可用集群
1.环境说明
- linux版本:CentOS Linux release 7.9.2009
- erlang版本:erlang-24.0
- rabbitmq版本:rabbitmq_server-3.9.13
2.1 下载erlang、RabbitMQ源码包 erlang官网下载地址:http://www.erlang.org/download 选择opt_src_版本号.tar.gz(这里我选择的是 otp_src_24.0.tar.gz)
或者在linux服务器上使用 wget http://www.erlang.org/download/otp_src_24.0.tar.gz 进行下载
文章图片
RabbitMq官网下载地址:https://www.rabbitmq.com/download.html
文章图片
文章图片
2.2 安装erlang 1. 安装相关依赖
yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel gtk2-devel binutils-devel unixODBC unixODBC-devel xz
2. 上传并解压erlang安装包
#上传otp_src_24.0.tar.gz rabbitmq-server-generic-unix-3.9.13.tar.xz 到指定路径(这里我上传到/lee) #解压安装包 [root@lee lee]# tar -zxvf otp_src_24.0.tar.gz [root@lee lee]# ll total 111200 drwxr-xr-x 12 2004 wheel4096 May 122021 otp_src_24.0 -rw-r--r--1 root root101193664 Mar 21 09:45 otp_src_24.0.tar.gz -rw-r--r--1 root root12660820 Mar 21 09:45 rabbitmq-server-generic-unix-3.9.13.tar.xz
3. 编译安装erlang
#创建erlang安装目录 [root@lee lee]# mkdir -p /lee/erlang #进入otp_src_24.0目录 [root@lee lee]# cd otp_src_24.0 [root@lee otp_src_24.0]# ./configure prefix=/lee/erlang [root@lee otp_src_24.0]# make && make install #安装完成后安装路径下的目录如下 [root@lee otp_src_24.0]# cd ../erlang/ [root@lee erlang]# ll total 8 drwxr-xr-x 2 root root 4096 Mar 21 11:03 bin drwxr-xr-x 3 root root 4096 Mar 21 11:02 lib
在执行 ./configure prefix=/lee/erlang 时出现如下问题可不用处理,这样编译完成
文章图片
4. 配置环境变量并验证erlang是否安装成功
# 将erlang安装路径/bin 添加到 /ect/profile 末尾 [root@lee erlang]# vi /etc/profile [root@lee erlang]# source /etc/profile # 验证是否安装成功,执行erl 结果如下表示正常 [root@lee erlang]# erl Erlang/OTP 24 [erts-12.0] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1]Eshell V12.0(abort with ^G) 1> # 执行 halt(). 退出控制台 1> halt(). [root@lee erlang]#
2.3. 安装RabbitMQ 安装RabbitMQ前需要确认erlang安装成功
# 解压 rabbitmq-server-generic-unix-3.9.13.tar.xz [root@lee lee]# xz -d rabbitmq-server-generic-unix-3.9.13.tar.xz [root@lee lee]# tar -xvf rabbitmq-server-generic-unix-3.9.13.tar [root@lee lee]# mv rabbitmq_server-3.9.13/ rabbitmq # 配置 RabbitMQ 环境变量 # 将 export PATH=$PATH:/lee/rabbitmq/bin:/lee/rabbitmq/sbin 添加到 /etc/profile 末尾 [root@lee lee]# vi /etc/profile [root@lee lee]# source /etc/profile # 后台启动 RabbitMQ [root@lee lee]# rabbitmq-server -detached # 查看端口 5672 存在表示启动成功 [root@lee lee]# ss -tnlp|grep 5672 LISTEN0128*:25672*:*users:(("beam.smp",pid=8418,fd=17)) LISTEN0128[::]:5672[::]:*users:(("beam.smp",pid=8418,fd=32)) # 添加rabbitmq浏览器管理插件 [root@lee lee]# rabbitmq-plugins enable rabbitmq_management # RabbitMQ有默认用户名密码 guest/guest,该用户名密码只能在本地登陆 # 如若在浏览器中登陆,须新创建用户名密码 # 添加用户名密码 [root@lee lee]# rabbitmqctl add_user rabbitmq_user rabbitmq_pwd # 检查用户列表 [root@lee lee]# rabbitmqctl list_users Listing users ... usertags guest[administrator] rabbitmq_user[] # 为 rabbitmq_user 用户添加administrator角色 [root@lee lee]# rabbitmqctl set_user_tags rabbitmq_user administrator Setting tags for user "rabbitmq_user" to [administrator] ... # 设置 rabbitmq_user 用户权限,允许访问vhost及read/write [root@lee lee]# rabbitmqctl set_permissions -p / rabbitmq_user ".*" ".*" ".*" Setting permissions for user "rabbitmq_user" in vhost "/" ... # 检查权限列表 [root@lee lee]# rabbitmqctl list_permissions -p / Listing permissions for vhost "/" ... userconfigurewriteread guest.*.*.* rabbitmq_user.*.*.* # 启动消息队列服务 [root@lee lee]# rabbitmqctl start_app Starting node rabbit@lee ... # 验证15672端口存在表示消息队列服务启动成功 [root@lee lee]# ss -tnlp | grep 15672
LISTEN0128*:15672*:*users:(("beam.smp",pid=8418,fd=35))
2.4 浏览器登陆RabbitMQ管理界面 在浏览器中访问 ip:15672 登陆管理页面,若重新安装 RabbitMQ 后,该界面底部出现 ReferenceError: disable_stats is not defined,ctrl+f5清除页面缓存后重新登陆
文章图片
查看服务状态,若为绿色表示正常
文章图片
3. 搭建RabbitMQ高可用集群 搭建集群前,需要在各个节点上安装好RabbitMQ,安装方法见 2.源码安装erlang、RabbitMQ,本文使用镜像模式搭建集群,HaProxy作为负载均衡,使用keepalived实现高可用。
3.1 镜像队列集群 RabbitMQ集群可以使得在RabbitMQ节点宕机时,消费者和生产者可以继续运行;并且可以承载更多的业务量增加系统吞吐量。
普通模式集群下可能会丢失数据,例如生产者通过连接node1上传消息,消息实体只会保存到node1节点的Queue中,node2和node3节点只是保存相同的Queue结构,不保存消息实体,消费者如通过node2(或mode3)进行消费时,RabbitMQ会临时在node1和node2(或node3)之间进行消息传输,将node1中的消息通过node2(或node3)发送给消费者,在此期间node1发生宕机,node2(或node3)无法获取该消息,若该消息进行了持久化,则node1节点恢复后,该消息可继续被消费,若未进行持久化,则该消息会丢失。此时在普通模式集群的基础上构建镜像队列,可以将消息数据保存在多个节点上。
Haproxy通过负载均衡算法将消息分发给集群中的各个节点。
搭建两台Haproxuy,使用keepalived对其做高可用,两台keepalived服务器之间通过vrrp进行交互,对外通过虚拟ip(192.168.0.45)进行访问haproxy
总结:客户端通过虚拟ip(192.168.0.45)进行访问haproxy,通过keepalived会将其发放到MASTER节点上的haproxy,若MASTER节点上的Haproxy宕机,则会发放到SLAVE节点上,通过Haproxy会访问到RabbitMQ集群,通过集群访问各个RabbitMQ
文章图片
3.2 搭建镜像模式集群 使用三台服务器制作集群,主机名可执行命令 hostname 查看
ip | 主机名 | 部署的服务 |
192.168.0.1 | node1 | RabbitMQ + HAProxy + KeepAlived |
192.168.0.2 | node2 | RabbitMQ + HAProxy + KeepAlived |
192.168.0.3 | node3 | RabbitMQ |
分别在三个节点上 /etc/hosts 文件中添加 三台服务器ip和主机名之间的映射
vi /etc/hosts # 添加如下内容 192.168.0.1 node1 192.168.0.2 node2 192.168.0.3 node3
2、同步erlang cookies
RabbitMQ是通过erlang编写,erlang语言通过同步erlang集群各个节点的cookie实现分布式。
随机选择一个节点(这里选择node1),将该节点上的 .erlang.cookie文件拷贝到另两个节点上,该文件相当于密钥令牌,集群中的各个节点通过该令牌进行相互认证。
RabbitMQ服务启动时,erlang虚拟机会自动创建.erlang.cookie文件,默认路径为 /var/lib/rabbitmq/.erlang.cookie(rpm方式安装)或$HOME/.erlang.cookie,该文件是个隐藏文件,可通过 ls -al查看。
在node1节点上执行如下命令同步 .erlang.cookie文件
# 将 node1上 /root/.erlang.cookie 拷贝到node2上 scp /root/.erlang.cookie node2:/root/.erlang.cookie # 将 node1上 /root/.erlang.cookie 拷贝到node2上 scp /root/.erlang.cookie node3:/root/.erlang.cookie # 分别在三个节点上查看该文件内容是否相同 more /root/.erlang.cookie
3. RabbitMQ集群添加节点
分别在node2、node3节点执行如下命令,将node2、node3加入集群中
#在node2节点执行 #关闭应用 rabbitmqctl stop_app #将node2加入集群中 rabbitmqctl join_cluster ‐‐ram rabbit@node2 #启动应用 rabbitmqctl start_app#在node3节点执行 #关闭应用 rabbitmqctl stop_app #将node3加入集群中 rabbitmqctl join_cluster ‐‐ram rabbit@node3 #启动应用 rabbitmqctl start_app
4. 验证集群是否配置成功
执行如下命令进行验证
# 在任一节点执行如下命令验证集群是否配置成功 rabbitmqctl cluster_status#出现如下内容表示成功 Disk Nodesrabbit@node1 rabbit@node2 rabbit@node3Running Nodesrabbit@node1 rabbit@node2 rabbit@node3
或使用任一节点 ip:15672 登陆管理页面,用户名密码为主节点node1的用户名密码,如下图显示绿色表示成功
文章图片
5. 配置镜像队列集群
以上配置的集群只是普通集群,该集群下可能会丢失数据,例如生产者通过连接node1上传消息,消息实体只会保存到node1节点的Queue中,node2和node3节点只是保存相同的Queue结构,不保存消息实体,消费者如通过node2(或mode3)进行消费时,RabbitMQ会临时在node1和node2(或node3)之间进行消息传输,将node1中的消息通过node2(或node3)发送给消费者,在此期间node1发生宕机,node2(或node3)无法获取该消息,若该消息进行了持久化,则node1节点恢复后,该消息可继续被消费,若未进行持久化,则该消息会丢失。
如下为通过命令方式和管理页面方式实现:同步 virtual host 为 "/"下名称前缀为 "mirroring"的交换机和队列,并且自动保存到两个节点上
(1)命令行方式配置镜像队列
在任一节点上执行如下命令:
#同步 virtual host 为 "/"下名称前缀为 "mirroring"的交换机和队列,并且自动保存到两个节点上 rabbitmqctl set_policy -p / --priority 1 --apply-to all myPolicy "^mirroring" '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'# policy配置格式 rabbitmqctl set_policy [-p] [--priority ] [--apply-to ]
policy参数说明
参数 | 描述 |
-p | 可选参数,针对指定 Virtual host |
–priority | 可选参数,policy 的优先级 |
–apply-to | 可选参数,策略适用的对象类型,其值可为 queues、exchanges、all,默认是 all |
name | policy 的名称 |
pattern | 匹配模式(正则表达式) |
definition | 镜像定义,json 格式,包括三部分(ha-mode,ha-params,ha-sync-mode)具体配置见下表 |
definition参数说明
参数 | 描述 |
ha-mode | ha-mode:指明镜像队列的模式,有效值为 all、exactly、nodes all:表示在集群中所有的节点上进行镜像 exactly:表示在指定个数的节点上进行镜像,节点的个数由ha-params指定 nodes:表示在指定的节点上进行镜像,节点名称通过ha-params指定 |
ha-params | ha-mode模式需要用到的参数(ha-mode=exactly时为整数,nodes时为字符串数组,all时不用指定) |
ha-sync-mode | ha-sync-mode:进行队列中消息的同步方式,有效值为automatic和manual manual:手动<默认模式>.新的队列镜像将不会收到现有的消息,它只会接收新的消息,除非显式调用同步命令 【Linux源码安装RabbitMQ高可用集群】automatic:自动同步,新加入节点时会默认同步已知的镜像队列 命令 rabbitmqctl sync_queue 队列名,手动同步队列 命令 rabbitmqctl cancel_sync_queue 队列名 取消某个queue的同步 |
(2)通过管理页面添加:通过该集群任一节点ip:15672登陆
文章图片
6. 集群策略相关命令
设置策略:rabbitmqctl set_policy [-p
清除策略:
rabbitmqctl clear_policy [-p ]
查看策略:
rabbitmqctl list_policies [-p ]
7. 将某节点退出集群
# 在退出的节点上执行 rabbitmqctl stop_app rabbitmqctl reset rabbitmqctl start_app# 在主节点上执行(如去除node2),offline参数代表允许离线删除 rabbitmqctl forget_cluster_node node2 [--offline]
3.3 配置HaProxy负载均衡 HAProxy提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费、 快速并且可靠的一种解决方案。HAProxy实现了一种事件驱动、单一进程模型,此模型支持非常大的并发连接数。
安装包下载地址:https://www.haproxy.org/#down
在linux上下载: wget https://www.haproxy.org/download/2.5/src/haproxy-2.5.5.tar.gz
3.3.1 上传安装
系统内核:3.10.0-1160.53.1.el7.x86_64
选择两个节点分别安装 Haproxy,这里选择node1,node2节点(分别在两个节点上执行如下操作)
# 上传 haproxy-2.5.5.tar.gz 并解压(此次上传到/usr/local) tar -zxvf haproxy-2.5.5.tar.gz mv haproxy-2.5.5.tar.gz haproxy cd haproxy#编译并指定安装目录 make TARGET=linux-glibc ARCH=x86_64 PREFIX=/usr/local/haproxy # 安装 make install PREFIX=/usr/local/haproxy
参数说明:
TARGET和ARCH通过uname -r 查看服务器内核,根据内核信息匹配 haproxy/INSTALL(有些版本可查看 haproxy/README)中对应的参数,该参数不同版本有所区别
TARGET:内核版本
ARCH:系统CUP(64位为 x86_64)
PREFIX:指定安装目录
文章图片
3.3.2 配置 Haproxy 环境变量
vi /etc/profile # 将如下内容导入该文件中 export HAPROXY_HOME=/usr/local/haproxy#安装路径 export PATH=$PATH:$HAPROXY_HOME/sbin # 生效 source /etc/profile
3.3.3 负载均衡配置
创建用户,日志目录
#添加haproxy组 groupadd haproxy #创建nginx运行账户haproxy并加入到haproxy组,不允许haproxy用户直接登录系统 useradd -g haproxy haproxy -s /bin/false #创建haproxy错误日志目录 mkdir -p /usr/local/haproxy/errors cp -r /usr/local/haproxy/examples/errorfiles/* /usr/local/haproxy/errors/#创建日志文件 mkdir -p /usr/local/haproxy/logs touch /usr/local/haproxy/logs/haproxy.log #创建haproxy配置文件 mkdir -p /usr/local/haproxy/conf touch /usr/local/haproxy/conf/haproxy.cfg touch /usr/local/haproxy/stats
修改配置文件:/usr/local/haproxy/conf/haproxy.cfg
#‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ # Global settings #‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ global log 127.0.0.1 local2 chroot /usr/local/haproxy #haproxy的pid存放路径,启动进程的用户必须有权限访问此文件 pidfile /var/run/haproxy.pid maxconn 4000 user haproxy group haproxy daemon #需要创建该文件 stats socket /usr/local/haproxy/statsdefaults mode http #采用全局定义的日志 log global #日志类别http日志格式 option httplog #不记录健康检查的日志信息 option dontlognull #每次请求完毕后主动关闭http通道 option http-server-close option forwardfor except 127.0.0.0/8 #serverId对应的服务器挂掉后,强制定向到其他健康的服务器 option redispatch ##3次连接失败就认为服务不可用,也可以通过后面设置 retries 3 timeout http-request 10s timeout queue 1m#default 10 second timeout if a backend is not found timeout connect 10s #客户端连接超时 timeout client 1m #服务器连接超时 timeout server 1m timeout http-keep-alive 10s timeout check 10s #最大连接数 maxconn 3000#开启haproxy监控服务 listen rabbitmq_cluster #通过6672对节点进行映射(自定义) bind 0.0.0.0:6672 #记录tcp连接的状态和时间 option tcplog #四层协议代理,即对TCP协议转发 mode tcp #开启TCP的Keep Alive(长连接模式) option clitcpka #haproxy与mq建立连接的超时时间 timeout connect 1s #客户端与haproxy最大空闲时间 timeout client 10s #服务器与haproxy最大空闲时间 timeout server 10s #采用轮询转发消息 balance roundrobin#每5秒发送一次心跳包,如连续两次有响应则代表状态良好 #如连续三次没有响应,则视为服务故障,该节点将被剔除。 server node1 节点1 ip地址:5672 check inter 5s rise 2 fall 3 server node2 节点2 ip地址:5672 check inter 5s rise 2 fall 3listen http_front #监听端口-页面访问的端口(自定义) bind 0.0.0.0:6001 #统计页面自动刷新时间stats refresh 30s stats refresh 30s#统计页面url(页面访问的uri,即浏览器通过 ip:6001/haproxy_stats 访问) stats uri /haproxy_stats# 指定Haproxy访问用户名密码 stats auth haproxy_admin:haproxy_pwd########设置haproxy 错误页面##### errorfile 403 /usr/local/haproxy/errors/403.http errorfile 500 /usr/local/haproxy/errors/500.http errorfile 502 /usr/local/haproxy/errors/502.http errorfile 503 /usr/local/haproxy/errors/503.http errorfile 504 /usr/local/haproxy/errors/504.http
开启日志记录:安装Haproxy之后,默认是没有开启日志记录的,需要根据rsyslog通过udp的方式获取Haproxy日志信息
vi /etc/rsyslog.conf #打开以下两行注解,开启 514 USP监听 $ModLoad imudp $UDPServerRun 514 #添加日志目录 (local2 与haproxy.cfg中global log保持一致) local2.*/usr/local/haproxy/haproxy.logvi /etc/sysconfig/rsyslog #修改如下内容(若没有则添加) SYSLOGD_OPTIONS="-r -m 0 -c 2" #重启生效 service rsyslog restart
3.3.4 启动Haproxy验证
haproxy相关命令
# 检查配置文件语法 haproxy -c -f /usr/local/haproxy/haproxy.cfg # 启动 haproxy -f /usr/local/haproxy/haproxy.cfg# 以daemon模式启动,以systemd管理的daemon模式启动 haproxy -D -f /usr/local/haproxy/haproxy.cfg haproxy -Ds -f /usr/local/haproxy/haproxy.cfg# 启动调试功能,将显示所有连接和处理信息在屏幕 haproxy -d -f /usr/local/haproxy/haproxy.cfg# 重启 需要使用st选项指定pid列表 haproxy -f /usr/local/haproxy.cfg -st `cat /var/run/haproxy.pid`# graceful restart,即reload。需要使用sf选项指定pid列表 haproxy -f /usr/local/haproxy.cfg -sf `cat /var/run/haproxy.pid`# 显示haproxy编译和启动信息 haproxy -vv
浏览器登陆 ip:6001/haproxy_stats,显示如下页面表示正常(须开放6001端口)
文章图片
3.4 安装keepalived 官网下载地址:https://www.keepalived.org/download.html
在安装Haproxy的两个节点 node1,node2 (192.168.0.1、192.168.0.2)上安装keepalived
# 安装依赖 yum install -y gcc openssl-devel popt-devel ipvsadm libnl3-devel net-snmp-devel libnl libnl-devel libnfnetlink-devel# 上传安装包到 /usr/local 并解压 tar -zxvf keepalived-2.2.4 # 创建安装目录 mkdir /usr/local/keepalived # 编译 cd keepalived-2.2.4 ./configure --prefix=/usr/local/keepalived # 安装 make && make install# 创建keepalived配置文件目录 keepalived默认会读取 /etc/keepalived/keepalived.conf madir keepalived.confcp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/ cp /usr/local/keepalived-2.2.4/keepalived/etc/init.d/keepalived /etc/init.d/ cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig cp /usr/local/keepalived/sbin/keepalived /usr/sbin/# 创建关闭keepalived的脚本 vi /etc/keepalived/stop_keepalived # 脚本内容 监听haproxy端口 6001 #! /bin/bash counter=$(ss -tanlp | grep "LISTEN" | grep "6001"|wc -l) if [ "${counter}" -eq 0 ] then pkill keepalived fi
修改配置文件: vi /etc/keepalived/keepalived.conf
主节点node1上的配置文件
! Configuration File for keepalivedglobal_defs { script_user root enable_script_security router_id node1 vrrp_skip_check_adv_addr #vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0 }vrrp_instance VI_1 { #keepalived角色,MASTER表示主节点 BACKUP从节点 state MASTER #指定检测的网卡 interface eth0 #虚拟路由的id,主备节点设置相同 virtual_router_id 60 #优先级,主节点的优先级需要设置的比从节点高 priority 90 #设置主备之间的检查时间,单位s advert_int 1 #定义验证类型和密码 authentication { auth_type PASS auth_pass 1111 } # 另一个节点的ip unicast_peer { 192.168.0.2 } #虚拟ip virtual_ipaddress { 192.168.0.45 } }virtual_server 192.168.0.45 6001 { virtual_server delay_loop 6 lb_algo rr lb_kind NAT persistence_timeout 50 protocol TCPreal_server 192.168.0.1 6001 { weight 1 # 监控脚本 notify_down /etc/keepalived/stop_keepalived.sh TCP_CHECK { connect_timeout 10 retry 3 connect_port 6001 } } }
从节点node2配置文件
! Configuration File for keepalivedglobal_defs { script_user root enable_script_security router_id node2 vrrp_skip_check_adv_addr #vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0 }vrrp_instance VI_1 { #keepalived角色,MASTER表示主节点 BACKUP从节点 state MASTER #指定检测的网卡 interface eth0 #虚拟路由的id,主备节点设置相同 virtual_router_id 60 #优先级,主节点的优先级需要设置的比从节点高 priority 90 #设置主备之间的检查时间,单位s advert_int 1 #定义验证类型和密码 authentication { auth_type PASS auth_pass 1111 } # 另一个节点的ip unicast_peer { 192.168.0.1 } #虚拟ip virtual_ipaddress { 192.168.0.45 } }virtual_server 192.168.0.45 6001 { virtual_server delay_loop 6 lb_algo rr lb_kind NAT persistence_timeout 50 protocol TCPreal_server 192.168.0.2 6001 { weight 1 # 监控脚本 notify_down /etc/keepalived/stop_keepalived.sh TCP_CHECK { connect_timeout 10 retry 3 connect_port 6001 } } }
启动命令 /etc/init.d/keepalived start
关闭命令 /etc/init.d/keepalived stop
查看日志 tail -f /var/log/messages
验证:启动后在两个节点上使用 ip addr 命令,查看配置虚拟ip(192.168.0.45)是否在配置的网卡(eth0)下,如果主节点下存在从节点点不存在,说明正常,此时关闭主节点haproxy,虚拟ip(192.168.0.45)会漂移到从节点,重新启动主节点keepalived会重新漂移到主节点,此时可以根据 192.168.0.45连接haproxy
推荐阅读
- 开发过程中的常见问题|解决 Public key for xxx.rpm is not installed 公钥未安装
- Linux之seq命令
- 小白都能看懂的|小白都能看懂的 Spring 源码揭秘之Spring MVC
- Vue源码学习之响应式是如何实现的
- Spring事件监听机制源码解析
- 安全|Kali Linux工具大全-信息收集
- 苦练基本功|liunx中如何查看软件的安装目录
- 源码阅读分析-PHP-laravel
- V2P|搭建V2P及中青看点教程
- 青龙傻妞若兰|青龙面板安装教程+手机京豆组件让你的手机美起来