Nacos+Feign+Sentinel(7.31)
一、Nacos实战
1、安装Nacos
下载地址: https://github.com/alibaba/nacos/releases
下载zip格式的安装包,然后进行解压缩操作,上课使用的Nacos Server版本是2.0.4
2、启动Nacos
#切换目录
cd nacos/bin
#命令启动
startup.cmd -m standalone
3、访问Nacos
打开浏览器输入 http://localhost:8848/nacos ,即可访问服务, 默认密码是nacos/nacos。
4、将商品服务和订单服务注册到Nacos
(1)在shop-product-server和shop-order-server的pom下添加Nacos的依赖。
<!--nacos客户端-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
(2)在各自的application.yml中添加Nacos服务的地址。
spring:cloud:nacos:discovery:server-addr: localhost:8848
(3)启动商品服务和订单服务, 观察Nacos的控制面板中是否有注册上来的商品和订单微服务。
拓展:Nacos 的上下线机制
- 核心作用:Nacos 的“下线”操作仅用于流量控制,而非停止服务。它能让实例平滑地退出服务调用链,避免突然停机导致的请求报错。
- 流量摘除:当某台服务器压力过大或需要维护时,在 Nacos 控制台将其“下线”。此时,该实例会从健康列表中移除,注册中心不再将新的用户请求分发给它,但服务器本身仍在正常运行,可以继续处理已经接收到的请求或进行内部维护。
- 动态恢复:当服务器压力缓解或维护完成后,只需在 Nacos 控制台点击“上线”,该实例会立刻重新加入健康列表,恢复接收新的流量。
5、使用Nacos实现服务自动注册与发现
①首先需要在shop-order-server从容器中取出Nacos对象。
//nacos客户端@Autowiredprivate DiscoveryClient discoveryClient;
②获取product的微服务的所有可用实例(即所有正在运行的商品服务的 IP 地址和端口号)。product即application.yml中配置的服务器名称。
List<String> urlList = new ArrayList<>();List<ServiceInstance> instanceList = discoveryClient.getInstances("product");for (ServiceInstance instance : instanceList) {String ip = instance.getHost();int port = instance.getPort();urlList.add(ip + ":" + port);}
TShopOrderController完整代码
@RestController
@RequestMapping("/order")
public class TShopOrderController {@Autowiredprivate ITShopOrderService orderService;//nacos客户端@Autowiredprivate DiscoveryClient discoveryClient;@RequestMapping("/save/{uid}/{pid}/{number}")public TShopOrder save(@PathVariable Long uid, @PathVariable Long pid, @PathVariable Integer number) {TShopOrder tShopOrder = new TShopOrder();tShopOrder.setUid(uid);tShopOrder.setPid(pid);tShopOrder.setNumber(number);List<String> urlList = new ArrayList<>();List<ServiceInstance> instanceList = discoveryClient.getInstances("product");for (ServiceInstance instance : instanceList) {String ip = instance.getHost();int port = instance.getPort();urlList.add(ip + ":" + port);}//用于发送restful风格的http请求的对象,在spring-boot-starter-web中提供RestTemplate restTemplate = new RestTemplate();Random random = new Random();int rand = random.nextInt(urlList.size());String url = urlList.get(rand);TShopProduct product = restTemplate.getForObject("http://"+url+"/product/get/3", TShopProduct.class);tShopOrder.setPname(product.getPname());tShopOrder.setPprice(product.getPprice());orderService.save(tShopOrder);return tShopOrder;}
}
二、使用增强restTemplate结合spring-cloud-LoadBalancer实现真正的负载均衡调用
RestTemplate 是 Spring 提供的用于发送 HTTP 请求的客户端工具,下面以getForObject 为例。
restTemplate.getForObject(url, responseType, uriVariables)
- url:目标服务的完整地址,例如
"http://192.168.1.100:8080/api/users/{id}" - responseType:期望返回的数据类型,如
User.class或String.class - uriVariables:可选,用于替换 URL 中的占位符
{id},可以是 Map 或可变参数
没使用@LoadBalanced增强时,只能根据 IP 和端口访问一台服务器,没法负载均衡。
增强时,使用http://微服务名/请求路径,自动把微服务名展开,去访问该服务下的所有端口。根据算法选出要访问的端口。
(1)去除取出的discoveryClient对象和获取url代码。
(2)在OrderApp把restTemplate注入到容器中,并进行增强。
@MapperScan("cn.wolfcode.mapper")
@SpringBootApplication
public class OrderApp {public static void main(String[] args) {SpringApplication.run(OrderApp.class, args);}@LoadBalanced //增加负载均衡功能,使用http://微服务名/请求路径@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}
(3)在shop-order-server的pom中加入依赖。
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
(4)在TShopOrderController中取出restTemplate对象。传入"product"访问服务端口。
@RestController
@RequestMapping("/order")
public class TShopOrderController {@Autowiredprivate ITShopOrderService orderService;@Autowiredprivate RestTemplate restTemplate;@RequestMapping("/save/{uid}/{pid}/{number}")public TShopOrder save(@PathVariable Long uid, @PathVariable Long pid, @PathVariable Integer number) {TShopOrder tShopOrder = new TShopOrder();tShopOrder.setUid(uid);tShopOrder.setPid(pid);tShopOrder.setNumber(number);TShopProduct product = restTemplate.getForObject("http://"+"product"+"/product/get/3", TShopProduct.class);tShopOrder.setPname(product.getPname());tShopOrder.setPprice(product.getPprice());orderService.save(tShopOrder);return tShopOrder;}
}
小结:对restTemplate做增强后,可以获取到Nacos上的服务清单,采用LoadBalancer上的算法实现负载均衡去访问服务器。
三、负载均衡
1、概念:负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。
2、分类:根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。
-
服务端负载均衡指的是发生在服务提供者一方,比如常见的Nginx负载均衡。
-
客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求。

3、修改负载均衡策略
LoadBalancer默认提供了两种负载均衡策略。
(1)RandomLoadBalancer-随机分配策略
(2)RoundRobinLoadBalancer-轮询分配策略(默认)
我们需要通过配置的方式修改默认的负载均衡策略
@LoadBalancerClient(name = "product-service",configuration =
RandomLoadbalancerConfig.class)
public class RandomLoadbalancerConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance>
reactorServiceInstanceLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name =
environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name,
ServiceInstanceListSupplier.class), name);
}
}
四、远程调用Feign
1、Feign
-
Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。
-
Nacos很好的兼容了Feign, Feign默认集成了 LoadBalancer, 所以在Nacos下使用Fegin默认就实现了负载均衡的效果。
-
对比restTemplate:
- 必须做增强:必须手动加
@LoadBalanced注解才能支持服务名调用和负载均衡。 - 方法调用繁琐:每次调用都要显式地写
getForObject()、postForObject()等 HTTP 动词方法,并且要手动拼接 URL 和参数。
- 必须做增强:必须手动加
2、订单微服务集成Feign
(1)在shop-order-server项目的pom文件加入Fegin的依赖。(因为使用了负载均衡,所以不要忘了加入LoadBalancer的依赖。)
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
3、在启动类OrderServer.java上添加Fegin的扫描注解,注意扫描路径(默认扫描当前包及其子包)
@EnableFeignClients // 开启Feign的支持
4、定义XXXFeignApi接口
在shop-order-server中新建feign包,创建ProductFeignApi接口。
实际上是把shop-product-server项目中的TShopProductController中的方法拷贝到刚刚定义的接口中。
- 去掉方法体
- 补全请求路径(/product)
- 添加FeignClient注解指定服务,name指定服务名,有多台机器,自动负载均衡。
@FeignClient(name = "product")
public interface ProductFeignApi {@GetMapping("/product/get/{id}")TShopProduct get(@PathVariable("id") Integer id);
}
5、修改OrderServiceImpl.java的远程调用方法
- 从容器中获取刚刚定义的接口。
- 调用接口中的方法。
@RestController
@RequestMapping("/order")
public class TShopOrderController {@Autowiredprivate ITShopOrderService orderService;@Autowiredprivate ProductFeignApi productFeignApi; //获取接口@Autowiredprivate UserFeignApi userFeignApi;@RequestMapping("/save/{uid}/{pid}/{number}")public TShopOrder save(@PathVariable Long uid, @PathVariable Long pid, @PathVariable Integer number) {TShopOrder tShopOrder = new TShopOrder();tShopOrder.setUid(uid);tShopOrder.setPid(pid);tShopOrder.setNumber(number);TShopProduct product = productFeignApi.get(pid.intValue()); //获取product对象tShopOrder.setPname(product.getPname());tShopOrder.setPprice(product.getPprice());TShopUser user = userFeignApi.get(uid.intValue());tShopOrder.setUsername(user.getUsername());orderService.save(tShopOrder);return tShopOrder;}}
五、服务熔断降级 Sentinel
1、服务器雪崩效应
在分布式微服务架构中,由于服务间存在复杂的依赖关系,当某个底层服务发生故障(如宕机、响应极慢)时,会导致调用它的上游服务出现大量线程阻塞。这种故障会沿着调用链路向上传播,最终导致整个微服务系统资源耗尽并全面瘫痪,这种连锁反应被称为“雪崩效应”。
2、发生过程(情景推演)
- 正常状态:微服务之间相互调用,关系错综复杂,系统运行平稳。
- 故障触发:某个时刻,底层服务 A 突然挂掉或响应极慢。
- 资源耗尽:上游服务 B 和服务 C 依然在持续调用服务 A。由于得不到响应,服务 B 和 C 中用于处理请求的线程被大量积压、阻塞,无法释放。
- 级联崩溃:随着请求不断涌入,服务 B 和 C 的线程池被彻底耗尽,导致它们也无法处理其他正常请求,最终相继挂掉。
- 全面瘫痪:相同的逻辑继续向上传播,导致整个调用链上的所有服务全部不可用。
3、常见容错方案
常见的容错思路有隔离、超时、限流、熔断、降级。
- 隔离机制: 比如服务A内总共有100个线程, 现在服务A可能会调用服务B,服务C,服务D.我们在服务A进行远程调用的时候,给不同的服务分配固定的线程,不会把所有线程都分配给某个微服务. 比如调用服务B分配30个线程,调用服务C分配30个线程,调用服务D分配40个线程. 这样进行资源的隔离,保证即使下游某个服务挂了,也不至于把服务A的线程消耗完。比如服务B挂了,这时候最多只会占用服务A的30个线程,服务A还有70个线程可以调用服务C和服务D.

- 超时机制: 在上游服务调用下游服务的时候,设置一个最大响应时间,如果超过这个时间,下游未作出反应,就断开请求,释放掉线程。

- 限流机制: 限流就是限制系统的输入和输出流量已达到保护系统的目的。为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流量并采取少量措施以完成限制流量的目的。

-
熔断机制: 在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
服务熔断一般有三种状态:
-
熔断关闭状态(Closed)
服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制。
-
熔断开启状态(Open)
后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法。
-
半熔断状态(Half-Open)
尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状态。
-

- 降级机制: 降级其实就是为服务提供一个兜底方案,一旦服务无法正常调用,就使用兜底方案。

4、常见的容错组件
(1)Hystrix
Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。
(2)Resilience4J
Resilicence4J一款非常轻量、简单,并且文档非常清晰、丰富的熔断工具,这也是Hystrix官方推
荐的替代产品。不仅如此,Resilicence4j还原生支持Spring Boot 1.x/2.x,而且监控也支持和prometheus等多款主流产品进行整合。
(3)Sentinel
Sentinel 是阿里巴巴开源的一款断路器实现,本身在阿里内部已经被大规模采用,非常稳定。
5、Sentinel
(1)Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。还可以做授权和黑白名单等。
(2)Sentinel分为两个部分:
-
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo /Spring Cloud 等框架也有较好的持。
-
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
6、订单微服务集成Sentinel
(1)安装Sentinel控制台
-
下载jar包 https://github.com/alibaba/Sentinel/releases
-
启动控制台
# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目) java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 - Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.5.jar
(2)通过浏览器访问localhost:8080 进入控制台 ( 默认用户名密码是 sentinel/sentinel )。
(3)在shop-order-server项目的pom文件中添加如下依赖。
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
(4)修改shop-order-server项目中的配置文件application.yml,新增如下配置:
spring:cloud:sentinel:transport:port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可dashboard: localhost:8080 # 指定控制台服务的地址
7、Sentinel容错的维度

流量控制:流量控制在网络传输中是一个常用的概念,它用于调整网络包的数据。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。
熔断降级:当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
系统负载保护:Sentinel 同时提供系统维度的自适应保护能力。当系统负载较高的时候,如果还持续让请求进入可能会导致系统崩溃,无法响应。在集群环境下,会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
拓展:熔断降级的维度
| 熔断维度 | 核心关注点 | 触发条件 | 典型场景 |
|---|---|---|---|
| 慢调用比例 | 响应时间 (RT) | 慢请求占比 > 阈值 | 服务变慢、网络卡顿 |
| 异常比例 | 错误率 | 异常占比 > 阈值 | 服务大量报错 |
| 异常数 | 报错总数 | 异常个数 > 阈值 | 低流量、核心业务 |
8、Sentinel规则种类
| 规则类型 | 核心关注点 | 典型应用场景 |
|---|---|---|
| 流控规则 | 限制流量大小 | 防止大促流量冲垮系统 |
| 降级规则 | 保护不稳定资源 | 下游服务变慢或报错时快速失败 |
| 系统规则 | 保护整个系统 | 服务器 CPU 或负载过高时自动限流 |
| 授权规则 | 限制调用方 | 接口黑白名单,防止非法调用 |
| 热点规则 | 限制特定参数 | 秒杀商品、高频访问的用户 ID |
拓展:阿里 Sentinel 和 Redis Sentinel
| 维度 | 阿里 Sentinel (流量治理) | Redis Sentinel (高可用) |
|---|---|---|
| 核心定位 | 微服务流量控制与熔断降级组件 | Redis 数据库的高可用保障方案 |
| 解决的问题 | 防止服务雪崩、限流、熔断 | 防止 Redis 主节点单点故障 |
| 工作机制 | 嵌入在应用代码中,拦截请求 | 独立的哨兵进程,监控 Redis 节点 |
| 故障处理 | 拒绝请求、返回降级数据 | 自动将 Slave 提升为新的 Master |
- 阿里 Sentinel 是为了保护微服务应用不被流量冲垮(防雪崩)。
- Redis Sentinel 是为了保护 Redis 数据库不因为主节点宕机而停服(高可用)。