仓库地址 视频地址
分布式:一个大应用分拆成多个小应用分布部署在各个机器上
微服务(Spring Boot)(自治):独立部署,数据隔离,单点故障
注册中心(Spring Cloud Alibaba Nacos):服务发现,服务注册,配置中心
远程调用(Spring Cloud Open Feign)
服务熔断(Spring Cloud Alibaba Sentinel):快速失败,服务雪崩
网关(Spring Cloud Gateway):请求的路由,负载均衡
分布式事务(Spring Cloud Alibaba Seata)
Nacos 注册中心 将微服务注册在nacos上
1 2 3 4 5 6 7 8 9 10 11 spring: application: name: service-product cloud: nacos: server-addr: 127.0 .0 .1 :8848 config: import-check: enabled: false server: port: 9000
并在主类中添加@EnableDiscoveryClient
1 2 3 4 5 6 7 8 @EnableDiscoveryClient @SpringBootApplication public class ProductMainApplication { public static void main (String[] args) { SpringApplication.run(ProductMainApplication.class, args); } }
A 服务调用 B 服务,A 服务并不知道 B 服务当前在哪几台服务器有,哪些正常的,哪些服务已经下线。解决这个问题可以引入注册中心; 如果某些服务下线,我们其他人可以实时的感知到其他服务的状态,从而避免调用不可用的服务
RestTemplate 某些微服务可能有多个实例部署在不同的服务器上,通过负载均衡算法调用微服务减轻服务器的压力
1 2 3 4 5 6 7 8 9 10 11 12 @Autowired LoadBalancerClient loadBalancerClient; private Product getProductFromRemoteWithLoadBalance (Long productId) { ServiceInstance instance = loadBalancerClient.choose("service-product" ); String address = "http://" + instance.getHost() + ":" + instance.getPort() + "/product/" + productId; RestTemplate restTemplate = new RestTemplate (); log.info("远程请求:{}" , address); return restTemplate.getForObject(address, Product.class); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Configuration public class OrderConfig { @LoadBalanced @Bean RestTemplate restTemplate () { return new RestTemplate (); } } private Product getProductFromRemoteWithLoadBalanceAnnotation (Long productId) { String address = "http://service-product/product/" + productId; log.info("远程请求:{}" , address); return restTemplate.getForObject(address, Product.class); }
restTemplate 第一次发起请求时,会首先向注册中心获取微服务访问地址列表,存入实例缓存,此后实例缓存 会与注册中心 实时更新,然后restTemplate 根据负载均衡算法 选择实例发起请求访问
配置中心 集中管理配置:将所有服务的配置信息统一管理,方便运维人员进行集中维护和管理 动态配置更新:当配置信息发生变化时,应用程序可以实时感知并自动更新配置,无需重启服务
名称空间(Namespace):区分多套环境
分组(Group):区分多种微服务
先在nacos中编写配置
然后再application.yml中导入配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 server: port: 8000 spring: profiles: active: dev application: name: service-order cloud: nacos: server-addr: 127.0 .0 .1 :8848 config: import-check: enabled: false namespace: ${spring.profiles.active:test} --- spring: config: import: - nacos:common.yml?group=order - nacos:database.yml?group=order activate: on-profile: dev --- spring: config: import: - nacos:common.yml?group=order - nacos:database.yml?group=order activate: on-profile: test --- spring: config: import: - nacos:common.yml?group=order - nacos:database.yml?group=order activate: on-profile: prod
最后可以向正常的配置信息使用
1 2 3 4 5 6 7 8 @RefreshScope @RestController() public class OrderController { @Value("${order.timeout}") String orderTimeout; @Value("${order.auto-confirm}") String orderAutoConfirm; }
OpenFeign 远程调用 先在主类加上@EnableFeignClients开启开启Feign远程调用功能
1 2 3 4 5 6 7 8 9 @FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class) public interface ProductFeignClient { @GetMapping("/product/{id}") Product getProductById (@PathVariable("id") Long id) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component public class ProductFeignClientFallback implements ProductFeignClient { @Override public Product getProductById (Long id) { Product product = new Product (); product.setId(id); product.setPrice(new BigDecimal ("100.00" )); product.setProductName("兜底数据 - " + product.getId()); product.setNum(10 ); return product; } }
超时控制 1 2 3 4 5 6 7 8 9 10 11 spring: cloud: openfeign: client: config: default: connect-timeout: 3000 read-timeout: 3000 service-product: connect-timeout: 3000 read-timeout: 3000
Sentinel Sentinel 能在流量控制、熔断降级、系统负载保护等方面保障服务的稳定性与可靠性
定义资源:
主流框架自动适配(Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor);所有Web接口均为资源
编程式:SphU API
声明式:@SentinelResource
定义规则:
流量控制规则
熔断降级规则
系统保护规则
来源访问控制规则
热点参数规则
设置sentinel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 spring: cloud: openfeign: client: config: default: connect-timeout: 3000 read-timeout: 3000 service-product: connect-timeout: 3000 read-timeout: 3000 sentinel: transport: dashboard: 127.0 .0 .1 :8858 eager: true web-context-unify: false feign: sentinel: enabled: true
1 2 3 4 5 6 7 spring: cloud: sentinel: transport: dashboard: 127.0 .0 .1 :8858 eager: true web-context-unify: false
spring.cloud.sentinel.transport.dashboard:指定 Sentinel 控制台的地址。这里设置为 127.0.0.1:8858,表示应用会将自身的监控信息发送到本地运行在 8858 端口的 Sentinel 控制台,方便在控制台上查看应用的流量、熔断等监控数据,并进行规则配置。
spring.cloud.sentinel.eager:设置为 true 表示 Sentinel 在应用启动时就会主动连接 Sentinel 控制台,而不是在第一次请求时才去连接,这样可以更快地将应用的信息注册到控制台上。
spring.cloud.sentinel.web-context-unify:设置为 false 表示关闭 Web 上下文统一处理。在这种情况下,Sentinel 会为每个不同的请求路径创建独立的上下文,从而可以针对不同的请求路径设置独立的限流规则和进行精准的流量统计。
1 2 3 feign: sentinel: enabled: true
feign.sentinel.enabled:设置为 true 表示开启 Feign 与 Sentinel 的集成。这样在使用 OpenFeign 进行远程调用时,Sentinel 会对这些调用进行监控和保护。当远程调用出现异常、超阈值等情况时,Sentinel 可以根据配置的规则进行熔断、限流等操作,从而提高系统的稳定性和可靠性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @SentinelResource(value = "createOrder", blockHandler = "createOrderFallback") @Override public Order createOrder (Long productId, Long userId) { Order order = new Order (); Product product = productFeignClient.getProductById(productId); order.setUserId(userId); order.setTotalAmount(product.getPrice().multiply(new BigDecimal (product.getNum()))); order.setId(1L ); order.setAddress("127.0.0.1" ); order.setProductList(Arrays.asList(product)); return order; }
以下是为你补充完整的关于 Sentinel 规则中流控、熔断、热点规则相关的详细介绍:
规则 流控 流量控制(简称流控)是一种通过对系统的输入流量进行调节,以确保系统在承受范围内稳定运行的机制。在 Sentinel 中,流控规则用于限制某个资源(如接口、方法等)的请求流量,防止过多的请求涌入导致系统过载、响应变慢甚至崩溃。
资源名 :唯一标识一个资源,通常为接口的路径或方法名。
阈值类型 :
QPS(每秒查询率) :限制资源每秒的请求数量。例如,将某个接口的 QPS 阈值设置为 100,表示该接口每秒最多处理 100 个请求。
线程数 :限制同时处理该资源请求的线程数量。当并发线程数达到阈值时,新的请求将被拒绝。
流控模式 :
直接 :这是 Sentinel 流控的默认模式,直接对配置规则的资源进行流量控制。一旦该资源的请求流量(根据设定的阈值类型,如 QPS 或线程数)超过设定的阈值,就会立即触发限流操作
关联 :该模式关注两个资源之间的关联关系。当关联资源的请求流量超过阈值时,会对当前配置规则的资源进行限流。即一个资源的流量情况会影响另一个关联资源的访问
链路 :根据请求的调用链路来进行流量控制。它只对从指定入口资源进入的请求进行限流,也就是说,只有当请求是从特定的入口资源发起并访问到当前配置规则的资源时,才会应用该限流规则
控制效果 :
快速失败 :当请求超过阈值时,直接拒绝该请求,并抛出 FlowException 异常。
Warm Up :系统刚启动时,允许的请求量较低,随着时间推移逐渐增加到设定的阈值。适用于系统启动后需要一段时间来达到最佳性能的场景。
排队等待 :将超过阈值的请求放入队列中,按照固定的速率依次处理,以平滑请求流量。
熔断 熔断机制是一种在系统出现异常或故障时,自动切断对某个资源的访问,以防止故障扩散和保护系统稳定性的策略。当资源的调用出现大量错误或响应时间过长时,Sentinel 会触发熔断规则,暂时拒绝后续对该资源的请求,直到一段时间后再尝试恢复。
资源名 :标识要进行熔断保护的资源。
熔断策略 :
慢调用比例 :当资源的响应时间超过设定的慢调用阈值,并且在统计时长内慢调用的比例超过设定的阈值时,触发熔断。例如,慢调用阈值为 500 毫秒,统计时长为 10 秒,慢调用比例阈值为 0.5,表示在 10 秒内,如果响应时间超过 500 毫秒的请求比例超过 50%,则触发熔断。
异常比例 :当资源的异常调用比例超过设定的阈值时,触发熔断。例如,异常比例阈值为 0.2,表示在统计时长内,异常调用的请求比例超过 20% 时,触发熔断。
异常数 :当资源在统计时长内的异常调用数量超过设定的阈值时,触发熔断。
熔断时长 :熔断触发后,拒绝请求的持续时间。
最小请求数 :在进行熔断判断前,需要满足的最小请求数量。只有当请求数达到该值时,才会进行熔断规则的判断。
熔断降级 断路器 :工作原理 :熔断与兜底 :
热点 热点参数限流是一种针对资源调用中某些特定参数值的限流方式。在实际业务场景中,某些参数值可能会被频繁访问,成为热点数据。Sentinel 的热点规则可以对这些热点参数值进行单独的限流控制,以避免因热点数据的高并发访问导致系统性能下降。
资源名 :标识要进行热点限流的资源。
参数索引 :指定要进行限流的参数在方法参数列表中的索引位置。例如,方法 public void doSomething(String param1, int param2) 中,如果要对 param2 进行热点限流,则参数索引为 1。
单机阈值 :针对每个热点参数值的单机限流阈值。
统计窗口时长 :统计热点参数访问次数的时间窗口长度,单位为秒。
例外项 :可以针对某些特定的参数值设置不同的限流阈值。例如,对于大部分参数值,单机阈值为 100,但对于参数值为 1001 的情况,单机阈值可以设置为 200。
抛出异常 如果资源违反规则会抛出异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Component public class MyBlockExceptionHandler implements BlockExceptionHandler { private ObjectMapper objectMapper = new ObjectMapper (); @Override public void handle (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String s, BlockException e) throws Exception { httpServletResponse.setStatus(429 ); httpServletResponse.setContentType("application/json;charset=utf-8" ); PrintWriter writer = httpServletResponse.getWriter(); R error = R.error(500 , s + "被Sentinel限制了,原因:" + e.getClass()); String json = objectMapper.writeValueAsString(error); writer.write(json); writer.flush(); writer.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @SentinelResource(value = "createOrder", blockHandler = "createOrderFallback") @Override public Order createOrder (Long productId, Long userId) { Order order = new Order (); Product product = productFeignClient.getProductById(productId); order.setUserId(userId); order.setTotalAmount(product.getPrice().multiply(new BigDecimal (product.getNum()))); order.setId(1L ); order.setAddress("127.0.0.1" ); order.setProductList(Arrays.asList(product)); return order; } public Order createOrderFallback (Long productId, Long userId, BlockException e) { Order order = new Order (); order.setId(0L ); order.setUserId(userId); order.setTotalAmount(BigDecimal.ZERO); order.setNickName("未知用户" ); order.setAddress("异常信息:" + e.getClass()); return order; }
Gateway 所有业务集群请求的入口,前端不需要记住每一个微服务的地址
统一入口
请求路由
负载均衡
流量控制
身份认证
协议转换
系统监控
安全防护
application.yml
1 2 3 4 5 6 7 8 9 10 spring: application: name: gateway cloud: nacos: server-addr: 127.0 .0 .1 :8848 profiles: include: route server: port: 80
application-route.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 spring: cloud: gateway: globalcors: cors-configurations: '[/**]' : allowed-origin-patterns: '*' allowed-headers: '*' allowed-methods: '*' routes: - id: order-service uri: lb://service-order predicates: - name: Path args: patterns: /api/order/** filters: - RewritePath=/api/order/?(?<segment>.*), /$\{segment} - OnceToken=X-Response-Token, uuid - id: product-route uri: lb://service-product predicates: - Path=/api/product/** filters: - RewritePath=/api/product/?(?<segment>.*), /$\{segment} - id: bing-route uri: https://cn.bing.com/ predicates: - name: Path args: patterns: /search - name: Vip args: param: user value: lei
自定义断言 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Component public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory <VipRoutePredicateFactory.Config> { public VipRoutePredicateFactory () { super (Config.class); } @Override public List<String> shortcutFieldOrder () { return Arrays.asList("param" , "value" ); } @Override public Predicate<ServerWebExchange> apply (Config config) { return new GatewayPredicate () { @Override public boolean test (ServerWebExchange serverWebExchange) { ServerHttpRequest request = serverWebExchange.getRequest(); HttpCookie first = request.getCookies().getFirst(config.param); if (first != null && first.equals(config.value)) { return true ; } return false ; } }; } @Validated public static class Config { @NotEmpty private String param; @NotEmpty private String value; public String getParam () { return param; } public void setParam (String param) { this .param = param; } public String getValue () { return value; } public void setValue (String value) { this .value = value; } } }
1 2 3 4 5 6 7 - id: bing-route uri: https://cn.bing.com/ predicates: - name: Vip args: param: user value: lei
此断言判断https://cn.bing.com/user=lei,如果user不等于lei则拒绝访问
过滤器的基本使用 1 2 3 filters: - RewritePath=/api/order/?(?<segment>.*), /$\{segment} - AddResponseHeader=X-Response-Abc, 123
1 2 default-filters: - AddResponseHeader=X-Response-Abc, 123
GlobalFilter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Slf4j @Component public class RtGlobalFilter implements GlobalFilter , Ordered { @Override public Mono<Void> filter (ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String uri = request.getURI().toString(); long start = System.currentTimeMillis(); log.info("请求{}开始,时间:{}" , uri, start); Mono<Void> filter = chain.filter(exchange) .doFinally(signal -> { long end = System.currentTimeMillis(); log.info("请求{}结束,时间:{},总耗时:{}" , uri, end, end - start); }); return filter; } @Override public int getOrder () { return -1 ; } }
自定义过滤器工厂 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Component public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply (NameValueConfig config) { return new GatewayFilter () { @Override public Mono<Void> filter (ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange).then(Mono.fromRunnable(() -> { ServerHttpResponse response = exchange.getResponse(); HttpHeaders headers = response.getHeaders(); String name = config.getName(); String value = config.getValue(); if ("uuid" .equalsIgnoreCase(value)) { value = UUID.randomUUID().toString(); } if ("jwt" .equalsIgnoreCase(value)) { value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ3Vlc3QiLCJyb2xlIjoidmlzaXRvciJ9.AwQV8c2xJw2V1f7l8p7x9v9m6n7k5y7u7i7w6f7a7d7e7s7t7" ; } headers.add(name, value); })); } }; } }
1 2 filters: - OnceToken=X-Response-Token, uuid
Seata
TC:事务协调者
TM:事务管理器
RM:资源管理器
在每个微服务中,添加配置信息
1 2 3 4 5 6 7 8 9 10 service { #transaction service group mapping vgroupMapping.default_tx_group = "default" #only support when registry.type=file, please don't set multiple addresses default.grouplist = "127.0.0.1:8091" #degrade, current not support enableDegrade = false #disable seata disableGlobalTransaction = false }
在主事务中添加 ==@GlobalTransactional== 注解,在各个分事务添加 ==@Transactional== 注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Service public class BusinessServiceImpl implements BusinessService { @Autowired StorageFeignClient storageFeignClient; @Autowired OrderFeignClient orderFeignClient; @GlobalTransactional @Override public void purchase (String userId, String commodityCode, int orderCount) { storageFeignClient.deduct(commodityCode, orderCount); orderFeignClient.create(userId, commodityCode, orderCount); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Service public class OrderServiceImpl implements OrderService { @Autowired OrderTblMapper orderTblMapper; @Autowired AccountFeignClient accountFeignClient; @Transactional @Override public OrderTbl create (String userId, String commodityCode, int orderCount) { int orderMoney = calculate(commodityCode, orderCount); accountFeignClient.debit(userId, orderMoney); OrderTbl orderTbl = new OrderTbl (); orderTbl.setUserId(userId); orderTbl.setCommodityCode(commodityCode); orderTbl.setCount(orderCount); orderTbl.setMoney(orderMoney); orderTblMapper.insert(orderTbl); return orderTbl; } private int calculate (String commodityCode, int orderCount) { return 9 *orderCount; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Service public class StorageServiceImpl implements StorageService { @Autowired StorageTblMapper storageTblMapper; @Transactional @Override public void deduct (String commodityCode, int count) { storageTblMapper.deduct(commodityCode, count); if (count == 5 ) { throw new RuntimeException ("库存不足" ); } } }
1 2 3 4 5 6 @Transactional @Override public void debit (String userId, int money) { accountTblMapper.debit(userId,money); }
二阶提交协议流程