微服务中,消息队列要单独拆一个服务进行消费吗

2023-02-02 17:00:55 +08:00
 simonlu9

如题,有两个单体项目,一个是管理后台,一个是接口服务,有一个子模块群组消息队列,管理后台应用和接口都引用了,现在是同一个消费组,同一个消费者,因为不能重复消费,消费的时候进行轮询,现在的问题是

  1. 如果接口部署了多实例,同一个消费者会争夺同一个消息进行处理,浪费了大量线程资源,也不能提高消费效率
  2. 如果消费者的代码更改了,有时候没有更新到位,接口更新了,可能管理后台没更新,可能消费逻辑又不一样

大家是否会碰到这种问题,docker 多实例部署会考虑定时任务,消息队列,在多个容器运行的情况吗, 定时任务已经通过 xxl-job 去解决这个问题,但消息队列不知道怎么处理,望赐教

3009 次点击
所在节点    程序员
34 条回复
199808lanlan1111
2023-02-02 17:02:57 +08:00
分成两个项目就意味着每次要打开两个 idea ,分支要高俩,发板要搞俩等等,可以先搞在一起,但是模块分开,后面请求量上来可以单独拆分保证稳定性可靠性
wangxin3
2023-02-02 17:04:05 +08:00
具体什么消息队列呢,rabbitmq 绑定在同一个队列上的消费者组是不会重复消费的
kafka 也是同理,消费者配置为同一个消费者组也是不会重复消费的
mooyo
2023-02-02 17:05:21 +08:00
前司分开了,现司没分开。感觉没区别。
dolorain
2023-02-02 17:06:17 +08:00
看体量多大了,小打小闹肯定没必要了
simonlu9
2023-02-02 17:06:44 +08:00
@199808lanlan1111 现在就是同一个项目的,只是不同 application ,就消费队列这个问题不好处理,能不能中心化
199808lanlan1111
2023-02-02 17:08:41 +08:00
@199808lanlan1111 没审题 说错了,但 op 的问题我只看懂了最后一个问题,需不需要考虑多实例问题。

首先肯定要考虑的,这就是分布式系统的特性。消息队列你不需要考虑,消息会靠队列进行负载均衡,每个实例会会处理一个或者多个队列的消息
koloonps
2023-02-02 17:10:24 +08:00
“如果接口部署了多实例,同一个消费者会争夺同一个消息进行处理,浪费了大量线程资源,也不能提高消费效率” rabbitmq 在消息没有退回 /超时之前 mq 服务器不会重新推送
simonlu9
2023-02-02 17:10:37 +08:00
@wangxin3 同一个消费组,是用 redis stream,只是消费者名称都是写死在代码里面,所以多实例,最终还是同时消费一条消息,除非部署多实例的时候消费者名称动态配置
wangxin3
2023-02-02 17:18:52 +08:00
@simonlu9 #8 原文:“@wangxin3 同一个消费组,是用 redis stream,只是消费者名称都是写死在代码里面,所以多实例,最终还是同时消费一条消息,除非部署多实例的时候消费者名称动态配置”
======
回复:不理解你说的 可以画个架构图?
colincat
2023-02-02 17:22:05 +08:00
@simonlu9 该消息模块是否可以动态传入 groupName ,当管理后台引用是使用管理后台消费组-配置到配置文件中,当接口服务使用时使用 接口消费组
simonlu9
2023-02-02 17:25:56 +08:00
@wangxin3 代码逻辑大概是这样,考虑以下方法再多实例运行,消费组是同一个,消费者名称是写死
@Override
public void run(ApplicationArguments args) throws Exception {
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, ChatGroupUserDTO>> options =
StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
.batchSize(10)
.executor(executor)
.pollTimeout(Duration.ofSeconds(5))
.targetType(ChatGroupUserDTO.class)
.build();
StreamMessageListenerContainer<String, ObjectRecord<String, ChatGroupUserDTO>> container =
StreamMessageListenerContainer.create(redisConnectionFactory, options);

prepareChannelAndGroup(redisTemplate.opsForStream(), MESSAGE_STREAM, MESSAGE_GROUP);

container.receive(Consumer.from(MESSAGE_GROUP, "consumer-1"),
StreamOffset.create(MESSAGE_STREAM, ReadOffset.lastConsumed()),
messageListener);
this.container = container;
// 启动监听
this.container.start();
logger.info("{}启动成功",MESSAGE_STREAM);
}
leeraya
2023-02-02 17:30:07 +08:00
现司就是专门搞了一个消息队列中转服务,根据不同的策略接收转发消息。
好处就是整个服务网格内几乎所有用到消息的服务都走中转,问题排查集中在中转站存的消息日志。
不过就是要专门有人维护这种基础设施服务,又是搞消息的,费人工。
wangxin3
2023-02-02 17:30:54 +08:00
@simonlu9 #11 原文:
回复:consumer-1 保证消费者名称不重复不就行了?你现在可以单实例,把这个代码在运行一份,两份不同的消费者名称,但在同一个消费者组,看看是否重复消费了。
simonlu9
2023-02-02 17:38:33 +08:00
@wangxin3 在同一个消费组,消费者名称一样的话,假设有三个实例,thread-1 thread-2 thread-3, 每条消息只会投递到某个实例,不会三个实例都投放,我的主要问题是如果像这种多个实例,好像会饿死线程,大量浪费
wangxin3
2023-02-02 17:44:21 +08:00
@simonlu9 #14
======
回复:怎么会呢,发布订阅模式不就是 redis 有消息才会给消费者发消息吗,redis 只有一条消息,轮询到实例 A 了,就发给实例 A ,实例 B 和 C 该干啥干啥呀,怎么会饿死线程,大量浪费。实例 B 和 C 又不会因为消费者线程阻塞在等 redis 发消息,有消息才会处理呀。
nothingistrue
2023-02-02 17:54:46 +08:00
微服务是跟着业务走,不跟着技术实现方式走的。消息队列消费者,绝大多数情况下,都不对应一种业务(具体的说就是实体、实体表、限界上下文这些),当然不能单独拆成一个服务。

问题 1 ,可以通过配置消费者组进行解决,一个消费者组,同一个消费者只会按调度规则扔给唯一的消费者。RabbitMq 、Kafka 都这只,Spring Cloud Stream 还提供了超简单的实现方式(不过运维要麻烦点,后面说)。

问题 2 ,我没明白你说得是什么,看起来像是系统升级时候的配合问题,这个解决起来稍微麻烦但也不是不能解决。生产者消费者如果不能同时更新,那么消息协议上,就要考虑多版本同时兼容的问题了。

最后说说运维麻烦的地方。多实例的时候,对于接口调用,是不用区分具体哪个示例的,负载均衡机制随便选一个就行了,所以实例无需明确 ID ,随机生成都可以。但是对于消费者组,就不能那么随意了,通常都是要明确给出实例 ID 的,不能随机生成,这会增加部署的麻烦成都。。
simonlu9
2023-02-02 18:07:43 +08:00
@wangxin3 我算一下资源成本,一个业务一个主题,10 个业务就 10 线程在监听队列,如果再单台机器部署多实例,10*n 个线程在跑,没意义
simonlu9
2023-02-02 18:09:52 +08:00
@nothingistrue 但是对于消费者组,就不能那么随意了,通常都是要明确给出实例 ID 的,不能随机生成,可以说说这块运维一般怎么搞的吗,公司运维也是我
liyanggyang
2023-02-02 18:42:45 +08:00
消费过后,数据存储的在一个地方,那不就没任何问题了。
关于消费者放在哪儿的问题,我理解,看这个消息的类型:
1. 这个消息是用户业务相关的,那么放在接口服务。比如接口服务是账单服务,那么支付系统发送过来的支付消息,就在接口服务。
2. 这个消息只是后台管理相关的,那么就放在管理后台。比如消息队列是 xxx 公司报表系统发送过来的,需要做 xxx 管控,那么就在管理后台,因为它与用户业务无关。
simonlu9
2023-02-02 18:57:01 +08:00
@liyanggyang 举一个很简单例子,比如群吧,解散后会有后续动作,接口端可以解散, 管理后台也可以解散,都是共用一个逻辑

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/912642

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX