Use Dubbo for gateway design

Use Dubbo for gateway design

Hello everyone, I am Yi An! Today let's talk about using dubbo for gateway design.

Business scene

Suppose you are in a logistics company, and the business volume of the express logistics industry can be comparable to that of the Internet. You happen to be in the gateway project team. The core task of the gateway is to ensure fast access to various e-commerce platforms

alt

Explain the picture above.

The logistics company has built an order center microservice domain based on Dubbo, where the definition of creating an order interface is as follows:​

alt

There are many external e-commerce platforms, and each e-commerce platform has its own internal standards and will not follow a unified standard. For example, in Taobao, when a user purchases a product, Taobao internally defines a unified order dispatch interface. Its request package might look like this:

{
  "seller_id":189,
  "buyer":"dingwei",
  "order":[
    {
      "goods_name":"华为笔记本",
      "num":1,
      "price":500000
    },
    {
      "goods_name":"华为手表",
      "num":1,
      "price":200000
    }
  ]
}

However, for the order dispatch interface defined internally by Pinduoduo, its request package may be as follows:

<order>
  <seller_uid>189</seller_uid>
  <buyer_uid>dingwei</buyer_uid>
  <order_items>
    <order_item>
      <goods_name>华为笔记本</goods_name>
      <num>1</num>
      <price>500000</price>
    </order_item>
    <order_item>
      <goods_name>华为手表</goods_name>
      <num>1</num>
      <price>200000</price>
    </order_item>
  </order_items>
</order>

When e-commerce express delivery accounts for more than half of the total business volume of express delivery companies, e-commerce platforms have a higher voice than express delivery companies. That is to say, no matter which logistics company the e-commerce platform connects with downstream, it will issue the order delivery interface defined within its own company, and the adaptation work needs to be undertaken by the logistics company itself.

From the perspective of logistics companies, what should we do? You can't develop a set of ordering services for each e-commerce platform every time you connect to them, right? In that case, as more and more e-commerce platforms are connected, the complexity of the system will become higher and higher, and the maintainability will become worse and worse.

Design

It is against this background that the gateway platform was developed as a project. How is this gateway platform designed? What common problems need to be solved in the design process?

In my opinion, the design of the gateway needs to include at least three aspects, namely signature verification , service configuration and current limiting.

Let's talk about signature verification first. Securing requests is a system design priority. There is a very classic communication security verification mechanism in the industry: signature verification.

The method of this mechanism is that the client and the server will first use HTTPS to communicate to ensure the privacy of the transmission process.

客户端在发送请求时,先将请求参数按参数名称进行排序,然后按顺序拼接成字符串,格式为key1=a & key2=b。接下来,客户端使用一个约定的密钥对拼接出来的参数字符串进行签名,生成签名字符串(我们用sign表示签名字符串)并追加到URL。通常,还会在URL中追加一个发送时间戳(时间戳不参与签名验证)。

服务端在接收到客户端的请求后,先从请求中解析出所有的参数,同样按照参数名对参数进行排序,然后使用同样的密钥对参数进行签名。得到的签名字符串需要与客户端计算的签名字符串进行对比,如果两者不同,则请求无效。与此同时,通常我们还需要将服务端当前的时间戳与客户端时间戳进行对比,如果相差超过一定的时间,同样认为请求无效,这个操作主要是为了避免使用同一个连接对网络进行连续攻击。

这整个过程里有一个非常重要的点,就是密钥自始至终并没有在网络上进行过传播,它的安全性可以得到十足的保证。签名验证的流程大概可以用下面这张图表示:

alt

如果要对验证签名进行产品化设计,我们通常需要:

  1. 为不同的接入端(电商平台)创建不同的密钥,并通过安全的方式告知他们;
  2. 为不同的接入端(电商平台)配置签名算法。

在确保能够安全通信后,接下来就是网关设计最核心的部分了: 服务接口配置化。 它主要包括两个要点:微服务调用协议(Dubbo服务描述)和接口定义与参数映射。

我们先来看一下微服务调用协议的配置,设计的原型界面如下图所示:

alt

将所有的微服务(细化到方法级名称)维护到网关系统中,网关应用就可以使用Dubbo提供的编程API,根据这些元信息动态构建一个个消费者(服务调用者),进而通过创建的服务调用客户端发起RPC远程调用,最终实现网关应用的Dubbo服务调用。

基于这些元信息构建消费者对象的关键代码如下:

public static GenericService getInvoker(String serviceInterface, String version, List<String> methods, int retry, String registryAddr ) {
        ReferenceConfig referenceConfig = new ReferenceConfig();
        // 关于消费者通用参数,可以从配置文件中获取,本示例取消
        ConsumerConfig consumerConfig = new ConsumerConfig();
        consumerConfig.setTimeout(3000);
        consumerConfig.setRetries(2);
        referenceConfig.setConsumer(consumerConfig);
        //应用程序名称
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("GateWay");
        referenceConfig.setApplication(applicationConfig);
        // 注册中心
        RegistryConfig registry = new RegistryConfig();
        registry.setAddress(registryAddr);
        registry.setProtocol("zookeeper");
        referenceConfig.setRegistry(registry);
        // 设置服务接口名称
        referenceConfig.setInterface(serviceInterface);
        // 设置服务版本
        referenceConfig.setVersion(version);
        referenceConfig.setMethods(new ArrayList<MethodConfig>());
        for(String method : methods) {
            MethodConfig methodConfig = new MethodConfig();
            methodConfig.setName(method);
            referenceConfig.getMethods().add(methodConfig);
        }
        referenceConfig.setGeneric("true");// 开启dubbo的泛化调用
        return (GenericService) referenceConfig.get();
    }

通过getInvoker方法发起调用远程RPC服务,这样, 网关应用就成为了对应服务的消费者

因为网关应用引入服务规约(API包)不太现实,所以这里使用的是泛化调用,这样方便网关应用不受约束地构建消费者对象。

值得注意的是,ReferenceConfig实例很重,它封装了与注册中心的连接以及所有服务提供者的连接,需要被缓存起来。因此,在真实的生产实践中,我们需要将ReferenceConfig对象存储到缓存中。否则,重复生成的ReferenceConfig可能造成性能问题并伴随着内存和连接泄漏。

除了ReferenceConfig,其实getInvoker生成对象也可以进行缓存,缓存的key通常为接口名称、版本和注册中心。

那如果配置信息动态发生了变化,例如需要添加新的服务,这时候网关应用如何做到动态感知呢?我们通常可以用基于MQ的方式来解决这个问题。具体的解决方案如下:

alt

也就是说,用户如果在网关运营平台上修改原有服务协议(Dubbo服务)或者添加新的服务协议,变动后的协议会首先存储到数据库中,然后运营平台发送一条消息到MQ,紧接着Gateway的后台进程以广播模式进行订阅。这样,所有后台网关进程都可以感知。

如果是对已有服务协议进行修改,在具体实践时有一个小细节请你一定注意。我们先看看这段代码:

Map<String /* 缓存key */,GenericService> invokerCache;
GenericService newInvoker = getInvoker(...);//参数省略
GenericService oldInvoker = invokerCache.get(key);
invokerCache.put(newInvoker);//先缓存新的invoker
// 然后再销毁旧的invoker对象
oldInvoker.destory();

如果已经存在对应的Invoker对象,为了不影响现有调用,应该先用新的Invoker对象去更新缓存,然后再销毁旧的Invoker对象。

上面的方法解决了网关调用公司内部的Dubbo微服务问题,但还有另外一个非常重要的问题,怎么配置服务接口相关参数呢?

联系之前的场景,我们需要在页面上配置公司内部Dubbo服务与外部电商的接口映射。

alt

为此,我们专门建立了一条参数映射协议:

alt

参数映射设计的说明如下。

  • 请求类型:主要分为请求参数与响应参数;
  • 字段名称:Dubbo服务对应的字段名称;
  • Field type: the attribute of the field corresponding to the Dubbo service;
  • The field category: the type of the field corresponding to the Dubbo service;
  • Node name: the field name corresponding to the external request interface;
  • Display order: sort field.

Because the gateway adopts a generalized call, when writing the conversion code, it mainly traverses the incoming parameters, queries the corresponding conversion rules according to each field, and then converts it into a Map. The return value is just the opposite, which is to convert the Map into XML or JSON.

When actually requesting a call, after constructing the request parameter Map according to the mapping rules, the real call is executed through Dubbo's generalized call:

GenericService genericService = (GenericService) invokeBean;
Map invokerPams;//省略转换过程
// 参数类型数组
String[] paramTypes = new String[1];
paramTypes[0]="java.util.Map";
// 参数值数组
Object[] paramValues = new Object[1];
invokerPams.put("class", "net.codingw.oms.vo.OrderItemVo");
paramValues[0] = invokerPams;
//由于我们已经转化为java.util.Map,并且Map中,需要有一个key为class的,表示服务端需要转化的类型,这个从协议转换器中获取
Object result = genericService.$invoke(this.getInvokeMethod(), paramTypes, paramValues);

In this way, the gateway has high scalability and stability, can support business expansion very flexibly, configure different parameter conversions for different e-commerce platforms, and only need to develop a set of interfaces internally to support business very flexibly The expansion of the gateway basically achieves zero modification of the gateway code.

Summarize

In this article, through a real scenario, I introduce the background of the requirements of gateway design in detail, and then give a design solution for the pain points of gateway design. Through the interpretation of the key codes in this solution, you should be able to have a deeper understanding of the logic behind Dubbo's generalized calls, and truly combine theory with practice. It is worth noting that the conversion protocol in this paper uses square brackets to define a multi-layer nested structure, making the protocol universal.

This article is published by mdnice multi-platform

Guess you like

Origin blog.csdn.net/qq_35030548/article/details/130151342
Recommended