Dubbo3.3 Triple协议处理东西向流量

前言

Apache Dubbo 3.3 对 Triple 协议做了升级,现在 Dubbo 不仅可以处理东西向流量,也可以处理南北向流量。

**东西向流量(East-West Traffic) **

指数据中心或网络内部同级设备/服务之间的通信。例如,微服务之间的内部调用。

南北向流量(North-South Traffic)

指网络外部用户与内部服务之间的通信。例如,客户端访问微服务。

之前的 Dubbo 一般只用来处理服务内部的高性能RPC调用,外部用户要想访问 Dubbo 服务,只能再额外部署一个网关层,网关负责把 HTTP 协议转换成 dubbo 协议。这样一来,不仅网络多了一跳增加时延,维护难度也大大增加。

另外,由于需要在 Web 框架和 RPC 框架之间频繁切换,开发复杂度和性能均有影响,比如:

  • 针对 Web 框架和 RPC 框架的诸如:日志、监控、拦截器、异常处理等,都需要重复开发
  • Web 框架和 RPC 框架都有自己处理任务的线程池,系统需要在二者之间频繁切换,影响性能

Apache Dubbo 3.3 Triple X 协议的诞生,直接回应了这些痛点。支持东西向流量,只是 Triple X 其中一个特性,完整功能参考:Apache Dubbo 3.3 全新发布:Triple X 领衔,开启微服务通信新时代

安装&使用

最新的版本目前是3.3.5,如果只用 dubbo,引入下面一个依赖即可。

xml 复制代码
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>3.3.5</version>
</dependency>

RestService 服务开发,因为要处理南北向流量,需要通过@Mapping等注解声明请求路径和参数等信息。

java 复制代码
@Mapping(path = "/")
public interface RestService {

    @Mapping(path = "/hello", method = HttpMethods.GET)
    String hello(@Param("name") String name);
}

public class RestServiceImpl implements RestService {
    @Override
    public String hello(String name) {
        return "hello " + name;
    }
}

接着,启动服务即可。如下代码,服务暴露的端口是 50000,协议名是简写的tri

java 复制代码
public class Provider {
    public static void main(String[] args) {
        ServiceConfig<RestService> serviceConfig = new ServiceConfig<>();
        serviceConfig.setInterface(RestService.class);
        serviceConfig.setRef(new RestServiceImpl());

        ApplicationConfig applicationConfig = new ApplicationConfig("provider");
        applicationConfig.setQosEnable(false);
        DubboBootstrap.getInstance()
                .application(applicationConfig)
                .protocol(new ProtocolConfig("tri", 50000))
                .service(serviceConfig)
                .registry(new RegistryConfig("N/A"))
                .start().await();
    }
}

triple 默认支持 HTTP1和HTTP2,如果要使用HTTP3需要引入额外依赖。现在,通过IP+端口就可以直接以 http 协议访问服务了。

bash 复制代码
$ curl 127.0.0.1:50000/hello\?name=triple
"hello triple"

整合Spring Boot

单独用 Dubbo 开发比较少,一般都是整合 Spring Boot 一起用。要整合 Spring Boot,引入下面依赖:

xml 复制代码
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>3.3.5</version>
</dependency>

application.yml配置 Dubbo 协议部分

yaml 复制代码
dubbo:
  protocol:
    name: tri
    port: 50000

Dubbo 默认支持 Spring Web 注解,所以用 Dubbo 写 Web API 和之前几乎没区别,如下所示:

没有@RestController,但是必须加@DubboService

java 复制代码
@DubboService
@RequestMapping
public class MyRestController {

    @GetMapping("/hello")
    public String hello(@RequestParam("name") String name) {
        return "hello " + name;
    }
}

启动类上加@EnableDubbo启用 Dubbo,访问 50000 端口即可访问服务。

bash 复制代码
$ curl 127.0.0.1:50000/hello?name=triple
"hello triple"

分端口混合模式

如果旧项目已经用 Spring MVC 开发了一些 API,但是新的 API 想用 Dubbo 开发,可以用区分端口的混合模式部署。区分端口,也就是 Web 容器占用一个端口,Dubbo 占用一个端口,彼此之间互不干扰。

项目同时依赖spring-boot-starter-web和 Dubbo

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>3.3.5</version>
</dependency>

application.yml分别配置 Web 容器端口和 Dubbo 协议端口

yaml 复制代码
server:
  port: 8080
dubbo:
  protocol:
    name: tri
    port: 50000

这样一来,之前的 API 通过 8080 端口访问,Dubbo API 通过 50000 端口访问,互不影响。

一种更方便的写法,在 Controller 上同时加@Controller@DubboService注解,8080 和 50000 端口都可以访问这个 API。但是要注意,Spring MVC 的所有功能并非 Dubbo 都支持,实测下来发现 Spring 的Web拦截器和全局异常处理,通过Dubbo访问是不生效的。

同端口混合模式

如果不想 Dubbo 再额外占用端口,也可以采用不区分端口的混合模式部署。

引入下面这个依赖

xml 复制代码
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-3-autoconfigure</artifactId>
    <version>3.3.5</version>
</dependency>

application.yml把 triple 协议端口和server.port配置相同,Dubbo 将不再占用新端口,可复用 Spring Boot 已有 servlet 监听端口来接入 HTTP 流量,符合filter-url-patterns路径的请求会转交给 Dubbo 处理。

yaml 复制代码
server:
  port: 8080
dubbo:
  protocol:
    name: tri
    port: 8080
    triple:
      servlet:
        enabled: true
        filter-url-patterns: /dubbo/*
        filter-order: -1000000

你可能会感到好奇,不监听新端口,Dubbo 是怎么处理请求的呢?

实际上,在引入上述依赖后,Spring 启动会触发 DubboTripleAutoConfiguration 自动装配,并向容器注册一个FilterRegistrationBean,它会向 Servlet 容器注册一个jakarta.servlet.Filter实现,只要它的优先级比默认的org.springframework.web.filter.OncePerRequestFilter高,就可以提前接管 Web 流量。

Dubbo 注册的是 TripleFilter,只要请求路径匹配,它将接管请求,不通过后续 Filter。并且内置了 HTTP1和HTTP2的支持。

异常处理

全局异常处理是基本操作,服务端处理异常需要返回一个友好的信息给到客户端。

默认发生异常时,Dubbo 会封装成 ErrorResponse 对象返回

java 复制代码
@Data
public class ErrorResponse {
    private String status;
    private String message;
}

默认 status = 500,message 是异常信息,例如:

json 复制代码
{
    "message": "name is null",
    "status": "500"
}

但是,一般系统会封装自己的错误码和错误信息,而且会区分是一般的业务异常,还是系统异常。Dubbo 的处理方式显得过于简单粗暴,所以我们要定义自己的异常处理器。

Triple 异常处理器实现 org.apache.dubbo.remoting.http12.ExceptionHandler 接口,如下所示:

java 复制代码
@Activate
public class CustomExceptionHandler implements ExceptionHandler<Throwable, HttpResult> {
    @Override
    public HttpResult handle(Throwable throwable, RequestMetadata metadata, MethodDescriptor descriptor) {
        if (throwable instanceof BizException exception) {
            return HttpResult.builder().status(200)
                    .body(JSON.toJSONString(R.failed(exception.getCode(), exception.getMessage())))
                    .build();
        }
        return HttpResult.builder().status(500)
                .body("system error")
                .build();
    }
}

Tips:CustomExceptionHandler 需要 Dubbo SPI 激活

如果发生业务异常,会正常返回 HttpStatus=200,body 示例:

json 复制代码
{
    "code": 400,
    "message": "name is too long"
}

如果是非业务异常,会返回 HttpStatus=500,body:system error

过滤器

之前 dubbo 协议的过滤器是基于 org.apache.dubbo.rpc.Filter 接口实现的,参数只能拿到 Invoker 和 Invocation。

java 复制代码
Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;

这对基于 HTTP 的 triple 协议来说显然不够友好,HTTP 协议的拦截我们一般会基于 Query Parameters 或 Headers 去做一些诸如:参数验签、认证授权、路由等操作,我们更熟悉的应该是 HttpRequest HttpResponse 对象。

所以,Dubbo 也提供了新的过滤器接口 org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter,如下所示,过滤器实现验签、鉴权等操作,前提是需要 SPI 激活。

java 复制代码
@Activate(group = "provider", order = 100)
public class CustomRestFilter implements RestFilter {
    @Override
    public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception {
        // todo 参数验签...
        String sign = request.header("sign");
        // todo 认证鉴权...
        String authorization = request.header("Authorization");

        chain.doFilter(request, response);
    }
}

RestFilter 还有一个内部接口 Listener,用于修改响应结果,或者发生异常时返回自定义信息。

java 复制代码
interface Listener {

    default void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception {}
    
    default void onError(Throwable t, HttpRequest request, HttpResponse response) throws Exception {}
}

尾巴

Apache Dubbo 3.3 通过升级 Triple 协议,不仅支持东西向流量(服务间调用),也扩展至南北向流量(外部访问服务)。这一改进消除了额外部署网关层的需求,减少了时延和维护难度。Triple协议默认支持HTTP1/HTTP2,并可选择支持HTTP3。

异常处理和过滤器功能也得到了优化,提供了更灵活、友好的处理方式。Triple 协议通过 SPI 激活的ExceptionHandlerRestFilter接口,使开发者可以自定义异常响应和请求拦截。

这些改进使Dubbo在微服务架构中更具灵活性和扩展性,适应多样化的应用场景。

相关推荐
敲上瘾9 小时前
Docker 容器核心指令与数据库容器化实践
linux·运维·服务器·docker·容器·eureka·dubbo
YF云飞1 天前
Dubbo分布式服务框架全解析
java·dubbo
@才华有限公司2 天前
vscode翻译插件
ide·vscode·dubbo
程序员小潘2 天前
Dubbo3单端口多协议源码分析
dubbo
EQ-雪梨蛋花汤2 天前
【代码里的英雄传】Dubbo 的一生:一位分布式勇士的传奇旅程
分布式·dubbo
音符犹如代码4 天前
基于Spring Boot的Dubbo微服务实践指南
spring boot·后端·微服务·dubbo
zzywxc7874 天前
大模型落地全流程实践:从技术选型到企业级部署
java·人工智能·spring·机器学习·spring cloud·数据挖掘·dubbo
zzywxc7874 天前
深入对比分析SpringCloud和Dubbo两大微服务框架的核心架构与特性。
java·spring·spring cloud·缓存·微服务·架构·dubbo