Ribbon,Feign,Hystrix,Hystrix-Dashboard,Zuul
Ribbon
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
客户端负载均衡工具。
常见的负载均衡软件:Nginx,Lvs
分类:
- 集中式Load Balance。在服务消费方和提供方使用都市Lb实时。Nginx。有lb实时负责把请求转发至服务提供方
- 进程式LB。将LB集中到消费方,消费方从注册中心获取有哪些地址可用,再从地址中选出一个合适的服务器。例:Ribbon
Ribbon默认轮询
自定义均衡策略:
主义:自定义负载均衡配置类不能喝启动类放在一个包下
-
启动类添加注解
//name:自定义均衡的服务提供者,configure:自定义负载均衡类 @RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = MyRule.class)
-
自定义均衡策略类
package com.moral.myrule; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import java.util.List; import java.util.concurrent.ThreadLocalRandom; //自定义负载均衡策略,每个服务使用5次 public class MyRandomRule extends AbstractLoadBalancerRule { public MyRandomRule() { } private int total = 0;//如果=5指向下一个节点 private int currentIndex = 0; public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } else { Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> upList = lb.getReachableServers(); List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } /*int index = this.chooseRandomInt(serverCount); server = (Server)upList.get(index);*/ if (total < 5) { server = upList.get(currentIndex); total++; } else { total = 0; currentIndex++; if (currentIndex > upList.size()) { currentIndex = 0; } upList=lb.getReachableServers(); server = upList.get(currentIndex); } if (server == null) { Thread.yield(); } else { if (server.isAlive()) { return server; } server = null; Thread.yield(); } } return server; } } protected int chooseRandomInt(int serverCount) { return ThreadLocalRandom.current().nextInt(serverCount); } public Server choose(Object key) { return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) { } }
-
配置bean
@Configuration public class MyRule { @Bean public IRule myRule11(){ return new MyRandomRule(); } }
Fign集成了ribbon
ribbon通过服务名字调用,feign通过接口+注解调用
注意:接口上的路径必须和服务提供者提供的路径一样
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
接口
@Component
//value:服务名字 多个服务提供最用同一个spring.application.name
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {
@PostMapping("/dept/add")
public boolean addDept(Dept dept);
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
}
Feign
Feign替代了restTemplate访问
访问时路径不必和服务提供者和接口路径一样
访问路径:localhost:80/consumer/dept/list
@RestController
@RequestMapping("/consumer/dept")
public class DeptController {
@Autowired
private DeptClientService deptClientService =null;
@RequestMapping("add")
public boolean addDept(Dept dept) {
return deptClientService.addDept(dept);
}
@RequestMapping("get/{id}")
public Dept queryById(@PathVariable("id") Long id) {
return deptClientService.queryById(id);
}
@RequestMapping("list")
public List<Dept> queryAll() {
return deptClientService.queryAll();
}
}
Hystrix 熔断
服务熔断
在服务提供方做
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
yml配置:
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.moral.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring配置
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/db01?seUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
#Eureka配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7003.com:7003/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/
instance:
instance-id: springcloud-provider-hystrix-dept8001 #修改eureka上默认描述信息
prefer-ip-address: true #true,可以显示服务的ip
#eureka上本服务信息
info:
app.name: springcloud-provider-dept8001
company.name: com.moral
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient//服务发现
@EnableCircuitBreaker//添加熔断支持
public class HystrixDeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDeptProvider_8001.class, args);
}
}
controller
@RestController
@RequestMapping("/dept")
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("get/{id}")
@HystrixCommand(fallbackMethod = "hystrixQueryById")//失败时调用的方法
public Dept queryById(@PathVariable("id") Long id) {
Dept dept = deptService.queryById(id);
if (dept == null) {
throw new RuntimeException("id=>" + id + ",不存在该用户");
}
return dept;
}
//备选方法
public Dept hystrixQueryById(@PathVariable("id") Long id) {
return new Dept().setDeptno(id).setDname("id=>" + id + ",不存在该用户@Hystrix").setDb_source("no database of mysql");
}
}
服务降级
在服务消费端,客户端做
api:
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public boolean addDept(Dept dept) {
return false;
}
@Override
public Dept queryById(Long id) {
return new Dept().setDeptno(id)
.setDname("id=>" +id+"没有对应的信息,客户端提供了降级的信息,这个服务现在已被关闭")
.setDb_source("no database");
}
@Override
public List<Dept> queryAll() {
return null;
}
};
}
}
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@PostMapping("/dept/add")
public boolean addDept(Dept dept);
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
}
服务消费端配置;
# 开启降级
feign:
hystrix:
enabled: true
启动eureka集群,启动普通服务提供者,启动feigin服务消费者,此时浏览器访问正常,关闭服务提供者时再访问,不会出现异常,会返回DeptClientServiceFallbackFactory中自定义的失败返回信息
服务熔断和服务降级对比
- 熔断:服务端,某个服务超市或异常,引起熔断,保险丝
- 降级:客户端,从整体网站请求负载考虑,当某个服务熔断或者关闭之后,服务将不再被调用
- 此时客户端可以准备一个FallbackFactory,返回一个默认值(缺省值),整体服务水平下降,但是,好歹能用,比直接挂掉强
Hystrix Dashboard
dashboard项目
-
创建dashboard子项目,配置port
-
启动类:
@SpringBootApplication @EnableHystrixDashboard//开启监控页面 public class DeptConsumerDashboard_9001 { public static void main(String[] args) { SpringApplication.run(DeptConsumerDashboard_9001.class, args); } }
服务提供方provider
服务提供方需添加依赖
<!--eureka完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient//服务发现
@EnableCircuitBreaker//添加熔断支持
public class HystrixDeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDeptProvider_8001.class, args);
}
//固定代码
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
监控流程
-
http://localhost:8001/actuator/hystrix.stream查看流信息,需访问接口才能看到动态数据
-
监控页面填写http://localhost:8001/actuator/hystrix.stream,进入监控具体页面
4.访问接口http://localhost/consumer/dept/get/7,就能看到页面刷新
5.访问不存在的id,就会触发配置好的熔断降级,上图失败/异常数就会增加1,错误百分比也会增加
Zuul网关
新建zuul子模块,也需要注册到eureka
修改本地hosts文件,添加映射127.0.0.1 www.moral.com
依赖:
<!--zuul-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
yml配置
server:
port: 9527
eureka:
client:
service-url:
defaultZone: http://eureka7003.com:7003/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/
instance:
instance-id: zuul9527.com
prefer-ip-address: true
info:
app.name: springcloud
company.name: moral
spring:
application:
name: springcloud-zuul
启动类
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class, args);
}
}
启动eureka集群,启动服务提供者,启动zuul
访问:http://www.moral.com:9527/springcloud-provider-dept/dept/get/1就可以访问
此时添加yml配置
zuul:
routes:
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/** # http://www.moral.com:9527/mydept/dept/get/1也可访问
#ignored-services: springcloud-provider-dept #不让通过http://www.moral.com:9527/springcloud-provider-dept/dept/get/1访问
ignored-services: '*' #忽略所有通过服务名访问的请求
prefix: /jpy #配置统一访问前缀
就可以通过:http://www.moral.com:9527/mydept/dept/get/1访问,添加ignoreed-services后可以禁止通过服务名访问
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY