目录
注册中心 nacos | eurekaServer |zookeeper(dubbo)
远程服务调用 httpClient | RestTemplate | OpenFeign
注册中心 nacos | eurekaServer |zookeeper(dubbo)
配置中心 nacos | config Server
远程服务调用 httpClient | RestTemplate | OpenFeign
负载均衡服务 ribbon | loadbalancer
网关 zuul | gateway
熔断器 hystrix | sentinel
网关
1、统一项目入口 不需要关注每一个微服务的ip和端口,客户端只需要网关的ip和端口
2、路由功能 转发请求到达目标服务
网关的配置文件,通过路由鉴权(断言匹配,路由地址匹配,路径匹配),满足规则,转发请求到目标服务
- id: provider
# uri: http://localhost:8080/ #静态路由
uri: lb://provider #动态路由
predicates: #断言匹配
- Path=/pro/**
filters:
- RewritePath=/pro/(?<segment>.*), /p/$\{segment}
# - StripPrefix=1 #删除前缀路径 删除一层
# - PrefixPath=/p
- AddRequestParameter=name,zhangsan
3、过滤无效请求,减少对目标服务器的压力,可以同时起到安全性作用
4、负载均衡服务器 负载均衡算法(轮询、随机) nginx(反向代理和负载均衡服务器)
5、局部过滤器
修改请求路径 StripPrefix=1 删除一层路径
- RewritePath=/pro/(?<segment>.*), /p/${segment} 重写路径
添加请求头 AddRequestHeader
添加请求参数 AddRequestparameter=key,value
6、全局过滤器
package com.hl.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/*
全局过滤器
*/
@Component
public class GlobalGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("filter.........");
//放行,继续到达下一个过滤器
return chain.filter(exchange);
}
//数字越小,执行越往前
@Override
public int getOrder() {
return 0;
}
}
GlobalFilter 全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路由上。
Spring Cloud Gateway 核心的功能也是通过内置的全局过滤器来完成。
sentinel
限流、降级
资源是Sentinel
中的核心概念之一。我们说的资源,可以是任何东西,服务,服务里的方法,甚至是一段代码。最常用的资源是我们代码中的Java方法。Sentinel
提供了@SentinelResource
注解用于定义资源,并提供了AspectJ
的扩展用于自动定义资源、处理BlockException
等。
流控
QPS(每秒请求数量):当调用该api的QPS达到阈值的时候,进行限流
线程数:当调用该api的线程数达到阈值的时候,进行限流
压测工具
压力测试工具,jmeter
1、作用
为什么要使用熔断和降级
在一个分布式系统里,一个服务依赖多个服务,可能存在某个服务调用失败,比如超时、异常等,需要保证在一个依赖出问题的情况下,不会导致整体服务失败。
2、熔断
暂时中断程序,稍后可继续访问。
3、降级
走降级方案,返回之前指定的失败给用户。(柔性失败)
避免服务整体崩溃,系统宕机。让当前方法失败,返回失败响应给调用者。
配置启动sentinel
1、下载安装包 https://github.com/alibaba/Sentinel/releases
2、启动(win+r cmd)
java -Dserver.port=8718 -jar 安装包路径/安装包名称
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:\IDEA2024\sentinel-dashboard-1.8.0.jar
默认用户名:sentinel
密码: sentinel
注册微服务到sentinel
配置文件:
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
# Nacos认证信息
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos
spring.cloud.nacos.config.contextPath=/nacos
# 设置配置中心服务端地址
spring.cloud.nacos.config.server-addr=localhost:8848
# Nacos 配置中心的namespace。需要注意,如果使用 public 的 namcespace ,请不要填写这个值,直接留空即可
# spring.cloud.nacos.config.namespace=
spring.config.import=nacos:product.yml?refresh=true
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=product
# Nacos认证信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
# 应用服务 WEB 访问端口
server.port=8088
# Sentinel 控制台地址
spring.cloud.sentinel.transport.dashboard=localhost:8718
# 取消Sentinel控制台懒加载
# 默认情况下 Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包
# 配置 sentinel.eager=true 时,取消Sentinel控制台懒加载功能
spring.cloud.sentinel.eager=true
# 如果有多套网络,又无法正确获取本机IP,则需要使用下面的参数设置当前机器可被外部访问的IP地址,供admin控制台使用
# spring.cloud.sentinel.transport.client-ip=
测试流控
@RestController
@RequestMapping("/demo")
public class DemoController {
@Autowired
private DemoService demoService;
@GetMapping("/bonjour/{name}")
public String apiSayHelloLocal(@PathVariable String name) throws Exception {
for(int i = 1;i<10;i++){
String s1 = demoService.bonjour(name);
System.out.println(s1);
}
return ff;
}
@Service
public class DemoService {
@SentinelResource(value = "DemoService#bonjour",
defaultFallback = "bonjourFallback")
public String bonjour(String name) throws Exception {
return "Bonjour, " + name;
}
public String bonjourFallback(Throwable t) {
if (BlockException.isBlockException(t)) {
return "Blocked by Sentinel: " + t.getClass().getSimpleName();
}
return "Oops, failed: " + t.getClass().getCanonicalName();
}
}
降级测试
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hl.product.demos.sentinel;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Eric Zhao
*/
@RestController
@RequestMapping("/demo")
public class DemoController {
@Autowired
private DemoService demoService;
@GetMapping("/bonjour/{name}")
public String apiSayHelloLocal(@PathVariable String name) throws Exception {
boolean flag = true;
String s0 = demoService.bonjour(name,flag);
System.out.println(s0);
flag = false;
for(int i = 1;i<10;i++){
String s1 = demoService.bonjour(name,flag);
System.out.println(s1);
}
System.out.println("---------------");
flag = true;
String ff = demoService.bonjour(name,flag);
System.out.println(ff);
return ff;
}
}
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hl.product.demos.sentinel;
import org.springframework.stereotype.Service;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
/**
* @author Eric Zhao
*/
@Service
public class DemoService {
@SentinelResource(value = "DemoService#bonjour",
defaultFallback = "bonjourFallback")
public String bonjour(String name,Boolean flag) throws Exception {
if(flag == false){
throw new Exception("异常。。。。。。");
}
return "Bonjour, " + name;
}
public String bonjourFallback(Throwable t) {
if (BlockException.isBlockException(t)) {
return "Blocked by Sentinel: " + t.getClass().getSimpleName();
}
return "Oops, failed: " + t.getClass().getCanonicalName();
}
}
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hl.product.demos.sentinel;
import java.io.PrintWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
/**
* @author Eric Zhao
*/
@Configuration
public class SentinelWebConfig {
@Bean
public BlockExceptionHandler sentinelBlockExceptionHandler() {
return (request, response, e) -> {
// 429 Too Many Requests
response.setStatus(429);
PrintWriter out = response.getWriter();
out.print("Oops, blocked by Sentinel: " + e.getClass().getSimpleName());
out.flush();
out.close();
};
}
}
流控模式详解
直接模式
概念:直接模式是最基本的流控模式。它是对当前资源(接口或方法)本身的请求进行流量控制。当请求到达被保护的资源时,Sentinel 会直接根据设置的规则(如 QPS 阈值、并发线程数阈值等)来判断是否允许该请求通过。
示例:假设有一个用户登录接口
/login
,设置其 QPS(每秒查询率)的直接流控阈值为 100。这意味着当每秒请求该/login
接口的次数超过 100 时,Sentinel 就会开始进行流量控制,可能会对超出的请求进行限流(如直接拒绝、排队等待等处理方式)。应用场景:适用于对单一资源的流量进行简单直接的控制,比如限制某个核心 API 接口的访问频率,以防止该接口被过度调用而导致系统崩溃或性能下降。
关联模式
概念:关联模式是一种相对复杂的流控方式。它主要用于当两个资源之间存在关联关系时,通过对其中一个资源的流量控制来间接控制另一个资源的流量。当关联的资源 A 达到流控阈值时,就会对目标资源 B 进行流量控制。
示例:考虑一个电商系统,有一个商品详情接口
/product/detail
和一个购买接口/product/buy
。这两个接口是关联的,因为用户在查看商品详情后可能会进行购买。如果设置/product/detail
接口与/product/buy
接口为关联模式,并且当/product/detail
的 QPS 达到某个阈值(如 50)时,开始对/product/buy
接口进行流量控制,比如限制/product/buy
接口的 QPS 为 30。这样可以防止在商品详情页被大量访问时,购买接口被过度请求而导致库存系统等后端服务压力过大。应用场景:常用于存在先后调用关系或者关联关系的资源之间,例如在一个业务流程中,前一个步骤的操作频繁可能会导致后一个步骤的服务压力过大,通过关联模式可以提前控制后一个步骤的流量。
链路模式
概念:链路模式主要是从调用链路的角度进行流量控制。它会根据请求的调用链路来判断是否进行流量控制。在微服务架构或者复杂的分布式系统中,一个请求可能会经过多个服务和方法的调用链路。Sentinel 链路模式可以针对特定的调用链路进行流控,而不是简单地基于资源本身。
示例:假设有一个分布式系统,包括服务 A、服务 B 和服务 C。服务 A 调用服务 B,服务 B 又调用服务 C。在服务 B 中有一个方法
methodB
,在链路模式下,可以针对从服务 A - 服务 B - 服务 C 这个特定链路中对methodB
进行流量控制。如果在其他链路中也调用了methodB
,只要不是这个特定的链路,就不受这个链路模式下的流控规则限制。应用场景:特别适用于微服务架构和复杂的分布式系统,能够精确地对特定的调用链路进行流量管理,避免因为某个链路的流量过大而影响整个系统的稳定性,同时又不会影响其他链路对相同资源的正常调用。
Sentinel 流控效果详解
Sentinel 流控效果主要有快速失败、Warm Up 和排队等待三种,它们的区别如下:
快速失败(Fail Fast)
概念
当请求流量超过设定的阈值时,直接拒绝超出的请求。这种方式能够快速地对超出流量进行限制,以保护后端服务。
示例
假设对一个 API 接口设置的 QPS(每秒查询率)阈值为 100,当每秒请求数达到 101 时,第 101 个及之后的请求会立即被拒绝,并返回相应的限流错误信息,如 HTTP 状态码为 429(Too Many Requests)。
应用场景
适用于对系统资源消耗较大,且对请求的实时性要求不是特别高的接口。例如,一些批量数据处理接口,当流量过载时,直接拒绝新请求可以避免系统因过度负载而崩溃。同时,对于一些非关键业务的接口,快速失败可以简单有效地控制流量,确保核心业务的稳定运行。
Warm Up(预热)
概念
预热模式是一种渐进式的流量控制方式。它会根据设定的预热时长和阈值,在系统启动或者流量突然增加时,缓慢地增加允许通过的流量,直到达到设定的最大阈值。这样可以避免系统在冷启动或者流量突发时,因为瞬间的高负载而出现性能问题。
示例
假设设置一个接口的 QPS 阈值为 100,预热时长为 5 分钟。系统启动时,开始允许通过的 QPS 可能只有 20,然后随着时间逐渐增加,在 5 分钟后达到 100。如果在预热期间流量就超过了当前允许的阈值,超出部分会被拒绝。
应用场景
常用于系统冷启动后需要逐步加载资源(如缓存预热、数据库连接池初始化等)的场景。同时,对于一些具有性能瓶颈,需要一定时间来适应高流量的服务,如数据库密集型的接口或者计算资源消耗大的算法接口,Warm Up 模式可以让系统有一个缓冲阶段,平稳地过渡到高流量状态。
排队等待(Rate Limiter)
概念
当请求流量超过阈值时,不是直接拒绝请求,而是让请求进行排队等待。Sentinel 会按照请求的到达顺序,将超出阈值部分的请求放入队列中,当系统有能力处理新请求时,再从队列中取出请求进行处理。这样可以保证请求在一定程度上的公平性,避免大量请求被直接拒绝。
示例
设一个接口的 QPS 阈值为 100,队列长度为 200。当每秒请求数达到 120 时,其中 20 个请求会被放入队列中排队等待。如果系统处理速度能够跟上,这些排队的请求会在后续得到处理。
应用场景
适用于对请求响应时间要求相对不那么严格,且希望尽可能处理所有请求的场景。比如,对于一些异步任务处理的接口,或者消息队列消费者接口,排队等待可以确保请求不会因为瞬间流量高峰而丢失,同时可以在一定程度上平滑流量,提高系统整体的资源利用率。
hystrix 熔断器
熔断和降级
熔断(目标服务不让访问,暂时中断)
降级(目标资源服务访问时,走降级方法,备份方法,返回特定信息给调用者,避免无限制等待)
熔断器 有三个状态: 打开 半开 关闭