1.RabbitMQ
什么是MQ
MQ全称为Message Queue,即消息队列。“消息队列”是在消息的传输过程中保存消息的容器。它是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦。
RabbitMQ介绍
1 每个消息都有一个称为路由键(routing key)的属性 (相当于前面介绍的地址)
2 交换机根据 [路由表] 和 [消息的 路由键] , 匹配他们, 将消息 放入一些队列里面
3 消息在队列里面, 等待被消费
RabbitMQ的优势和劣势
优势
- 应用解耦
- 异步提速
- 削峰填谷
劣势
- 系统可用性降低
- 系统复杂度提高
- 一致性问题
2.利用RabbitMQ改进文章订阅功能
controller
//根据文章id和用户id建立订阅关系
//http://127.0.0.1:9004/article/subscribe
@RequestMapping(value = "/subscribe", method = RequestMethod.POST)
public Result subscribe(@RequestBody Map map) {
Boolean flag = articleService.subscribe(map.get("articleId").toString(),
map.get("userId").toString());
if (flag == true) {
//订阅成功
return new Result(StatusCode.OK, true, "订阅成功");
} else {
return new Result(StatusCode.OK, false, "取消订阅成功");
}
}
service
public Boolean subscribe(String articleId, String userId) {
//根据文章id查询作者id
String authorId = articleDao.selectById(articleId).getUserid();
//1.创建rabbit管理器
RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory());
//2.声明Direct类型交换机,处理新增文章消息
DirectExchange exchange = new DirectExchange("article_subscribe");
rabbitAdmin.declareExchange(exchange);
//3.创建队列,每个用户都有自己的队列,通过用户id区分
Queue queue = new Queue("article_subscribe_" + userId);
//4.声明交换机和队列的绑定关系,确保队列只能收到对应的作者消息
//通过路由键绑定坐着,队列只能收到绑定作者的消息
//参数分别为队列、交换机、作者id路由键
Binding binding = BindingBuilder.bind(queue).to(exchange).with(authorId);
//redis中用户key
String userKey = "article_subscribe_" + userId;
//redis中作者key
String authorKey = "article_author_" + authorId;
//设置redis中数据按照String类型进行序列化
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
//通过用户key查询是否订阅该作者
Boolean flag = redisTemplate.boundSetOps(userKey).isMember(authorId);
if (flag == true) {
//true则取消订阅
//移除池中数据
redisTemplate.boundSetOps(userKey).remove(authorId);
redisTemplate.boundSetOps(authorKey).remove(userId);
//取消订阅则取消绑定关系
rabbitAdmin.removeBinding(binding);
return false;
} else {
//false则订阅
redisTemplate.boundSetOps(userKey).add(authorId);
redisTemplate.boundSetOps(authorKey).add(userId);
//订阅则声明要绑定的队列
rabbitAdmin.declareQueue(queue);
//添加绑定关系
rabbitAdmin.declareBinding(binding);
return true;
}
}
掌握rabbitmq的使用方法
3.IO编程与NIO编程
IO编程:
每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。阻塞
I/O 的通信模型示意图如下:
这种模型每个连接都对应一个线程,当访问量大的时候存在以下问题:
- 客户端越多就会建立越多的线程,线程是非常宝贵的资源,同一时刻有大量线程处于阻塞状态是严重的资源浪费。
- 因为是阻塞式通信,线程爆炸后系统进行频繁的线程切换,应用性能急剧下降
- io编程中数据读取是以字节流为单位,效率不高
NIO编程
1.NIO介绍
NIO
,也叫做
new-IO
或者
non-blocking-IO
,可理解为非阻塞
IO
。
NIO
编程模型 中,新来一个连接不再创建一个新的线程,而是可以把这条连接直接绑定到某个固定的线程,然后这条连接所有的读写都由这个线程来负责。
在 NIO 模型中,可以把这么多的 while 死循环变成一个死循环,这个死循环由一 个线程控制。这就是NIO 模型中选择器( Selector )的作用,一条连接来了之后,现在不创建一个while 死循环去监听是否有数据可读了,而是直接把这条连接注册到 选 择器 上,通过检查这个 选择器 ,就可以批量监测(批量轮询)出有数据可读的连接,进而读取数据。
2.NIO组件
通道(
Channel
)
-
是传统 IO 中的 Stream( 流 ) 的升级版。 Stream 是单向的、读写分离 (inputstream 和 outputstream ), Channel 是双向的,可以进行读写操作
缓冲(
Buffffer
)
- Buffffer可以理解为一块内存区域,可以写入数据,并且在之后读取它
选择器(
Selector
)
-
选择器( Selector )可以实现一个单独的线程来监控多个注册在她上面的信道(Channel ),通过一定的选择机制,实现多路复用的效果。
3.NIO相比IO的优势所在
- IO面向流,读写效率低下。NIO面向缓冲区,读写效率高
- IO是阻塞式,NIO为非阻塞式,可以在等待过程中做别的事,执行效率大大提高
- NIO引入多路复用选择器,线程数量大量减少,线程切换效率大幅度提高
4.Netty
1.概念介绍
官方术语:
Netty
是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务器和客户端。
简单来说netty封装了底层jdk的NIO,简化了我们使用NIO编程的步骤