乐趣区

关于dubbo:高性能dubbo网关

dubbo-gateway

dubbo-gateway 高性能 dubbo 网关,提供了 http 协定到 dubbo 协定的转换, 但【并非】应用 dubbo 的【泛化】调用(泛化调用性能比一般调用有 10-20% 的损耗, 通过一般异步的调用形式与基于 webflux 系列的响应式网关 (spring cloud gateway) 整合进步零碎的吞吐量, 一般调用须要依赖 api jar 包, 须要对接口定义进行革新, 除此之外不须要做任何其它革新. 另外也反对基于 servlet 类的利用或网关 (spring cloud zuul) 进行整合

泛化毛病

  • 泛化过程数据流会通过了三次转换, 会产生大量的长期对象, 有很大的内存要求。应用反射形式对于旨在榨干服务器性能以获取高吞吐量的零碎来说, 难以达到性能最佳
  • 同时服务端也会对泛化申请多一重 Map <-> POJO 的来回转换的过程。整体上,与一般的 Dubbo 调用相比有 10-20% 的损耗
  • 泛化调用在网关或服务消费者阶段无奈校验参数类型的有效性,数据要到服务提供者反序列化时能力校验出参数类型的有效性

    开源地址

    https://github.com/smallbeant…

    相干注解

    @GateWayDubbo

    标识这个接口须要主动进行协定转换

    /**

    • 服务 id, 能够和 dubbo 一般调用的配置属性关联.
      */

    @AliasFor(“id”)
    String value() default “”;

    /**

    • 服务 id, 能够和 dubbo 一般调用的配置属性关联.
    • 例如:

       com.atommiddleware.cloud.config.dubboRefer.<userService>.version=1.1.0
       com.atommiddleware.cloud.config.dubboRefer.<userService>.group=userSystem
       以上相当于会调用版本号为 1.1.0 并且 groupw 为 userSystem 的 dubbo 服务, 与 @DubboReference 的参数对齐,具体反对哪些参数详见配置类 DubboReferenceConfigProperties

      */

    @AliasFor(“value”)
    String id() default “”;

    @PathMapping

    标记这个接口办法须要进行协定主动转换

    /**

    • 门路表达式
      */

    @AliasFor(“path”)
    String value() default “”;

    /**

    • 门路表达式
      */

    @AliasFor(“value”)
    String path() default “”;

    /**

    • 提交办法,GET 或 POST
      */

    RequestMethod requestMethod() default RequestMethod.POST;

    public enum RequestMethod {

      GET, POST
    

    }

    @FromBody

    示意参数对象来源于音讯体

    /**

    • 是否查看参数
      */

    @AliasFor(annotation = ParamAttribute.class)
    boolean required() default true;

    @FromHeader

    示意参数对象来源于音讯头

    /**

    • 音讯头名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String value() default “”;

    /**

    • 音讯头名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String name() default “”;

    /**

    • 是否查看参数
      */

    @AliasFor(annotation = ParamAttribute.class)
    boolean required() default true;

    @FromCookie

    示意参数对象来源于 cookie

    /**

    • cookie 名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String value() default “”;

    /**

    • cookie 名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String name() default “”;

    /**

    • 是否查看参数
      */

    @AliasFor(annotation = ParamAttribute.class)
    boolean required() default true;

    @FromPath

    示意参数对象来源于 path, 反对 path 表达式

    /**

    • path 占位符名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String value() default “”;

    /**

    • path 占位符名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String name() default “”;

    /**

    • 是否查看参数
      */

    @AliasFor(annotation = ParamAttribute.class)
    boolean required() default true;

    @FromQueryParams

    示意参数来源于 query 局部

    /**

    • query 名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String value() default “”;

    /**

    • query 名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String name() default “”;

    /**

    • 是否查看参数
      */

    @AliasFor(annotation = ParamAttribute.class)
    boolean required() default true;

    @FromAttribute

    示意参数来源于 attribute

    /**

    • attribute 名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String value() default “”;

    /**

    • attribute 名称
      */

    @AliasFor(annotation = ParamAttribute.class)
    String name() default “”;

    /**

    • 是否查看参数
      */

    @AliasFor(annotation = ParamAttribute.class)
    boolean required() default true;

    配置示例

    @GateWayDubbo(“userService”)
    public interface UserService {

    /**

    • hello world
    • @return hello
      */

    @PathMapping(value=”/sample/helloWorld”,requestMethod=RequestMethod.GET)
    Result helloWorld();
    /**

    • 参数为空 post 申请
    • @return 后果
      */

    @PathMapping(value=”/sample/helloWorldPost”,requestMethod=RequestMethod.POST)
    Result helloWorldPost();
    /**

    • 返回值为空
      */

    @PathMapping(value=”/sample/helloVoid”,requestMethod=RequestMethod.GET)
    void helloVoid();
    /**

    • 返回值为空 post 申请
      */

    @PathMapping(value=”/sample/helloVoidPost”,requestMethod=RequestMethod.POST)
    void helloVoidPost();
    /**

    • 注册用户
    • @param user 用户信息
    • @return 注册后果
      */

    @PathMapping(“/sample/registerUser”)
    Result registerUser(@FromBody User user);
    /**

    • 对象数据源来自 header,headerName=user,headerValue=json(UrlEncoder 后的字符串)
    • @param user 用户信息
    • @return 后果
      */

    @PathMapping(value=”/sample/registerUserFromHeader”,requestMethod=RequestMethod.GET)
    Result registerUserFromHeader(@FromHeader(“user”) User user);
    /**

    • header 中以 key value 形式传递对象参数,headerName=headerValue 转换为 beanPropertyName=beanPropertyValue
    • headerName 对应 bean 的 propertyName,headerValue 对应 bean 的 propertyValue
    • @param user 用戶信息
    • @return 后果
      */

    @PathMapping(value=”/sample/registerUserFromHeaderMap”,requestMethod=RequestMethod.GET)
    Result registerUserFromHeaderMap(@FromHeader(value=”user”,paramFormat =ParamFormat.MAP) User user);
    /**

    • 对象数据源来自 cookie,cookieName=user,cookieValue=json(UrlEncoder 后的字符串)
    • @param user 用户信息
    • @return 后果
      */

    @PathMapping(value=”/sample/registerUserFromCookie”,requestMethod=RequestMethod.GET)
    Result registerUserFromCookie(@FromCookie(“user”) User user);
    /**

    • cookie 中以 key value 形式传递对象参数,cookieName=cookieValue 转化为 beanPropertyName=beanPropertyValue
    • cookieName 对应 bean 的 propertyName,cookieValue 对应 bean 的 propertyValue, 不反对嵌套对象转换,嵌套对象或简单参数请用 json
    • @param user 用戶信息
    • @return 后果
      */

    @PathMapping(value=”/sample/registerUserFromCookieMap”,requestMethod=RequestMethod.GET)
    Result registerUserFromCookieMap(@FromCookie(value=”user”,paramFormat = ParamFormat.MAP) User user);
    /**

    • 对象数据源来自 path,{user}=json(UrlEncoder 后的字符串)
    • @param user 用户信息
    • @return 后果
      */

    @PathMapping(value=”/sample/registerUserFromPath/{user}”,requestMethod=RequestMethod.GET)
    Result registerUserFromPath(@FromPath(“user”) User user);
    /**

    • path pattern 对应 bean 的属性名称
    • @param user 用户信息
    • @return 后果
      */

    @PathMapping(value=”/sample/registerUserFromPathMap/{userName}/{age}/{gender}”,requestMethod=RequestMethod.GET)
    Result registerUserFromPathMap(@FromPath(value=”user”,paramFormat = ParamFormat.MAP) User user);

    /**

    • 对象参数来源于 query json 字符串,user=json(UrlEncoder 后的字符串)
    • @param user 用户信息
    • @return 后果
      */

    @PathMapping(value=”/sample/getUserInfoFromQueryParamsParamFormatJSON”,requestMethod=RequestMethod.GET)
    Result getUserInfoFromQueryParamsParamFormatJSON(@FromQueryParams(value=”user”)User user);

    /**

    • 对象参数来源于 query, 以 key,value 形式传参,key 对应 bean propertyName,value 对应 propertyValue, 嵌套对象或简单对象请应用 JSON
    • @param user 用户
    • @return 后果
      */

    @PathMapping(value=”/sample/getUserInfoFromQueryParamsParamFormatMap”,requestMethod=RequestMethod.GET)
    Result getUserInfoFromQueryParamsParamFormatMap(@FromQueryParams(value=”user”,paramFormat = ParamFormat.MAP)User user);
    /**

    • 数据起源 queryParam
    • @param userId 用户 id
    • @return 勾销登记后果
      */

    @PathMapping(value=”/sample/unRegisterUser”,requestMethod=RequestMethod.GET)
    Result unRegisterUser(@FromQueryParams(“userId”)Long userId);
    /**

    • 数据起源 path
    • @param userId 用戶 id
    • @return 后果
      */

    @PathMapping(value=”/sample/getUserInfo/{userId}/{gender}”,requestMethod=RequestMethod.GET)
    Result getUserInfo(@FromPath(“userId”) Long userId,@FromPath(“gender”) Short gender);
    /**

    • 数据起源 header 和 cookie
    • @param userId 用户 id
    • @param age 年龄
    • @return 返回查问后果
      */

    @PathMapping(value=”/sample/getUserInfo/byHeaderAndCookie”,requestMethod=RequestMethod.GET)
    Result getUserInfo(@FromHeader(“userId”)Long userId,@FromCookie(“age”)Integer age);

    /**

    • 全场景
    • @param userId 用户 id
    • @param age 年龄
    • @param gender 性别
    • @param user 用户信息
    • @return 查问后果
      */

    @PathMapping(“/sample/getUserUserInfoAll/{userId}”)
    Result getUserUserInfoAll(@FromPath(“userId”) Long userId,@FromCookie(“age”)Integer age,@FromHeader(“gender”)Long gender,@FromBody User user);
    }

参数注意事项:

@PathMapping 参数 requestMethod:用于限定拜访服务的办法,反对 POST 与 GET, 默认为 POST

@FromCookie、@FromPath、@FromHeader、@FromQueryParams 参数 paramFormat:

  • JSON 形式 限定参数名称对应的值为 json 字符串,而后通过反序列化 json 字符串失去参数对象(如 @FromHeader(“user”) httpHeader 应该要有一个头名称为 user 并且值为【样例数据】的 json 字符串(UrlEncode)),此将入参看做一个整体 json 字符串,默认 json 形式
  • MAP 形式 限定对象的 propertyName 与单个参数一一对应【例如 @FromHeader(value=”user”,paramFormat =ParamFormat.MAP) httpHeader 应该要有头名称为 userName、password、age、gender、dt 等与 User 对象属性对应的头信息,User 将获取这些头信息最终组装成残缺对象】, 此形式不反对简单嵌套对象, 简单嵌套对象请应用 json
  • 此外如果办法的参数类型自身为根本数据类型,将固定应用 Map 形式,具体差别能够通过导入 postman 的测试用例体验

样例数据

{
"userName": "admin",
"password": "123456",
"age": 99,
"gender": 1,
"dt": 1644431796892,
"workHistory": {"workDescriptions": ["中学","大学"]} 
}

应用步骤

第一步:依照示例革新 api 接口,接口须要引入 dubbo-gateway-api jar 包


      <dependency>
        <groupId>com.atommiddleware</groupId>
        <artifactId>dubbo-gateway-api</artifactId>
        <version>1.0.9</version>
    </dependency>

第二步:网关引入革新后的 jar 包,同时援用以下 jar 包

`    <dependency>
        <groupId>com.atommiddleware</groupId>
        <artifactId>dubbo-gateway-spring-boot-starter</artifactId>
        <version>1.0.9</version>
    </dependency>`

第三步:在启动类上增加要扫描的 api 包名 @DubboGatewayScanner(basePackages = “ 需扫描的 api 包名 ”)

第四步: 网关配置 routes(-> 看【配置核心】阐明),如果只是独自的 spring mvc 则不须要配置了

第五步:没了 … 就是这么简略

我的项目阐明

  • dubbo-gateway-api 是外围的 api,相干注解都是在此我的项目中定义
  • dubbo-gateway-core 外围实现,实现 dubbo-gateway 的相干逻辑都在此我的项目中
  • dubbo-gateway-spring-boot-autoconfigure dubbo-gateway 的主动拆卸
  • dubbo-gateway-spring-boot-starter dubbo-gateway 的 starter
  • dubbo-gateway-sample-api 示例服务 api 定义在此我的项目中
  • dubbo-gateway-sample-provider 基于 spring cloud 的 dubbo 服务提供者示例
  • dubbo-gateway-sample 基于 webflux(spring cloud gateway)的接入 dubbo-gateway 示例
  • dubbo-gateway-sample-web-provider 基于 sevlet 类型的 dubbo 服务提供者示例
  • dubbo-gateway-sample-web-consumer 基于 sevlet 类型 spring mvc 的我的项目接入 dubbo-gateway 示例
  • dubbo-gateway-sample-zuul 基于 spring cloud zuul 接入 dubbo-gateway 示例
  • dubboGateWay.postman_collection.json 是导出的一份 postman 自测用例

    配置核心

    依照 dubbo 的失常接入配置进行配置就好了,以下贴出例子应用的配置在 nacos 配置核心的配置, 其中 filters 应用了 Dubbo 作为过滤器

服务提供者配置:

dubbo:
  protocol:
    name: dubbo
    port: 20861
server:
  port: 8861
spring:
  cloud:
    nacos:
      discovery:
        namespace: dev
        server-addr: 127.0.0.1:8848

整合 spring cloud gateway 网关配置:


dubbo:
  cloud:
    subscribed-services: dubbo-gateway-sample-provider
server:
  port: 8862
spring:
  cloud:
    nacos:
      discovery:
        namespace: dev
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: myGateway
        uri: dubbo://127.0.0.1:8862
        predicates:
        - Path=/**

整合 spring cloud zuul 网关配置:

dubbo:
  cloud:
    subscribed-services: dubbo-gateway-sample-provider
server:
  port: 8862
spring:
  cloud:
    nacos:
      discovery:
        namespace: dev
        server-addr: 127.0.0.1:8848
  main:
    allow-bean-definition-overriding: true
zuul:
  routes:
    dubboService:
      stripPrefix: false
      url: dubbo://127.0.0.1
      path: /**

留神:配置中的 ”D(d)ubbo” 子眼示意应用的是 dubbo gateway 相干性能去解决路由,如果不配置则不会失效.
其中配置中 uri(l) 配置除了 dubbo 相干字眼,其它信息并无实际意义,只是为了合乎网关的配置标准要求,能够配成 127.0.0.1 等

整合 spring mvc 配置:

com.atommiddleware.cloud.config.includUrlPatterns=/sample/*,/order/*
com.atommiddleware.cloud.config.excludUrlPatterns=

includUrlPatterns 参数用于配置须要进行协定转换的 url,excludUrlPatterns 用于排除个别 url, 这两个参数只对【非】网关整合无效(因与网关整合 path 匹配交给了网关的 path 参数进行匹配)

序列化

接口:com.atommiddleware.cloud.core.serialize
json 序列化默认采纳的是 jackson, 如果须要定制能够自行定制实现

输入响应

spring cloud gateway 类型接口:com.atommiddleware.cloud.core.annotation.ResponseReactiveResult

spring mvc 类型接口:com.atommiddleware.cloud.core.annotation.ResponseServletResult

spring cloud zuul 类型接口:com.atommiddleware.cloud.core.annotation.ResponseZuulServletResult

默认实现增加了一些简略的头信息,如果须要定制实现能够自行实现接口

错误码表

  • 404 匹配的地址在 dubbo 未发现对应的服务
  • 415 不反对的 Media Type, 默认反对[application/json,application/x-www-form-urlencoded]
  • 500 外部服务器谬误,个别为调用的 dubbo 服务抛出了异样或其它
  • 405 办法不容许, 默认只反对[post,get], 并且要与 @PathMapping 的 requestMethod 参数匹配

其它阐明

基于 webflux 的网关与基于 servlet 类的 web 利用接入整合形式是一样的步骤,例子应用的 nacos 版本 2.0.3,如果须要在 cookie,header,url, 传递简单参数【非 java 根本类型】,需先将参数转为 json, 而后应用 UrlEncode 进行编码,js 中能够应用 encodeURIComponent 进行编码,默认只反对 GET,POST 形式接入,ContentType 反对 application/json,application/x-www-form-urlencoded,简单参数倡议应用 application/json, 或我的项目整体都应用 application/json

版本阐明

举荐应用 1.0.9 版本

退出移动版