说两句

提前说两句

使用消息队列做了注册发送邮件的逻辑

之前做的秒杀系统也用 RabbitMQ 重写了一下秒杀逻辑…

简单秒杀系统

CentOS 下安装

# centos

# 先安装 erlang
yum -y install erlang

# 下载 RabbitMQ
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-3.6.6-1.el6.noarch.rpm
# 安装
yum -y install rabbitmq-server-3.6.6-1.el6.noarch.rpm

# 缺少啥依赖,相应安装即可

# 比如 提示缺少 erlang-solutions
wget http://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
安装 rpm 仓库: rpm -Uvh erlang-solutions-1.0-1.noarch.rpm

基本命令

# 启动
service rabbitmq-server start
# 状态
service rabbitmq-server status

# 自启
$ chkconfig rabbitmq-server on

# 启动 web 管理插件
rabbitmq-plugins enable rabbitmq-management
# 访问 ip:15672 就可以通过 web 页面管理 RabbitMQ 了

访问 web 管理页面

访问 ip:15672 就可以通过 web 页面管理 RabbitMQ 了

见到是一个登录页面.

RabbitMQ 提供了默认用户名和密码

username : guest

password : guest

但是该用户名与密码只能在本机登录,不支持远程访问.

新建一个用户,并设定权限,就可以使用这个新用户远程访问了.

先看看基本操作.

# 删除用户
rabbitmqctl  delete_user  Username

# 新增一个用户
rabbitmqctl  add_user  Username  Password

# 修改用户的密码
rabbitmqctl  change_password  Username  Newpassword

# 设定用户 administrator 角色
rabbitmqctl set_user_tags <用户名> administrator

# 用户角色可以分为超级管理员administrator、监控者monitoring、策略制定者policymaker、普通管理者management等。

比如我就这样操作一番 :

# 删除用户
rabbitmqctl  delete_user  Username

# 新增一个用户
rabbitmqctl  add_user  Username  Password

# 修改用户的密码
rabbitmqctl  change_password  Username  Newpassword

# 设定用户 administrator 角色
$ rabbitmqctl set_user_tags <用户名> administrator

# 用户角色可以分为超级管理员administrator、监控者monitoring、策略制定者policymaker、普通管理者management等。

这样创建一个用户并配置角色确实可以在 web 管理界面登录,但是配置 Spring Boot 时,还不能连接访问。

我报了这样的错。

Caused by: com.rabbitmq.client.ShutdownSignalException: connection error; protocol method: #method<connection.close>(reply-code=530, reply-text=NOT_ALLOWED - access to vhost '/' refused for user 'hqweay', class-id=10, method-id=40)

还需要对用户配置权限。

sudo rabbitmqctl  set_permissions -p / hqweay '.*' '.*' '.*'
# 该命令使用户 hqweay 具有‘/’这个virtual host中所有资源的配置、写、读权限以便管理其中的资源
# 具体是啥意思还得稍微了解下 RabbitMQ 的相关配置

其他

配置了 web 插件不能访问,配置防火墙之类的…

不提

整合 Spring Boot

配置访问端口是 5672

web 访问端口是 15672

不提引入与具体编码,看看配置。

# application.yml
server:
  port: 8082

spring:
  application:
    name: rabbitMQ
  rabbitmq:
    # 配置连接IP地址,本机安装的 RabbitMQ 的话可以用 localhost
    host: 118.24.xxx.xxx
    # 这里配置的是 RabbitMQ 的连接端口,是 5672 哦不是 15672
    port: 5672
    username: hqweay
    password: xxx

info:
  app:
    name: RabbitMQ测试连接
    version: 0.0.1

可以参考这个仓库的整合

参考

centos rabbitMq 安装

SpringBoot使用RabbitMQ做消息中间件

Spring Boot 揭秘与实战(六) 消息队列篇 - RabbitMQ

其它——使用的一些情况

这篇博文里的,贴一下。

https://hqweay.cn/2019/05/24/kill-me/#8-RabbitMQ

异步——消息队列

不错,现在排队了,但是现在有个最重要的问题我们还没考虑.

回头看一下.

无论怎么写代码,最终的目的是给用户提供良好的体验.

但是用户在秒杀界面等待着服务端执行完秒杀后返回的秒杀结果,这一个最影响体验的操作还没有得到解决.

现在的秒杀,就相当于我去我们村里的信用社办理业务,大家都在窗口前排着队,前面的人办完业务才能轮到后面的人.

但是想象一下大城市里的银行,用户先领取一个排队号,然后坐到一旁玩手机,等到窗口呼叫用户的排队号码时,用户才上前去办理业务…

这就是 消息队列的概念.

我们把 秒杀 的数据放入一个消息队列,然后直接向前端返回信息,告诉用户”我们先帮你排队执行秒杀,待会告诉你结果,你先去干其他的事吧.”

真棒.然后用户就可以去干其他事了.

过一阵子,通过主动查询或者被动通知,了解到自己的秒杀结果.

比如前端轮询

不过这里值得注意的是,消息队列最主要的功能是提供了一个异步执行的操作.

把秒杀真正的操作与这个用户的行为解耦了!

为什么要强调主要是为了解耦,下面会提到.

同时,消息队列也能够把秒杀数据进行排队.

8 RabbitMQ

再重复一下前面提到的,Servlet 是单例多线程,引入消息队列后,多线程把 秒杀数据 放入消息队列就完事了,然后告诉用户我们正在帮你排队.具体的秒杀执行逻辑则由消息队列来做.

稍微准确点,实际是消息队列提供的 消费者 按照队列的顺序,把 队列 里的秒杀数据拿出来,对秒杀数据执行秒杀的逻辑.

OK!!

下面拿具体的消息队列框架谈谈.

在 RabbitMQ 中,默认 单线程串行消费 .

也就是单线程,串行来执行队列里的秒杀数据来秒杀.

这里,在理论上,秒杀操作无需加入事务控制.

因为已经是串行啦!

这里面当然还有很多细节,比如如果抛出异常,秒杀执行失败,是继续尝试执行呢?还是直接秒杀失败?

又比如既然引入了消息队列,那当请求来了时就可以直接判断队列中的秒杀数据的个数与库存比较,直接拒绝用户的秒杀操作…

不错!!

9 消息积压

不过引入消息队列,又有可能遇到消息队列的一些问题…

比如消息过多,造成消息积压.

一种解决方式就是引入多线程处理消息.(当然消息队列自己还有多消费者等等细节)

可是引入多线程了,那是不是又会有线程安全的问题呢?

确实如此.

测试了一下,发现 消费者使用 多线程,的确会有数据不一致问题.

我用 int 和 AtomicInteger 做了个测试.

当 RabbitMQ 默认单线程时,int 数据正常计算.

当多线程时,int 数据计算不正常,但是 AtomicInteger 计算正常.

所以我在前面提到消息队列只是用于解耦,并不能保证线程安全.(虽然默认 单线程串行执行 肯定是线程安全的)

如果使用多线程,那就又回到并发的问题了…

使用事务?