Java如何实现消费数据隔离?
我是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如何实现消费数据隔离?的更多相关文章
- JAVA代码之RocketMQ生产和消费数据
一.启动RocketMQ [root@master ~]# cat /etc/hosts # Do not remove the following line, or various programs ...
- JAVA多线程之间共享数据BlockingQueue介绍
在JAVA的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利. ...
- Android多线程研究(6)——多线程之间数据隔离
在上一篇<Android多线程研究(5)--线程之间共享数据>中对线程之间的数据共享进行了学习和研究,这一篇我们来看看怎样解决多个线程之间的数据隔离问题,什么是数据隔离呢?比方说我们如今开 ...
- vivo 评论中台的流量及数据隔离实践
一.背景 vivo评论中台通过提供评论发表.点赞.举报.自定义评论排序等通用能力,帮助前台业务快速搭建评论功能并提供评论运营能力,避免了前台业务的重复建设和数据孤岛问题.目前已有vivo短视频.viv ...
- Java在处理大数据的时候一些小技巧
Java在处理大数据的时候一些小技巧 发布时间:2013-05-09 00:00:00 来源:中国IT实验室 作者:佚名 关键字:Java 众所周知,java在处理数据量比较大的时候,加载到内存必 ...
- Android java传递string类型数据给C
本文接着实现<Android java传递int类型数据给C>的还未实现的方法: public native String sayHelloInC(String s); 先贴一个工具方法, ...
- Android java传递int类型数据给C
本文根据<Android jni简便开发流程>中的开发流程来实现一个java传递int类型数据给C 新建项目,进行简单的布局 <LinearLayout xmlns:android= ...
- Java学习-022-Properties 文件数据写入
Properties 配置文件写入主要通过 Properties.setProperty 和 Properties.store 两个方法,此文以一个简单的 properties 文件写入源码做示例. ...
- ThreadLocal 多线程并发,数据隔离
ThreadLocal: 创建一个线程本地变量. 本质:在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本. 优点:既实现多线程并发,游兼顾数据的安全性. 区别:Synchro ...
随机推荐
- 将ymal文件内容转换成字典格式
yaml文件内容如图: 转换代码如下: import yaml def init_yaml(): with open(r"..\config.yaml", 'r', encodin ...
- [ vue ] 解耦vuex(按照组件来组织vuex的结构)
问题描述 随着应用复杂度的增加,vuex用一个 store/index.js 文件来描述已经很难维护了,我们想把这些状态分割到单独文件里面. 参考1:https://vuex.vuejs.org/zh ...
- Linux上天之路(四)之Linux界面介绍
Linux界面 linux为使用者提供了图形界面和文本界面,但是很多操作依然需要文本界面的操作才能完成,很多人使用起来比较蹩脚,又因为linux平台的个人应用APP相对较少,使得大家的个人PC安装了l ...
- Word2010制作自动目录
原文链接:https://www.toutiao.com/i6488296610873737741/ 原文从网上复制: 查看"开始"选项卡,"样式"功能组,我们 ...
- 利用代码生成工具Database2Sharp生成ABP VNext框架项目代码
我们在做某件事情的时候,一般需要详细了解它的特点,以及内在的逻辑关系,一旦我们详细了解了整个事物后,就可以通过一些辅助手段来提高我们的做事情的效率了.本篇随笔介绍ABP VNext框架各分层项目的规则 ...
- Solon 1.6.15 发布,增加部分jdk17特性支持
关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web.Data.Jo ...
- unittest测试框架
unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果. 一.u ...
- 《剑指offer》面试题56 - II. 数组中数字出现的次数 II
问题描述 在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次.请找出那个只出现一次的数字. 示例 1: 输入:nums = [3,4,3,3] 输出:4 示例 2: 输入:nums ...
- 【记录一个问题】opencv官网的opencv android sdk使用opencl并未用到GPU
UMat u_mat;mat.copyTo(u_mat);cv::cvtColor(u_mat, cv::BGR2GARY);这样的代码反复执行,并未发现GPU占用提升.执行时间与不使用UMat相当. ...
- Spring Boot 3.0.0 发布第一个里程碑版本M1,你的 Java 升到17 了吗?
2022年1月20日,Spring官方发布了Spring Boot 3.0.0的第一个里程碑版本M1. 下面一起来来看看Spring Boot 3.0.0 M1版本都有哪些重大变化: Java基线从 ...