23 Feb 2021 |
Soul |
本篇文章主要内容如下:
Soul
是什么
- 如何在本地运行
Soul
- 对
Soul
进行压测
Soul 是什么
Soul
是什么?它可不是灵魂交友软件!
引用Soul
的官网,它是这样描述Soul
的:
这是一个异步的,高性能的,跨语言的,响应式的API
网关。我希望能够有一样东西像灵魂一样,保护您的微服务。参考了Kong
,Spring-Cloud-Gateway
等优秀的网关后,站在巨人的肩膀上,Soul
由此诞生!
Soul
是一个网关,它的特点如下:
- 支持各种语言(
http
协议),支持 dubbo
,spring cloud
协议。
- 插件化设计思想,插件热插拔,易扩展。
- 灵活的流量筛选,能满足各种流量控制。
- 内置丰富的插件支持,鉴权,限流,熔断,防火墙等等。
- 流量配置动态化,性能极高,网关消耗在
1~2ms
。
- 支持集群部署,支持
A/B Test
,蓝绿发布。
来自官网的一张架构图

从上面的架构图可以看出,Soul
网关与语言无关,各种语言(Java
,PHP
,.NET
等)都可以接入到网关中。它通过对不同插件的支持实现各种功能(监控,认证,限流,熔断,不同用户接入等)。在后台管理系统(soul-admin
)就可以灵活配置各种流量。
如何在本地运行 Soul
好了,知道了Soul
是一个网关,那么接下来就看看怎么使用它。 通过案例演示的方式比直接了解各个概念的方式更能激发兴趣,所以开始play it
吧!
-
从官网拉取项目源码 git clone git@github.com:dromara/soul.git
,当前最新版本是2.2.1
。
-
创建并切换分支git checkout -b myLearn
,就在自己的在本地跑,直接用master
分支也行。
-
使用IDEA
打开项目,然后本地编译一下,确保没有错。
第一次编译会很慢,需要下载依赖。当然,也可以跳过相关测试和注释,这样会快一点。
mvn clean install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
-
启动Soul
的后台管理地址,就是项目源码中的soul-admin
模块,这是一个SpringBoot
项目,只需要修改一下数据库的地址就可以运行了。项目会自动创建对应的库和表。

项目启动后的登录地址是http://localhost:9095/
,默认用户名是admin
,密码是123456
。后台界面如下:

主要模块有插件列表和系统管理,在插件列表中可以对各个插件进行管理,每个插件都可以添加多个选择器,每个选择器都可以添加多条规则。实际这就是Soul
根据请求的URL
去匹配规则:插件->选择器->规则
,这个后面再细说。
-
启动Soul
的核心模块soul-bootstrap
,这是网关的核心处理逻辑。不要怕它,这个模块本身不复杂,目录结构如下:

启动成功后,就可以访问这个网关了http://127.0.0.1:9195/
,返回信息如下:
{"code":-107,"message":"Can not find selector, please check your configuration!","data":null}
因为还没有接入业务系统,所以没有相关返回值,上面展示的信息是Soul
网关没有找到相应的选择器,返回的一个提示信息
-
通过上述步骤,就成功的搭建起Soul
网关服务了,后面就是在自己的业务系统上使用网关。使用例子可以参考soul-examples
模块。
-
本文运行soul-examples
下面的 http
服务,结合divde
插件,发起http
请求到soul
网关,体验http
代理。模块目录及配置信息如下:

配置文件中有关soul
的配置我们后面再详细解释,现在只需要知道这个服务就是一个很普通的Spring Boot
项目。其中的一个接口信息如下:

运行SoulTestHttpApplication
,启动这个项目。成功之后,通过postman
进行访问:

上面就是一个普通的http
请求,直接请求业务系统的后端服务,现在通过Soul
网关来访问该服务。

这个localhost:9195
地址就是网关的地址,/http
是业务系统在网关中的名称。那么,现在的请求就是先通过Soul
网关,再由网关转发到实际的请求接口。
对 Soul 进行压测
成功在本地运行Soul
网关之后,我们最后再来看看Soul
网关的性能如何。在windows
平台可以通过SuperBenchmarker
工具进行压测。压测设置的参数是200个请求,32个并发,100秒内执行完。

然后再来看一下直连的情况:

可以看到 直连的RPS(3916.9)
还是大于经过网关转发后的RPS(1526.3 )
,毕竟多了网关这一层的转发。
小结一下,本篇文章介绍了Soul
网关的特点和架构设计。然后,拉取soul
源码,在本地运行测试案例。最后,对soul
网关进行了简单的压测。
参考文章:
07 Feb 2021 |
Soul |
本篇文章分析的是Sentinel
插件,它也可以提供熔断和限流的功能。
操作前准备:启动soul-admin
,soul
网关,soul-examples-http
测试用例。
Sentinel
功能演示
首先在soul
网关中添加Sentinel
插件,引入依赖:
<!-- soul sentinel plugin start-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-plugin-sentinel</artifactId>
<version>${project.version}</version>
</dependency>
<!-- soul sentinel plugin end-->
然后需要在soul-admin
中依次开启插件,添加选择器,添加规则:



规则字段解析:
degrade count
: 熔断阈值;
whether to open the degrade (1 or 0)
: 是否开启熔断;
degrade type
: 熔断策略,支持秒级 RT/秒级异常比例/分钟级异常数;
control behavior
: 流控策略(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流;
grade count
:限流阈值;
whether control behavior is enabled (1 or 0)
: 是否开启限流;
grade type
: 限流类型,QPS
或线程数模式。
上述准备工作完成后,就能进行测试了。开启限流,设置流控阈值为1,然后多点几次 postman
,即会发生限流现象:

Sentinel
原理分析
SentinelPlugin
继承于模板抽象类 AbstractSoulPlugin
,所以 doExecutor
是其执行真正功能逻辑的代码:
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
//省略了其他代码
SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), SentinelHandle.class);
//限流或降级执行类 SentinelReactorTransformer
//责任链继续执行
return chain.execute(exchange).transform(new SentinelReactorTransformer<>(resourceName)).doOnSuccess(v -> {
//不成功,则抛出降级异常
if (exchange.getResponse().getStatusCode() != HttpStatus.OK) {
HttpStatus status = exchange.getResponse().getStatusCode();
exchange.getResponse().setStatusCode(null);
throw new SentinelFallbackException(status);
}
}).onErrorResume(throwable -> sentinelFallbackHandler.fallback(exchange, UriUtils.createUri(sentinelHandle.getFallbackUri()), throwable));
}
限流或降级执行类是 SentinelReactorTransformer
,来自于Sentinel
插件。集成Sentinel
插件就是以上代码,使用起来还是比较简单。
Sentinel
规则在admin
修改后,会同步更新到SentinelRuleHandle
:
public void handlerRule(final RuleData ruleData) {
SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(ruleData.getHandle(), SentinelHandle.class);
//更新限流规则
List<FlowRule> flowRules = FlowRuleManager.getRules()
if (sentinelHandle.getFlowRuleEnable() == Constants.SENTINEL_ENABLE_FLOW_RULE) {
//省略了其他代码
}
FlowRuleManager.loadRules(flowRules);
//更新熔断降级规则
List<DegradeRule> degradeRules = DegradeRuleManager.getRules()
if (sentinelHandle.getDegradeRuleEnable() == Constants.SENTINEL_ENABLE_DEGRADE_RULE) {
//
}
DegradeRuleManager.loadRules(degradeRules);
}
小结,本文结合实际案例演示了Sentinel
插件,跟踪了Sentinel
插件源码。
06 Feb 2021 |
Soul |
本篇文章分析的是Resilience4j
插件,它可以提供熔断和限流的功能。
操作前准备:启动soul-admin
,soul
网关,soul-examples-http
测试用例。
Resilience4j功能演示
要在soul
网关使用Resilience4j
插件,需要引入依赖:
<!-- soul resilience4j plugin start-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-plugin-resilience4j</artifactId>
<version>${project.version}</version>
</dependency>
<!-- soul resilience4j plugin end-->
然后需要在soul-admin
中依次开启插件,添加选择器,添加规则:



规则字段解析:
token filling period(limitRefreshPeriod)
: 刷新令牌的时间间隔,单位ms,默认值:500。
token filling number(limitForPeriod)
: 每次刷新令牌的数量,默认值:50。
control behavior timeout(timeoutDurationRate)
: 熔断超时时间,单位ms,默认值:30000。
circuit enable(circuitEnable)
: 是否开启熔断,0:关闭,1:开启,默认值:0。
circuit timeout (timeoutDuration)
: 熔断超时时间,单位ms,默认值:30000。
fallback uri(fallbackUri)
: 降级处理的uri
。
sliding window size(slidingWindowSize)
: 滑动窗口大小,默认值:100。
sliding window type(slidingWindowType)
: 滑动窗口类型,0:基于计数,1:基于时间,默认值:0。
enabled error minimum calculation threshold(minimumNumberOfCalls)
: 开启熔断的最小请求数,超过这个请求数才开启熔断统计,默认值:100。
degrade opening duration(waitIntervalFunctionInOpenState)
: 熔断器开启持续时间,单位ms,默认值:10。
half open threshold(permittedNumberOfCallsInHalfOpenState)
: 半开状态下的环形缓冲区大小,必须达到此数量才会计算失败率,默认值:10。
degrade failure rate(failureRateThreshold)
:错误率百分比,达到这个阈值,熔断器才会开启,默认值50。
上述准备工作完成后,就能进行测试了。每次刷新令牌的数量调成了只有一个,刷新时间要 100s,所以只要多点几次 postman,即会发生限流现象:

Resilience4j原理分析
Resilience4JPlugin
继承于模板抽象类 AbstractSoulPlugin
,所以 doExecutor
是其执行真正功能逻辑的代码:
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
//省略了其他代码
//是否开启熔断
if (resilience4JHandle.getCircuitEnable() == 1) {
return combined(exchange, chain, rule);
}
//限流处理
return rateLimiter(exchange, chain, rule);
}
主要功能是判断是否开启熔断,如果是,就进入组合模式(即处理熔断又处理限流);否则,只做限流处理。
private Mono<Void> combined(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) {
//设置管理后台配置的规则
Resilience4JConf conf = Resilience4JBuilder.build(rule);
return combinedExecutor.run(
//执行责任链后续操作
chain.execute(exchange).doOnSuccess(v -> {
//省略了其他代码
}),
fallback(combinedExecutor, exchange, conf.getFallBackUri()), //降级处理
conf); // Resilience4J相关配置
}
combinedExecutor.run()
方法负责执行真实的逻辑:先执行熔断的功能,后执行限流的功能。再处理超时,错误信息,最后进行降级处理。
public <T> Mono<T> run(final Mono<T> run, final Function<Throwable, Mono<T>> fallback, final Resilience4JConf resilience4JConf) {
//链式调用
Mono<T> to = run.transformDeferred(CircuitBreakerOperator.of(circuitBreaker))//先处理熔断
.transformDeferred(RateLimiterOperator.of(rateLimiter))//后限流
.timeout(resilience4JConf.getTimeLimiterConfig().getTimeoutDuration())//超时处理
.doOnError(/*...*/);//错误处理
if (fallback != null) {
to = to.onErrorResume(fallback);//降级处理
}
return to;
}
private Mono<Void> rateLimiter(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) {
return ratelimiterExecutor.run(
chain.execute(exchange), //责任链继续执行
fallback(ratelimiterExecutor, exchange, null), //降级处理
Resilience4JBuilder.build(rule))//规则配置
.onErrorResume(throwable -> ratelimiterExecutor.withoutFallback(exchange, throwable));
}
限流的功能主要在ratelimiterExecutor.run()
中,负责生成限流器并执行。
@Override
public <T> Mono<T> run(final Mono<T> toRun, final Function<Throwable, Mono<T>> fallback, final Resilience4JConf conf) {
//限流器
RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(conf.getId(), conf.getRateLimiterConfig());
Mono<T> to = toRun.transformDeferred(RateLimiterOperator.of(rateLimiter));//执行限流功能
if (fallback != null) { //降级
return to.onErrorResume(fallback);
}
return to;
}
小结,本文结合实际案例演示了Resilience4j
插件,并分析了组合模式和限流模式的执行原理。