Nacos
概念
- 下载:github.com/alibaba/nac…
- 启动:
startup.cmd -m standalone - 访问:http://localhost:8848/nacos/ 账号密码:nacos
- 配置文件通过
namespace、group和dataId来唯一确定 - dataId的格式:
${spring.application.name}-${spring.profiles.active}.${file-extension}spring.profiles.active为空时,连接符-也将不存在${file-extension}:默认为properties
- 拉取多个共享文件,要在同一命名空间下,不能跨命名空间
- 注意springboot和springcloud的版本对应
配置中心
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.0.1.0</version>
</dependency>
- 完整
bootstrap.properties配置:
spring.application.name=testccb
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos
spring.cloud.nacos.config.server-addr=192.168.1.211:8848
spring.cloud.nacos.config.namespace=4f4b-363e-47a5-8a-dbd288c
spring.cloud.nacos.config.group=com.iotsh
# 拉取共享的配置
spring.cloud.nacos.config.extension-configs[0].data-id=testccb.properties
spring.cloud.nacos.config.extension-configs[0].group=ccb
spring.cloud.nacos.config.extension-configs[0].refresh=true
注册中心
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.0.1.0</version>
</dependency>
- 配置文件
spring.cloud.nacos.discovery.server-addr=192.168.1.211:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
spring.cloud.nacos.discovery.namespace=ed4b94b3-deff-49f1-8973-34029756083d
- 主启动类:
@EnableDiscoveryClient
OpenFeign
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 主启动类:
@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(CategoryApplication.class,args);
}
}
- 配置文件
feign:
client:
config:
default:
connect-timeout: 5000
read-timeout: 5000
- 创建
FeignClient(调用者),注意传参
@FeignClient(value = "product") // 被调用的服务名称
public interface ProductClient {
@RequestMapping("/status") // 被调用的服务接口
public String status();
@GetMapping("/paramRequest")
public String paramRequest(@RequestParam("id") Integer id, @RequestParam("name") String name);
@PostMapping("/bodyRequest")
public String bodyRequest(@RequestBody User user);
}
- Controller中使用
@RestController
public class UserController {
@Autowired
private ProductClient productClient;
@GetMapping("/find")
public String find(){
return productClient.status();
}
}
- 被调用的服务
@RestController
public class ProductController {
// 没有参数
@RequestMapping("/status")
public String status(){
return "client b is ok!";
}
// param参数
@GetMapping("/paramRequest")
public String paramRequest(@RequestParam("id") Integer id, @RequestParam("name") String name){
return "paramRequest method is ok!, id:" + id +", name:"+ name;
}
// body有参数
@PostMapping("/bodyRequest")
public String bodyRequest(@RequestBody User user){
return "bodyRequest method is ok!, user:" + user.toString();
}
}
Sentinel
Sentinel提供了两个服务组件:控制台(Dashboard)和核心库,控制台用来监控流量调用情况;核心库用来实现服务熔断降级
Dashboard
-
下载官方提供的Sentinel Dashboard的jar包:github.com/alibaba/Sen…
-
java -Dserver.port=9191 -jar sentinel-dashboard-1.7.2.jar -
访问localhost:9191,用户名密码都是
sentinel,微服务被首次调用的时候向Dashboard发送心跳信息
创建项目
- 引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 修改配置
server.port=9642
spring.cloud.nacos.server-addr=localhost:8848
spring.cloud.sentinel.transport.dashboard=localhost:9191 # 配置dashboard连接地址
- 主启动加注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelApplication.class,args);
}
}
服务降级
@SentinelResource注解 只是 “装了个开关和应急灯”,不管是熔断还是降级结果都是“拉闸”,只是拉闸原因不同- 熔断是为了防止故障扩散,切断对下游的调用;降级是 主动舍弃非核心功能,把资源留给核心接口
@GetMapping("/user/info")
@SentinelResource(value = "app", fallback = "innerBlock")
public String getUserInfo() throws InterruptedException {
Thread.sleep(800);
System.out.println("UserController::/userInfo");
return "{name:ccb, age: 22}";
}
public String innerBlock(){ // 返回类型、参数与原方法一致
return "innerBlock...限流";
}
Gateway
作用:路由转发 + 过滤器
使用
-
引入依赖,consul里面集成了ribbon,网关可以实现转发的负载均衡
-
gateway使用了Flux异步非阻塞模型,不要添加spring-boot-starter-web,会冲突
<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> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> -
主启动类:@EnableDiscoveryClient
-
配置路由规则
spring: application: name: gateway cloud: consul: host: localhost # 向注册中心注册 port: 8500 gateway: routes: - id: route_product uri: lb://product # 2.转发到对应服务,product为服务名字 predicates: # 断言 - Path=/product/**,/list # 1.匹配到对应路径,可以配置多个 - id: route_category uri: lb://category predicates: - Path=/category server: port: 9500
过滤器
客户端请求各个服务时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等,可以分为GatewayFilter过滤器和GlobalFilter过滤器
- GatewayFilter:在配置文件中配置,作用在具体路由上
- GlobalFilter:作用在所有的路由上
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter,请求路由后,会将当前路由过滤器、DefaultFilter、GlobalFilter,合并到一个过滤器链中,排序后依次执行每个过滤器
排序规则:order值越小,优先级越高,路由过滤器和defaultFilter的order由Spring指定,当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行
GatewayFilter
-
常见过滤器:
- PrefixPath=/emp // 增加前缀,在请求的路径前(端口号后)加前缀 - StripPrefix=1 // 去掉前缀,数字代表去掉几级前缀,例:/emp/product/ 变为 /product -
StripPrefix过滤器使用spring: application: name: gateway cloud: consul: host: localhost port: 8500 gateway: routes: - id: route_category uri: lb://category predicates: - Path=/xxxxxxxx/** filters: - StripPrefix=1访问路径:http://localhost:9500/xxx/category 匹配到指定路由后会将前缀去掉一级:/xxx/category ==> /category
GlobalFilter
@Component
public class DemoFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1 获得请求路径
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 2 处理异常,拒绝放行
if("/echo/abc".equals(path)) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
DataBuffer wrap = response.bufferFactory().wrap("没有权限".getBytes(StandardCharsets.UTF_8));
return exchange.getResponse().writeWith(Flux.just(wrap));
}
// 3 放行
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
}));
}
// 编写过滤器优先级, 值越小优先级越高
@Override
public int getOrder() {
return 1;
}
}
查看路由
management:
endpoints:
web:
exposure:
exclude: "*"
通过访问该地址查看 http://localhost:9500/actuator/gateway/routes,改成网关的端口,效果如下