dubbo-gateway 高性能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/smallbeanteng/dubbo-gateway

相關注解

@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(value = "user",paramFormat = ParamFormat.JSON) 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(value="user",paramFormat = ParamFormat.JSON) 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(value="user",paramFormat = ParamFormat.JSON) 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",paramFormat = ParamFormat.JSON)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字符串
  • MAP方式 限定對象的propertyName與單個參數一一對應【例如@FromHeader(value="user",paramFormat =ParamFormat.MAP) httpHeader應該要有頭名稱為userName、password、age、gender、dt等與User對象屬性對應的頭信息,User將獲取這些頭信息最終組裝成完整對象】,此方式不支持復雜嵌套對象,復雜嵌套對象請使用json,1.1.3+版本后默認為Map方式
  • 此外如果方法的參數類型本身為基本數據類型,將固定使用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.1.3-beta</version>
	</dependency>

第二步:網關引入改造后的jar包,同時引用以下jar包

`	<dependency>
		<groupId>com.atommiddleware</groupId>
		<artifactId>dubbo-gateway-spring-boot-starter</artifactId>
		<version>1.1.3-beta</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自測用例
  • dubboGateWay_XSS.postman_collection.json 導出的一份包含xss攻擊代碼的自測用例

配置中心

按照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參數進行匹配)

安全

xss防御 1.1.1版本+

參數校驗組件 1.1.2版本+

cas 認證登錄 1.1.3-beta+ spring mvc 與zuul 集成了spring security cas認證與spring session

配置:

com:
  atommiddleware:
    cloud:
      config:
        security:
          cas:
             enable: true
             baseUrl: https://cas.atommiddleware.com:8812/sys/menusAndPermissions
             serverUrl: https://cas.atommiddleware.com:8443/cas
             principalAttrs: userId,companyCode,isSuperAdmin,username
             ignoringUrls:
             permitUrls:
             anonymousUrls:
          xss:
             enable: true
             filterStrategy: 0
             filterMode: 0
             antisamyFileLocationPattern:
          csrf:
             enable: false
          paramCheck:
             enable: true
             validatorMode: 0
        session:
          cookie:
             enable: true
             domain: atommiddleware.com
             name: atomSessionId
             path: /

cas認證

cas認證中心開源傳送門:https://github.com/apereo/cas

開啟步驟:

引入jar包:spring-boot-starter-security,spring-security-cas
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.security</groupId>
		<artifactId>spring-security-cas</artifactId>
	</dependency>

參數配置說明:
image

xss防御

參數配置說明:
image

參數校驗

參數配置說明:
image

csrf防御

參數配置說明
image

session共享

開啟步驟:

引入jar包:spring-boot-starter-data-redis,spring-session-data-redis,commons-pool2
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-redis</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.session</groupId>
		<artifactId>spring-session-data-redis</artifactId>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-pool2</artifactId>
	</dependency>

參數配置說明:
image

參數校驗示例:

public class Order implements Serializable {

private static final long serialVersionUID = 1L;
@NotBlank(message = "訂單號不能為空")
private String orderCode;

public String getOrderCode() {
	return orderCode;
}

public void setOrderCode(String orderCode) {
	this.orderCode = orderCode;
}

}

序列化

接口: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參數匹配
  • 400 錯誤的請求,一般情況是參數校驗未通過
  • 其它對照HttpStatus

其它說明

基於webflux的網關與基於servlet類的web應用接入整合方式是一樣的步驟,例子使用的nacos版本2.0.3,默認支持GET,POST方式接入,ContentType支持application/json,application/x-www-form-urlencoded,復雜參數【類的屬性為非基本數據類型】建議使用application/json,或項目整體都使用application/json

版本說明

推薦試用1.1.3-beta


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM