分布式基础概念

微服务

微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己进程中,并使用轻量级机制通信,通常是 HTTP API。这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。

简而言之:拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行。

集群&分布式&节点

集群是个物理形态,分布式是个工作方式。

只要是一堆机器,就可以叫集群,他们是不是一起协作着干活,这个谁也不知道;

“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”

分布式系统(distributed system)是建立在网络之上的软件系统。

分布式是指将不同的业务分布在不同的地方。

集群指的是将几台服务器集中在一起,实现同一业务。

例如:京东是一个分布式系统,众多业务运行在不同的机器,所有业务构成一个大型的业务集群。每一个小的业务,比如用户系统,访问压力大的时候一台服务器是不够的。我们就应该将用户系统部署到多个服务器,也就是每一个业务系统也可以做集群化

分布式中的每一个节点,都可以做集群。 而集群并不一定就是分布式的。

节点:集群中的一个服务器

远程调用

在分布式系统中,各个服务可能处于不同主机,但是服务之间不可避免的需要互相调用,我们称为远程调用。

SpringCloud 中使用 HTTP+JSON 的方式完成远程调用

负载均衡

分布式系统中,A 服务需要调用 B 服务,B 服务在多台机器中都存在,A 调用任意一个服务器均可完成功能。

为了使每一个服务器都不要太忙或者太闲,我们可以负载均衡的调用每一个服务器,提升网站的健壮性。

常见的负载均衡算法:

  1. 轮询:为第一个请求选择健康池中的第一个后端服务器,然后按顺序往后依次选择,直到最后一个,然后循环。
  2. 最小连接:优先选择连接数最少,也就是压力最小的后端服务器,在会话较长的情况下可以考虑采取这种方式。
  3. ip_hash:根据请求源的 IP 的hash来选择要转发的服务器。这种方式可以一定程度上保证特定用户能连接到相同的服务器。如果你的应用需要处理状态而要求用户能连接到和之前相同的服务器,可以考虑采取这种方式

服务注册/发现&注册中心

A 服务调用 B 服务,A 服务并不知道 B 服务当前在哪几台服务器有,哪些正常的,哪些服务已经下线。解决这个问题可以引入注册中心;

如果某些服务下线,我们其他人可以实时的感知到其他服务的状态,从而避免调用不可用的服务

配置中心

每一个服务最终都有大量的配置,并且每个服务都可能部署在多台机器上。我们经常需要变更配置,我们可以让每个服务在配置中心获取自己的配置。

配置中心用来集中管理微服务的配置信息

服务熔断&服务降级

在微服务架构中,微服务之间通过网络进行通信,存在相互依赖,当其中一个服务不可用时,有可能会造成雪崩效应。要防止这样的情况,必须要有容错机制来保护服务。

API 网关

在微服务架构中,API Gateway 作为整体架构的重要组件,它抽象了微服务中都需要的公共功能,同时提供了客户端负载均衡服务自动熔断灰度发布统一认证限流流控日志统计等丰富的功能,帮助我们解决很多 API 管理难题。

Spring Cloud & Spring Cloud Alibaba

基础环境

技术搭配

Spring Cloud 系列: 官⽹:https://spring.io/projects/spring-cloud

  1. 远程调⽤:OpenFeign
  2. ⽹关:Gateway

Spring Cloud Alibaba 系列: 官⽹:https://sca.aliyun.com/

  1. 注册中⼼/配置中⼼:Nacos
  2. 服务保护:Sentinel
  3. 分布式事务:Seata

工程架构

pom.xml

创建⽗项⽬引⼊公共依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<modelVersion>4.0.0</modelVersion>

<groupId>com.lfy</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0</version>
<packaging>pom</packaging>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud.version>2023.0.3</spring-cloud.version>
<spring-cloud-alibaba.version>2023.0.3.2</spring-cloud-alibaba.version>
</properties>

<dependencyManagement>
<dependencies>
<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>

</project>

Nacos:注册中心

快速入门

入门

官⽹:https://nacos.io/zh-cn/docs/v2/quickstart/quick-start.html

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的⾸字⺟简称,⼀个更易于构建云原⽣应⽤的动态服务发现配置管理服务管理平台。

安装

docker run -d -p 8848:8848 -p 9848:9848 -e MODE=standalone --name nacos nacos/nacos-server:v2.4.3

引入依赖

<dependency> 
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

编写配置

application.properties 中编写

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 
#暂未⽤到配置中⼼功能,需要关闭配置检查
#spring.cloud.nacos.config.import-check.enabled=false

开启注册发现功能

@EnableDiscoveryClient //核⼼注解 
@SpringBootApplication
public class OrderMainApplication {
public static void main(String[] args) {
SpringApplication.run(OrderMainApplication.class, args);
}
}

服务注册

启动项目,访问:http://localhost:8848/nacos 可以看到服务已经注册上来;

注册更多服务

  1. 创建 service-product 服务 (注意:每个微服务端⼝不⼀样 )
    1. 引⼊ nacos依赖
    2. 配置 nacos地址信息;
  2. 启动应⽤,查看是否注册成功

单服务模拟集群

模拟某一种服务,以集群方式启动起来。由于都是在本机,就只能通过不同端口来区分微服务。如果是真实Linux服务器集群。每个微服务就可能拥有不同的地址

以 service-order 为例,启动 order 的三个服务。

  1. idea 搜索 services ⾯板,添加 SpringBoot 项⽬。
  2. 复制 OrderMainApplication 三份,每个启动命令重新指定端⼝

服务发现

DiscoveryClient

@Autowired 
DiscoveryClient discoveryClient;

@Test
void discoveryClientTest(){

for (String service : discoveryClient.getServices()) {
System.out.println("service = " + service);
//获取ip+port
List<ServiceInstance> instances = discoveryClient.getInstances(service);
for (ServiceInstance instance : instances) {
System.out.println("ip:"+instance.getHost()+";"+"port = " + instance.getPort());
}
}
}

NacosServiceDiscovery

@Autowired 
NacosServiceDiscovery nacosServiceDiscovery;

@Test
void nacosServiceDiscoveryTest() throws NacosException {
for (String service : nacosServiceDiscovery.getServices()) {
System.out.println("service = " + service);
List<ServiceInstance> instances = nacosServiceDiscovery.getInstances(service);

for (ServiceInstance instance : instances) {
System.out.println("ip:"+instance.getHost()+";"+"port = " + instance.getPort());
}
}
}

远程调用

配置RestTemplate

Spring 中提供了 RestTemplate 组件。这个组件可以给任何地方发送请求。自然也能给我们的微服务发送请求。我们提前给容器中创建一个 RestTemplate 组件。以后就可以 @Autowired 直接使用

@Configuration 
public class AppConfiguration {

@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}

发送远程请求

@Autowired 
RestTemplate restTemplate;

@Test
void testRestTemplate() {
String forObject = restTemplate.getForObject("http://localhost:8080/product/4", String.class);
System.out.println(forObject);
System.out.println("-----------------------------");
}
  1. 使⽤ RestTemplate 可以获取到远程数据
  2. 必须精确指定地址和端⼝
  3. 如果远程宕机将不可⽤

负载均衡

引入依赖

Spring Cloud 提供了 loadbalancer,来实现负载均衡功能

<dependency> 
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

编程式负载均衡:

自动注入使用 LoadBalancerClient组件。

// 进阶2:完成负载均衡发送请求 
private Product getProductFromRemoteWithLoadBalance(Long productId){
//1、获取到商品服务所在的所有机器IP+port
ServiceInstance choose = loadBalancerClient.choose("service-product");

//远程URL
String url = "http://"+choose.getHost() +":" +choose.getPort() +"/product/"+productId;
log.info("远程请求:{}",url);

//2、给远程发送请求
Product product = restTemplate.getForObject(url, Product.class);
return product;

}

注解式负载均衡

@Configuration 
public class UserConfiguration {

@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
// 进阶3:基于注解的负载均衡 
private Product getProductFromRemoteWithLoadBalanceAnnotation(Long productId){
String url = "http://service-product/product/"+productId;
//2、给远程发送请求; service-product 会被动态替换
Product product = restTemplate.getForObject(url, Product.class);
return product;
}
  1. 负载均衡调⽤只需要传⼊ 服务名
  2. 请求发起之前会⾃动去注册中⼼确定微服务地址
  3. 如果微服务宕机,会⾃动剔除在线名单,请求将不会发过去

思考

经典面试题:如果注册中⼼宕机,远程调⽤是否可以成功?

  1. 从未调⽤过,如果注册中心宕机,调⽤会⽴即失败
  2. 调⽤过,如果注册中心宕机,因为会缓存名单,调⽤会成功
  3. 调⽤过,如果注册中⼼和对⽅服务都宕机,因为会缓存名单,调⽤会阻塞后失败(Connection

Refused)

Nacos:配置中心

引入依赖

<dependency> 
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

配置文件

application.properties

# 指定配置中⼼地址 
spring.cloud.nacos.server-addr=localhost:8848
spring.config.import=nacos:service-order.properties

动态刷新

@RefreshScope

凡是 @Value 动态从配置文件中取到的值。都可以通过 @RefreshScope 实现自动刷新功能

@RefreshScope // ⾃动刷新
@RestController
public class OrderController {

//从配置文件或者配置中心拿
@Value("${order.timeout}")
String timeout;
@Value("${order.auto-confirm}")
String confirm;


@GetMapping("/config")
public String orderConfigInfo(){
// 订单超时: order.timeout=30min
return "订单超时:" +timeout +" , 订单自动确认:" + confirm;
// 订单自动确认: order.auto-confirm=7d
}
}

ConfigurationProperties

使用 @ConfigurationProperties 绑定配置文件的属性类。⽆需 @RefreshScope,就能动态更新

@Component
//配置批量绑定,可以⽆需@RefreshScope就能实现⾃动刷新
@ConfigurationProperties(prefix = "order")
@Data
public class OrderProperties {
String timeout;
String autoConfirm;
String dbUrl;
}

NacosConfigManager

编程式监听某个属性文件的变化

@Bean
ApplicationRunner applicationRunner(NacosConfigManager manager){
return args -> {
ConfigService configService = manager.getConfigService();
configService.addListener("service-order.properties", "DEFAULT_GROUP",
new Listener() {
@Override
public Executor getExecutor() {
return Executors.newFixedThreadPool(4);
}
@Override
public void receiveConfigInfo(String configInfo) {
System.out.println("configInfo = " + configInfo);
}
});
};
}

namespace、dataId、group

namespace:名称空间

名称空间是用来隔离数据的。从而实现多环境隔离,如:开发、测试、预发、⽣产等

dataId:数据集id

数据集id:就是以前配置⽂件的名字。完整写法: 名字.后缀 如: common.properties

groupId:分组id

分组id:⼀般可以⽤微服务的名字作为⾃⼰的组。

OpenFeign:远程调用

官⽹:https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html#spring-cloud-feign

OpenFeign 是⼀个声明式远程调⽤客户端;

引入依赖

由于⼤型项⽬中,每个项⽬都可能需要使⽤远程调⽤。所以我们可以在⽗项⽬中统⼀引⼊

<dependency> 
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

开启功能

@SpringBootApplication 
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

远程调⽤

注意用法:

  1. @EnableFeignClients
  2. @EnableFeignClients(basePackages = "com.example.clients")
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value = "/stores")
List<Store> getStores();

@GetMapping("/stores")
Page<Store> getStores(Pageable pageable);

@PostMapping(value = "/stores/{storeId}", consumes = "application/json", params = "mode=upsert")
Store update(@PathVariable("storeId") Long storeId, Store store);

@DeleteMapping("/stores/{storeId:\\d+}")
void delete(@PathVariable Long storeId);
}

进阶配置

开启日志

logging: 
level:
com.lfy.order.feign: debug
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}

超时控制

精确控制某个feignClient;

spring:
cloud:
openfeign:
client:
config:
default:
logger-level: full
connect-timeout: 1000
read-timeout: 2000
service-product:
logger-level: full
connect-timeout: 3000
read-timeout: 5000

重试机制

容器中只要有 Retryer 组件,就可以用到重试器;

@Bean 
Retryer retryer(){
return new Retryer.Default();
}

fallback - 兜底返回

远程调用经常出现超时、断网等异常。我们可以触发兜底返回。保证能拿到数据,哪怕是默认数据,好让业务可以继续进行。

引入 sentinel

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

开启熔断

feign: 
sentinel:
enabled: true

指定兜底逻辑

// feign客户端
@FeignClient(value = "service-product",fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {
//mvc注解的两套使⽤逻辑
//1、标注在Controller上,是接受这样的请求
//2、标注在FeignClient上,是发送这样的请求
@GetMapping("/product/{id}")
Product getProductById(@PathVariable("id") Long id);
}
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
@Override
public Product getProductById(Long id) {
System.out.println("兜底回调....");
Product product = new Product();
product.setId(id);
product.setPrice(new BigDecimal("0"));
product.setProductName("未知商品");
product.setNum(0);
return product;
}
}
@Import({ProductFeignClientFallback.class})
@EnableDiscoveryClient //功能开关
@EnableFeignClients(basePackages = "com.lfy.api.product")
@SpringBootApplication
public class StockMainApplication {
}

拦截器

OpenFeign 支持请求和响应拦截。可以在远程发送请求之前,对请求数据进行定制修改。也可以在得到响应之后,对响应数据进行处理。

注意:请求拦截器的使用场景非常多!比如远程调用链路传递用户信息数据等

  1. 写一个拦截器的实现类;必须实现 RequestInterceptor
  2. 把这个实现类放到容器中,以后发请求就会自动启用;
package com.lfy.stock.interceptor;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
* @author leifengyang
* @version 1.0
* @date 2025/8/30 11:30
* @description:
*/
@Slf4j
@Component
public class ServiceNameHeaderRequestInterceptor implements RequestInterceptor {

//获取当前微服务的名字
@Value("${spring.application.name}")
String serviceName;

@Override
public void apply(RequestTemplate template) {
log.info("feign拦截器准备添加服务名请求头");
template.header("X-Service",serviceName);
}
}

调用第三方API

以下是墨迹天气API接口:更多接口参考阿里云API市场

请求方式:POST http://aliv18.data.moji.com/whapi/json/alicityweather/condition

请求头: Authorization=APPCODE 93b7e19861a24c519a7548b17dc16d75

请求参数

  • token=50b53ff8dd7d9fa320d3d3ca32cf8ed1
  • cityId=2182

短信发送API

  1. 去 阿里云 市场购买短信API:https://market.aliyun.com/detail/cmapi00037415#sku=yuncode31415000022
  2. 去控制台,查看API详情
  3. 调试API

Sentinel:流量保护

官⽹:https://sentinelguard.io/zh-cn/index.html

wiki:https://github.com/alibaba/Sentinel/wiki

随着微服务的流⾏,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切⼊点,从流量控制、流量路由、熔断降级、系统⾃适应过载保护、热点流量防护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

丰富的应⽤场景:Sentinel 承接了阿⾥巴巴近 10 年的双⼗⼀⼤促流量的核⼼场景,例如秒杀 (即突发流量控制在系统容量可以承受的范围)、消息削峰填⾕、集群流量控制、实时熔断下游不可⽤应⽤等。

完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接⼊应⽤的单台机器秒级数据,甚⾄ 500 台以下规模的集群的汇总运⾏情况。

⼴泛的开源⽣态:Sentinel 提供开箱即⽤的与其它开源框架/库的整合模块,例如与 SpringCloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引⼊相应的依赖并进⾏简单的配置即可快速地接⼊ Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语⾔的原⽣实现。

完善的 SPI 扩展机制:Sentinel 提供简单易⽤、完善的 SPI 扩展接⼝。您可以通过实现扩展接⼝来快速地定制逻辑。例如定制规则管理、适配动态数据源等

基础入门

架构方式

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
  1. 核心概念

定义资源:

主流框架⾃动适配:(Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、 Reactor);

  1. 所有Web接⼝均为资源;
  2. 资源名是请求路径

编程式:SphU API

  1. 资源名自己指定,比如:haha
try(Entry entry = SphU.entry("haha");) {
String str = "订单超时:" +timeout +" , 订单自动确认:" + confirm + " , 哈哈:" + haha;
String plus = orderProperties.getTimeout() + "~~~" + orderProperties.getAutoConfirm();
result = str + "\n" + plus;
} catch (BlockException e) {
//sentinel如果不允许访问这个资源就会来到异常
throw new RuntimeException(e);
}

声明式:@SentinelResource

  1. 资源名自己指定,比如:product-rpc
@SentinelResource("product-rpc")
public Product getProductFromRemoteV5(Long productId){
Product product = productFeignClient.getProduct(productId);

return product;
}

定义规则:

  1. 流量控制规则
  2. 熔断降级规则
  3. 系统保护规则
  4. 来源访问控制规则
  5. 热点参数规则

导入依赖

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

启动控制台

java -jar sentinel.jar

配置文件

spring: 
cloud:
sentinel:
transport:
dashboard: localhost:8080

异常处理

这些机制不是靠记忆的。可以通过分析源码得到!

每种错误都有默认的处理方式,我们一般不用。我们需要自定义错误处理

  1. web请求被控制住的错误:自定义 BlockExceptionHandler

默认返回:

@Component
public class MyJsonBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
String resourceName,
BlockException e) throws Exception {
response.setStatus(HttpStatus.SC_TOO_MANY_REQUESTS);

response.setContentType("application/json; charset=utf-8");


Map<String,Object> result = new HashMap<>();
result.put("code","429");
result.put("msg","请求太多,稍后再试:异常:"+e.getMessage());
String json = JSON.toJSONString(result);

response.getWriter().write(json);

}
}

@SentinelResouce 注解标注的资源:

由 SentinelResourceAspect 处理这中资源被控制的异常

默认错误:Service 出错了,自己不处理,SpringBoot 返回了默认错误页

  1. 如果是 BlockException(也就是Sentinel把你摁住了);去 @SentinelResource 注解中找 blockHandler 指定的方法名,作为异常处理的方式
  2. 如果是业务自己炸了;抛出的异常肯定 不是 BlockException 旗下的。去 @SentinelResource 注解 中找 fallback 指定的方法处理异常;

最佳实践:@SentinelResource标准的资源,都使用fallback 指定兜底回调逻辑,捕获所有异常

/**
* V5: OpenFeign 版本;
* Remote Procedure Call: 远程过程调用: RPC
* QPS:Query Per Second: 每秒查询数/每秒请求数
*
* blockHandler:指定的方法,是sentinel流控后调用的回调
* fallback: 指定的方法,是业务炸了以后得回调
* @param productId
* @return
*/
@SentinelResource(value = "product-rpc",
fallback = "productFallback")
public Product getProductFromRemoteV5(Long productId){
log.info("product-rpc 开始...");
Product product = productFeignClient.getProduct(productId);
int i = 10/0;
return product;
}

/**
* Sentinel 把上面业务摁住了,就会调用这个回调
*/
// public Product productBlock(Long productId, BlockException exception){
// log.info("product-rpc 被sentiel 流控了 {}",exception);
// Product product = new Product(0L,new BigDecimal("0"),"xxx产品不存在",0);
// return product;
// }

public Product productFallback(Long productId, Throwable exception){
// 识别 exception 是sentinel的blockexception 还是业务的其他 exception

if (BlockException.isBlockException(exception)){
log.info("product-rpc 被sentiel 流控了 {}",exception);
Product product = new Product(0L,new BigDecimal("0"),"xxx产品不存在",0);
return product;
}

log.info("product-rpc 业务炸了 {}",exception);
Product product = new Product(0L,new BigDecimal("0"),"xxx产品不存在",0);
return product;
}

流量控制规则:FlowRule

https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

熔断降级规则:DegradeRule

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

避免雪崩效应解决方案

  1. 切断不稳定调用:对不稳定的服务不发起远程调用
  2. 快速返回不积压快速返回失败或者兜底数据

热点参数规则

  1. 规则介绍

https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

需求1:每个用户秒杀 QPS 不得超过 1(秒杀下单 userId 级别)

​ 效果:携带此参数的参与流控,不携带不流控

需求2:6号用户是vvip,不限制QPS(例外情况)

需求3:666号是下架商品,不允许访问

注意:目前 Sentinel 自带的 adapter 仅 Dubbo 方法埋点带了热点参数,其它适配模块(如 Web)默认不支持热点规则,可通过自定义埋点方式指定新的资源名并传入希望的参数。注意自定义埋点的资源名不要和适配模块生成的资源名重复,否则会导致重复统计。

生产环境使用Sentinel

https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel

Gateway:网关

Nginx就可以做网关

https://docs.spring.io/spring-cloud-gateway/reference/4.2/spring-cloud-gateway/starter.html

网关介绍

Nginx就可以做网关

https://docs.spring.io/spring-cloud-gateway/reference/4.2/spring-cloud-gateway/starter.html

网关整合

场景需求

  1. 客户端发送 /api/order/** 转到 service-order
  2. 客户端发送 /api/product/** 转到 service-product
  3. 以上转发有负载均衡效果

创建网关

创建网关项目,引入网关依赖

<dependencies>
<!-- 说明当前项目是网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 注册中心 -->
<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-loadbalancer</artifactId>
</dependency>
</dependencies>

网关配置

spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order
predicates:
- Path=/api/order/**
- id:product-route
uri: lb://service-product
predicates:
- Path=/api/product/**

断言:Predicate

https://docs.spring.io/spring-cloud-gateway/reference/4.2/spring-cloud-gateway/request-predicates-factories.html

全局跨域

spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOriginPatterns: "*"
allowedMethods: "*"
allowedHeaders: "*"
allowCredentials: true