1.Spring Cloud概述
1.1.集群和分布式
集群: 将⼀个系统完整的部署到多个服务器上, 每个服务器都能提供系统的所有服务, 多个服务器通过负载均衡调度完成任务., 每个服务器称为集群的节点
分布式: 将⼀个系统拆分为多个子系统,多个子系统部署在多个服务器上,多个服务器上的子系统协同合作完成⼀个特定任务.
1.2.微服务简介
微服务, 就是一种经过良好架构设计的分布式架构方案, 其将系统拆分为更细粒度的服务,每个服务只负责一个单一功能,服务独立部署和运行,并通过轻量级协议(如REST或RPC)进行通信
1.3.Spring Cloud简介
Spring Cloud 是分布式微服务架构的一站式解决方案, 是微服务架构落地的多种技术的集
合, 通过整合多种开源技术,提供了一套工具集来简化微服务开发中的常见问题处理
如:
- 分布式版本配置:管理不同环境下的配置
- 服务注册和发现:自动注册和发现服务实例
- 路由:智能路由请求到目标服务
- 服务调用:简化服务间的通信
- 负载均衡:分配请求负载,提高系统稳定性
- 断路器:防止故障扩散,提升系统韧性
- 分布式消息:处理异步消息传递
2.拆分原则
2.1.单⼀职责
⼀个微服务应该只负责⼀个功能或业务领域, 每个服务应该有清晰的定义和边界, 只
关注自己的特定业务领域.
2.2.服务自治
每个微服务都应该具备⾼度自治的能力, 每个服务要能做到独立开发, 独立测试, 独立构
建, 独立部署, 独立运行
2.3.单向依赖
微服务之间需要做到单向依赖, 严禁循环依赖, 双向依赖
循环依赖: A -> B -> C ->A
双向依赖: A -> B, B -> A
3.服务注册与服务发现
3.1.注册中心
注册中心可以维护⼀个服务列表, 哪个机器上线了, 哪个机器宕机了, 这些信息都会自动更新到服务列表上, 客户端拿到这个列表时可以直接进行服务调用
注册中心主要有三种角色:
• 服务提供者:⼀次业务中, 被其它微服务调用的服务. 也就是提供接口给其它微服务.
• 服务消费者:⼀次业务中, 调用其它微服务的服务. 也就是调用其它微服务提供的接口.
• 服务注册中心: 用于保存Server 的注册信息, 当Server 节点发生变更时, Registry 会同步变更. 服务与注册中心使用⼀定机制通信, 如果注册中心与某服务长时间无法通信, 就会注销该实例
他们之间的关系以及工作内容, 可以通过两个概念来描述:
服务注册:服务提供者在启动时, 向 Registry 注册自身服务, 并向 Registry 定期发送心跳汇报存活状态.
服务发现:服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口, 从而提供给服务消费者⼀个可用的服务列表
服务启动/变更时, 向注册中心报道. 注册中心记录应用和IP的关系.
调用方调用时, 先去注册中心获取服务方的IP, 再去服务方进行调用
3.2.CAP
• ⼀致性:CAP理论中的⼀致性, 指的是强⼀致性. 所有节点在同⼀时间具有相同的数据
• 可用性:保证每个请求都有响应(响应结果可能不对)
• 分区容错性:当出现网络分区后,系统仍然能够对外提供服务
CAP 理论指出:分布式系统中最多同时满足两个特性,由于网络分区不可避免,所以注册中心只有CA、CP两种架构:
CP架构:优先保证数据一致性(C),但可能牺牲可用性(A)。例如,在网络异常时,拒绝请求以维护数据一致
AP架构:优先保证可用性(A),但可能返回不一致的数据。例如,在网络异常时,返回缓存数据(即使旧版本也一样),确保服务不中断
4.负载均衡
负载均衡(Load Balance,简称 LB) , 是高并发, 高可用系统必不可少的关键组件
当服务流量增大时, 将请求流量合理分配到多个资源(如服务器实例)
4.1.Spring Cloud LoadBalancer
@Configuration
public class BeanConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
public OrderInfo selectOrderById(Integer id) {
OrderInfo orderInfo = orderMapper.selectOrderById(id);
String url = "http://product-service/product/" + orderInfo.getProductId();
ProductInfo product = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(product);
return orderInfo;
}
4.1.2.负载均衡策略
4.1.3.自定义负载均衡策略
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
public class LoadBalancerConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory
) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
System.out.println("==============" + name);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name
);
}
}
@LoadBalancerClient(name = "product-service", configuration = LoadBalancerConfig.class)
@Configuration
public class BeanConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
5.Nacos
5.1 引入依赖
在父工程的pom文件中的 <dependencyManagement> 中引入Spring Cloud Alibaba的依赖
<properties>
<spring-cloud-alibaba.version>2022.0.0.0-RC2</spring-cloud-alibaba.version>
</properties>
<dependency>
<groupId>***.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
在子工程中引入 Nacos 和 Load Balance 依赖
// alibaba 依赖
<dependency>
<groupId>***.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
// Load Balance 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
Nacos的对应服务中配置子工程 yml
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
配置服务器代码
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public OrderInfo selectOrderById(Integer id) {
OrderInfo orderInfo = orderMapper.selectOrderById(id);
System.out.println(orderInfo.getProductId());
String url = "http://product-service/product/" + orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
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;
@Configuration
public class BeanConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
启动两个服务, 观察Nacos的管理界面 发现两个服务都注册在Nacos上了
5.2.权重配置
5.3 开启Nacos负载均衡策略
# 开启Nacos的负载均衡策略
spring:
cloud:
loadbalancer:
nacos:
enabled: true
5.4.Nacos 健康检查
5.4.1.两种健康检查机制
5.4.2.Nacos服务实例类型
5.5.Nacos环境隔离
切勿忘记, 也要在idea上修改此服务在nacos上的命名空间
spring:
cloud:
nacos:
config:
namespace: xxxxxx
5.6.Nacos配置中心
<dependency>
<groupId>***.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud 2020.*之后版本需要引入bootstrap -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
6.OpenFeign
6.1 Spring Cloud Feign
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
import ***.bite.order.model.ProductInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@GetMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
import org.springframework.beans.factory.annotation.Autowired;
@Autowired
private ProductApi productApi;
/**
* Feign实现远程调用
* @param orderId 订单ID
* @return 包含商品信息的订单详情
*/
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
ProductInfo productInfo = productApi.getProductById(orderInfo.getProductId());
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
6.2.Feign 抽取
在企业开发中,将Feign接口抽取为一个独立模块是常见做法。这能提高代码重用性、解耦服务提
供方和消费者,并简化维护
通过打 Jar 包放在本地 Maven 仓库的方式来模拟服务器之间的调度
服务消费方使用 product-api 依赖,并且指定启动类扫描 ProducApi 接口
@EnableFeignClients(clients = {ProductApi.class})
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
@RestController
@RequestMapping("/product")
public class ProductApiController {
@Autowired
private ProductApi productApi;
@RequestMapping("/{id}")
ProductInfo getProductInfo(@RequestParam("id") Integer id) {
return productApi.gerProductById(id);
}
@RequestMapping("/o1")
public String returnId (@RequestParam("id") Integer id){
return productApi.returnId(id);
}
@RequestMapping("/o2")
public String returnId2 (@RequestParam("name") String name,@RequestParam("id") Integer id){
return productApi.getNameAndId(id,name);
}
@RequestMapping("/o3")
public String returnId3 (ProductInfo productInfo){
return productApi.returnInfo(productInfo);
}
@RequestMapping("/o4")
public String returnId4 (@RequestBody ProductInfo productInfo){
return productApi.returnInfoJson(productInfo);
}
}
服务提供方:
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
public ProductService productService;
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId) {
return productService.selectProductById(productId);
}
@RequestMapping("/p1")
public String getProductByName1(Integer id,String name) {
return "product-server 接收到参数 id = " + id + " name = " + name;
}
@RequestMapping("/p2")
public String getProductByName(Integer id,String name) {
return "product-server 接收到参数 id = " + id + " name = " + name;
}
@RequestMapping("/p3")
public String getInfo (ProductInfo productInfo) {
return productInfo.toString();
}
@RequestMapping("/p4")
public String getInfoJson (@RequestBody ProductInfo productInfo) {
return productInfo.toString();
}
}
流程:
a)服务消费方 Controller 调用公共 productApi 接口
b)公共 productApi 接口处理请求, Feign 在运行时自动生成代理实现,将接口方法调用转换为 HTTP 请求。基于服务注册与发现 ,请求会被路由到服务器提供方的对应端点。
c)服务提供方的方法执行, Controller 接受 HTTP 请求并执行具体业务逻辑。地址的发现通过 HTTP 响应返回给服务消费方 Feign 客户端,然后再传到消费方 Controller 获取数据
URL 拼接过程:公共接口 @FeignClient.path + 公共接口方法的 URL = 要调用的 Controller 方法的 URL (服务提提供方的实际路径)
7.统⼀服务入口-Gateway
7.1.Spring Cloud Gateway
7.1.1.快速上手
创建网关项目
<!--⽹关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--基于nacos实现服务发现依赖-->
<dependency>
<groupId>***.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>
server:
port: 10030 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
discovery:
server-addr: xxxxx # Nacos服务发现地址
gateway:
routes: # 网关路由配置
- id: product-service # 路由ID,自定义,唯一即可
uri: lb://product-service # 目标服务地址,lb表示负载均衡
predicates: # 路由条件
- Path=/product/**
- id: order-service # 订单服务路由
uri: lb://order-service
predicates:
- Path=/order/**
7.2.Gateway Filter Factories(网关过滤器工厂)
7.3 .GlobalFilter
GlobalFilter是Spring Cloud Gateway提供的一个接口,它允许开发者在所有路由请求上应用自定义逻辑。无论路由配置如何,GlobalFilter都会在请求处理链中被执行。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
spring:
cloud:
gateway:
metrics:
enabled: true # 开启Gateway的指标收集功能(如请求计数、延迟等)
management:
endpoints:
web:
exposure:
include: "*" # 暴露所有管理端点(健康检查/指标/shutdown等)
endpoint:
health:
show-details: always # 始终显示健康检查的详细信息(包括组件状态)
shutdown:
enabled: true # 启用应用关闭端点(可通过HTTP请求优雅停止服务)
7.3.1.过滤器执行顺序
在Spring Cloud Gateway中,当请求路由后,网关会将项目中的所有过滤器(包括GatewayFilter和GlobalFilter)合并到一个统一的过滤器链中,并按照特定的顺序执行。
过滤器链的合并与排序
网关在运行时,会将当前项目中的GatewayFilter和GlobalFilter合并到一个集合中。
这个集合会按照每个过滤器的 order 值进行排序。order 是一个 int 类型的属性,默认值为0。
排序规则是:order 值越小,优先级越高,执行顺序越靠前。
当order值相同时的执行优先级
如果多个过滤器的 order 值相等,网关会按照以下固定优先级顺序执行:
defaultFilter:网关的默认过滤器(优先级最高)。
GatewayFilter:用户定义的路由级过滤器。
GlobalFilter:全局过滤器(优先级最低)
7.4.自定义过滤器
7.4.1.自定义GatewayFilter
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@Slf4j
@Service
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.CustomConfig> implements Ordered {
public CustomGatewayFilterFactory() {
super(CustomConfig.class);
}
@Override
public GatewayFilter apply(CustomConfig config) {
/**
* Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
* ServerWebExchange: HTTP请求-响应交互的契约, 提供对HTTP请求和响应的访问,
* 服务器端请求属性, 请求实例,响应实例等, 类似Context角色
* GatewayFilterChain: 过滤器链
* Mono: Reactor核心类, 数据流发布者, Mono最多只触发一个事件,
* 所以可以把Mono用于在异步任务完成时发出通知.
* Mono.fromRunnable: 创建一个包含Runnable元素的数据流
*/
return ((exchange, chain) -> {
log.info("[Pre] Filter Request, name:" + config.getName());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("[Post] Response Filter");
}));
});
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; // 配置优先级, order越大, 优先级越低
}
@Data
public static class CustomConfig {
private String name;
}
}
7.4.2.⾃定义GlobalFilter
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Service
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("[Pre] CustomGlobalFilter enter...");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("[Post] CustomGlobalFilter return...");
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; // 配置优先级, order越大, 优先级越低
}
}