1. 阿里熔断限流Sentinel研究

1.1. 功能特点

  1. 丰富的应用场景:例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用
  2. 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  3. 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  4. 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

开源生态

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

1.2. 快速开始

1.2.1. 公网接入

  • 根据该步骤,建立demo

  • 控制台效果图

1.3. 手动接入

  1. 引入pom
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.6.2</version>
</dependency>
  1. 定义资源:也就是对哪个资源进行流量控制,现在已经提供了注解形式,所以新的接入直接用注解,@SentinelResource

    • 关于SentinelResource注解,这里列出几个好用和必填的参数,具体参考这里

1.3.1. apollo接入

由于我的系统使用的是apollo管理配置,所以我用apollo来管理规则,官方也提供了apollo的接入说明,然后我想到需要自动提交规则,而不是自己手动去配,又找到了官方的推送例子,可是该例子还是存在问题的,或者说要运行该例子需要对apollo的开放设置有进一步了解

期间我遇到了401问题,是因为需要apollo授权第三方应用,配置token后才能起效;

之后又遇到400问题,是因为openItemDTO.setDataChangeCreatedBy("apollo");namespaceReleaseDTO.setReleasedBy("apollo");该配置项,例子并不是这么写的,需要将参数改成apollo才行

下面给出完整配置,结合apollo读取配置

@Configuration
@ConditionalOnProperty(name = "sentinel.enabled",havingValue = "true")
@Slf4j
public class SentinelAspectConfiguration { @Autowired
private ApolloOpenApiClient apolloOpenApiClient; @Autowired(required = false)
private IRuleManage ruleManage; @Value("${appId:}")
private String appId; /**
* 已有配置,可直接使用
*/
@Value("${profile:}")
private String env; /**
* 没有profile属性配置,则必须配置env
*/
@Value("${env:pro}")
private String envReal; @Bean
public SentinelResourceAspect sentinelResourceAspect() {
pushlish();
return new SentinelResourceAspect();
} private void pushlish(){
List<FlowRule> flowRules = null;
if (ruleManage != null) {
flowRules = ruleManage.getFlowRules();
} if (appId == null) {
return;
} String flowDataId = appId+"-flow-rules";
String degradeDataId = appId+"-degrade-rules";
if("".equals(env)){
env = envReal;
}
env = env.toUpperCase(); //代码级别的规则,初始化加载,可不实现IRuleManage接口
setRules(flowRules, flowDataId); //流控
flowConfig(flowDataId); //降级
degradeConfig(degradeDataId); } private void degradeConfig(String degradeDataId) {
//读取
String namespaceName = "application";
String defaultFlowRules = "[]";
ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new ApolloDataSource<>(namespaceName,
degradeDataId, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {
}));
DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty()); //写入配置
WritableDataSource<List<DegradeRule>> wds = new WritableDataSource<List<DegradeRule>>() {
@Override
public void write(List<DegradeRule> rules) throws Exception {
setRules(rules, degradeDataId);
} @Override
public void close() throws Exception {
log.info("WritableDataSource DegradeRule close");
}
};
WritableDataSourceRegistry.registerDegradeDataSource(wds);
} private void flowConfig(String flowDataId) {
//读取
String namespaceName = "application";
String defaultFlowRules = "[]";
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName,
flowDataId, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); //写入配置
WritableDataSource<List<FlowRule>> wds = new WritableDataSource<List<FlowRule>>() {
@Override
public void write(List<FlowRule> rules) throws Exception {
setRules(rules, flowDataId);
} @Override
public void close() throws Exception {
log.info("WritableDataSource FlowRule close");
}
};
WritableDataSourceRegistry.registerFlowDataSource(wds);
} private void setRules(List rules, String flowDataId) {
if (rules != null && rules.size() > 0) {
// Increase the configuration
OpenItemDTO openItemDTO = new OpenItemDTO();
openItemDTO.setKey(flowDataId);
openItemDTO.setValue(JSON.toJSONString(rules));
openItemDTO.setComment("Program auto-join");
openItemDTO.setDataChangeCreatedBy("apollo");
apolloOpenApiClient.createOrUpdateItem(appId, env, "default", "application", openItemDTO); // Release configuration
NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
namespaceReleaseDTO.setEmergencyPublish(true);
namespaceReleaseDTO.setReleaseComment("Modify or add configurations");
namespaceReleaseDTO.setReleasedBy("apollo");
namespaceReleaseDTO.setReleaseTitle("Modify or add configurations");
apolloOpenApiClient.publishNamespace(appId, env, "default", "application", namespaceReleaseDTO);
}
}
}

@Configuration
@EnableApolloConfig(value = "application", order = 10)
public class AppBaseConfig { @Value("${apollo.token}")
private String token; @Value("${apollo.portalUrl}")
private String portalUrl; @Value("${sentinel.project.name:}")
private String projectName; @Value("${spring.application.name:}")
private String applicationName; @Value("${sentinel.console.server:}")
private String consoleServer; @Value("${sentinel.heartbeatClient:}")
private String heartbeatClient; @Bean
@ConditionalOnProperty(name = "sentinel.enabled",havingValue = "true")
public ApolloOpenApiClient apolloOpenApiClient() {
System.setProperty(AppNameUtil.APP_NAME, "".equals(projectName) ? applicationName : projectName);
System.setProperty(TransportConfig.CONSOLE_SERVER, consoleServer);
if (!"".equals(heartbeatClient)) {
String[] split = heartbeatClient.split(":");
System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP, split[0].trim());
System.setProperty(TransportConfig.SERVER_PORT, split[1].trim());
}
ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()
.withPortalUrl(portalUrl)
.withToken(token)
.build();
return client;
}
}

1.4. 控制台修改规则apollo写入

  1. 这个功能是可以有的,只是官方文档我没找到,我直接debug源码查看哪里可以把apollo写入加进去,果然发现它是提供了写入接口的
  2. 写入接口为WritableDataSource,参考上面的完整代码

1.5. 控制台

  1. 下载启动控制台,下载地址
  2. 启动命令java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

登录名密码都是sentinel

1.6. 工作原理

  1. 官方说明

1.7. 问题

1.7.1. 将控制台部署在公网,本机启动连接出现错误日志

2019-07-23 14:57:33.256 ERROR 14788 --- [pool-2-thread-1] c.a.c.s.dashboard.metric.MetricFetcher   : Failed to fetch metric from <http://172.16.100.141:8721/metric?startTime=1563865044000&endTime=1563865050000&refetch=false> (ConnectionException: Connection refused: no further information)

说明发送了内网地址,导致fetch拉取埋点信息不通

我通过System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP, split[0].trim());设置心跳地址为外网地址解决这个问题

本质上是因为控制台主动通过接口来客户端拉信息,但若是访问不通,也是没辙,所以本地测试部在服务器上的控制台,除非外网映射

1.7.2. 部署上去后发现可以访问通,且项目注册进来了,但没有任何调用信息,且没有任何规则信息

我的这个问题基础是因为我部署到docker上的,之后debug源码,发现控制台调用客户端的地址是我根本没配过的,深入后发现如下代码段

Runnable serverInitTask = new Runnable() {
int port; {
try {
port = Integer.parseInt(TransportConfig.getPort());
} catch (Exception e) {
port = DEFAULT_PORT;
}
} @Override
public void run() {
boolean success = false;
ServerSocket serverSocket = getServerSocketFromBasePort(port); if (serverSocket != null) {
CommandCenterLog.info("[CommandCenter] Begin listening at port " + serverSocket.getLocalPort());
socketReference = serverSocket;
executor.submit(new ServerThread(serverSocket));
success = true;
port = serverSocket.getLocalPort();
} else {
CommandCenterLog.info("[CommandCenter] chooses port fail, http command center will not work");
} if (!success) {
port = PORT_UNINITIALIZED;
} TransportConfig.setRuntimePort(port);
executor.shutdown();
} }; new Thread(serverInitTask).start();

该代码段的作用是为客户端在分配一个socketServer,之后的信息交互都是通过该服务提供的端口来提供;

这样一来客户端需要额外提供一个端口了,而我的docker只暴露了1个服务端口,所以不可避免的会出现问题,以上是我到目前的思路,正在验证中

至于端口如何决定,它是用了一个简单的技巧,若设置了csp.sentinel.api.port配置项,则会取该配置端口,若没有设,则是默认端口8719;但如果你用的是官网的启动方式,那8719应该是被控制台占用了,所以进入小技巧getServerSocketFromBasePort方法,内容如下

    private static ServerSocket getServerSocketFromBasePort(int basePort) {
int tryCount = 0;
while (true) {
try {
ServerSocket server = new ServerSocket(basePort + tryCount / 3, 100);
server.setReuseAddress(true);
return server;
} catch (IOException e) {
tryCount++;
try {
TimeUnit.MILLISECONDS.sleep(30);
} catch (InterruptedException e1) {
break;
}
}
}
return null;
}

它会循环尝试端口是否被占用,每个端口尝试三次,若被占用则取下一个+1端口,一直到可用的端口返回;所以如果我们的客户端应用放到了docker,而开放的端口只有一个,那就获取不了信息了

这里csp.sentinel.api.port配置项很容易理解成客户端的端口地址,因为启动也不会报错啥的,会误让我们误会这个参数可以不填,虽然文档上写着必填,但本地测试的时候可没影响-_-||,所有都注意了,这个配置项是必填的

  • 还要注意一点,因为是socket连接,两边端口要一致,所以docker端口号映射需要一样

阿里熔断限流Sentinel研究的更多相关文章

  1. 微服务熔断限流Hystrix之流聚合

    简介 上一篇介绍了 Hystrix Dashboard 监控单体应用的例子,在生产环境中,监控的应用往往是一个集群,我们需要将每个实例的监控信息聚合起来分析,这就用到了 Turbine 工具.Turb ...

  2. Envoy熔断限流实践(二)Rainbond基于RLS服务全局限流

    Envoy 可以作为 Sevice Mesh 微服务框架中的代理实现方案,Rainbond 内置的微服务框架同样基于 Envoy 实现.本文所描述的全局限速实践也是基于 Envoy 已有的方案所实现. ...

  3. Envoy熔断限流实践(一)基于Rainbond插件实现熔断

    Envoy 可以作为 Sevice Mesh 微服务框架中的代理实现方案,Rainbond 内置的微服务框架同样基于 Envoy 实现.本文所描述的熔断实践基于 Rainbond 特有的插件机制实现. ...

  4. 微服务熔断限流Hystrix之Dashboard

    简介 Hystrix Dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard可以直观地看到各Hystrix Command的请求响应时间,请求成功率等数据 ...

  5. Spring Cloud微服务Sentinel+Apollo限流、熔断实战总结

    在Spring Cloud微服务体系中,由于限流熔断组件Hystrix开源版本不在维护,因此国内不少有类似需求的公司已经将眼光转向阿里开源的Sentinel框架.而以下要介绍的正是作者最近两个月的真实 ...

  6. Spring Cloud alibaba网关 sentinel zuul 四 限流熔断

    spring cloud alibaba 集成了 他内部开源的 Sentinel 熔断限流框架 Sentinel 介绍 官方网址 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentine ...

  7. Dubbo使用Sentinel来对服务进行降级与限流

    一.Sentinel 是什么 Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定 ...

  8. Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流

    最近管点闲事浪费了不少时间,感谢网友libinwalan的留言提醒.及时纠正路线,继续跟大家一起学习Spring Cloud Alibaba. Nacos作为注册中心和配置中心的基础教程,到这里先告一 ...

  9. Spring Cloud Alibaba | Sentinel: 服务限流高级篇

    目录 Spring Cloud Alibaba | Sentinel: 服务限流高级篇 1. 熔断降级 1.1 降级策略 2. 热点参数限流 2.1 项目依赖 2.2 热点参数规则 3. 系统自适应限 ...

随机推荐

  1. Java多线程编程核心技术-第7章-拾遗增补-读书笔记

    第 7 章 拾遗增补 本章主要内容 线程组的使用. 如何切换线程状态. SimpleDataFormat 类与多线程的解决办法. 如何处理线程的异常. 7.1 线程的状态 线程对象在不同的运行时期有不 ...

  2. 洛谷 P1167 刷题

    洛谷 P1167 刷题 洛谷传送门 题目描述 noip临近了,小A却发现他已经不会写题了.好在现在离竞赛还有一段时间,小A决定从现在开始夜以继日地刷题.也就是说小A废寝忘食,一天二十四小时地刷题. 今 ...

  3. AtCoder Grand Contest 038题解

    好久没更了 写点东西吧= = A 01Matrix 简单构造 左上角和右下角染成1其他染成0即可 #include<bits/stdc++.h> #define ll long long ...

  4. Jupyter-notebook安装问题及解决

    两种方式: 1.pip install jupyter notebook 2.安装Anaconda 1.pip安装 通过命令行pip,要注意是在哪个虚拟环境,安装好后jupyter notebook所 ...

  5. ABP JS调用接口 获取返回的数据

    var _userService = abp.services.app.user; console.log(abp.services.app.user); _userService.getUserBy ...

  6. 踩iviewui中Select 选择器两级联动,重置查询条件时第二级数据无法清除的坑

    小颖公司最近做的项目用的vue+iviewui+axios,在做项目的过程中,遇到一个问题: 二级联动的下拉框,第一个下拉框一直都有值,第二个下拉框是在选择了第一个下拉框之后采取调用ajax获取其值, ...

  7. vue中异步请求渲染问题(swiper不轮播)(在开发过程中遇到过什么问题、踩过的坑)

    问题描述: 用vue封装一个swiper组件的时候,发现轮播图不能轮播了. 原因: 异步请求的时间远大于生命周期执行的时间,mounted初始化DOM时数据未返回,渲染数据是空数组,导致轮播图的容器层 ...

  8. Python之文件读写(csv文件,CSV库,Pandas库)

    前言 一.Python文件读取 二.读取CSV文件 一.Python文件读取 1. open函数是内置函数之with操作 - 关于路径设置的问题斜杠设置成D:\\文件夹\\文件或是D:/文件夹/文件 ...

  9. ES6解构赋值常见用法

    解构赋值出现的契机: let obj = { a: 1, b: 2 } // 取值 let a = obj.a let b = obj.b 问题核心: 每次取值既要确定对象属性名,还得重新定义一个变量 ...

  10. 前端,DJ

    Vue模块 1.Vue都有哪些指令,简单说说? """ Vue里面常见指令有文本指令:v-text.v-html,属性指令:v-bind,方法指令:v-on,条件指令:v ...