RabbitMq:一个基于AMQP协议实现的分布式消息中间件
AMQP的工作机制如下图所示 :

首先生产者把消息发送到RabbitMQ Broker上的Exchange交换机上,Exchange交换机会把收到的消息根据路由规则分发给绑定的队列,最后再把消息投递给订阅了这个队列的消费者从而去完成消息的异步通信。
其中Exchange交换机就可以去定义这个消息的路由规则,将消息去路由到指定的队列,然后队列就是消息的载体,每个消息就可以根据路由规则路由到一个或者多个队列中。
完成RabbitMq路由的核心组件是Exchange交换机,而消息的路由又是由Exchange类型(交换机类型)和Binding决定的。
Binding是表示在队列和交换机之间的一个绑定关系,那么每个绑定关系会存在一个BindingKey,通过这种方式相当于是在Exchange交换机中建立了一个路由关系表,在每个生产者发送消息的时候,需要声明一个RoutingKey(路由键),Exchange拿到RoutingKey之后,根绝RoutingKey和路由表里面的BindingKey进行匹配,而匹配的规则是通过Exchange类型来决定的
在 RabbitMq中默认有四种类型,常用的有三种:
其中Direct叫做直连,也就是完整的匹配方式,他需要Routing Key和Binding Key完全一致,相当于是点对点的发送,原理如下图所示:

如果发生了Routing Key为spring的消息,只有一个队列能接受到消息
Topic叫做主题,那么这种方式是通过设置通配符来动态匹配,类似于正则,就是用Routing Key去匹配Binding Key,Binding Key支持两个通配符:
另外Binding Key是用 点隔开两个单词,所以*和#就相当于是正则表达式的一个作用

我们有4个队列绑定到 Topic类型的交换机中,而且使用的是不同的绑定键,如上图所示,那么如果我们发送routing key为“junior.abc.jvm”的消息,那么只有第一个队列可以收到。
如果我们发送routing key为“senior.netty”的消息,那么第二个队列第三个队列都可以收到。
Fanout叫做广播,这种方式是不需要设置Routing key的,而且他会把消息广播给绑定到当前Exchange上的所有队列上,如下图所示:

我们只需要发送消息到Fanout的Exchange上,那么3个队列都会收到消息。
目前主流的分布式中间件有:
时间轮:一种用来存储定时任务的环状数组。

他的工作原理和钟表的表盘类似,他有两个部分组成:
首先要定义一个固定长度的环形数组,然后数组的每一个元素代表一个时间刻度,假设每个刻度之间的间隔是1s,那么长度为8s的数组就代表8秒钟。
然后就是需要有一个指针,那么这个指针是按照顺时针的方向,无限的循环这个数组,每隔一个最小的时间单位就前进一个数组的索引,那么这个指针完整的转一圈的话就代表8秒钟,转一圈的话就代表16秒钟,假设从0点0分0秒开始,转一圈之后就到了0点0分9秒
环形数组里面的每一个元素,都是用来存储定时任务的容器,当我们向时间轮里添加一个定时任务的时候,我们会根据定时任务的执行时间计算他所存储的数组下标,当然会在某个时间刻度上会存在多个定时任务,那么这个时候就会采用双向链表的方式进行存储。
当我们的指针指向某个数组的时候,就会把这个数组中存储的任务取出来,然后就遍历这个链表,逐个去运行这个链表中的任务
那么如果某个定时任务的执行时间,大于环状数组的长度,一般就可以使用一个圈数来表示该任务的延时执行时间,比如一个第16秒执行的任务,那就意味着这个任务应该在第2圈的数组下标为0的时候去执行。
使用时间轮的方式来管理多个定时任务的好处有很多,我认为有两个比较重要的优点:
当然时间轮也有缺点:对于执行时间非常严格的任务,时间轮不是很合适,因为时间轮算法的精度取决于最小时间单元的粒度,假设以1秒为时间刻度的话,那么小于1s的任务就无法被时间轮调度
同时时间轮算法在很多框架中都有用到,比如说:Dubbo,Netty,Kafka等。
幂等是一个数学上的概念,而在计算机编程领域中幂等是指一个方法任意多次执行所产生的影响均与一次执行的影响相同。
简单来说:一个逻辑即使被重复执行多次,也不影响最终结果的一致性。
之所以要考虑幂等性问题,主要是因为在网络通讯中有两种行为都有可能导致我们的接口被重复执行:
所以在我的程序设计中对于数据变更操作的接口都要去保证接口的幂等性,而幂等性的核心思想,其实就是保证这个接口的执行结果只影响一次,后续再次调用,也不能对数据产生影响。
所以基于这个需求呢,如何去解决幂等性呢?
解决幂等性问题的方法有很多,下面我分享一下一些常用的解决方案:
比如说对于数据插入的场景而言,假设我们要创建一个订单,因为订单号肯定是唯一的,所以如果我们多次去调用数据库的唯一约束,他就会产生异常,从而去避免一个请求创建多个订单的问题。
比如说我们对于MQ的消息的场景,我们要去避免MQ重复消费,从而导致数据多次被修改的问题,可以在接受MQ消息的时候把这个消息通过setNX写入到redis中,一旦这个消息被消费之后,我们就就不会被再次消费
比如说订单的状态,因为他的状态只会向前变更,所以多次修改同一条数据的时候一旦状态发生改变,那么这条数据修改造成的影响也只会发生一次。
除了以上三种方法之外,我们还可以通过token机制或者去增加重表的方法来实现幂等。
但是无论使用何种方法,无非也就是两种思路,要么就是接口只允许调用一次,比如说唯一约束、基于Redis的锁机制,要么就是对数据的影响只会发生一次,比如说悲观锁、乐观锁等等。