目录
[Nacos 简介](#Nacos 简介)
[Nacos 的特点](#Nacos 的特点)
[Nacos 的使用步骤可以查看黑马教程文档:day03-微服务01 - 飞书云文档 (feishu.cn)](#Nacos 的使用步骤可以查看黑马教程文档:day03-微服务01 - 飞书云文档 (feishu.cn))
[OpenFeign 的使用步骤](#OpenFeign 的使用步骤)
[Feign Logger.Level 的选项](#Feign Logger.Level 的选项)
Nacos 简介
Nacos 是阿里巴巴开源的一个动态服务发现、配置管理和服务管理平台。它在微服务架构中常被用作服务注册中心和配置中心。
Nacos 的特点
- 服务发现与注册:支持 DNS 和 HTTP 协议的服务发现。
- 动态配置管理:支持实时动态配置刷新。
Nacos 的使用步骤可以查看黑马教程文档: day03-微服务01 - 飞书云文档 (feishu.cn)
通常Nacos会和OpenFeign结合使用 更加方便
OpenFeign的产生就是为了让远程调用像本地方法调用一样简单。
使用步骤:
1.引入服务注册和服务发现的依赖
XML
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 配置Nacos地址
cpp
spring:
cloud:
nacos:
server-addr: 自己的虚拟机Ip:8848
OpenFeign 的使用步骤
-
引入依赖
XML<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
开启 Feign 功能 在主类上添加
@EnableFeignClients
注解。java@SpringBootApplication @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
定义 Feign 客户端 使用
@FeignClient
注解标注接口,指定调用的服务名称。java@FeignClient(name = "service-name") public interface MyFeignClient { @GetMapping("/api/endpoint") String getEndpoint(); }
-
调用 Feign 接口 注入定义的 Feign 接口并调用方法。
java@RestController public class MyController { @Autowired private MyFeignClient myFeignClient; @GetMapping("/call") public String callService() { return myFeignClient.getEndpoint(); } }
如果将这个代理的专门使用一个包提取出来,那么我们需要在启动类上面添加扫描包的注解
java
package com.hmall.pay;
import com.hmall.api.config.DefaultFeignConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@MapperScan("com.hmall.pay.mapper")
@SpringBootApplication
@EnableFeignClients(basePackages = "com.hmall.api.client" ,defaultConfiguration = DefaultFeignConfig.class)
public class PayApplication
{
public static void main(String[] args) {SpringApplication.run(PayApplication.class, args);}
}
在 Spring Boot 的微服务项目中,上述代码配置了 defaultConfiguration
属性来指定一个默认的配置类 DefaultFeignConfig
,它用于 Feign 客户端的默认行为设置。具体来说,这是为了增强日志打印功能,方便开发者在调用微服务时追踪和调试。
以下是DefaultFeignConfig的内容:
java
package com.hmall.api.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfig
{
@Bean
public Logger.Level feignLoggerLevel()
{
return Logger.Level.FULL;
}
}
Feign Logger.Level 的选项
- NONE:不记录任何日志(默认值)。
- BASIC:记录请求方法、URL、响应状态码以及执行时间。
- HEADERS:在 BASIC 的基础上,记录请求和响应的头信息。
- FULL:记录请求和响应的所有信息,包括头、体、元数据。
网关
服务拆分之后面临两大问题:
1.服务地址过多,前端不知道如何请求谁
- 每个服务都可以需要登录信息
自定义过滤器(拦截器)
NettyRoutingFilter
是 Spring Cloud Gateway 中的一个核心过滤器,它负责将请求真正路由到目标服务,并处理响应的回传。简单来说,这是网关在处理请求时,实际完成路由和转发的核心组件
网关到微服务的用户传递
网关层的请求拦截: 在微服务架构中,为了提高安全性和请求处理的一致性,需要在网关层对所有请求进行统一拦截。具体要求如下:
- 放行无需登录的请求:对于公共接口或资源,不进行Token校验,直接放行。
- 拦截需要登录的请求 :对于需要登录的请求,检查
Request
中是否包含有效的Token。- 如果Token不存在或无效,返回HTTP状态码
401 (UNAUTHORIZED)
,表示用户未登录,并终止请求的后续处理(调用response.setComplete()
)。 - 如果Token有效,解析Token获取用户的唯一标识(如用户ID),将该用户ID通过
ServerWebExchange
注入到请求头中,然后继续放行请求。
- 如果Token不存在或无效,返回HTTP状态码
java
String id = userId.toString();
ServerWebExchange swe = exchange.mutate().
request(build -> build.header("user-info", id)).build();
微服务中如何获取用户ID: 通常情况下,微服务可以通过读取请求头中的user-info
字段获取用户ID。然而,如果每个微服务都单独实现一个拦截器,会导致代码重复、不便于维护。
为了解决这个问题,可以将通用的拦截器逻辑抽取到common
模块。所有微服务都引入common
模块,直接复用其中的拦截器。这种方式可以避免重复开发,提高代码的复用性和一致性。但是这个时候springBoot无法扫描到这个拦截器的包,因此拦截器不会失效。所以要配置spring.factories
文件
基于Spring Boot的拦截器配置: 微服务通常基于Spring Boot,而不是传统的Spring MVC。如果直接使用Spring MVC的拦截器,在Spring Boot环境下可能会报错。
总结:为了解决上述问题,可以通过在common
模块中为拦截器配置META-INF
文件,同时在拦截器类上添加@ConditionalOnClass(DispatcherServlet.class)
注解。这样可以确保拦截器仅在支持Spring MVC的环境中加载,从而避免不兼容问题。
META-INF是什么?
META-INF 是Java程序的一个特殊目录,用于存放配置文件和元数据信息。它通常位于JAR文件的根目录下,主要包含以下类型的内容:
-
MANIFEST.MF 文件:
- Java的清单文件,用于描述JAR包的信息。
- 包括主类、版本号、依赖项等。
-
Spring Boot相关配置:
- 例如,
spring.factories
文件,用于注册自动配置类。 - 格式为键值对,可以定义拦截器、过滤器等组件的自动加载逻辑。
- 例如,
-
服务注册文件:
- 用于SPI(服务提供接口)机制,例如
META-INF/services
目录下的文件定义了特定接口的实现类。
- 用于SPI(服务提供接口)机制,例如
示例:在common
模块中的spring.factories
bash
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.hmall.common.config.MyBatisConfig,\
com.hmall.common.config.JsonConfig,\
com.hmall.common.config.MvcConfig
微服务之间的用户传递
如果是微服务之间的用户Id传递的话,其实获取到的用户id的值是null,因为微服务之间的网络请求不是通过网关的,所以网关无法在请求头中设置userId,其他服务也不可以获取到userId的值了
微服务之间是通过openFeign进行网络请求转发的,在转发之前我们是可以获得用户Id的,所以我们可以在请求发送之前设置拦截器,如果我们在每一个微服务都加一个拦截器太麻烦了。所以我们选择加在专门写FeignClient的模块
中(hm-api)
那么我们应该如何改造转发的请求呢?在转发之前我们是可以获取到用户id的,所以我们可以借助Feign中提供的一个拦截器接口:feign.RequestInterceptor
java
public interface RequestInterceptor {
/**
* Called for every request.
* Add data using methods on the supplied {@link RequestTemplate}.
*/
void apply(RequestTemplate template);
}
我们只需要实现这个接口,然后实现apply方法,利用RequestTemplate
类来添加请求头,将用户信息保存到请求头中。这样以来,每次OpenFeign发起请求的时候都会调用该方法,传递用户信息。
java
package com.hmall.api.config;
import com.hmall.common.utils.UserContext;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfig
{
@Bean
public Logger.Level feignLoggerLevel()
{
return Logger.Level.FULL;
}
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
// 获取用户Id
Long user = UserContext.getUser();
if(user == null) {
// 如果为空则直接跳过
return;
}
// 存入请求头当中
requestTemplate.header("user-info",user.toString());
}
};
}
}
- 网关部分:用户身份校验,并将用户信息存入请求头。
- 微服务部分 :使用拦截器提取用户信息存入
ThreadLocal
,微服务间通过 OpenFeign 的 RequestInterceptor 传递用户信息。
配置管理
在微服务架构中,服务数量众多且配置管理复杂,常常导致重复代码和维护成本增加。通过 Nacos 的配置管理功能,我们能够实现配置的统一管理和动态更新,提升开发效率并减少运维压力。
在微服务架构中,每个服务都需要独立管理自己的配置文件,例如数据库连接(JDBC)、日志配置、Swagger 接口文档配置等。这些配置中往往存在大量重复内容,既增加了维护成本,也容易引发配置不一致问题。
Nacos 是一款集服务注册与配置管理于一体的工具,通过将公共配置提取到 Nacos 中并实现动态加载,可以有效减少代码冗余、降低维护难度并支持配置实时更新。
如何进行配置管理?
引入 Maven 依赖
在 Spring Boot 项目中,添加以下依赖:
XML
<!--nacos配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--读取bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
- 在Nacos网址上面添加配置,分别提取jdbc-mysql配置,swagger配置,日志的配置
jdbc共享配置:
bash
spring:
datasource:
url: jdbc:mysql://${hm.db.host:192.168.88.130}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true&failOverReadOnly=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${hm.db.un:root}
password: ${hm.db.pw:123}
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
global-config:
db-config:
update-strategy: not_null
id-type: auto
日志的共享配置:
bash
logging:
level:
com.hmall: debug
pattern:
dateformat: HH:mm:ss:SSS
file:
path: "logs/${spring.application.name}"
swagger的共享配置:
bash
knife4j:
enable: true
openapi:
title: ${hm.swagger.title:"黑马商城购物车接口文档"}
description: ${hm.swagger.desc:"黑马商城购物车接口文档"}
email: zhanghuyi@itcast.cn
concat: 虎哥
url: https://www.itcast.cn
version: v1.0.0
group:
default:
group-name: default
api-rule: package
api-rule-resources:
- com.hmall.cart.controller
- ${hm.swagger.package}
- 创建bootstrap.yaml 公共配置是动态的,所以我们需要在 bootstrap.yaml进行配置
必须配置其 spring: application: name,profiles: active: ,cloud:nacos:config:extension
bash
spring:
application:
name: cart-service # 服务名称
profiles:
active: dev
cloud:
nacos:
server-addr: 192.168.88.130 # nacos地址
config:
file-extension: yaml # 文件后缀名
shared-configs: # 共享配置
- dataId: shared-jdbc.yml # 共享mybatis配置
- dataId: shared-log.yml # 共享日志配置
- dataId: shared-swagger.yml # 共享日志配置
注意这里的shared-configs的dataId的配置名一定要和Nacos里面的一模一样 ,如果 dataId
与 Nacos 中的配置名称不一致,服务将无法加载到对应的共享配置,从而导致启动失败或无法正确读取配置
- 剩余部分就需要在application.yaml里面配置
bash
server:
port: 8082
feign:
okhttp:
enabled: true # 开启OKHttp连接池支持
hm:
swagger:
title: 购物车服务接口文档
package: com.hmall.cart.controller
db:
host: 192.168.88.130 # 修改为你自己的虚拟机IP地址
database: hm-cart
如果进行热部署更新?
进入Nacos网站http://192.168.88.130:8848/nacos 开始添加配置
使用默认的 应用名称.yaml
(例如,cart-service.yaml
),这样无需显式在 bootstrap.yaml
中指定 dataId
什么是默认的?
应用名称作为默认 Data ID
Spring Boot 项目的 spring.application.name
属性会作为 Nacos 配置的默认 Data ID
。
例如: 如果你的 bootstrap.yaml
配置如下:
bash
spring:
application:
name: cart-service
cloud:
nacos:
server-addr: 192.168.88.130:8848
那么 Nacos 会自动尝试加载以下文件作为配置:
bash
Data ID: cart-service.yaml
Group: DEFAULT_GROUP
-
优先加载的文件格式
默认情况下,Nacos 会加载和
spring.cloud.nacos.config.file-extension
对应的文件格式(默认为yaml
)。 -
无需显式指定 Data ID
只要 Nacos 配置文件的
Data ID
与spring.application.name
一致(并以.yaml
作为后缀),就会被自动加载,而无需手动在bootstrap.yaml
中声明shared-configs
或特定dataId
。
配置项前缀匹配 :prefix
指定的值必须在配置文件中明确存在。例如,hm.cart
对应的配置项必须是以下形式之一:
-
在对应的
yml
中,配置如下,bashhm: cart: maxAmount: 10
此时对应的配置类的注解@ConfigurationProperties(prefix = "hm.cart")的prefix必须是hm.cart
java
package com.hmall.cart.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {
private Integer maxAmount;
}
更新路由
微服务保护和分布式管理
雪崩问题
sentinel是什么?
Sentinel 是阿里巴巴开源的一款用于分布式系统的流量控制和熔断降级的中间件。它主要用于保护微服务的稳定性,帮助应对高并发场景下的流量突增或服务调用异常。
Sentinel 提供了灵活的流量控制规则和强大的监控能力,是 Spring Cloud Alibaba 技术栈中的核心组件之一,用于服务治理和高可用保障。
如何使用sentinel?
- 引入依赖
XML
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2021.0.3.0</version>
</dependency>
- 连接
sentinel-dashboard
控制台,需要配置控制台,修改application.yaml文件,添加下面内容:
bash
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
- 由于我们的SpringMVC接口是按照Restful风格设计,因此购物车的查询、删除、修改等接口全部都是
/carts
路径,所以我们需要在application.yaml里面添加 http-method-specify: true # 开启请求方式前缀
bash
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8090
http-method-specify: true # 开启请求方式前缀
详细请点击黑马教程:day05-服务保护和分布式事务 - 飞书云文档 (feishu.cn)
接下来详细介绍一下熔断的解决办法
熔断的具体实现步骤
步骤 1:配置 Sentinel 的熔断规则
在 Sentinel 可视化控制台 中配置熔断规则,基于:
- 异常比例(当调用失败率超过设定值时触发)。
- 异常数(当调用失败总数超过设定值时触发)。
- 响应时间(当接口响应时间超过设定值时触发)。
示例:
- 熔断条件:某接口失败率 >50% 且调用量 >10,在 1 分钟内触发熔断。
- 熔断恢复:在熔断 5 秒后恢复尝试。
步骤 2:编写 FallbackFactory
当服务触发熔断时,FallbackFactory
提供降级逻辑。正常会去调用ItemClient接口,但是当ItemClinet接口对应的Controller的处理出现异常或者请求被限流了,就会走这个fallback"备选方案",它也会创建一个ItemClient,去返回一些提示信息。以 ItemClientFactoryFallback
为例:
主要逻辑
- 实现
FallbackFactory
接口。 - 在
create
方法中捕获异常信息Throwable
。 - 定义服务降级逻辑:
- 非关键业务返回默认值(如返回空集合)。
- 关键业务抛出业务异常,触发事务回滚,确保数据一致性。
代码解读
java
package com.hmall.api.fallback;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.CollUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.Collection;
import java.util.List;
@Slf4j
public class ItemClientFactoryFallback implements FallbackFactory<ItemClient> {
@Override
public ItemClient create(Throwable cause) {
return new ItemClient() {
@Override
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
log.error("远程调用ItemClient#queryItemByIds方法出现异常,参数:{}", ids, cause);
// 查询购物车允许失败,查询失败,返回空集合
return CollUtils.emptyList();
}
@Override
public void deductStock(List<OrderDetailDTO> items) {
// 库存扣减业务需要触发事务回滚,查询失败,抛出异常
throw new BizIllegalException(cause);
}
};
}
}
步骤 3:在配置类中注册为 Bean
原因
- 默认情况下,Spring Boot 不会扫描非显式标注为
@Component
的类。 - 需要显式将
FallbackFactory
注册为 Bean,才能生效。
代码解读
在 DefaultFeignConfig
配置类中:
- 注册
ItemClientFactoryFallback
:
java
package com.hmall.api.config;
import com.hmall.api.fallback.ItemClientFactoryFallback;
import com.hmall.common.utils.UserContext;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfig
{
@Bean
public Logger.Level feignLoggerLevel()
{
return Logger.Level.FULL;
}
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
// 获取用户Id
Long user = UserContext.getUser();
if(user == null) {
// 如果为空则直接跳过
return;
}
// 存入请求头当中
requestTemplate.header("user-info",user.toString());
}
};
}
// 注册为Bean
@Bean
public ItemClientFactoryFallback itemClientFallbackFactory(){
return new ItemClientFactoryFallback();
}
}
步骤 4:在服务接口中绑定降级逻辑
使用 @FeignClient
注解绑定服务,并指定:
value
:目标微服务名称。fallbackFactory
:对应的FallbackFactory
类。configuration
:自定义的 Feign 配置类。
代码解读
java
@FeignClient(
value = "item-service",
fallbackFactory = ItemClientFactoryFallback.class,
configuration = DefaultFeignConfig.class
)
public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
@PostMapping("/stock/deduct")
void deductStock(@RequestBody List<OrderDetailDTO> items);
}
如何解决雪崩问题
-
快速失败:
- 熔断器在触发后立即返回降级结果,避免资源被耗尽。
-
默认降级逻辑:
- 通过
FallbackFactory
提供兜底逻辑,为非关键业务返回默认值,降低错误扩散的影响。
- 通过
-
保护核心业务:
- 关键业务如库存扣减,触发异常并回滚,确保数据一致性。
-
监控与恢复:
- Sentinel 自动监控系统恢复状态,当异常比例降低时自动关闭熔断器,恢复正常调用。
分布式事务(多个服务或数据库之间数据一致性的问题)
day05-服务保护和分布式事务 - 飞书云文档 (feishu.cn)
什么事seata?
Seata (Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的一款分布式事务解决方案。它为分布式系统中的事务一致性问题提供了简单、高效的解决方案,支持多种事务模型,帮助开发者轻松实现分布式事务。
在微服务架构中,各服务通常独立部署,拥有独立的数据库。这种设计虽然带来了高可用性和扩展性,但也引入了分布式事务问题(如跨服务的数据一致性)。Seata 专注于保证事务的一致性。
XA模式
首先我们可以在Nacos中的共享shared-seata.yaml配置文件中设置:
seata:
data-source-proxy-mode: XA
然后我们要利用**@GlobalTransactional
标记分布式事务的入口方法即可**
AT模式
RabbitMQ
详细查看请查看RabbitMQ高级篇-CSDN博客