4

Dubbo服务提供者如何优雅升级? - 问北

 1 year ago
source link: https://www.cnblogs.com/ibigboy/p/17276181.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

文章首发于公众号:BiggerBoy。欢迎关注。

往期文章推荐

大坑!隐式转换导致索引失效...
高性能分布式限流:Redis+Lua真香!
MySQL索引知识点&常见问题汇总联合索引在B+树上的存储结构及数据查找方式
Redis分布式锁实战Mybatis第三方PageHelper插件分页原理MySQL索引底层原理 

一、问题交代

当我们使用dubbo作为服务间通信的组件时,在后期的系统维护中可能会因为业务需要,服务提供者某些接口需要升级,对应的服务消费者配合作相应的修改,测试通过后一起发版上线即可。但是在这个过程中,有很多需要注意的点,不妨来梳理一下以作记录,希望对此不清楚的开发者有所帮助。
再次申明一下我们的需求吧。假设服务A提供了一个服务IHelloService,其中有一个方法sayHello,服务B、C、D...都对此有依赖,假设服务A提供的该接口已上线且被服务B、C、D正常消费,线上运行稳定。

1205690-20230331142217310-569226555.png

 先将服务提供者和服务消费者分别启动,此时消费方正常消费服务。

public interface IHelloService {
    Result sayHello(ModelParent param);
}

服务提供者:

@Component
public class HelloServiceImpl implements IHelloService {
    @Override
    public Result sayHello(ModelParent param) {
        MyDto dto = new MyDto();
        dto.setMsg("hello," + param.getName());
        return Result.success(dto);
    }
}

服务消费者:

@Service
public class MyService {
    @DubboReference(group = "test",version = "1.0",registry = "registry2")
    private IHelloService helloService;

    public Result test(ModelParent param) {
        return helloService.sayHello(param);
    }
}

二、接口迭代升级方案

过了几个月,服务B接到产品需求,为了满足需求,对服务A的IHelloService.sayHello提出新的要求,因为本次需求不涉及服务C、D的改动,所以服务A对该服务还需要满足服务C、D调用时保持老逻辑不变,因此服务A需要格外注意服务的兼容性。

注意:这里说的服务A、B、C...是一个个应用或系统。应用或系统对外提供多种服务,例如用户服务对外提供用户信息查询服务、用户注册服务等。

假设服务A和服务B的开发人员确认完需求后,服务A的开发人员想到的接口升级方案如下:

  1. 服务A新提供一个sayHelloV2方法或新服务IHelloServiceV2,提供新的业务逻辑,服务B调这个新方法或新服务,原来的sayHello保留,服务C、D继续调用且本次不用修改上线。
  2. 服务A修改IHelloService.sayHello,在原来的入参中新增字段,用于区分走新逻辑还是老逻辑。在这种方式下就需要特别注意了,因为入参增加了参数,要考虑是否会影响本次需求不涉及的服务C和服务D。

    2.1 在原入参对象中新增字段。服务C、D对于新增的字段肯定传的是null,所以可以用null标识走老逻辑,用其他值比如1标识走新逻辑。

    2.2 创建新的入参对象 ModelChildren 增加新字段,且该入参对象继承原入参对象。此时又分不同的情况。

      a.接口方法入参对象保持原入参对象不变,即,方法签名不变,仍然是Result sayHello(ModelParent param)。仅需改动有新需求的服务B的代码,将入参对象改为新入参对象,由于该对象继承了原入参对象,所以不会有问题,但服务A需要判断传的是父类 ModelParent 还是子类 ModelChildren,并从子类 ModelChildren 中获取新字段。

      b.接口方法入参对象改为新入参对象,即,方法签名修改,入参改为新对象Result sayHello(ModelChildren param)。此时,所有消费该服务的消费方均需要修改,如果消费方不改,消费方调用该方法时会抛异常org.apache.dubbo.remoting.RemotingException: Fail to decode request due to:java.lang.IllegalArgumentException:Service not found:com.biggerboy.api.IHelloService, sayHello

1205690-20230331142258767-1201324978.png
方案 参数列表 优点 缺点
新增方法sayHelloV2 - 1.新老逻辑分开,兼容老版本2.对本次不涉及的服务消费者友好,因为可以不用改动代码继续使用老方法 1.额外创建新的服务方法,对内存消耗略微增加(可忽略)
新增服务IHelloServiceV2 - 1.新老逻辑分开,兼容老版本2.对本次不涉及的服务消费者友好,因为可以不用改动代码继续使用老服务 1.额外创建新的服务,对内存消耗略微增加(可忽略)
修改入参ModelParent增加字段 不变 1.兼容老版本,对不涉及的服务无影响2.不用新增服务,不会额外消费内存 1.程序中需使用新增的字段区分新老逻辑
新增入参子类ModelChildren增加字段并继承原入参ModelParent 不变 1.兼容老版本,对不涉及的服务无影响2.不用新增服务,不会额外消费内存3.新增加了入参不会影响其他同样使用ModelParent作为参数的服务 1.程序中需使用新增的字段区分新老逻辑2.额外创建新的类,对内存消耗略微增加(可忽略)
新增入参子类ModelChildren增加字段并继承原入参ModelParent 改为子类 1.不用新增服务,不会额外消费内存2.新增加了入参不会影响其他同样使用ModelParent作为参数的服务 1.程序中需使用新增的字段区分新老逻辑2.额外创建新的类,对内存消耗略微增加(可忽略)3.需修改参数列表,当方法调用链较长且使用入参对象作为参数列表时,需修改的方法较多4.不兼容老版本,本次不涉及的服务消费者也需修改,负责报错

你们公司是如何做的呢?欢迎评论区一起讨论~ 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK