消息队列中的队列, 一大设计核心
RPC通信协议 存储选型 消费关系处理 实现事务 防丢失、重复 批量、异步与性能
市面上有不少成熟的RPC框架, 例如dubbo, thrift等, 无需重复造轮子, 消息队列对rpc框架并没有什么特别的要求. 甚至复用RPC框架, 可以直接获得RPC的高可用特性, 例如服务自动发现, 负载均衡, failover等
Spring提供了实现事务的基类 DataSourceTransactionManager, 继承即可. 可以通过实现自己的事务管理器, 将provider的业务逻辑和消息本地落地绑定, 确保消息落地成功.
待续
批量处理, 无疑是性能的要求和体现.
典型的生产消费者模式, 消费者到底应该何时消费? 两种常见的批量方式: 达到一定的数量, 等待一定的时间. 单纯的使用任何一种, 都有极大的局限性. 比如要求凑够4个消息, 批量处理一次, 那么当消息的间隔长的时候, 性能就极大的浪费了. 如果固定等待一定时间, 显然也对消息频率高, broker处理能力有限的场景不够适用. 而实际的业务场景可能是多变的, 所以大多的解决方案都是在数量和时间中取得一个平衡, 争取达到最优的性能.
举一个简单的实现例子
一个队列存放所有待发送消息一个线程池批量发送消息 (最大线程数=核心线程数=n, 队列长度=1, reject=忽略)线程池中的每个Runnable, 批量尝试从队列里捞a条消息去发送. 发送完继续回来捞, 捞空为止.(捞a条的batch模式, 是为了减少队列的并发冲突, 和RPC通信的损耗)每次新消息入队, 都会尝试向线程池提交一个新的Runnable.线程池任务满时, 直接丢弃.为什么这么设计
动态调整并发量, provider消息频率高, 并发就大一点, 频率低, 就单线程发送, 以免浪费资源.有速度上限, provider提交消息频率太高, 也有一个限制的最大发送速度. 不至于压力太大, 冲垮broker.有一个问题, 如果以恰好的频率发送消息, 可能导致线程池始终保持最大线程数, 但是每次只发一条消息, 利用率极低, 远不如单线程独立发送. 但是在业务场景中, 几乎不可能出现.那么, 还有什么问题?另一种设计
一个线程监管队列长度, 达到x, 就调用线程池去取x个任务发送.一个线程计时, 达到y, 就调用线程池去取所有任务发送.每次发送前, 归零时间和数量的计数器.问题: 死板, 需要线程资源来监控…接下来再来聊聊异步. 郑重声明: 异步, 同步, oneway是三种东西; 客户端异步和服务端异步也是不一样的.
异步和同步的区别在于是否阻塞等待, 而oneway的意思是不关注结果客户端异步是指的提交任务之后, 不阻塞等待结果; 服务端异步指的是不阻塞执行任务直到返回. 代码举例如下 // 客户端同步, 服务端同步 Result ret = service.request(); // 客户端异步, 服务端同步 Future future = executor.submit(new Callable<Result>() { @Override public Result call() throws Exception { return service.request(); } }) // 客户端同步, 服务端异步 Future future = service.request(); Result ret = future.get(); //阻塞等待服务端返回 // 客户端异步, 服务端异步 Future future = service.request(); return future; //客户端也直接返回future, 想怎么用这个future就怎么用, 比如启个线程等待这个future isDone也可以. // oneway service.request(); 在消息队列的实际中, 客户端是显而易见应该异步投递消息.broker服务端是否应该异步处理消息? 异步处理消息可以减少rpc通信线程的挂起数量, 还可以有选择的合并消息, batch insert, 但是这就不可避免的会带来消息的延迟.