这里不做过多理论说明,直接进入实战操作。
第一章:Nacos Discovery——服务发现与注册管理
- Nacos Server启动。
- 创建父工程alibaba-spring-cloud。
- 创建maven工程,只保留pom.xml用作添加公共依赖。
- 添加公共依赖,仓库路径,以及版本定位。
-
<properties>
<spring-cloud.version>2021.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
</properties>
<!-- 引入 Spring Boot 的依赖 -->
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>3.1.1</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- Spring Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<repositories>
<!--阿里云代理-->
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</repository>
</repositories>
3. Nacos client provider创建。
a) 创建alibaba-spring-cloud-provider子工程,添加依赖。
<parent>
<groupId>com.cloud</groupId>
<artifactId>alibaba-spring-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--web 模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies> - 启动类添加@EnableDiscoveryClient注解。
- application.yml配置文件添加配置,定义服务名称,配置nacos连接地址及账号密码。
-
server:
port: 18080
spring:
application:
name: provider-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
username: nacos
password: nacos
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always - 创建Controller并添加接口。
-
package com.cloud.controller;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
/**
* 项目名称: provider
* 包名称: com.cloud.controller
* 类名称: ProviderController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/9 14:45
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class ProviderController {
@GetMapping("/echo/{string}")
public String echo(@PathVariable String string) {
return "hello Nacos Discovery " + string;
}
} - 启动之后,登录nacos查看服务列表已存在该服务。
- Nacos client consumer创建。
- 创建alibaba-spring-cloud-consumer子工程,添加依赖。
-
<parent>
<groupId>com.cloud</groupId>
<artifactId>alibaba-spring-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies> - 启动类添加@EnableDiscoveryClient注解。
- application.yml配置文件添加配置,定义服务名称,配置nacos连接地址及账号密码。
-
server:
port: 18011
spring:
application:
name: consumer-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
username: nacos
password: nacos
loadbalancer.nacos.enabled: true
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always - 创建Controller并添加接口。
-
package com.cloud.controller;
import com.cloud.service.EchoService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* 项目名称: consumer
* 包名称: com.cloud.controller
* 类名称: ConsumerController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/11 9:34
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class ConsumerController {
@Resource
private RestTemplate restTemplate;
@RequestMapping("/echo/{string}")
public String echo(@PathVariable String string) {
return restTemplate.getForObject("http://provider-service/echo/{string}", String.class, string);
}
} - 创建RestTemplateConfig配置类,同时添加负载均衡支持注解。
-
package com.cloud.configurer;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* 项目名称: consumer
* 包名称: com.cloud.configurer
* 类名称: RestTemplateConfig
* 类描述: RestTemplate配置
* 创建人: zhihong.zhu
* 创建时间: 2022/8/11 9:32
* 修改人:
* 修改时间:
* 修改备注:
*/
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate createRestTemplate(){
return new RestTemplate();
}
} - 启动之后,登录nacos查看服务列表已存在该服务。
- 访问http://localhost:18011/echo/hello-world实现服务调用。
第二章:Nacos OpenFeign——使用openFeign实现服务调用
- 还使用alibaba-spring-cloud-consumer子工程,添加依赖。
-
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> - 创建EchoService接口类,添加@FeignClient(name = "provider-service",fallback = EchoServiceFallback.class)注解。name—远程服务名称,fallback—熔断降级处理类。(FeignClient 已经默认集成了 Ribbon)
-
package com.cloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "provider-service",fallback = EchoServiceFallback.class)
public interface EchoService {
@GetMapping("/echo/{str}")
String echo(@PathVariable("str") String str);
@GetMapping("/notFound")
String notFound();
} - 创建EchoServiceFallback类,实现EchoService接口类,实现两个方法,编写降级后处理业务。
-
package com.cloud.service;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 项目名称: consumer
* 包名称: com.cloud.service
* 类名称: EchoServiceFallback
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/11 9:53
* 修改人:
* 修改时间:
* 修改备注:
*/
@Component
public class EchoServiceFallback implements EchoService{
@Override
public String echo(@PathVariable("str") String str) {
return "echo fallback";
}
@Override
public String notFound() {
return "notFound fallback";
}
} - 在ConsumerController再添加接口,调用EchoService的两个方法。
-
package com.cloud.controller;
import com.cloud.service.EchoService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* 项目名称: consumer
* 包名称: com.cloud.controller
* 类名称: ConsumerController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/11 9:34
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class ConsumerController {
@Resource
private RestTemplate restTemplate;
@Resource
private EchoService echoService;
@RequestMapping("/echo/{string}")
public String echo(@PathVariable String string) {
return restTemplate.getForObject("http://provider-service/echo/{string}", String.class, string);
}
@RequestMapping("/echoForFeign/{string}")
public String echoForFeign(@PathVariable String string) {
return echoService.echo(string);
}
@RequestMapping("/notFound")
public String notFound(){
return echoService.notFound();
}
} - 启动服务,访问http://localhost:18011/echoForFeign/hello-world实现服务调用,访问http://localhost:18011/notFound返回降级的业务内容。
第三章:Nacos config——配置管理
- 与spring cloud config相比,Nacos config不需要单独创建配置中心服务,不需要创建云仓库,不需要MQ传递更新消息。一站式完成动态配置。
- 创建alibaba-spring-cloud-config子工程,添加依赖。
-
<parent>
<groupId>com.cloud</groupId>
<artifactId>alibaba-spring-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies> - resources下创建bootstrap.yml并添加配置。(项目启动时最先加载bootstrap.yml配置)
-
server:
port: 18082
spring:
application:
name: nacos-config
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yml
#优先级shared-configs < extension-configs < ${spring.application.name}-${spring-profiles-active}.${file-extension}
shared-configs:
- data-id: nacos-config-shared1.yml
group: SHARED1_GROUP
refresh: true
- data-id: nacos-config-shared2.yml
group: SHARED1_GROUP
refresh: false
extension-configs:
- data-id: nacos-config-extension1.yml
group: EXTENSION1_GROUP
refresh: true
- data-id: nacos-config-extension2.yml
group: EXTENSION2_GROUP
refresh: false
profiles:
active: dev - 配置属性说明:
- file-extension——配置文件后缀(properties、yml)
- shared-configs——共享配置,有多个配置可用此属性,格式如上。
- data-id——资源ID,指定Nacos创建配置文件时的Data ID。此属性值必须加后缀名,file-extension不会自动拼接到此属性值。
- group——分组,指定Nacos创建配置文件时的Group。默认DEFAULT_GROUP。
- refresh——是否动态刷新。此属性实现时需与注解配合,后面讲。
- extension-configs——扩展配置。与shared-configs用途一样,只是优先级不一样。格式如上。
- 优先级shared-configs < extension-configs < ${spring.application.name}-${spring-profiles-active}.${file-extension}
- 创建Controller类,并创建接口。添加@RefreshScope注解结合配置文件的refresh配置实现动态刷新。
-
package com.cloud.controller;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* 项目名称: config
* 包名称: com.cloud.controller
* 类名称: ConfigController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/11 14:31
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
@RefreshScope
public class ConfigController {
@Value("${user.name}")
private String name;
@Value("${user.age}")
private String age;
@Value("${shared1}")
private String shared1;
@Value("${shared2}")
private String shared2;
@Value("${extension1}")
private String extension1;
@Value("${extension2}")
private String extension2;
@RequestMapping("/configTest")
public Map configTest() {
HashMap<Object, Object> map = Maps.newHashMap();
map.put("name", name);
map.put("age", age);
map.put("shared1", shared1);
map.put("shared2", shared2);
map.put("extension1", extension1);
map.put("extension2", extension2);
return map;
}
}
- 登录Nacos控制台,添加nacos-config-dev.yml(DEFAULT_GROUP)、nacos-config-extension1.yml(EXTENSION1_GROUP)、nacos-config-extension2.yml(EXTENSION2_GROUP)、nacos-config-shared1.yml、(SHARED1_GROUP)nacos-config-shared2.yml(SHARED1_GROUP)配置。
-
#nacos-config-dev.yml
user:
name: zhihong.zhu
age: 288
#nacos-config-extension1.yml
user:
name: zhihong.zhu
age: 288
extension1: extension11
#nacos-config-extension2.yml
user:
name: zhihong.zhu
age: 289
extension2: extension22
#nacos-config-shared1.yml
user:
name: zhihong.zhu
age: 28
shared1: shared1
#nacos-config-shared2.yml
user:
name: zhihong.zhu
age: 28
shared2: shared2 - 启动服务,调用接口http://localhost:18082/configTest查询配置。多次修改配置文件多次访问。可验证动态刷新配置及优先级。
第四章:Nacos Gateway——API网关
- Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。
- 创建alibaba-spring-cloud-gateway子工程,添加依赖。
-
<parent>
<groupId>com.cloud</groupId>
<artifactId>alibaba-spring-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies> - application.yml配置文件添加配置。
-
server:
port: 18083
spring:
application:
name: nacos-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
gateway:
discovery:
locator:
enabled: true
routes:
- id: nacos-route
uri: lb://provider-service
predicates:
- Path=/nacos/**
filters:
- StripPrefix=1
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always - 配置属性说明:
- routes——路由配置。
- id——路由ID。
- uri——路由指向地址。lb://是注册中心匹配方式,将从注册中心找对应的服务,所以注册中心必须有该服务。对服务命名方式有特殊要求。命名规则为:"[a-zA-Z]([a-zA-Z]|\\d|\\+|\\.|-)*:.*"。还有两种分别是ws(websocket)方式和http方式。
- predicates——断言。
- Path——与此值相匹配的路径进行路由。除此之外,断言有很多种路由方式。
- filters——拦截。
- StripPrefix——匹配路径的节数。
- 启动服务,访问http://localhost:18083/nacos/echo/hello-world。实际会指向http://provider-service/echo/hello-world
第五章:Sentinel——流控、熔断、降级
- 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 https://github.com/alibaba/Sentinel?以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
- 启动Sentinel 控制台。
- 创建alibaba-spring-cloud-sentinel子工程,添加依赖。
-
<parent>
<groupId>com.cloud</groupId>
<artifactId>alibaba-spring-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
</dependency>
</dependencies> - application.yml配置文件添加配置。
-
server:
port: 18086
spring:
application:
name: nacos-sentinel
cloud:
sentinel:
transport:
dashboard: localhost:8868
eager: true
web-context-unify: true
filter:
enabled: false
http-method-specify: false
datasource:
# ds6:
# nacos:
# server-addr: 127.0.0.1:8848
# username: nacos
# password: nacos
# dataId: flowrule.json
# data-type: json
# rule-type: flow
ds1:
file:
file: "classpath: flowrule.xml"
data-type: xml
rule-type: flow
ds2:
file:
file: "classpath: degraderule.json"
data-type: json
rule-type: degrade
ds3:
file:
file: "classpath: authority.json"
rule-type: authority
ds4:
file:
file: "classpath: system.json"
rule-type: system
ds5:
file:
file: "classpath: param-flow.json"
rule-type: param_flow
management:
endpoints:
web:
exposure:
include: "*"
feign:
sentinel:
enabled: true - 配置属性说明:
- eager——是否提前触发 Sentinel 初始化。
- web-context-unify——是否根据不同的URL 进行链路限流。
- 添加文件到resources。文件地址https://gitee.com/zzh13520704819/sentinel-config.git
- 文件说明:
- authority.json——授权规则。
- degraderule.json——熔断降级规则。
- flowrule.json——流控规则。
- flowrule.json——流控规则,xml数据类型配置。
- param-flow.json——热点规则。
- system.json——系统规则。
- 文件属性说明:
- 限流规则配置:
|属性|说明| |:----| |app |应用名| |resource |资源名(唯一名称,默认请求路径)| |limitApp |针对来源 (Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制))| |grade |阈值类型(阀值类型,0:线程数,1:QPS)| |count|单机阀值| |clusterMode|是否集群(false:否,true:是)| |controlBehavior|流控效果 (0:失败,1:warmUp,2:排队等待) |strategy|流控模式(0:直接,1:关联,2:链路)| |clusterConfig|thresholdType: 0| 降级规则配置: |属性|说明| |:----| |app |应用名| |count |熔断策略为慢调用比例:最大Rt(需要设置的阈值,超过该值则为慢应用),异常比例中为:比例阈值,异常数中为:异常数| |limitApp |针对来源 (Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制))| |grade |熔断策略(0:慢调用比例,1:异常比例,2:异常数)| |minRequestAmount|最小请求数(允许通过的最小请求数,在该数量内不发生熔断)| |timeWindow|熔断时长(在这段时间内发生熔断,拒绝所有请求)| |slowRatioThreshold|比例阈值 (慢调用占所有的调用比率,范围[0~1]) |resource |资源名(唯一名称,默认请求路径)| |statIntervalMs |熔断时长(熔断时长,默认为1秒)| 热点规则配置: |属性|说明| |:----| |app |应用名| |resource |资源名(唯一名称,默认请求路径)| |limitApp |针对来源 (Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制))| |grade |限流模式(0:线程数,1:QPS)| |count|单机阀值| |durationInSec|统计窗口时间|| |clusterMode|是否集群(false:否,true:是)| |paramIdx|参数索引| |paramFlowItemList|参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型| |controlBehavior|流控效果,默认为0 (0:快速失败,1:warmUp,2:排队等待) |maxQueueingTimeMs|最大排队等待时长,默认0(仅在匀速排队模式生效)| 授权规则配置: |属性|说明| |:----| |app |应用名| |resource|资源名| |limitApp |流控应用(指调用方,多个调用方名称用半角英文逗号(,)分隔)| |strategy |授权类型(0:白名单,1:黑名单)| 系统规则配置: |属性|说明| |:----| |app |应用名| |highestSystemLoad |阈值(阈值类型为Load的阈值)[0,1)的正整数| |avgRt |阈值(阈值类型为RT的阈值)所有入口流量的平均响应时间,[0,1)的正整数| |maxThread |阈值(阈值类型为线程数的阈值)入口流量的最大并发数,[0,1)的正整数| |qps|阈值 (阈值类型为入口 QPS的阈值)所有入口资源的 QPS,[0,1)的正整数| |highestCpuUsage|阈值(阈值类型为CPU 使用率的阈值)[0,1]的小数,代表百分比|
- 启动类添加@EnableDiscoveryClient注解。
- 启动服务,查看sentinel控制台,可以看到各项配置已通过本地文件自动创建。
- 可以继续在控制台创建各种规则。
- 流控规则测试:
- 创建SentinelController类,添加test方法。
-
package com.cloud.controller;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* 项目名称: consumer
* 包名称: com.cloud.controller
* 类名称: SentinelController {
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/15 14:12
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class SentinelController {
@GetMapping("/test")
@SentinelResource(value = "test")
public String test(){
return "Hello test";
}
} - 启动服务,访问http://localhost:18086/test,QPS超过1触发流控规则。
- 响应的信息可以看出,触发流控规则后直接返回状态码为500的错误信息,不够友好,太过笼统,所以我们可以利用注解的一些属性自由处理一些可选的异常。
- 创建ExceptionUtil类,添加blockException方法。
-
package com.cloud.configurer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
public final class ExceptionUtil {
private ExceptionUtil() {
}
public static String blockException(BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
return ex.getClass().getCanonicalName();
}
} - SentinelController类的test的@SentinelResource注解添加blockHandler = "blockException",blockHandlerClass = ExceptionUtil.class属性。
-
package com.cloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.cloud.configurer.ExceptionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 项目名称: sentinel
* 包名称: com.cloud.controller
* 类名称: SentinelController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/12 14:56
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class SentinelController {
@GetMapping("/test")
@SentinelResource(value = "test",
blockHandler = "blockException",
blockHandlerClass = ExceptionUtil.class)
public String test(){
return "Hello test";
}
} - 注解属性说明:
- blockHandler——异常处理方法名称。
- blockHandlerClass——异常处理类。(如果和主方法在同一类里,可省略此属性,如果不在则必须添加此属性)
- 异常处理方法返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException。
- 启动服务访问http://localhost:18086/test,QPS超过1触发流控规则。并返回处理后的数据。
- 响应中文乱码处理:添加mvc配置类如下
-
package com.cloud.configurer;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
/**
* @author yuhuangbin
*/
@Configuration
@EnableWebMvc
public class WebMvcConfiguration implements WebMvcConfigurer {
// 使用阿里 FastJson 作为JSON MessageConverter
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.WriteNullListAsEmpty, // 集合为null时返回空集合
SerializerFeature.WriteMapNullValue, // 保留空的字段
SerializerFeature.WriteDateUseDateFormat,// 使用时间转换
SerializerFeature.WriteNullStringAsEmpty);//String null -> ""
// SerializerFeature.WriteNullNumberAsZero//Number null -> 0
// 按需配置,更多参考FastJson文档
// config.setParserConfig();
converter.setFastJsonConfig(config);
// converter.setDateFormat("yyyy-MM-dd HH:mm:ss");
converter.setDefaultCharset(Charset.forName("UTF-8"));
converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
converters.add(0, converter);
}
}
- 熔断降级规则测试:
- SentinelController添加test2方法:
-
package com.cloud.controller;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.cloud.configurer.ExceptionUtil;
import com.cloud.configurer.SentinelFallbackFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* 项目名称: sentinel
* 包名称: com.cloud.controller
* 类名称: SentinelController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/12 14:56
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class SentinelController {
@GetMapping("/test")
@SentinelResource(value = "test",
blockHandler = "blockException",
blockHandlerClass = ExceptionUtil.class)
public String test(){
return "Hello test";
}
@GetMapping("/test2/{p1}")
@SentinelResource(value = "abc0",
fallback = "test2Fallback",
fallbackClass = SentinelFallbackFactory.class)
public String test2(@PathVariable String p1){
if (StringUtils.equals("1",p1)){
throw new RuntimeException("参数为1导致异常");
}
return "Hello(你好) test1-"+p1;
}
} - 创建熔断处理类SentinelFallbackFactory,添加test2Fallback方法:
-
package com.cloud.configurer;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 项目名称: sentinel
* 包名称: com.cloud.configurer
* 类名称: SentinelFallbackFactory
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/15 14:17
* 修改人:
* 修改时间:
* 修改备注:
*/
public class SentinelFallbackFactory {
public static String test2Fallback(@PathVariable String p1,Throwable throwable) {
return "触发熔断"+throwable.getMessage();
}
} - 注解属性说明:
- value——规则资源名称。(此资源通过本地规则文件已配置,可通过控制台查看此资源规则配置)
- fallback——熔断后处理方法。(该方法必须为静态方法,可以额外多一个 Throwable 类型的参数用于接收对应的异常。)
- fallbackClass——熔断处理类。(如果和主方法在同一类里,可省略此属性,如果不在则必须添加此属性)
- 熔断处理方法返回类型需要与原方法相匹配,参数类型需要和原方法相匹配。
- 启动服务访问http://localhost:18086/test2/1,返回熔断及异常信息。
- 热点参数规则测试:
- 热点参数规则是一种精准的流控规则,它允许将规则绑定到参数上。比如方法有两个参数,我们对第一个参数进行限流,对第二个参数不限流。
- SentinelController添加test3方法:
-
package com.cloud.controller;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.cloud.configurer.ExceptionUtil;
import com.cloud.configurer.SentinelFallbackFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* 项目名称: sentinel
* 包名称: com.cloud.controller
* 类名称: SentinelController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/12 14:56
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class SentinelController {
@GetMapping("/test")
@SentinelResource(value = "test",
blockHandler = "blockException",
blockHandlerClass = ExceptionUtil.class)
public String test(){
return "Hello test";
}
@GetMapping("/test2/{p1}")
@SentinelResource(value = "abc0",
fallback = "test2Fallback",
fallbackClass = SentinelFallbackFactory.class)
public String test2(@PathVariable String p1){
if (StringUtils.equals("1",p1)){
throw new RuntimeException("参数为1导致异常");
}
return "Hello(你好) test1"+p1;
}
@GetMapping("/test3/{p1}/{p2}")
@SentinelResource(value = "aa",
blockHandler = "test3BlockException",
blockHandlerClass = ExceptionUtil.class)
public String test3(@PathVariable String p1,@PathVariable String p2){
return "Hello(你好) test3-p1="+p1+"p2="+p2;
}
} - ExceptionUtil类添加test3BlockException方法:
-
package com.cloud.configurer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
public final class ExceptionUtil {
private ExceptionUtil() {
}
public static String blockException(BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
return ex.getClass().getCanonicalName();
}
public static String test3BlockException(String p1,String p2,BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
return ex.getClass().getCanonicalName();
}
} - 热点流控规则说明:
- 查看Sentinel控制台的热点规则可知:
- 参数索引=0——第一个参数。
- 单机阀值=0——QPS超过0触发规则。
- 启动服务访问http://localhost:18086/test3/1/2,QPS超过1触发流控规则。并返回处理后的数据。
- 授权规则测试:
- Sentinel提供了RequestOriginParser接口来处理来源。如果Sentinel保护的资源被访,Sentinel就会调用RequestOriginParser的实现类去处理访问来源。
- SentinelController添加test4方法:
-
package com.cloud.controller;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.cloud.configurer.ExceptionUtil;
import com.cloud.configurer.SentinelFallbackFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* 项目名称: sentinel
* 包名称: com.cloud.controller
* 类名称: SentinelController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/12 14:56
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class SentinelController {
@GetMapping("/test")
@SentinelResource(value = "test",
blockHandler = "blockException",
blockHandlerClass = ExceptionUtil.class)
public String test() {
return "Hello test";
}
@GetMapping("/test2/{p1}")
@SentinelResource(value = "abc0",
fallback = "test2Fallback",
fallbackClass = SentinelFallbackFactory.class)
public String test2(@PathVariable String p1) {
if (StringUtils.equals("1", p1)) {
throw new RuntimeException("参数为1导致异常");
}
return "Hello(你好) test1" + p1;
}
@GetMapping("/test3/{p1}/{p2}")
@SentinelResource(value = "aa",
blockHandler = "test3BlockException",
blockHandlerClass = ExceptionUtil.class)
public String test3(@PathVariable String p1, @PathVariable String p2) {
return "Hello(你好) test3-p1=" + p1 + "p2=" + p2;
}
@GetMapping("/test4")
@SentinelResource(value = "bad",
blockHandler = "test4BlockException",
blockHandlerClass = ExceptionUtil.class)
public String test4(HttpServletRequest request) {
return "Hello(你好) test4-p1=" + request.getParameter("p1");
}
} - 创建MyRequestOriginParser类实现RequestOriginParser接口类:
-
package com.cloud.configurer;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 项目名称: sentinel
* 包名称: com.cloud.configurer
* 类名称: MyRequestOriginParser
* 类描述: 自定义来源处理规则
* 创建人: zhihong.zhu
* 创建时间: 2022/8/15 16:40
* 修改人:
* 修改时间:
* 修改备注:
*/
@Component
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
return request.getParameter("p1");
}
} - 授权规则说明:
- 查看Sentinel控制台的热点规则可知:
- 资源名=bad——规则资源名称。
- 流控应用=bcd——资源bad参数p1=bcd。
- 授权类型=黑名单——资源bad参数p1=bcd时无法访问(黑名单)
- 注意:如果配置文件spring.cloud.sentinel.filter.enabled=false,授权规则不生效。
- 启动服务访问http://localhost:18086/test4?p1=bcd,触发授权规则。并返回处理后的数据。
- OpenFeign 支持
- SentinelController添加test5方法:
-
package com.cloud.controller;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.cloud.configurer.ExceptionUtil;
import com.cloud.configurer.SentinelFallbackFactory;
import com.cloud.service.SentinelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* 项目名称: sentinel
* 包名称: com.cloud.controller
* 类名称: SentinelController
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/12 14:56
* 修改人:
* 修改时间:
* 修改备注:
*/
@RestController
public class SentinelController {
@Autowired
private SentinelService sentinelService;
@GetMapping("/test")
@SentinelResource(value = "test",
blockHandler = "blockException",
blockHandlerClass = ExceptionUtil.class)
public String test() {
return "Hello test";
}
@GetMapping("/test2/{p1}")
@SentinelResource(value = "abc0",
fallback = "test2Fallback",
fallbackClass = SentinelFallbackFactory.class)
public String test2(@PathVariable String p1) {
if (StringUtils.equals("1", p1)) {
throw new RuntimeException("参数为1导致异常");
}
return "Hello(你好) test1" + p1;
}
@GetMapping("/test3/{p1}/{p2}")
@SentinelResource(value = "aa",
blockHandler = "test3BlockException",
blockHandlerClass = ExceptionUtil.class)
public String test3(@PathVariable String p1, @PathVariable String p2) {
return "Hello(你好) test3-p1=" + p1 + "p2=" + p2;
}
@GetMapping("/test4")
@SentinelResource(value = "bad",
blockHandler = "test4BlockException",
blockHandlerClass = ExceptionUtil.class)
public String test4(HttpServletRequest request) {
return "Hello(你好) test4-p1=" + request.getParameter("p1");
}
@GetMapping("/test5/{p1}")
public String test5(@PathVariable String p1) {
return sentinelService.echo(p1);
}
} - 创建SentinelService接口类,添加echo方法:
-
package com.cloud.service;
import com.cloud.configurer.FeignFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "provider-service",fallbackFactory = FeignFallbackFactory.class)
public interface SentinelService {
@GetMapping("/echo/{string}")
String echo(@PathVariable String string);
} - 创建FeignFallbackFactory类,实现FallbackFactory接口,重写方法:
-
package com.cloud.configurer;
import com.cloud.service.SentinelService;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* 项目名称: sentinel
* 包名称: com.cloud.service
* 类名称: FeignFallbackFactory
* 类描述:
* 创建人: zhihong.zhu
* 创建时间: 2022/8/15 14:17
* 修改人:
* 修改时间:
* 修改备注:
*/
@Component
public class FeignFallbackFactory implements FallbackFactory<SentinelService> {
@Override
public SentinelService create(Throwable throwable) {
return new SentinelService() {
@Override
public String echo(String str) {
return throwable.getClass().getCanonicalName();
}
};
}
} - pom.xml添加依赖:
-
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency> - 启动类添加@EnableFeignClients注解。
- 修改provider-service服务的echo接口,添加错误代码抛出异常。
- 启动provider-service服务和该服务访问http://localhost:18086/test5/sentinel,触发熔断降级处理。
以上为入门实战的一些操作,如有初学者按照上面方式操作发现问题,请留言,看到后会修正解决。
想了解更多建议看相关源码:https://gitee.com/mirrors/Spring-Cloud-Alibaba/repository/archive/2021.0.1.0?
|