我是3y,一年CRUD经验用十年的markdown程序员‍常年被誉为优质八股文选手

今天继续更新austin项目,如果还没看过该系列的同学可以点开我的历史文章回顾下,在看的过程中不要忘记了点赞哟!建议不要漏了或者跳着看,不然这篇就看不懂了,之前写过的知识点和业务我就不再赘述啦。

今天要实现的是handler模块的消费数据隔离。在聊这个之前,先看下之前的实现是怎么样的。

austin-api接收到了请求之后,将请求发往Kafka,topicName为austin。而在austin-handler起了一个groupName名为austinGroup监听austin这个topic的数据,进而实现消息发送。

从系统架构来说,austin项目是可以发送多种类型消息的:短信、微信小程序、邮件等等等

那如果是单个topic单个group的话,有没有想过一个问题:如果某个发送渠道接口存在异常,超时了,此时会怎么样

没错,消息都会堵住,因为它们消费同一个topic,用的是同一个消费者。

01、数据隔离

要破局?很简单。多topic多group就行啦

上面这种能解决所有问题吗?并不。即便是同一个渠道,但不同类型的消息发送特性是不一样的。比如我要发push营销消息,有可能在某个时刻就要推送4000W的人群。

那这4000W人在短时间内完全发送出去,不太现实。这很可能意味着会影响到通知类的push消息

还要破局?很简单。 毕竟我们在设计消息模板的时候就已经考虑到这点了。消息模板有msgType字段来标识当前的模板属于哪种类型,那我们可以根据不同的消息类型再划分对应的group。

从理论上来说,我们可以为每种渠道的每种消息类型单独区分一个topic和group。因为topic间的数据是隔离的,不同的group间消费也是隔离的,那我们消费时肯定是数据隔离的。

不过,我目前的做法是:单topic多group。消费是隔离的,但生产的topic是共享的。我认为这样代码会更加清晰和易懂些,后期如果存在瓶颈了我们可以继续改。

02、消费端设计

从上面已经定了通过单topic多group来实现数据隔离。比如,我目前定义了6个渠道(im/push/邮件/短信/小程序/微信服务号)和3种消息类型(通知/营销/验证码),那相当于起了18个消费者。

从kafka获取得到消息以后,我暂定规划是走几个步骤:消息丢弃->去重->真正发送

从本质上看去重发送消息都是网络IO密集型。于是,为了提高吞吐量,我这边决定消费Kafka后存入缓存,做一层缓冲区

做一层缓冲区可提高吞吐量,但同样会带来别的问题。如:当应用重启时,缓冲区的数据还没消费完,那是不是就会丢失?

这个我们可以后面再看看怎么把带来的问题给搞掂(持续关注,项目优化后面多着呢)。现在还是认为缓冲区的利大于弊,所以回到缓冲区上。

缓冲区给我的第一反应是实现生产者消费者模式

要实现这种模式,我初想了下挺简单的:消费Kafka的消息作为生产者,然后把数据扔进阻塞队列上,开多个线程去消费阻塞队列的数据就完事了。

后来又想了下,直接线程池不就完事了吗?线程池不就是生产者和消费者的实现吗。

于是乎,架构就变成了下图:

03、代码设计

在消费端首先看Receiver的代码,该类看起来看简单,就只有一个@KafkaListener注解修饰方法,从Kafka消费出来随后交给pending做处理

我用的是@KafkaListener注解从Kafka拉取消息,而没有用低级的Kafka api,原因无他:在项目前期无需做到完美,等有瓶颈的时候再想办法就好了。虽说如此,但我写的时候还是给我带来了不少的麻烦。

第一个问题@KafkaListener是一个注解,从源码注释看它的传值只能够用Spring EL表达式和读取某个配置。但要知道的是,我的目的是想有多个group消费同一个topic。而我不可能说给每个group都定义一个消费的方法吧?(写这种破代码,我都睡不着觉

翻了一个晚上技术博客我都没找到方案,甚至还发了个朋友圈吐槽下有没有人遇到过。第二天我仔细翻了下Spring的官方文档,终于给我找到了方案。

还是官方文档实在

有了解决办法了以后,那事情就好办了。既然我是每种消息渠道的每种消息类型都要隔离,那我把这给枚举出来就完事啦!

我的Receiver是多例的,那么只要我遍历这个List就好了(初始化消费者在ReceiverStart类上)。

解决了用@KafkaListener注解动态传入groupId 进而创建多个消费者了之后。

我又遇到了第二个问题:Spring有@Aysnc注解来优雅实现线程池的方法调用。我之前是没用过@Aysnc注解的,但我看了下原理和使用姿势。我感觉这样挺优雅的(优雅永不过时)。但是用@Aysnc是肯定要自己创建线程池,并且我要给每个消费者都创建自己独有的线程池。而我不可能说给每个group都定义一个创建线程池的方法吧?(写这种破代码,我都睡不着觉

这次翻了官网和各种技术博客,都没能解决掉我的问题:在Spring环境下@Async注解上动态传入线程池实例,以及创建线程池实例时可支持根据条件传参。

最后只能放弃掉@Aysnc注解了,以编程的方式去实现:

下面是TaskPendingHolder的实现(无非就是给每个消费者创建对应的线程池),后面会考虑是否做成动态的:

而Task实现目前就比较简单啦,直接调用对应的Handler进而下发消息就好:

04、总结

代码看似简单,业务看似容易理解,但是要知道的是即便是很多小公司的生产项目都没有这种设计。一把梭可真的是太常见了(功能又不是不能实现,代码又不是不能跑,最主要的:人也不是不能跑)

这篇文章主要讲述了一个思路:在消费MQ的时候,多group是可以实现数据隔离的,想要提高消费的吞吐量,可以再做一层缓冲区(前提是消费是IO密集型的)

关注我的微信公众号【Java3y】除了技术我还会聊点日常,有些话只能悄悄说~ 【对线面试官+从零编写Java项目】 持续高强度更新中!求star!!原创不易!!求三连!!

源码Gitee链接:gitee.com/austin

源码GitHub链接:github.com/austin

Java如何实现消费数据隔离?的更多相关文章

  1. JAVA代码之RocketMQ生产和消费数据

    一.启动RocketMQ [root@master ~]# cat /etc/hosts # Do not remove the following line, or various programs ...

  2. JAVA多线程之间共享数据BlockingQueue介绍

    在JAVA的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利. ...

  3. Android多线程研究(6)——多线程之间数据隔离

    在上一篇<Android多线程研究(5)--线程之间共享数据>中对线程之间的数据共享进行了学习和研究,这一篇我们来看看怎样解决多个线程之间的数据隔离问题,什么是数据隔离呢?比方说我们如今开 ...

  4. vivo 评论中台的流量及数据隔离实践

    一.背景 vivo评论中台通过提供评论发表.点赞.举报.自定义评论排序等通用能力,帮助前台业务快速搭建评论功能并提供评论运营能力,避免了前台业务的重复建设和数据孤岛问题.目前已有vivo短视频.viv ...

  5. Java在处理大数据的时候一些小技巧

    Java在处理大数据的时候一些小技巧 发布时间:2013-05-09 00:00:00 来源:中国IT实验室 作者:佚名   关键字:Java 众所周知,java在处理数据量比较大的时候,加载到内存必 ...

  6. Android java传递string类型数据给C

    本文接着实现<Android java传递int类型数据给C>的还未实现的方法: public native String sayHelloInC(String s); 先贴一个工具方法, ...

  7. Android java传递int类型数据给C

    本文根据<Android jni简便开发流程>中的开发流程来实现一个java传递int类型数据给C 新建项目,进行简单的布局 <LinearLayout xmlns:android= ...

  8. Java学习-022-Properties 文件数据写入

    Properties 配置文件写入主要通过 Properties.setProperty 和 Properties.store 两个方法,此文以一个简单的 properties 文件写入源码做示例. ...

  9. ThreadLocal 多线程并发,数据隔离

    ThreadLocal:  创建一个线程本地变量. 本质:在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本. 优点:既实现多线程并发,游兼顾数据的安全性. 区别:Synchro ...

随机推荐

  1. django rest framework 自定义验证器

    一.基于钩子函数: 官网上的例子: 官方提示:如果字段声明在序列化类上时,就具有参数required=Fasle的作用,当函数名中没有包括字段名时,那么这个验证函数就不起作用 二.基于类的验证器: 使 ...

  2. 阿里云服务器ECS Ubuntu16.04 初次使用配置教程(图形界面安装)

    原文链接:? 传送门 前一阵子购买了阿里云的云服务器ECS(学生优惠),折腾了一阵子后对有些东西不太满意,所以就重新初始化了磁盘,刚好要重新安装图形界面,于是就顺手写了这么一篇文章. 第一次登陆服务器 ...

  3. xray与burp联动被动扫描

    最近也是刚实习了几天,看见带我的那位老哥在用xray,而且贼溜,所以我想写几篇关于xray的使用的文章 0x00 xray建立监听 在实际测试过程中,除了被动扫描,也时常需要手工测试.这里使用 Bur ...

  4. markdownFormat

      对文档编辑主要还是用wps,因为以前毕业论文都是用的它来编排(刚开始用wps毕业论文的时候真的是用的想吐,感觉非常不好用,而且功能太多但对于自己需要的功能又偏偏找不到),用过几次后还觉得用它编辑文 ...

  5. day 19 C语言顺序结构基础2

    (1).算术运算符和圆括号有不同的运算优先级,对于表达式:a+b+c*(d+e),关于执行步骤,以下说法正确的是[A] (A).先执行a+b的r1,再执行(d+e)的r2,再执行c*r2的r3,最后执 ...

  6. Maven 框架结构知识总结

    1.maven目录结构 目录 内容 ${basedir} 存放pom.xml和所有子目录 ${basedir}/src/main/java 项目Java代码 ${basedir}/src/main/r ...

  7. java实现excel表格导入数据库表

    导入excel就是一个上传excel文件,然后获取excel文件数据,然后处理数据并插入到数据库的过程 一.上传excel 前端jsp页面,我的是index.jsp 在页面中我自己加入了一个下载上传文 ...

  8. CMake语法—内置变量

    目录 CMake语法-内置变量 1 CMake变量分类 1.1 普通变量 1.2 缓存变量 1.3 环境变量 1.4 内置变量 2 CMake内置变量分类 2.1 提供信息的变量 2.2 改变行为的变 ...

  9. synergy最佳解决方案——barrier

    synergy最佳解决方案--barrier ​ 不知道大家有没有一套键盘鼠标控制多台电脑的需求,主流的硬件或说软件有大神整理如下: 软件方案: Windows 之间:Mouse Without Bo ...

  10. centos6.6手动安装mysql5.5并配置主从同步

    0.实验环境 主机IP(Master) 192.168.61.150 centos6.6 从机IP(Slave)   192.168.61.157 centos6.6 1.查看centos系统版本 [ ...