逅弈 转载请注明原创出处,谢谢!java
Sentinel 原理-全解析node
Sentinel 原理-滑动窗口github
Sentinel 实战-限流篇spring
经过 Sentinel 的控制台,咱们能够对规则进行查询和修改,也能够查看到实时监控,机器列表等信息,因此咱们须要对 sentinel 的控制台作个完整的了解。bash
首先须要启动控制台, sentinel 的控制台是用 spring boot 写的一个web 应用,咱们有几种方式来获取控制台:app
从 release 页面 下载截止目前为止最新版本的控制台 jar 包,以下图所示:curl
除了能够下载预先构建好的可执行 jar 包以外,咱们还能够把控制台的工程下载下来自行用源码构建,sentinel 是一个多 maven 模块的项目,控制台是其中的一个项目,以下图所示:
如上图所示,咱们能够下载完整的 sentinel 的项目,而后构建其中的 sentinel-dashboard 模块,也能够只下载 sentinel-dashboard 模块而后构建。
这里我选择将完整的 sentinel 工程下载下来,而后构建 sentinel-dashboard 模块,首先在项目根目录下执行:
cd sentinel-dashboard
复制代码
将会进入 dashboard 模块,而后在 dashboard 目录下执行:
mvn clean package
复制代码
maven将会把 sentinel-dashboard 模块打包成一个可执行的 fat jar包,以下图所示:
构建成功后,就能够启动控制台了,执行如下命令:
java -Dserver.port=8080 \
-Dcsp.sentinel.dashboard.server=localhost:8080 \
-jar target/sentinel-dashboard.jar
复制代码
其中 -Dserver.port=8080
用于指定 Sentinel 控制台端口为 8080
。
执行完以后,你将看到以下信息:
当看到 Started DashboardApplication in xx seconds
时,说明你的控制台已经启动成功了,访问 http://localhost:8080/ 就能够看到控制台的样子了,以下图所示:
能够看到当前控制台中没有任何的应用,由于尚未应用接入。
要想在控制台中操做咱们的应用,除了须要部署一个控制台的服务外,还须要将咱们的应用接入到控制台中去。
首先须要在咱们使用 sentinel 的服务中引入 sentinel-transport 的依赖,由于咱们的应用是做为客户端,经过transport模块与控制台进行通信的,依赖以下所示:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>x.y.z</version>
</dependency>
复制代码
版本依然选择最新的 1.4.0
引入了依赖以后,接着就是在咱们的应用中配置 JVM 启动参数,以下所示:
-Dproject.name=xxx -Dcsp.sentinel.dashboard.server=consoleIp:port
复制代码
其中的consoleIp和port对应的就是咱们部署的 sentinel dashboard 的ip和port,我这里对应的是 127.0.0.1 和 8080,按照实际状况来配置 dashboard 的ip和port就行了,以下图所示:
从图中能够看到我设置的客户端的应用名为:lememo,当客户端链接上控制台后,会显示该应用名。
PS:须要注意的是,除了可经过 JVM -D 参数指定以外,也可经过 properties 文件指定,配置文件的路径为 ${user_home}/logs/csp/${project.name}.properties
。
配置文件中参数的key和类型以下所示:
优先级顺序:JVM -D 参数的优先级最高,若 properties 文件和 JVM 参数中有相同项的配置,以 JVM -D 参数配置的为准。
客户端配置好了与控制台的链接参数以后,并不会主动链接上控制台,须要触发一次客户端的规则才会开始进行初始化,并向控制台发送心跳和客户端规则等信息。
客户端与控制台的链接初始化是在 Env 的类中触发的,即下面代码中的 InitExecutor.doInit();
:
public class Env {
public static final NodeBuilder nodeBuilder = new DefaultNodeBuilder();
public static final Sph sph = new CtSph();
static {
// If init fails, the process will exit.
InitExecutor.doInit();
}
}
复制代码
上篇文章中咱们建立了一个 UserService 来作验证,正常时会返回一个用户对象,被限流时返回一个null,可是这样不太直观,本篇文章我换一个更简单和直观的验证方式,代码以下所示:
@GetMapping("/testSentinel")
public @ResponseBody
String testSentinel() {
String resourceName = "testSentinel";
Entry entry = null;
String retVal;
try{
entry = SphU.entry(resourceName,EntryType.IN);
retVal = "passed";
}catch(BlockException e){
retVal = "blocked";
}finally {
if(entry!=null){
entry.exit();
}
}
return retVal;
}
复制代码
PS:这里有个须要注意的知识点,就是 SphU.entry 方法的第二个参数 EntryType 说的是此次请求的流量类型,共有两种类型:IN 和 OUT 。
IN:是指进入咱们系统的入口流量,好比 http 请求或者是其余的 rpc 之类的请求。
OUT:是指咱们系统调用其余第三方服务的出口流量。
入口、出口流量只有在配置了系统规则时才有效。
设置 Type 为 IN 是为了统计整个系统的流量水平,防止系统被打垮,用以自我保护的一种方式。
设置 Type 为 OUT 一方面是为了保护第三方系统,好比咱们系统依赖了一个生成订单号的接口,而这个接口是核心服务,若是咱们的服务是非核心应用的话须要对他进行限流保护;另外一方面也能够保护本身的系统,假设咱们的服务是核心应用,而依赖的第三方应用总是超时,那这时能够经过设置依赖的服务的 rt 来进行降级,这样就不至于让第三方服务把咱们的系统拖垮。
下图描述了流量的类型和系统之间的关系:
应用接入 transport 模块以后,咱们主动来访问一次 /testSentinel
接口,顺利的话,客户端会主动链接上控制台,并将本身的ip等信息发送给控制台,而且会与控制台维持一个心跳。
如今咱们在来访问下控制台,看到客户端已经链接上来了,以下图所示:
客户端链接上dashboard以后,咱们就能够为咱们定义的资源配置规则了,有两种方式能够配置规则:
咱们能够在【流控规则】页面中新增,点击【流控规则】进入页面,以下图所示:
在弹出框中,填写资源名和单机阈值,其余的属性保持默认设置便可,以下图所示:
点击【新增】后,规则即生效了。
第二种方式就是在【簇点链路】的页面中找到咱们埋点的资源名,而后直接对该资源进行增长流控规则的操做,以下图所示:
上图中右侧的【+流控】的按钮点击后,弹出框与直接新增规则是同样的,只是会自动将资源名填充进去,省去了咱们设置的这一步。
规则建立完成以后,咱们就能够在【流控规则】页面查询到了,以下图所示:
接着咱们就能够来验证效果了,让咱们在浏览器中快速的刷新来请求 /testSentinel
这个接口,不出意外,应该会看到以下图所示的状况:
说明咱们设置的流控规则生效了,请求被 block 了。
如今咱们再到控制台的【实时监控】页面查询下,刚刚咱们的一顿疯狂请求应该有不少都被 block 了,经过的 qps 应该维持在2如下,以下图所示:
咱们知道 sentinel 的核心就是围绕着几件事:资源的定义,规则的配置,代码中埋点。
并且这些事在 sentinel-core 中都有能力实现,也对外暴露了相应的 http 接口方便咱们查看 sentinel 中的相关数据。
sentinel-core 在第一次规则被触发的时候,启动了一个 CommandCenter,也就是咱们引入的 sentinel-transport-simple-http 依赖中被引入的实现类:SimpleHttpCommandCenter。
这个 SimpleHttpCommandCenter 类中启动了两个线程池:主线程池和业务线程池。
主线程池启动了一个 ServerSocket 来监听默认的 8719 端口,若是端口被占用,会自动尝试获取下一个端口,尝试3次。
业务线程池主要是用来处理 ServerSocket 接收到的数据。
将不重要的代码省略掉以后,具体的代码以下所示:
public class SimpleHttpCommandCenter implements CommandCenter {
// 省略初始化
private ExecutorService executor;
private ExecutorService bizExecutor;
@Override
public void start() throws Exception {
Runnable serverInitTask = new Runnable() {
int port;
{
try {
port = Integer.parseInt(TransportConfig.getPort());
} catch (Exception e) {
port = DEFAULT_PORT;
}
}
@Override
public void run() {
// 获取可用的端口用以建立一个ServerSocket
ServerSocket serverSocket = getServerSocketFromBasePort(port);
if (serverSocket != null) {
// 在主线程中启动ServerThread用以接收socket请求
executor.submit(new ServerThread(serverSocket));
// 省略部分代码
} else {
CommandCenterLog.info("[CommandCenter] chooses port fail, http command center will not work");
}
executor.shutdown();
}
};
new Thread(serverInitTask).start();
}
class ServerThread extends Thread {
private ServerSocket serverSocket;
ServerThread(ServerSocket s) {
this.serverSocket = s;
}
@Override
public void run() {
while (true) {
Socket socket = null;
try {
socket = this.serverSocket.accept();
setSocketSoTimeout(socket);
// 将接收到的socket封装到HttpEventTask中由业务线程去处理
HttpEventTask eventTask = new HttpEventTask(socket);
bizExecutor.submit(eventTask);
} catch (Exception e) {
// 省略部分代码
}
}
}
}
}
复制代码
具体的状况以下图所示:
SimpleHttpCommandCenter 启动了一个 ServerSocket 来监听8719端口,也对外提供了一些 http 接口用以操做 sentinel-core 中的数据,包括查询|更改规则,查询节点状态等。
PS:控制台也是经过这些接口与 sentinel-core 进行数据交互的!
提供这些服务的是一些 CommandHandler 的实现类,每一个类提供了一种能力,这些类是在 sentinel-transport-common 依赖中提供的,以下图所示:
运行下面命令,则会返回现有生效的规则:
curl http://localhost:8719/getRules?type=<XXXX>
复制代码
其中,type有如下取值:
flow
以 JSON 格式返回现有的限流规则;degrade
则返回现有生效的降级规则列表;system
则返回系统保护规则。同时也能够经过下面命令来修改已有规则:
curl http://localhost:8719/setRules?type=<XXXX>&data=<DATA>
复制代码
其中,type 能够输入 flow
、degrade
等方式来制定更改的规则种类,data
则是对应的 JSON 格式的规则。
其余的接口再也不一一详细举例了,有须要的你们能够自行查看源码了解。
更多原创好文,请关注公众号「逅弈逐码」