实践网关之soul(2.1.2)

Soul是一个异步的,高性能的,跨语言的,响应式的API网关。我希望能够有一样东西像灵魂一样,保护您的微服务。参考了Kong,Spring-Cloud-Gateway等优秀的网关后,站在巨人的肩膀上,Soul由此诞生!

基本概念

adminUrl:Soul的管理配置平台

contextPath:给后端每个应用定义的一个目录,基于此来路由到不同的应用,与应用自己的contextPath是不同的概念。

appName:后端每个应用的名称,仅用于展示

path:给后端每个接口定义的一个访问路径,基于此来做路由到不同的接口,对于http接口,就是原始http访问路径(比如:Controller里面方法定义的访问路径);对于dubbo接口,可以理解为一个访问用的映射路径

选择器(SelectorData)

名称:

类型

SelectorTypeEnum:选择器类型,分为全流量和自定义流量

匹配方式:条件的逻辑运算(and(AndMatchStrategy),or(OrMatchStrategy))

条件

条件(ConditionData)用来匹配(OperatorJudge)请求(AbstractMatchStrategy),包括四个属性:

  • paramType:参数类型(ParamTypeEnum),包括header、uri、query、host、ip、post等
  • paramName:参数名称,只有在paramType是header、query和post时有作用,根据参数名称获取真实值(realData)
  • operator:操作符(OperatorEnum),包括match、=、regEx和like,具体算法可以参见OperatorJudge
  • paramValue:参数值,会被trim处理,如果是uri,则支持逗号分隔多个

OperatorJudge

类型 对应operator 描述
MatchOperatorJudge match 对于非uri,跟like一样;对于uri,采用Ant-style模式匹配
EqOperatorJudge = 相等
LikeOperatorJudge like 包含
RegExOperatorJudge regEx 正则表达式

继续后续选择器:

打印日志:

是否开启:

规则(RuleData)

名称:

处理(handle)

不同插件定制参数及其处理

http

{“loadBalance”:”random”,”retry”:0,”timeout”:3000}

waf

{“permission”:”reject”,”statusCode”:”403”}

限流

{“replenishRate”:”1”,”burstCapacity”:”1”}

dubbo

{“version”:null,”group”:null,”retries”:0,”loadBalance”:”random”,”timeout”:3000}

hystrix

1
{"requestVolumeThreshold":"0","errorThresholdPercentage":"0","maxConcurrentRequests":"0","sleepWindowInMilliseconds":"0","loadBalance":"random","version":null,"timeout":"6000","group":null,"retries":0}
1
{"requestVolumeThreshold":"20","errorThresholdPercentage":"50","maxConcurrentRequests":"100","sleepWindowInMilliseconds":"5000","groupKey":"","commandKey":"","loadBalance":"random","version":"","timeout":"3000","group":"","retries":"1"}

数据结构

元数据

接口的定义、参数等基础信息,由@SoulClient自动填入。

metadata

HTTP元数据

http metadata

Dubbo元数据

dubbo metadata

其中rpc扩展参数DubboParamExt,代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
public class DubboParamExt {

    private String group;

    private String version;

    private String loadbalance;

    private Integer retries;

    private Integer timeout;
}

数据同步

websocket

zookeeper

http长连接

请求核心流程

WebFilter

ParamWebFilter

请求过滤器:org.dromara.soul.web.filter.ParamWebFilter

ParamService

参数处理

TimeWebFilter

WebSocketFilter

SoulWebHandler

遍历插件

插件

SoulPlugin

SoulPlugin

org.dromara.soul.web.plugin.SoulPlugin

PluginEnum:内置plugin,包括sign、waf、dubbo等13个值,实际不止,还有一些是硬编码的。

PluginTypeEnum:插件类型,包括before、function和last,处理顺序是before-》function-》last

AbstractSoulPlugin

org.dromara.soul.web.plugin.AbstractSoulPlugin

遍历选择器,遍历规则。

如下9个SoulPlugin继承了AbstractSoulPlugin,包括:DividePlugin,DubboPlugin,HystrixPlugin,MonitorPlugin,RateLimiterPlugin,RewritePlugin,SpringCloudPlugin,WafPlugin,WebSocketPlugin

GlobalPlugin

org.dromara.soul.web.plugin.before.GlobalPlugin

静默

SignPlugin

org.dromara.soul.web.plugin.before.SignPlugin

静默

WafPlugin

org.dromara.soul.web.plugin.before.WafPlugin

样例

dubbo selector

dubbo selector config

dubbo selector rule config

1
2
$ curl http://localhost:9195/http/test/findByUserId?userId=abc
{"code":404,"message":" You are forbidden to visit","data":null}

RateLimiterPlugin

org.dromara.soul.web.plugin.function.RateLimiterPlugin

插件的配置中配置redis地址:{"master":"mymaster","mode":"Standalone","url":"localhost:6379","password":""}

样例

rate_limiter selector

rate_limiter selector config

rate_limiter selector rule config

请求

1
2
$ curl http://localhost:9195/http/test/findByUserId?userId=abc
{"code":429,"message":"您已经被限流,请稍后重试!","data":null}

RewritePlugin

org.dromara.soul.web.plugin.function.RewritePlugin

HystrixPlugin

org.dromara.soul.web.plugin.hystrix.HystrixPlugin

DividePlugin

org.dromara.soul.web.plugin.function.DividePlugin

样例
Admin配置

divide selector

divide selector config

divide selector rule config

get

请求

1
2
$ curl http://localhost:8186/test/findByUserId?userId=abc
$ curl http://localhost:9195/http/test/findByUserId?userId=abc
post

请求

1
2
$ curl -H 'Content-Type:application/json' -d '{"userId":"1234","userName":"XIAO5y"}'  http://localhost:8186/test/payment
$ curl -H 'Content-Type:application/json' -d '{"userId":"1234","userName":"XIAO5y"}'  http://localhost:9195/http/test/payment

###

SpringCloudPlugin

org.dromara.soul.web.plugin.function.SpringCloudPlugin

WebClientPlugin

处理http和spring cloud请求发送

org.dromara.soul.web.plugin.http.WebClientPlugin

WebSocketPlugin

org.dromara.soul.web.plugin.function.WebSocketPlugin

DubboPlugin

org.dromara.soul.web.plugin.function.DubboPlugin

插件的配置中配置zookeeper地址:{"register":"zookeeper://localhost:2181"}

利用dubbo泛化调用实现(org.dromara.soul.web.plugin.dubbo.DubboProxyService

  • interfaceName:必填,请求的接口的全限定名。
  • method:必填,请求的方法。
  • 如果请求的方法的参数是primary类型,则
    • params:请求的参数类型和值,json对象表示,比如你的参数是一个String,Int 等。 如果是连续相同类型的参数,那么就要写成一个数组。
  • 如果请求参数是对象,比如自定义对象、java.util.Map、java.util.List等
    • paramClass:对象全限定名,多个用都逗号分隔。
    • classParams:对象值,json数组。

2.1.2版本默认仅支持单个参数,比如基本类型、DTO、Map和List等,如需要支持多个参数,可扩展实现。

样例
Admin配置

dubbo selector

dubbo selector config

dubbo selector rule config

simple parameter
DTO parameter

请求

1
$ curl -H 'Content-Type:application/json' -d '{"id":"1234","name":"XIAO5y"}' http://localhost:9195/dubbo/insert
Map parameter
List parameter
no parameter
1
$ curl -H 'Content-Type:application/json' -d '{}' http://localhost:9195/dubbo/findAll

由于不需要参数,因而元数据参数类型没有用处,可以随便填

异步非阻塞

DubboProxyService.java

1
2
3
4
5
6
7
8
9
10
11
log.info("发起RPC调用,MethodName={},pair.getLeft={},pair.getRight={}",metaData.getMethodName(),pair.getLeft(),pair.getRight());
CompletableFuture<Object> future= genericService.$invokeAsync(metaData.getMethodName(), pair.getLeft(), pair.getRight());
return Mono.fromFuture(future.thenApply(ret -> {
    log.info("RPC调用应答:{}",ret)
	if (Objects.nonNull(ret)) {       				 					 exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT,ret);
                } else {
                    exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, Constants.DUBBO_RPC_RESULT_EMPTY);
                }
                exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName());
                return ret;
            }));

堆栈

1
2
3
4
5
[2021-04-13 17:50:19,856] [INFO ] [soul-work-threads-14] o.d.s.web.plugin.dubbo.DubboProxyService 115 -- [4a2345c9-5c27-4ebc-8f2a-397aa194e824] 
发起RPC调用,MethodName=storeJoinStoreInfo,pair.getLeft=[com.zh3721.commons.api.ApiRequestDTO],pair.getRight=[{data={storeId=36887, class=com.organ.boss.api.storeJoin.req.StoreJoinStoreInfoReqDTO, userCode=HSSNBLT2W4VAK}, sysId=2016, timestamp=2021-04-13 09:50:19}]

[2021-04-13 17:50:19,892] [INFO ] [Dubbo-thread-10] o.d.s.web.plugin.dubbo.DubboProxyService 127 -- [4a2345c9-5c27-4ebc-8f2a-397aa194e824] RPC调
用应答:{total=null, code=2000, data={storeAddress=杨新路88号, storeName=明捏你, customerList=[{userName=13772533788, class=com.organ.boss.api.storeJoin.res.StoreJoinCustomerInfoResDTO, userId=262}, {userName=18975467132, class=com.organ.boss.api.storeJoin.res.StoreJoinCustomerInfoResDTO, userId=263}], class=com.organ.boss.api.storeJoin.res.StoreJoinStoreInfoResDTO, firstPurchase=0, createDate=2021年04月09日}, size=null, sign=null, page=null, message=, class=com.zh3721.commons.api.ApiResultDTO, status=OK}

MonitorPlugin

WebClientResponsePlugin

处理http和spring cloud应答响应

静默

DubboResponsePlugin

处理dubbo应答响应

NettyHttpClientPlugin

处理http和spring cloud请求发送

NettyClientResponsePlugin

处理http和spring cloud应答响应

SoulPluginChain

线程模型

客户端

soul针对不同的后端分别提供了相应的客户端接入包

客户端自动注册

基于@SoulClientBeanPostProcessor相应实现,客户端在启动时自动注册(包括元数据选择器规则)。

客户端需要配置soul-admin的地址:

1
2
3
4
5
soul:
  http:
    adminUrl: http://localhost:9095
    contextPath: /http
    appName: http

http

针对http的BeanPostProcessor实现为org.dromara.soul.client.springmvc.spring.SoulClientBeanPostProcessor

后端服务器的ip和端口如果希望自动登记,则soul-admin增加配置:

1
2
3
4
soul:
  http:
    register: true
    zookeeperUrl: localhost:2181

客户端增加配置:

1
2
3
soul:
  http:
    zookeeperUrl: localhost:2181

Alibaba Dubbo

针对alibaba dubbo的BeanPostProcessor实现为org.dromara.soul.client.dubbo.spring.DubboServiceBeanPostProcessor

Apache Dubbo

针对apache dubbo的BeanPostProcessor实现为org.dromara.soul.client.dubbo.spring.DubboServiceBeanPostProcessor

Spring Cloud

针对Spring Cloud的BeanPostProcessor实现为org.dromara.soul.client.springcloud.spring.SoulSpringCloudClientBeanPostProcessor