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

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

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

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

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

3046 次点击
所在节点    程序员
34 条回复
wolfie
2023-02-02 18:59:37 +08:00
> 如果接口部署了多实例,同一个消费者会争夺同一个消息进行处理,浪费了大量线程资源,也不能提高消费效率

广播?
liyanggyang
2023-02-02 19:21:28 +08:00
@simonlu9 这样来说,业务功能是面向用户的,管理后台是面向软件运维者。
这个例子,那这个很好理解了,群创建或解散,属于业务的功能(当然你说后台也具备功能,这说法没问题,但是后台只是给软件的管理员提供一个集中管理的便捷),所以这个肯定是接口服务,一个群解散的接口服务,消息在接口服务里面消费。管理后台走内部通道去调用接口服务(即接口服务是 api1 ,可以专门写一个内部接口 api2 ,但是 api1 2 里面的实现 service.dell()是一个 )
kjstart
2023-02-03 05:21:07 +08:00
根据异步负载决定, 因为要分别做弹性伸缩
kjstart
2023-02-03 05:22:48 +08:00
可以用一套代码, 用配置文件决定是否拉取消息就可以了
nothingistrue
2023-02-03 09:22:09 +08:00
@simonlu9 #17 生产者、消费者、消息中间件之间,只有消息中间件是服务器端需要维持多个连接,生产者、消费者都是客户端,只需要用一个线程维持一条连接,不管有多少消息队列。你把消费者监听消息,跟处理消息这两个阶段混在一起看了,这俩不能放到一个线程上同步搞,不然就算你一个队列一个线程,都要严重阻塞。通常来说,消费者收到消息后,是要把实际处理,再转交给异步执行器(对 Java 来说就是线程池)的,这样只需一个线程负责接受消息,一个执行器(线程池)负责处理消息。
@simonlu9 #18 如果是单纯的随机分配或均衡分配的消费者组的话,应该也是无需给出实例 ID 的。
winglight2016
2023-02-03 09:34:41 +08:00
@nothingistrue #25 说得对,我司以前就是把监听消息和业务处理放在一起,会导致非常严重的问题。我现在正在做优化,完全分离 MQ 和业务,专门做一个单独的应用来负责映射转发。

另外,lz 担心的应用如何确定具体实例问题,一般业务后台如果是多实例部署,前面会放一个 LB ,由 LB 来决定具体哪个实例来响应请求
nothingistrue
2023-02-03 09:41:48 +08:00
关于实例 ID 这部分,这个重点是,消费者必须明确的告知消息中间件两个属性:一共有多少个实例,当前实例区分其他实例的标识(可以简单的只是个序号)。这是业务无关的,只跟运维实例的部署配置有关。配置的内容也不多,只需要消费者多加两个配置项,但是这俩配置项管理起来比较麻烦,因为它们的值不可预定义,只能在部署的时候动态决定,且每次部署都要重新决定。

单纯负载均衡或者随机分配的多实例消费者组的话,理论上是无需理会实例 ID 的。当消息不是自动均衡分配,而是按规则分区分配(比如说 1-5 给实例 1 ,6-10 给实例 2 )的时候,就必须明确给出实例 ID 了。

我这里参考的是 Spring Cloud Stream ( https://docs.spring.io/spring-cloud-stream/docs/current/reference/html/spring-cloud-stream.html#spring-cloud-stream-overview-partitioning )。请注意这些规则不是 Spring Cloud Stream 决定的,是消息中间件决定的,Spring Cloud Stream 只是做了上层抽象使其用起来更简单些。
nothingistrue
2023-02-03 09:49:08 +08:00
@winglight2016 #25
只需要把监听消息和业务处理在应用内部解开就行了,不要拆成不同应用。监听消息和业务处理放在一个服务中,就只是底层的一个线程和异步线程池,对上层业务逻辑没影响。你要分成两个应用,先不考虑资源浪费问题,业务逻辑就搞复杂了。通常来说,映射转发也就几条规则,处理几十个队列,不管是业务逻辑还是性能上,都不具备独立出去的需要。只有映射转发规则多到需要专门的管理界面的时候,才能考虑独立出去。
morty0
2023-02-03 10:16:56 +08:00
@nothingistrue 全部消息都异步处理, 监听服务收到消息就直接 ack 吗, 还是等异步处理完 ack 呢?
NoKey
2023-02-03 10:19:49 +08:00
没怎么看懂,大概给你出个主意:
1. 把 kakfa 拿来好好研究一下
2. 如果有很多服务器,然后更新可能不及时,消息连结构都改了,会导致旧的消费者拿到之后出错,那么,新增一个 topic ,新旧 topic 共存一段时间,等全部更新完,应该就 ok 了。
看看有没其他大神有好办法。
nothingistrue
2023-02-03 10:39:37 +08:00
@morty0 #28 如果是异步处理,那么收到即表示成功,自然是收到就 ack 。但不是所有消息都要异步处理,这个具体要看是啥消息。ack 或者 死信机制,只对同步处理的消息有作用,异步处理的消息,需要有其他机制做异常处理。
winglight2016
2023-02-03 11:38:35 +08:00
@nothingistrue #28 我们的场景不太一样,需要容器化部署在 k8s 上,而且基于 python ,必须在单独的容器中启动监听服务。然后,这个容器进程如果爆出异常又没有捕获会导致整个容器都不能正常启动,所以必须和业务代码分离。另外,虽然只有几条规则,但是不能写在代码里,不然规则变化也会导致重新打包和部署。
zeonll
2023-02-03 16:49:05 +08:00
要分开,方便隔灾和扩容
cnlinjie
2023-03-21 21:45:53 +08:00
spring cloud stream 官方好像没有支持 redis 的库,你是用 `https://github.com/spring-attic/spring-cloud-stream-binder-redis` 这套吗?

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

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

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

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

© 2021 V2EX