写在前面
本文参照Spring官方文档,并实现了代码,在此做下笔记;
随着微服务的流行,服务调用的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以“流量”为切入点,从流量控制、断路和负载保护等多个维度来保障服务可靠性。
哨兵拥有以下特性:
新建 sentinel-app
子模块。如果想要在项目中使用 Sentinel ,需要导入如下依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
下面是一个关于如何使用 Sentinel 的用例:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
@RestController
public class TestController {
@GetMapping(value = "/hello")
@SentinelResource("hello")
public String hello() {
return "Hello Sentinel";
}
}
@SentinelResource
注解是用于标识资源是否受到速率限制或降级。在上面的用例中,注解中的 ‘hello’ 属性值指的是资源名。
资源 是 Sentinel 中的核心概念之一。最常用的资源是我们代码中的 Java 方法。当然,也可以更灵活的定义资源,例如,将需要控制流量的代码用 Sentinel API SphU.entry("helloWorld")
和 entry.exit()
包围起来。
@GetMapping("/helloWorld")
public String helloWorld(){
try(Entry entry = SphU.entry("helloWorld")){
//被保护的业务逻辑
return "helloWorld";
}catch (BlockException exception){
//资源访问阻止,被限流或被降级
System.out.println("blocked!");
return "blocked!";
}
}
在我们运行上面的测试用例之前,先让我们来搭建好 Sentinel Dashboard。
Sentinel Dashboard 是一种轻量级控制台,它提供机器发现、单服务器资源监视、集群资源数据概览以及规则管理等功能。要使用这些特性,您需要完成以下几个步骤:
集群统计只支持节点少于 500 个的集群,延迟大约 1 到 2 秒。
下载稳定的 dashboard JAR 包;
启动:java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
访问地址 http://
:
3. 客户端访问 dashboard:在上面的测试用例中添加如下配置:
spring:
cloud:
sentinel:
transport:
port: 8720
dashboard: localhost:8080
使用如上的配置,你需要在程序启动后,手动访问用例的 http://192.168.3.18:18085/hello
地址,才能看如下界面:
不过,你也可以通过新增 eager
配置,在启动后就能初始化:
spring:
application:
name: sentinel-app
cloud:
sentinel:
transport:
dashboard: localhost:8080
heartbeat-interval-ms: 10000
port: 8720
eager: true
现在,启动我们的应用程序,可以尝试不断地访问接口地址,来观察 Dashboard 实时监控栏的变化!
实现 openFeign
支持,项目需要做比较大的改动;
引入依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
不过由于这个依赖是由 spring-cloud-dependencies
所管理的,所以我们需要先在顶层模块下加入如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.1.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwich.SR2version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
引入 nacos-discovery
来支持远程调用:
>
>com.alibaba.cloud >
>spring-cloud-starter-alibaba-nacos-discovery >
>
修改启动类,使其支持 nacos-discovery
以及 feign
:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
添加 openFeign
测试用例:
@FeignClient(name = "provider-app", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
public interface EchoService {
@GetMapping("/echo/{str}")
String echo(@PathVariable("str") String str);
}
class FeignConfiguration{
@Bean
public EchoServiceFallback echoServiceFallback(){
return new EchoServiceFallback();
}
}
class EchoServiceFallback implements EchoService{
@Override
public String echo(String str) {
return "echo fallback";
}
}
修改当前的测试用例,使其使用 feign
的测试用例:
@RestController
public class TestController {
@Resource
private EchoService echoService;
@GetMapping("/hello")
@SentinelResource("hello")
public String hello(){
return "Hello Sentinel";
}
@GetMapping("/echo/{str}")
public String echo(@PathVariable String str){
return echoService.echo(str);
}
}
修改配置文件:
server:
port: 18085
spring:
application:
name: sentinel-app
cloud:
sentinel:
transport:
dashboard: localhost:8080
heartbeat-interval-ms: 10000
port: 8720
eager: true
nacos:
discovery:
server-addr: 127.0.0.1:8848
network-interface: eth9
namespace: ecbbf883-5744-42e3-b448-09aabee90d7f
feign:
sentinel:
enabled: true
management:
endpoints:
web:
exposure:
include: nacos-discovery
现在,启动 provider-app
子模块(服务提供者)以及当前的 sentinel-app
项目;查看 Nacos
控制台,两个服务都注册成功。我们可以尝试不断访问上面的测试用例地址:http://192.168.3.18:18085/echo/hello
,然后观察 Sentinel Dashboard 的实时监控一栏的变化。
Sentinel 也支持保护 RestTemplate
的服务调用。我们只需要在构造 RestTemplate
bean 时添加 @SentinelRestTemplate
注解:
@Bean
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
return new RestTemplate();
}
该注解的属性支持流量控制(blockHandler
,blockHandlerClass
) 和断路(fallback
,fallbackClass
)。并且,这个 blockHandler
或者 fallback
指定的方法必须是 blockHandlerClass
或者 fallbackClass
的静态方法。
方法的入参和返回应该和 org.springframework.http.client.ClientHttpRequestInterceptor#interceptor
一样,但是,可以多一个 BlockException
来通过 Sentinel 捕获异常。所以,handleException
方法如下:
public class ExceptionUtil {
public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) throws IOException {
return execution.execute(request, body);
}
}
现在,让我们来完善这个测试用例。除了上面的两处变动外,我们需要在 TestController
类中做些改动来测试。TestController
新增如下代码:
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String appName;
@GetMapping("/echo/app-name")
public String echoAppName(){
ServiceInstance serviceInstance = loadBalancerClient.choose("provider-app");
String path = String.format("http://%s:%s/echo/%s", serviceInstance.getHost(), serviceInstance.getPort(), appName);
System.out.println("request path: " + path);
return restTemplate.getForObject(path, String.class);
}
重启应用程序,访问上面的接口地址,观察 dashboard 实时监控栏变化!(关于流控的具体介绍,我打算重新写一篇博客来介绍)。
Sentinel 也提供了对 Zuul
以及 Gateway
的支持,这里就不再介绍了!
Sentinel 提供了一个 /sentinel
端点,该端点暴露的主要内容如下:
末尾有几个内容不太懂,我们都将在后续一一弄明白。注意,端点都需要在配置文件中进行暴露以后,才可以访问。
当 ApplicationContext
中存在下列相应的 bean 时,会产生相应的行为:
UrlCleaner
: 处理 url,对一些路径变量进行统一处理,例如 collect_item_relation--10200012121-.html
将被转换为 collect_item_relation.html
。
UrlBlockHandler
: 在 URL 被 sentinel 阻塞时,应执行的处理程序;
RequestOriginParser
: 解析给定请求的来源;
上面的 bean 都将通过 WebCallbackManager
统一管理调用。
Sentinel 的配置可以查看相应的文档或者 SentinelProperties
类。