中间件(指令和原理)
2022-07-13 16:14:23 0 举报
AI智能生成
中间件相关
作者其他创作
大纲/内容
ES
指令
运维指令
开发指令
Nginx
作用和介绍
负载均衡
反向代理
搭建
/usr/sbin/nginx:主程序
/etc/nginx/:存放Nginx配置文件。在指向域名、安装SSL证书时都在这个目录里
/usr/share/nginx/:存放Nginx默认指向的静态网页
/var/log/nginx/:存放Nginx访问和错误日志
/etc/nginx/:存放Nginx配置文件。在指向域名、安装SSL证书时都在这个目录里
/usr/share/nginx/:存放Nginx默认指向的静态网页
/var/log/nginx/:存放Nginx访问和错误日志
sudo apt-get install nginx
service nginx restart
查看nginx状态
ps -A | grep nginx
server_name的作用
server name 为虚拟服务器的识别路径。因此不同的域名会通过请求头中的HOST字段,
匹配到特定的server块,转发到对应的应用服务器中去
匹配到特定的server块,转发到对应的应用服务器中去
负载均衡算法
URLhash
用于缓存,对同样URL的请求,可以hash到同一台上,这样缓存就有效
万一某节点挂了,会导致会序hash算法失效,缓存访问错位,如何解决?
一致性hash
把所有服务器的ip映射到了一致性hash环
如何保证平衡性?
即一致性hash倾斜的问题
即一致性hash倾斜的问题
增加虚拟节点,虚拟节点映射真实节点
iphash算法
保证一个ip只映射到某一台固定的后端,解决回话保持问题
nginx启用iphash算法后,backup不能用
分布式session解决方案与一致性hash
分布式session解决方案
单点登录
单点登录
session同步法
多个web-server之间相互同步session,这样每个web-server之间都包含全部的session
客户端存储法
服务端存储所有用户的session,内存占用较大,可以将session存储到浏览器cookie中,每个端只要存储一个用户的数据了
反向代理hash一致性
方案一:四层代理hash
反向代理层使用用户ip来做hash,以保证同一个ip的请求落在同一个web-server上
反向代理层使用用户ip来做hash,以保证同一个ip的请求落在同一个web-server上
方案二:七层代理hash
反向代理使用http协议中的某些业务属性来做hash,例如sid,city_id,user_id等,能够更加灵活的实施hash策略,
以保证同一个浏览器用户的请求落在同一个web-server上
反向代理使用http协议中的某些业务属性来做hash,例如sid,city_id,user_id等,能够更加灵活的实施hash策略,
以保证同一个浏览器用户的请求落在同一个web-server上
后端统一存储
将session存储在web-server后端的存储层,数据库或者缓存,比如:redis
fair
按后端服务器的响应时间来分配请求,响应时间短的优先分配
关键属性
max_fails和fail_timeout的区别
启用backup功能,表示热备,在节点down机后,立刻启动,主节点恢复后切换回原节点
负载均衡的几种常用方式
nginx为什么这么快
路径跳转
软件基础知识
SoftwareBasic
SoftwareBasic
Java学习笔记
Java_Practice
Java_Practice
Go学习笔记
GO_Practice
GO_Practice
高效开发工具
EfficientDevTools
EfficientDevTools
数据结构和算法
AlgorithmPractice
AlgorithmPractice
机器学习
高可用/高并发/高性能
解决方案(3H)
解决方案(3H)
数据库(原理和指令)
中间件(指令和原理)
踩坑记录(复盘和总结)
工作项目UML图汇总
思考
Docker
(K8s)
(K8s)
指令
运维指令
安装
docker安装之后避免每次都需要sudo
#查看当前用户在哪些组中:确实不在docker组中
groups
#将当前用户加入docker组中
sudo usermod -aG docker huyahui
#重新登陆当前用户
su - huyahui
#验证不用再使用sudo来执行docker命令
docker ps
groups
#将当前用户加入docker组中
sudo usermod -aG docker huyahui
#重新登陆当前用户
su - huyahui
#验证不用再使用sudo来执行docker命令
docker ps
容器管理
查看进程
docker ps -a
docker ps
docker container ls
docker container ls -all
查找容器版本
docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地
拉取容器
docker pull ubuntu
不带版本号,默认是最新
进入容器
docker run -it 容器名字
docker run -it 容器名字 bash
docker run -d ubuntu:15.10
docker attach 容器名字
docker exec -it 243c32535da7 /bin/bash
此处结尾的 bash 必须加上
docker run -p 8848:8848 容器名称
停止一个容器
docker stop <容器 ID>
删除容器
docker rm -f 1e560fca3906
删除容器时,容器必须是停止状态,否则会报如下错误
Error response from daemon: You cannot remove a running container
bf08b7f2cd897b5964943134aa6d373e355c286db9b9885b1f60b6e8f82b2b85.
Stop the container before attempting removal or force remove
bf08b7f2cd897b5964943134aa6d373e355c286db9b9885b1f60b6e8f82b2b85.
Stop the container before attempting removal or force remove
删除镜像,需要先删除容器
根据ID 查找容器
docker ps -a| grep IDIDIDID
重启
docker restart <容器 ID>
清理掉所有处于终止状态的容器
docker container prune
Docker容器和本机之间的文件传输
docker cp ID全称:容器文件路径 本地路径
或者反过来
开发指令
制作docker
docker file
将java项目打包成docker镜像
docker 会在当前文件夹下进行镜像打包操作
1、新建文件夹,把需要的文件copy进去
2、在该目录下编写docker file
示例
#基于centos镜像
FROM centos
#工作目录
WORKDIR /home/lj
#维护人的信息
MAINTAINER ljfirst <ljfirst@mail.ustc.edu.cn>
#install jdk and jar包
ADD jdk-8u231-linux-x64.tar.gz /usr/java/
ADD monitoringalarm-0.0.1-SNAPSHOT.jar /home/lj/
#jdk enviroment
ENV JAVA_HOME=/usr/java/jdk1.8.0_231
ENV JRE_HOME=/usr/java/jdk1.8.0_231/jre
ENV CLASSPATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib
ENV PATH=$JAVA_HOME/bin:$PATH
#设置端口
EXPOSE 8080
#执行命令
CMD ["java","-jar","/home/lj/monitoringalarm-0.0.1-SNAPSHOT.jar"]
FROM centos
#工作目录
WORKDIR /home/lj
#维护人的信息
MAINTAINER ljfirst <ljfirst@mail.ustc.edu.cn>
#install jdk and jar包
ADD jdk-8u231-linux-x64.tar.gz /usr/java/
ADD monitoringalarm-0.0.1-SNAPSHOT.jar /home/lj/
#jdk enviroment
ENV JAVA_HOME=/usr/java/jdk1.8.0_231
ENV JRE_HOME=/usr/java/jdk1.8.0_231/jre
ENV CLASSPATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib
ENV PATH=$JAVA_HOME/bin:$PATH
#设置端口
EXPOSE 8080
#执行命令
CMD ["java","-jar","/home/lj/monitoringalarm-0.0.1-SNAPSHOT.jar"]
Dockerfile中ADD的时候,copy的是目录下的文件,而目录本身不复制,
所以如果是某个文件夹,需要额外加一层文件夹
所以如果是某个文件夹,需要额外加一层文件夹
3、运行打包指令
docker build -t myapp . (.不要忘了)
4、运行镜像
运行的时候需要注意制定端口映射
Kubernetes
(k8s)
(k8s)
概念
k8s功能
服务的发现与负载的均衡
容器的自动装箱
自动化的容器的恢复
应用的自动发布与应用的回滚,以及与应用相关的配置密文的管理
job 类型任务,Kubernetes 可以去做批量的执行
支持水平的伸缩
k8s架构
架构图
API Server:
顾名思义是用来处理 API 操作的,Kubernetes 中所有的组件都会和 API Server 进行连接,
组件与组件之间一般不进行独立的连接,都依赖于 API Server 进行消息的传送;
Controller:
是控制器,它用来完成对集群状态的一些管理。比如刚刚我们提到的两个例子之中,
第一个自动对容器进行修复、第二个自动进行水平扩张,都是由 Kubernetes 中的 Controller 来进行完成的;
Scheduler:
是调度器,“调度器”顾名思义就是完成调度的操作,就是我们刚才介绍的第一个例子中,
把一个用户提交的 Container,依据它对 CPU、对 memory 请求大小,找一台合适的节点,进行放置;
etcd:
是一个分布式的一个存储系统,API Server 中所需要的这些原信息都被放置在 etcd 中,
etcd 本身是一个高可用系统,通过 etcd 保证整个 Kubernetes 的 Master 组件的高可用性。
顾名思义是用来处理 API 操作的,Kubernetes 中所有的组件都会和 API Server 进行连接,
组件与组件之间一般不进行独立的连接,都依赖于 API Server 进行消息的传送;
Controller:
是控制器,它用来完成对集群状态的一些管理。比如刚刚我们提到的两个例子之中,
第一个自动对容器进行修复、第二个自动进行水平扩张,都是由 Kubernetes 中的 Controller 来进行完成的;
Scheduler:
是调度器,“调度器”顾名思义就是完成调度的操作,就是我们刚才介绍的第一个例子中,
把一个用户提交的 Container,依据它对 CPU、对 memory 请求大小,找一台合适的节点,进行放置;
etcd:
是一个分布式的一个存储系统,API Server 中所需要的这些原信息都被放置在 etcd 中,
etcd 本身是一个高可用系统,通过 etcd 保证整个 Kubernetes 的 Master 组件的高可用性。
核心概念
Pod
最小调度以及资源单元
Volume
管理 Kubernetes 存储的
Deployment
定义一组 Pod 的副本数目、以及这个 Pod 的版本。
一般大家用 Deployment 这个抽象来做应用的真正的管理,
而 Pod 是组成 Deployment 最小的单元
一般大家用 Deployment 这个抽象来做应用的真正的管理,
而 Pod 是组成 Deployment 最小的单元
Kubernetes 是通过 Controller,也就是我们刚才提到的控制器去维护 Deployment 中 Pod 的数目,
它也会去帮助 Deployment 自动恢复失败的 Pod。
比如说我可以定义一个 Deployment,这个 Deployment 里面需要两个 Pod,
当一个 Pod 失败的时候,控制器就会监测到,它重新把 Deployment 中的 Pod 数目从一个恢复到两个,
通过再去新生成一个 Pod。通过控制器,我们也会帮助完成发布的策略。
比如说进行滚动升级,进行重新生成的升级,或者进行版本的回滚
它也会去帮助 Deployment 自动恢复失败的 Pod。
比如说我可以定义一个 Deployment,这个 Deployment 里面需要两个 Pod,
当一个 Pod 失败的时候,控制器就会监测到,它重新把 Deployment 中的 Pod 数目从一个恢复到两个,
通过再去新生成一个 Pod。通过控制器,我们也会帮助完成发布的策略。
比如说进行滚动升级,进行重新生成的升级,或者进行版本的回滚
Service
提供了一个或者多个 Pod 实例的稳定访问地址
一个 Deployment 可能有两个甚至更多个完全相同的 Pod。对于一个外部的用户来讲,
访问哪个 Pod 其实都是一样的,所以它希望做一次负载均衡,在做负载均衡的同时,
我只想访问某一个固定的 VIP,也就是 Virtual IP 地址,而不希望得知每一个具体的 Pod 的 IP 地址
访问哪个 Pod 其实都是一样的,所以它希望做一次负载均衡,在做负载均衡的同时,
我只想访问某一个固定的 VIP,也就是 Virtual IP 地址,而不希望得知每一个具体的 Pod 的 IP 地址
Namespace
集群内部的逻辑隔离的,它包括鉴权、资源管理等
Kubernetes 的每个资源,比如刚才讲的 Pod、Deployment、Service 都属于一个 Namespace,
同一个 Namespace 中的资源需要命名的唯一性,不同的 Namespace 中的资源可以重名
同一个 Namespace 中的资源需要命名的唯一性,不同的 Namespace 中的资源可以重名
k8s核心api
pod
共享网络
Infra container
共享存储
volume 叫做 shared-data,它是属于 Pod level 的
安装
Ubuntu16.04安装K8s步骤和踩坑记录
安装前奏
系统配置修改
禁用swap
swapoff -a
同时把/etc/fstab包含swap那行记录删掉。
关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
禁用Selinux
apt install selinux-utils
setenforce 0
swapoff -a
同时把/etc/fstab包含swap那行记录删掉。
关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
禁用Selinux
apt install selinux-utils
setenforce 0
各主机的主机名及ip配置。
同时在每台机器的/etc/hosts配置如下
10.2.14.78 wangcf-k8s-m
10.2.14.79 wangcf-k8s-n1
10.2.14.80 wangcf-k8s-n2
同时在每台机器的/etc/hosts配置如下
10.2.14.78 wangcf-k8s-m
10.2.14.79 wangcf-k8s-n1
10.2.14.80 wangcf-k8s-n2
安装docker
先安装相关工具
apt-get update && apt-get install -y apt-transport-https curl
可以拆开执行
添加docker密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
docker安装
apt-get install docker.io -y
-y 表示 一路yes
查看docker版本
docker version
使用 sudo docker version,可以看到Client和Server
启动docker service
systemctl enable docker
systemctl start docker
systemctl status docker
systemctl start docker
systemctl status docker
安装kubectl,kubelet,kubeadm
获取密钥
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
可以分开执行
先保存一个apt-key.gpg的文件,再通过apt-key add apt-key.gpg来加载
添加Kubernetes软件源
cat /etc/apt/sources.list.d/kubernetes.list
加入以下内容
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
加入以下内容
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
源有google、ustc、aliyun的
阿里Kubernetes 镜像
更新和安装
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
systemctl enable kubelet
配置master
配置node
Sidecar模式
Cache
Redis
安装
安装指令
状态检查和命令行访问
检查Redis服务器系统进程
ps -aux|grep redis
通过启动命令检查Redis服务器状态
netstat -nlt|grep 6379
通过启动命令检查Redis服务器状态
redis-server status
redis启动.停止.重启
守护进程方式在后台启动Redis
bin/redis-server conf/redis.conf
编辑配置文件,将daemonize参数设置为yes,并使用此配置文件启动
这里采用vim打开并编辑配置文件
$ sudo vim conf/redis.conf
输入/daemonize定位到daemonize no这一行,如果为yes则不需要修改了
再输入i或者a转换为输入模式把no改成yes
最后输入:wq保存并退出
指定此配置文件启动Redis:$ bin/redis-server conf/redis.conf
这里采用vim打开并编辑配置文件
$ sudo vim conf/redis.conf
输入/daemonize定位到daemonize no这一行,如果为yes则不需要修改了
再输入i或者a转换为输入模式把no改成yes
最后输入:wq保存并退出
指定此配置文件启动Redis:$ bin/redis-server conf/redis.conf
从软件仓库中安装的Redis使用init.d脚本启动
/etc/init.d/redis-server stop
/etc/init.d/redis-server start
/etc/init.d/redis-server restart
/etc/init.d/redis-server start
/etc/init.d/redis-server restart
/etc/init.d/redis start
/etc/init.d/redis 是安装的地方
版本
使用
mac使用redis
命令行使用
redis-cli
auth 密码
keys *
增加一条字符串记录key1
set key1 "hello"
打印记录
get key1
增加一条数字记录key2
set key2 1
INCR key2
增加一条列表记录key3
LPUSH key3 a
LPUSH key3 b
原理
redis为什么这么快
数据结构
快表,增删改查的时间复杂度低
单线程
不需要创建/销毁线程,避免上下文切换,无并发资源竞争的问题
单线程机制也避免了不必要的上下文切换和锁机制
基于内存
Redis的每一次I/O操作都是基于内存的,非常高效
网络模型
Redis 使用了I/O多路复用,保证了redis在进行I/O操作时依然能处理socket请求,不会在I/O上浪费时间
Redis是单线程的么?
多线程是怎么回事
多线程是怎么回事
首先明确CPU不是Redis的瓶颈,瓶颈是网络IO,
挑选一个优良的网络IO模型:epoll
挑选一个优良的网络IO模型:epoll
fd_set
是文件描述符,里面存的是文件句柄
IO多路复用的三种机制Select,Poll,Epoll
epoll的两种工作方式:1.水平触发(LT)2.边缘触发(ET)
边缘触发是强触发
1、epoll的核心是事件驱动,触发回调函数,
2、事件注册在红黑树上,效率高,无数量限制
2、事件注册在红黑树上,效率高,无数量限制
红黑树和双链表数据结构,并结合回调机制,造就了epoll的高效
同步和异步
同步值得是:数据已经在内核中ready
异步指的是:数据不光在内核中ready,而且还在用户缓存区ready
异步完成后,内核在IO完成后通知用户线程直接使用即可,
而同步完成,还需要把内核数据copy到用户线程缓存区
而同步完成,还需要把内核数据copy到用户线程缓存区
redis内存
redis内存模型
redis内存回收
错误集锦
连接报错
Exception in thread "main" redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketT
1)机器之间网络无法联通
2)ip和端口号不正确
3)虚拟机中防火墙的原因(可能性较大)
4)redis.conf 中bind 127.0.0.1 未用#注释掉
2)ip和端口号不正确
3)虚拟机中防火墙的原因(可能性较大)
4)redis.conf 中bind 127.0.0.1 未用#注释掉
启动报错
[....] Starting redis-server (via systemctl): redis-server.serviceJob for redis-server.service failed
because the control process exited with error code. See "systemctl status redis-server.service" and "journalctl -xe" for details.
because the control process exited with error code. See "systemctl status redis-server.service" and "journalctl -xe" for details.
MQ
RabbitMQ
安装
单体搭建
安装
二进制的安装方式
RabbitMQ安装及其基本命令
RabbitMQ安装及其基本命令
联网安装
RabbitMQ教程(tar)
RabbitMQ教程(tar)
安装注意事项
在安装rabbitmq之前需要安装 socat
rabbitmq的版本需要和 erlang 一致
记安装RabbitMQ中踩坑
安装erlang依赖
安装erlang的rpm库
下载erlang的仓库
wget http://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
如果提示wget不是一个命令执行yum install wget
安装erlang仓库
rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
安装erlang
yum install erlang
验证erlang
启动erlang,成功
# erl
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:24:24] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V7.3 (abort with ^G)
1>
# erl
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:24:24] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V7.3 (abort with ^G)
1>
erlang
安装socat
密钥
yum install -y socat
安装RabbitMQ
解压rabbitmq-server-generic-unix-3.6.12.tar.xz
注意事项
*.tar.xz 需要使用xz -d 解压后在 tar -xf 解压
安装xz: yum install xz.x86_64
进入sbin目录启动rabbitMQ
./rabbitmq-server start
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.5/rabbitmq-server-3.6.5-1.noarch.rpm
安装报错
error: Failed dependencies:
erlang >= R16B-03 is needed by rabbitmq-server-3.6.5-1.noarch
erlang >= R16B-03 is needed by rabbitmq-server-3.6.5-1.noarch
解决办法
rpm -ivh --nodeps rabbitmq-server-3.6.5-1.noarch.rpm
RabbitMQ配置
./rabbitmqctl add_user admin 123456(创建用户)
./rabbitmqctl set_user_tags admin administrator(将创建好的用户加入管理员)
./rabbitmqctl set_permissions -p "/" admin "." "." ".*"(授权)
./rabbitmq-plugins enable rabbitmq_management 启动RabbitMQ管理页面
重启MQ服务、开放5672/15672/25672端口。在浏览器输入ip:15672后出现RabbitMQ管理台。
./rabbitmqctl set_user_tags admin administrator(将创建好的用户加入管理员)
./rabbitmqctl set_permissions -p "/" admin "." "." ".*"(授权)
./rabbitmq-plugins enable rabbitmq_management 启动RabbitMQ管理页面
重启MQ服务、开放5672/15672/25672端口。在浏览器输入ip:15672后出现RabbitMQ管理台。
管控台默认端口号
15672
安装问题
如果已安装
先使用 ps -ef | grep rabbitmq
kill相应的进程
kill相应的进程
修改log日志,存储到指定地方
注意log的位置
注意log的名称
查看端口号占有
lsof -i:5672
linux配置安装 RabbitMQ详细教程
RabbitMQ官网
windows上
安装docker,再安装rabbitmq
安装docker,再安装rabbitmq
安装docker
win7、win8 等需要利用 docker toolbox 来安装,国内可以使用阿里云的镜像来下载,
下载地址:http://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/
下载地址:http://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/
一键安装
如果本地装有git,可以不用勾选git
docker toolbox 是一个工具集
Docker CLI - 客户端,用来运行 docker 引擎创建镜像和容器。
Docker Machine - 可以让你在 Windows 的命令行中运行 docker 引擎命令。
Docker Compose - 用来运行 docker-compose 命令。
Kitematic - 这是 Docker 的 GUI 版本。
Docker QuickStart shell - 这是一个已经配置好Docker的命令行环境。
Oracle VM Virtualbox - 虚拟机。
Docker Machine - 可以让你在 Windows 的命令行中运行 docker 引擎命令。
Docker Compose - 用来运行 docker-compose 命令。
Kitematic - 这是 Docker 的 GUI 版本。
Docker QuickStart shell - 这是一个已经配置好Docker的命令行环境。
Oracle VM Virtualbox - 虚拟机。
启动Docker Quickstart Terminal
它允许的命令实际上是"C:\Program Files\Git\bin\bash.exe" --login -i "D:\program\Docker Toolbox\start.sh",
这个脚本会检查名字叫default的虚拟机是否存在,如果不存在会调用virtualbox中的create api创建一个
这个脚本会检查名字叫default的虚拟机是否存在,如果不存在会调用virtualbox中的create api创建一个
default虚拟机的默认用户名和密码
用户名:docker
密码: tcuser
用户名:docker
密码: tcuser
安装RabbitMQ
docker pull rabbitmq:management
docker pull rabbitmq:3.7.7-management
带版本的
docker images
docker run -d --hostname my-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:management
打开浏览器,进入rabbitMQ控制台
docker上rabbitmq的访问地址
安装后检验
docker ps
安装出错指南
windows上
非docker安装
非docker安装
下载erlang
下载RabbitMQ
安装RabbitMQ-Plugins
ubuntu下
RabbitMQ安装与简单使用
RabbitMQ安装与简单使用
推荐
配置
开放15672端口,通过浏览器访问rabbitmq控制台
首先通过lsof i :15672,或者通过ps -aux |grep 15672|grep -v grep
查看是否开启这个15672端口
查看是否开启这个15672端口
然后找到rabbitmq的sbin文件,可以全局搜sbin然后找mq下面的这个
使用指令: rabbitmq-plugins enable rabbitmq_management
如果出现Error: {cannot_write_enabled_plugins_file,"/etc/rabbitmq/enabled_plugins",eacces} 错误,需要使用 sudo
新建用户,在赋权限
rabbltmqctl delete_user liujun
rabbitmqctl set_user_tags xxxUser xxxTag
RabbitMQ消息追踪插件rabbitmq_tracing
rabbitmq-plugins enable rabbitmq_tracing
查看版本
rabbitmqctl status |grep rabbit
验证
查询mq的状态\开启/关闭mq
1.启动rabbitmq:
sudo service rabbitmq-server start
2.停止rabbitmq:
sudo service rabbitmq-server stop
3.可以运行命令查看rabbitmq:
sudo service rabbitmq-server status
4.开启web管理接口
sudo rabbitmq-plugins enable rabbitmq_management
sudo service rabbitmq-server start
2.停止rabbitmq:
sudo service rabbitmq-server stop
3.可以运行命令查看rabbitmq:
sudo service rabbitmq-server status
4.开启web管理接口
sudo rabbitmq-plugins enable rabbitmq_management
rabbltmqctl命令
rabbltmqctl 用户
rabbltmqctl start_app
开启应用
rabbltmqctl stop_app
关闭应用
rabbltmqctl status
节点状态
rabbltmqctl add_user liujun 123456
添加用户和密码
$sudo rabbitmqctl set_user_tags user_admin administrator
给用户赋权限
rabbltmqctl list_users
查看所有用户
rabbltmqctl delete_user liujun
删除用户
rabbltmqctl clear_permissions -p vhostpath liujun
清除用户权限
rabbltmqctl change_password liujun 123456new
修改密码
vhost
rabbltmqctl add_vhost vhostpath
添加虚拟主机
rabbltmqctl list_vhosts
查看虚拟机列表
rabbltmqctl delete vhostpath
删除
queue
rabbltmqctl -p vhostpath purge_queue blue
清除队列消息
exchange
高级操作
集群
集群
rabbltmqctl reset
移除所有数据,
需要在rabbltmqctl stop_app之后使用
需要在rabbltmqctl stop_app之后使用
rabbltmqctl join_cluster clustrernode --ram
组建集群命令
--ram 指定加入节点的内存模式
--disk 指定加入节点的磁盘模式
rabbltmqctl cluster_status
查看集群状态
rabbltmqctl change_cluster_node_type disc | ram
修改集群节点的存储模式
rabbltmqctl forget_cluster_node --offline
忘记节点
--offline 在服务不起动的情况下摘除节点
集群搭建
Centos7 离线安装RabbitMQ,并配置集群
MQ高可用
Rabbitmq集群高可用部署详细
搭建思路
队列之间用镜像队列;
队列之上用HAproxy:
HAproxy之上用VIP;vip可以写在springboot里面
队列之上用HAproxy:
HAproxy之上用VIP;vip可以写在springboot里面
使用
介绍
MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务
rabbitmq组成
server/broker
接受客户端的连接
connection
应用程序和broker之间的连接
channel
消息读写的通道,
所有操作在channel中进行
所有操作在channel中进行
MQ核心架构
发送方
业务调用方
MQ-client-sender
SendMsg(bytes[] msg)
SendCallback()
MQ核心集群
MQ-server
zk
db
管理后台web
接收方
业务接收方
MQ-client-receiver
RecvCallback(bytes[] msg)
SendAck()
实际组成
虚拟机
virtual host
virtual host
交换机
exchange
exchange
交换机属性
name
交换机名称
type
交换机;类型
重置路由规则的时候记得清空之前的规则
durabilty
是否持久化
auto delete
最后一个队列消失后,删除该交换机
队列
massage queue
massage queue
Queue是用来存储消息的容器,RabbitMQ提供了FIFO(先进先出)的机制,
可以缓存消息也可以将消息持久化、临时或者自动删除。
可以缓存消息也可以将消息持久化、临时或者自动删除。
durabilty
是否持久化
auto delete
最后一个监听消失后,删除该队列
绑定
bing
bing
将交换机与一个特定的队列绑定起来,且交换机与队列的关系可以是一对一、一对多、多对多
bing中可以含有route key
massage
reply_id
消息失败回退到那个队列
correlationid
唯一ID
expiration
消息失效时间
message
properties
body
路由规则
直接交换器(Dirct)
routingkey对应同名队列
广播交换器(Fanout)
直接绑定交换机和队列,
一个交换机对应多个队列
一个交换机对应多个队列
性能最好,速度最快
主题交换器(Topic)
交换机和队列
可以进行模糊匹配
可以进行模糊匹配
对应routingkey和bindingkey
头交换机(Headers)
调用和消息队列的区别
结论:
调用方实时依赖执行结果的业务场景,请使用调用,而不是MQ
MQ只用来传递上游任务执行完成的消息,并不用于传递真正的输入输出数据
高可用设计
防止队列崩溃和恢复
(持久化策略)
(持久化策略)
如果消息想要从Rabbit崩溃中恢复,那么消息必须
把他的投递模式选项设置为2(持久)
发送到持久化的交换器
到达持久化的队列
发送到持久化的交换器
到达持久化的队列
交换机和队列持久化,
不需要在服务器断电后重新创建队列和交换器了
不需要在服务器断电后重新创建队列和交换器了
RabbitMQ确保持久性消息
能从服务器重启中恢复的方式是
能从服务器重启中恢复的方式是
将他们写入磁盘上的一个持久化日志文件。
当发布一条持久性消息到持久交换器上时,
Rabbit会在消息提交到日志文件后才发送响应
当发布一条持久性消息到持久交换器上时,
Rabbit会在消息提交到日志文件后才发送响应
记住,之后这条消息如果路由到了非持久队列的话,
他会自动从持久性日志中移除,
并且无法从服务器重启中恢复
他会自动从持久性日志中移除,
并且无法从服务器重启中恢复
一旦你从持久化队列中消费了一条持久性消息的话(并且确认了他),
RabbitMQ会在持久化日志中把这条消息标记为等待垃圾收集
RabbitMQ会在持久化日志中把这条消息标记为等待垃圾收集
性能影响
写入磁盘要比存入内存中慢不止一点点,
而且会极大的减少RabbitMQ服务器每秒可处理的消息总数
而且会极大的减少RabbitMQ服务器每秒可处理的消息总数
使用持久化机制而导致消息吞吐量降低至少10倍的情况并不少见
(将RabbitMQ的消息存储于SSD上的话,就可以极大的提升持久化消息通信的性能)
(将RabbitMQ的消息存储于SSD上的话,就可以极大的提升持久化消息通信的性能)
持久性消息在RabbitMQ内建集群环境下工作得并不好
虽然RabbitMQ集群允许你和集群中的任何节点的任一队列进行通信,
但是事实上那些队列均匀的分布在各个节点而没有冗余
(在集群中任何一个队列都没有备份的拷贝)
但是事实上那些队列均匀的分布在各个节点而没有冗余
(在集群中任何一个队列都没有备份的拷贝)
Client端
使用方式
使用方式
生产者和消费者
demo
demo
生产者示例
消费者示例
与spring结合
rabbitmq整合Spring-AMQP
池化
组件
RabbitAdmin
为了更方便的操作rabbitmq
autostartup设置为true,否则spring容器不会加载
底层是从spring容器中获取
exchange、routingkey、binging、queue的@bean
exchange、routingkey、binging、queue的@bean
SpringAMQP声明
通过bean方式注入
bean写在config里面
RabbitTemplate
RabbitAdmin的底层具体实现
binging的格式
真正执行工作的是execute
发送消息的方法
可靠性投递消息
回调监听confirmcallback
返回值确认returncallback
simpleMessageListenerContainer
为什么可以动态的感知配置变更
MessageListenerAdapter
监听的适配器,一个代理
MessageConverter
rabbitmq整合spring boot
问题集锦
Caused by: java.lang.ClassNotFoundException: com.rabbitmq.client.ConfirmCall
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
这两个依赖不能同时存在,会导致启动报错
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
这两个依赖不能同时存在,会导致启动报错
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
spring-rabbit和amqp-client不能同时存在,否则会出现class引用错误
Spring整合rabbitmq实践(一):基础
Spring整合rabbitmq实践(一):基础
Spring整合rabbitmq实践(二):扩展
Spring整合rabbitmq实践(一):基础
Spring整合rabbitmq实践(二):扩展
业务场景
典型场景一:数据驱动的任务依赖
互联网公司经常在凌晨进行一些数据统计任务,这些任务之间有一定的依赖关系,比如:
1)task3需要使用task2的输出作为输入
2)task2需要使用task1的输出作为输入
这样的话,tast1, task2, task3之间就有任务依赖关系,必须task1先执行,再task2执行,载task3执行
1)task3需要使用task2的输出作为输入
2)task2需要使用task1的输出作为输入
这样的话,tast1, task2, task3之间就有任务依赖关系,必须task1先执行,再task2执行,载task3执行
典型场景二:上游不关心执行结果
58同城的很多下游需要关注“用户发布帖子”这个事件,
比如
招聘用户发布帖子后,招聘业务要奖励58豆,
房产用户发布帖子后,房产业务要送2个置顶,
二手用户发布帖子后,二手业务要修改用户统计数据。
比如
招聘用户发布帖子后,招聘业务要奖励58豆,
房产用户发布帖子后,房产业务要送2个置顶,
二手用户发布帖子后,二手业务要修改用户统计数据。
典型场景三:上游关注执行结果,但执行时间很长
典型的是调用离线处理,或者跨公网调用
微信支付,跨公网调用微信的接口,执行时间会比较长,但调用方又非常关注执行结果
mq的使用
死信队列
一个单独的交换机和队列
TTL消息
消息有存活时间
队列有存活时间
消费段限流
消息的ACK和重回队列
消费段自定义监听
示例
MQ消息可靠投递核心流程
流程
MQ消息投递上半场
(1)MQ-client将消息发送给MQ-server(此时业务方调用的是API:SendMsg)
(2)MQ-server将消息落地,落地后即为发送成功
(3)MQ-server将应答发送给MQ-client(此时回调业务方是API:SendCallback)
(2)MQ-server将消息落地,落地后即为发送成功
(3)MQ-server将应答发送给MQ-client(此时回调业务方是API:SendCallback)
MQ消息投递下半场
(4)MQ-server将消息发送给MQ-client(此时回调业务方是API:RecvCallback)
(5)MQ-client回复应答给MQ-server(此时业务方主动调用API:SendAck)
(6)MQ-server收到ack,将之前已经落地的消息删除,完成消息的可靠投递
(5)MQ-client回复应答给MQ-server(此时业务方主动调用API:SendAck)
(6)MQ-server收到ack,将之前已经落地的消息删除,完成消息的可靠投递
保障
消息丢失
为了降低消息丢失的概率,MQ需要进行超时和重传
上半场的超时与重传
MQ上半场的1或者2或者3如果丢失或者超时,MQ-client-sender内的timer会重发消息,直到期望收到3,
如果重传N次后还未收到,则SendCallback回调发送失败,需要注意的是,这个过程中MQ-server可能会收到同一条消息的多次重发
如果重传N次后还未收到,则SendCallback回调发送失败,需要注意的是,这个过程中MQ-server可能会收到同一条消息的多次重发
下半场的超时与重传
MQ下半场的4或者5或者6如果丢失或者超时,MQ-server内的timer会重发消息,直到收到5并且成功执行6,这个过程可能会重发很多次消息,
一般采用指数退避的策略,先隔x秒重发,2x秒重发,4x秒重发,以此类推,需要注意的是,这个过程中MQ-client-receiver也可能会收到同一条消息的多次重发。
一般采用指数退避的策略,先隔x秒重发,2x秒重发,4x秒重发,以此类推,需要注意的是,这个过程中MQ-client-receiver也可能会收到同一条消息的多次重发。
重传
一般采用指数退避的策略,先隔x秒重发,2x秒重发,4x秒重发,以此类推
消息去重
为了避免步骤2落地重复的消息,对每条消息,MQ系统内部必须生成一个inner-msg-id,作为去重和幂等的依据
架构幂等性设计
上半场的幂等性设计
内部消息ID的特性
(1)全局唯一
(2)MQ生成,具备业务无关性,对消息发送方和消息接收方屏蔽
(2)MQ生成,具备业务无关性,对消息发送方和消息接收方屏蔽
下半场的幂等性设计
biz-id
(1)对于同一个业务场景,全局唯一
(2)由业务消息发送方生成,业务相关,对MQ透明
(3)由业务消息消费方负责判重,以保证幂等
(2)由业务消息发送方生成,业务相关,对MQ透明
(3)由业务消息消费方负责判重,以保证幂等
最常见的业务ID有:支付ID,订单ID,帖子ID等
消息延时
高效延时消息设计与实现
两个重要的数据结构
环形队列
例如可以创建一个包含3600个slot的环形队列(本质是个数组)
任务集合
环上每一个slot是一个Set<Task>
(1)Cycle-Num:当Current Index第几圈扫描到这个Slot时,执行任务
(2)Task-Function:需要执行的任务指针
图片显示
实现
(1)计算这个Task应该放在哪一个slot,现在指向1,3610秒之后,应该是第11格,所以这个Task应该放在第11个slot的Set<Task>中
(2)计算这个Task的Cycle-Num,由于环形队列是3600格(每秒移动一格,正好1小时),这个任务是3610秒后执行,
所以应该绕3610/3600=1圈之后再执行,于是Cycle-Num=1
(2)计算这个Task的Cycle-Num,由于环形队列是3600格(每秒移动一格,正好1小时),这个任务是3610秒后执行,
所以应该绕3610/3600=1圈之后再执行,于是Cycle-Num=1
即一个取商,一个取余数
削峰填谷
RabbitMQ的异步调用
消息落库
图示
消息的延迟投递
图示
消息队列设计
RabbitMQ消息流转
RabbitMQ内存与磁盘管理
顺序消息
开发问题集锦
$exception {"None of the specified endpoints were reachable"}
RabbitMQ.Client.Exceptions.BrokerUnreachableException
RabbitMQ.Client.Exceptions.BrokerUnreachableException
解决办法
原因一般是ConnectionFactory参数设置不对,比如HostName、UserName、Password
Kafka
中间件的
搭建流程
搭建流程
单体搭建
kafka
安装
启动
kafka-server-start.sh ../config/server.properties
后台启动
kafka-server-start.sh -daemon ../config/server.properties
查看状态
使用命令:netstat -anlpt | grep 9092 或者 lsof -i:9092 来查看9092端口占用情况
查看版本
echo stat|nc localhost 2181
停止
zookeeper-server-stop /usr/local/etc/kafka/zookeeper.properties & kafka-server-stop /usr/local/etc/kafka/server.properties
集群搭建
使用
创建创建主题,
该主题包含一个分区,
该分区为Leader分区,
它没有Follower分区副本。
该主题包含一个分区,
该分区为Leader分区,
它没有Follower分区副本。
命令行
消费者
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic kafkatest
生产者
kafka-console-producer.sh --broker-list localhost:9092 --topic kafkatest
问题
使用问题
Error while executing topic command : Replication factor: 1 larger than available brokers: 0.
[2022-05-02 19:57:11,008] ERROR org.apache.kafka.common.errors.InvalidReplicationFactorException: Replication factor: 1 larger than available brokers: 0.
[2022-05-02 19:57:11,008] ERROR org.apache.kafka.common.errors.InvalidReplicationFactorException: Replication factor: 1 larger than available brokers: 0.
关闭zookeeper和kafka,重启服务
先kill 掉
zookeeper-server-start.sh -daemon ../config/zookeeper.properties
kafka-server-start.sh -daemon ../config/server.properties
zookeeper-server-start.sh -daemon ../config/zookeeper.properties
kafka-server-start.sh -daemon ../config/server.properties
Connection to node -1 (localhost/127.0.0.1:9020) could not be established. Broker may not be available. (org.apache.kafka.clients.NetworkClient)
创建消费者报错
WARN [Consumer clientId=consumer-console-consumer-8559-1, groupId=console-consumer-8559]
Error while fetching metadata with correlation id 18 : {topictest=LEADER_NOT_AVAILABLE} (org.apache.kafka.clients.NetworkClient)
Error while fetching metadata with correlation id 18 : {topictest=LEADER_NOT_AVAILABLE} (org.apache.kafka.clients.NetworkClient)
1、将server.properties里面的 ip 改成 localhost
2、将启动指令kafka-console-producer.sh --broker-list localhost:9092 --topic kafkatest
修改成 localhost
修改成 localhost
Error while fetching metadata with correlation id 26 : {kafkatest=LEADER_NOT_AVAILABLE} (org.apache.kafka.clients.NetworkClient)
Kafka的使用和错误解决
Zookeeper
安装
下载地址
验证
启动
zkServer.sh start
看状态
./zkServer.sh status
看版本
echo stat|nc 127.0.0.1 2181
版本号
问题
安装问题
Error: Could not find or load main class org.apache.zookeeper.server.quorum.QuorumPeerMain
先看这个里面是否有相关的问题
zookeeper启动报错出现Starting zookeeper ... FAILED TO START详细解决方案
stat is not executed because it is not in the whitelist.
中间件
常见中间件安装语句
Redis安装脚本:
installRedis.sh、Redis.conf、Sentinel.conf、VIP绑定脚本
Nginx安装脚本:
installNginx.sh、makeNginx.sh、nginx.conf
RabbitMQ及集群的安装脚本:
installRabbitMQ.sh 、 installRabbitMQMirrorCluster.sh
HAProxy安装脚本:
installHAProxy.sh、haproxy.cfg
Keepalived安装脚本:
installKeepalived.sh 、keepalived.conf、 ha_check.sh
Mysql安装脚本
installMysql.sh、my.cnf
centos7的repo样本、刷新yum源的语句:
centos7.repo 、yum.sh
installRedis.sh、Redis.conf、Sentinel.conf、VIP绑定脚本
Nginx安装脚本:
installNginx.sh、makeNginx.sh、nginx.conf
RabbitMQ及集群的安装脚本:
installRabbitMQ.sh 、 installRabbitMQMirrorCluster.sh
HAProxy安装脚本:
installHAProxy.sh、haproxy.cfg
Keepalived安装脚本:
installKeepalived.sh 、keepalived.conf、 ha_check.sh
Mysql安装脚本
installMysql.sh、my.cnf
centos7的repo样本、刷新yum源的语句:
centos7.repo 、yum.sh
中间件安装过程中的一些坑
1、通用
2、Nginx
3、Redis
4、Rabbitmq
2、Nginx
3、Redis
4、Rabbitmq
模版
中间件的
搭建流程
搭建流程
单体搭建
集群搭建
中间件的
源码阅读/
设计理念/
框架结构
源码阅读/
设计理念/
框架结构
如何实现高并发
如何高效复用
网络连接数
网络连接数
高可用设计
高并发设计
源码参考
类AA实现 了。。。
使用时间轮循环。。。。
流程图/架构图
中间件
的使用
的使用
命令行
使用方式
使用方式
Client端
使用方式
使用方式
可视化界面
使用方式
使用方式
业务场景
幂等
延时
重试
数据库
Mysql
分库分表、主从复制、读写分离
Tidb
TiDB 集群主要分为三个组件
TiDB Server
TiDB Server 负责接收 SQL 请求,处理 SQL 相关的逻辑,并通过 PD 找到存储计算所需数据的 TiKV 地址,与 TiKV 交互获取数据,最终返回结果。
TiDB Server 是无状态的,其本身并不存储数据,只负责计算,可以无限水平扩展,可以通过负载均衡组件(如LVS、HAProxy 或 F5)对外提供统一的接入地址。
TiDB Server 是无状态的,其本身并不存储数据,只负责计算,可以无限水平扩展,可以通过负载均衡组件(如LVS、HAProxy 或 F5)对外提供统一的接入地址。
PD Server
Placement Driver (简称 PD) 是整个集群的管理模块,其主要工作有三个:
一是存储集群的元信息(某个 Key 存储在哪个 TiKV 节点);
二是对 TiKV 集群进行调度和负载均衡(如数据的迁移、Raft group leader 的迁移等);
三是分配全局唯一且递增的事务 ID。
PD 是一个集群,需要部署奇数个节点,一般线上推荐至少部署 3 个节点
一是存储集群的元信息(某个 Key 存储在哪个 TiKV 节点);
二是对 TiKV 集群进行调度和负载均衡(如数据的迁移、Raft group leader 的迁移等);
三是分配全局唯一且递增的事务 ID。
PD 是一个集群,需要部署奇数个节点,一般线上推荐至少部署 3 个节点
TiKV Server
TiKV Server 负责存储数据,从外部看 TiKV 是一个分布式的提供事务的 Key-Value 存储引擎。
存储数据的基本单位是 Region,每个 Region 负责存储一个 Key Range (从 StartKey 到 EndKey 的左闭右开区间)的数据,每个 TiKV 节点会负责多个 Region 。
TiKV 使用 Raft 协议做复制,保持数据的一致性和容灾。副本以 Region 为单位进行管理,不同节点上的多个 Region 构成一个 Raft Group,互为副本。数据在多个 TiKV 之间的负载均衡由 PD 调度,这里也是以 Region 为单位进行调度
存储数据的基本单位是 Region,每个 Region 负责存储一个 Key Range (从 StartKey 到 EndKey 的左闭右开区间)的数据,每个 TiKV 节点会负责多个 Region 。
TiKV 使用 Raft 协议做复制,保持数据的一致性和容灾。副本以 Region 为单位进行管理,不同节点上的多个 Region 构成一个 Raft Group,互为副本。数据在多个 TiKV 之间的负载均衡由 PD 调度,这里也是以 Region 为单位进行调度
MongoDB
Ubuntu 上安装 MongoDB
sudo apt install mongodb
sudo systemctl status mongodb
sudo systemctl stop mongodb
sudo systemctl start mongodb
sudo systemctl restart mongodb
sudo systemctl stop mongodb
sudo systemctl start mongodb
sudo systemctl restart mongodb
从 Ubuntu 仓库安装 MongoDB
并想要卸载它
并想要卸载它
sudo systemctl stop mongodb
sudo apt purge mongodb
sudo apt autoremove
sudo apt purge mongodb
sudo apt autoremove
使用
使用(创建和编辑)数据库,请输入
mongo
在Idea上配置MongoDB插件并连接MongoDB
安装MongoDB插件
Mongodb使用手册
使用
show dbs
use liujundb
db.users.save({a:"l",name:"zhangsan"})
db.users.insert({a:"l",name:"zhangsan"})
db.users.find()
db.users.find({age:18})
db.users.counnt()
db.users.find({age:18}).count()
问题
telnet mongodb的27017端口连接失败
1、mongodb的配置文件中的bind_ip 默认为127.0.0.1,默认只有本机可以连接。
此时,需要将bind_ip配置为0.0.0.0,表示接受任何IP的连接。
此时,需要将bind_ip配置为0.0.0.0,表示接受任何IP的连接。
2、防火墙阻止了27017端口。
java使用mongodb
demo
//连接到 mongodb 服务
MongoClient mongoClient = new MongoClient("192.168.42.101", 27017);
//连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("ljtest");
MongoCollection<Document> collection = mongoDatabase.getCollection("stu");
//创建文档
/*Document document = new Document("name", "张三").append("sex", "男").append("age", 18);
collection.insertOne(document);*/
System.out.println(collection.count());
System.out.println(collection.find().toString());
MongoClient mongoClient = new MongoClient("192.168.42.101", 27017);
//连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("ljtest");
MongoCollection<Document> collection = mongoDatabase.getCollection("stu");
//创建文档
/*Document document = new Document("name", "张三").append("sex", "男").append("age", 18);
collection.insertOne(document);*/
System.out.println(collection.count());
System.out.println(collection.find().toString());
使用mongoTemplate实现多条件加分组查询
SpringBoot整合MongoDB,使用mongoTemplate实现高级查询
mongo-express
SQLServer
Ubuntu下安装配置SQLSERVER2017
ubuntu中连接mssql数据库sqlserver
sqsh -U sa -P Lxxxxxxxx82 -S localhost
sudo apt-get install tdsodbc
sudo apt-get install sqsh
sqsh -U 用户名 -P 密码 -S 服务器地址
等一会儿如果出现1> ,就说明连接成功了,可以试着输入如下语句:
SELECT @@version
go
注意输入go后才执行语句
sudo apt-get install sqsh
sqsh -U 用户名 -P 密码 -S 服务器地址
等一会儿如果出现1> ,就说明连接成功了,可以试着输入如下语句:
SELECT @@version
go
注意输入go后才执行语句
nacicat连接本地sqlserver数据库
排序规则
Chinese_PRC_CI_AS
ETCD
中间件的
搭建流程
搭建流程
单体搭建
集群搭建
中间件的
源码阅读/
设计理念/
框架结构
源码阅读/
设计理念/
框架结构
如何实现高并发
如何高效复用
网络连接数
网络连接数
高可用设计
高并发设计
源码参考
类AA实现 了。。。
使用时间轮循环。。。。
流程图/架构图
中间件
的使用
的使用
命令行
使用方式
使用方式
Client端
使用方式
使用方式
可视化界面
使用方式
使用方式
业务场景
幂等
延时
重试
0 条评论
下一页