单体应用改造微服务疑惑求解 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
love2075904
V2EX    Java

单体应用改造微服务疑惑求解

  •  
  •   love2075904 2022-02-16 15:01:44 +08:00 3510 次点击
    这是一个创建于 1334 天前的主题,其中的信息可能已经有所发展或是发生改变。

    长期以来公司架构是基于 Spring 的单体应用,经过这么多年的发展,在企业应用开发层面沉淀颇多,对于新需求也能快速响应。但是由于目前公司业务多元化,单体应用的瓶颈就上来了,比如某个业务需要独立迭代,SAAS 改造的提出等,所以计划将单体升级改造为微服务架构。

    目前经过调研,整体升级代价不大,因为虽然是单体应用,但是依然使用多模块开发,各应用天然解耦。但是目前遇到几个比较麻烦的题,看看有做过相同事情的 hxd 有没有解决方案。

    • 基于 Spring 容器的扩展机制

    为了降低模块之间的耦合度,系统中大量利用了 Spring 的一些特性,比如 getBeansOfType(),这种模式下,只需要在 A 模块定义好接口,在 B 和 C 模块定义好实现,即可在不同的业务场景下调用不同的实现。这种方式在同一个 Spring 上下中可以轻易获取实现类,但是微服务后就是多个上下文环境,就无法获取到这些实现类了。 伪代码如下:

    模块 A:

     public interface MessageSender { /** * 发送者唯一标识 */ String key(); /** * 发送消息 */ void send(List<Message> messages); } @Controller public class MessageController { private ApplicationContext ac; public void sendMessage(String key){ List<MessageSender> messageSenders = ac.getBeansOfType(MessageSender.class); for(MessageSender sender: messageSenders){ // 获取对应的实例 if(Objects.equals(sender.getKey(), key)){ // 推送消息 sender.send(...); break; } } } } 

    模块 B

    @Component public class EmailMessageSender implements MessageSender { /** * 发送者唯一标识 */ String key(){ return "email": } /** * 发送消息 */ void send(List<Message> messages){ // 发送邮件 } } 

    模块 C

    @Component public class SMSMessageSender implements MessageSender { /** * 发送者唯一标识 */ String key(){ return "sms": } /** * 发送消息 */ void send(List<Message> messages){ // 发送短信 } } 
    • 事件机制

    利用 Spring 的ApplicationEventPublisher实现事件推送,目前了解到可以通过对接Spring Cloud Bus实现跨服务事件推送。现在在我们应用中有同步事件和异步事件,异步事件使用@Async实现,目前没有了解到Spring Cloud Bus是否支持同步事件?

    模块 A

    public class MessageService{ ApplicationEventPublisher publisher; public void sendMessage(){ publisher.publishEvent(event); } } 

    模块 B

    // 模块 B 监听程序 public class EventListener{ @EventListener public void onEvent(){ // 同步监听 } @Async @EventListener public void onEvent(){ // 异步监听 } } 
    第 1 条附言    2022-02-17 09:26:11 +08:00
    关于第一个问题,MQ 确实是一个思路,感谢 @psydonki ,但是利用 MQ 会有事务问题,这个也需要考虑一下。
    14 条回复    2022-02-22 02:50:26 +08:00
    love2075904
        1
    love2075904  
    OP
       2022-02-16 15:28:46 +08:00
    目前针对第一个问题,有一个解决方案是模仿 xxl-job 这种,让模块 B 和模块 C 向模块 A 进行注册,然后通过 http 调用执行
    sky857412
        2
    sky857412  
       2022-02-16 17:55:51 +08:00
    第一个问题,应该将 MessageSender 的实现类都放到一个微服务中,controller 调用这个微服务暴露的接口,具体是调用哪个实现类,在这个暴露的接口中处理
    第二个,应该无法没办法实现同步事件
    wolfie
        3
    wolfie  
       2022-02-16 18:11:05 +08:00
    @FeignClient
    public interface EmailMessageSender extends MessageSender {}
    psydonki
        4
    psydonki  
       2022-02-17 01:10:56 +08:00
    我理解使用 MQ 作为中间件可以很好的解决这个问题。
    Sender 只负责向 MQ 发送消息,同时在 Header 里面注明消息的类型:

    ```json
    {
    "type": "email"
    }
    ```

    然后根据 header 中的类型,将消息分流到不同的 queue 。
    模块 B/C 分别是不同的微服务,监听不同的 queue 就好。
    /div>
    love2075904
        5
    love2075904  
    OP
       2022-02-17 09:19:06 +08:00
    @sky857412 之前有过这种想法,但是整体看来是不可能的,MessageSender 只是伪代码,实际业务中是不同的业务模块处理代码。
    love2075904
        6
    love2075904  
    OP
       2022-02-17 09:20:45 +08:00
    @wolfie 这种就写死了,但是实际上,模块 A 根本不需要知道有哪些实现,这个时候完全可以出现一个模块 D 来实现一个新的 MessageSender
    love2075904
        7
    love2075904  
    OP
       2022-02-17 09:21:43 +08:00
    @psydonki MQ 确实是一个思路,感谢!
    wolfie
        8
    wolfie  
       2022-02-17 09:44:09 +08:00
    @love2075904 #6
    这个就是放在公共依赖里的 FeignClient 声明。
    你还是可以根据 MessageSender 类型获取所有的 bean 。
    kowgarnett
        9
    kowgarnett  
       2022-02-17 10:41:21 +08:00
    我司的做法是抽出来了一个单独的微服务负责所有的 send message ,需要同步的用 REST call ,不需要的用 Kafka event 处理
    freeup
        10
    freeup  
       2022-02-17 16:55:40 +08:00
    对于拆分单体项目成微服务的我说几个我经历过得坑
    1.方法间调用问题,原来都是直接调用,拆分出去后就是远程调用,所以相关代码都得改造
    2.查询。。查询是最麻烦的,如果都在同一个库都还好,如果拆分时拆分了库。。那么以前的查询要么直接跨库联查,要么单独查
    3.公共部分需要单独打包,也就是一些公共的依赖需要单独新建项目进行开发然后整合,各个服务进行依赖
    4.事务问题,我们用 seata 解决分布式事务问题
    其他的 都是一些业务上的问题了 我遇到过的就这几个问题比较麻烦
    love2075904
        11
    love2075904  
    OP
       2022-02-18 13:40:57 +08:00
    @wolfie 我大概了解您的意思了,感谢。
    love2075904
        12
    love2075904  
    OP
       2022-02-18 13:42:12 +08:00
    @kowgarnett 这也是一个方案,不过目前这样改动影响比较大,这样一来这个单独的微服务可能就不够纯粹,会去调用其他的业务模块代码了。
    love2075904
        13
    love2075904  
    OP
       2022-02-18 13:42:47 +08:00
    @freeup 确实,这几个坑我们肯定也要踩。
    kowgarnett
        14
    kowgarnett  
       2022-02-22 02:50:26 +08:00
    @love2075904 那就看你们对于微服务的业务范围的定义了,这个都是要讨论看 trade-off 找平衡的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5453 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 38ms UTC 01:22 PVG 09:22 LAX 18:22 JFK 21:22
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86