- 五、负载均衡 Spring Cloud Ribbon
- 六、熔断器 Spring Cloud Hystrix
- 七、远程调用 Spring Cloud Feign
跳转到目录
5.1 Ribbon 简介解决了集群服务中,多个服务高效率访问的问题。
什么是Ribbon?
Ribbon是Netflix发布的负载均衡器,有助于控制HTTP客户端行为。为Ribbon配置服务提供者地址列表后,Ribbon就可基于负载均衡算法,自动帮助服务消费者请求。
Ribbon默认提供的负载均衡算法:轮询,随机其他…。当然,我们可用自己定义负载均衡算法
实现负载均衡访问用户服务。
如果想要做负载均衡,我们的服务至少2个以上。所有第一步目标
实现步骤:
第一步:启动两个user_service服务
- 修改配置文件端口获取方式
- 编辑应用启动配置
- 启动两个提供者服务
- 在注册中心查询是否启动成功
第二步:开启消费者负载均衡
- 在RestTemplate的注入方法上加入@LoadBalanced注解
- 修改调用请求的Url地址,改为服务名称调用
- 访问页面查看效果
实现过程:
第一步:启动两个user_service应用
-
修改UserServiceApplication的application.yml配置文件
- 端口9091改为,
${port:9091}
- 端口9091改为,
-
编辑应用启动配置
-
复制一份UserServiceApplication
-
启动两个UserServiceApplication应用
-
Eureka服务中心可查看,注册成功
第二步:开启消费者调用负载均衡
Eureka已经集成Ribbon,所以无需引入依赖。
-
在RestTemplate的配置方法上添加
@LoadBalanced
注解即可@Bean @LoadBalanced//开启负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(); }
-
修改ConsumerController调用方式,不再手动获取ip和端口,而是直接通过服务名称调用
@GetMapping("{id}") public User queryById(@PathVariable Long id){ String url = String.format("http://user-service/user/%d", id); return restTemplate.getForObject(url,User.class); }
-
访问页面查看结果;并在9091和9092的控制台查看执行情况
配置修改轮询策略:Ribbon默认的负载均衡策略是轮询,通过如下
# 修改服务地址轮询策略,默认是轮询,配置之后变随机
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
SpringBoot可以修改负载均衡规则,配置为ribbon.NFLoadBalancerRuleClassName
格式{服务提供者名称}.ribbon.NFLoadBalancerRuleClassName
六、熔断器 Spring Cloud Hystrix跳转到目录
6.1 Hystrix 简介Hystrix,英文意思是豪猪,全身是刺,刺是一种保护机制。Hystrix也是Netflix公司的一款组件。
Hystrix的作用是什么?
Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库、防止出现级联失败也就是雪崩效应。
- 微服务中,一个请求可能需要多个微服务接口才能实现,会形成复杂的调用链路。
- 如果某服务出现异常,请求阻塞,用户得不到响应,容器中线程不会释放,于是越来越多用户请求堆积,越来越多线程阻塞。
- 单服务器支持线程和并发数有限,请求如果一直阻塞,会导致服务器资源耗尽,从而导致所有其他服务都不可用,从而形成雪崩效应;
Hystrix解决雪崩问题的手段,主要是服务降级**(兜底)**,线程隔离;
6.3 熔断案例**目标:服务提供者的服务出现了故障,服务消费者快速失败给用户友好提示。体验服务降级 **
实现步骤:
- 引入熔断的依赖坐标
- 开启熔断的注解
- 编写服务降级处理的方法
- 配置熔断的策略
- 模拟异常代码
- 测试熔断服务效果
实现过程:
-
引入熔断的依赖坐标:
- consumer_service中加入依赖
org.springframework.cloud spring-cloud-starter-netflix-hystrix
-
开启熔断的注解
//注解简化写法:微服务中,注解往往引入多个,简化注解可以使用组合注解。@SpringCloudApplication =等同于@SpringBootApplication+@EnableDiscoveryClient+@EnableCircuitBreaker @SpringBootApplication @EnableDiscoveryClient//开启服务发现 @EnableCircuitBreaker//开启熔断 public class ConsumerApplication { @Bean @LoadBalanced//开启负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class,args); } }
-
编写服务降级处理方法:使用@HystrixCommand定义fallback方法。
@RequestMapping("{id}") @HystrixCommand(fallbackMethod =" ") public String queryById(@PathVariable Long id){ String url = String.format("http://user-service/user/%d", id); return restTemplate.getForObject(url,String.class); } public String queryByIdFallback(Long id){ return "对不起,网络太拥挤了!"; }
-
配置熔断策略
- 常见熔断策略配置
- 熔断后休眠时间:sleepWindowInMilliseconds
- 熔断触发最小请求次数:requestVolumeThreshold
- 熔断触发错误比例阈值:errorThresholdPercentage
- 熔断超时时间:timeoutInMilliseconds
# 配置熔断策略: hystrix: command: default: circuitBreaker: # 原理分析中解释配置含义 # 强制打开熔断器 默认false关闭的。测试配置是否生效 forceOpen: false # 触发熔断错误比例阈值,默认值50% errorThresholdPercentage: 50 # 熔断后休眠时长,默认值5秒 sleepWindowInMilliseconds: 5000 # 熔断触发最小请求次数,默认值是20 requestVolumeThreshold: 10 execution: isolation: thread: # 熔断超时设置,默认为1秒 timeoutInMilliseconds: 2000
-
模拟异常代码
@GetMapping("{id}") @HystrixCommand(fallbackMethod ="queryByIdFallback") public String queryById(@PathVariable Long id){ //如果参数为1抛出异常,否则 执行REST请求返回user对象 if (id == 1){ throw new RuntimeException("too busy!!!"); } String url = String.format("http://user-service/user/%d", id); return restTemplate.getForObject(url,String.class); }
-
测试熔断的情况(模拟请求超时):
- 访问超时:服务提供者线程休眠超过2秒,访问消费者触发fallback方法。
@Service public class UserService { @Autowired private UserMapper userMapper; public User queryById(Long id){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return userMapper.selectByPrimaryKey(id); } }
- 服务不可用:停止user-service服务提供者。访问消费者触发fallback方法。
熔断器的原理很简单,如同电力过载保护器。
熔断器状态机有3个状态:
- 关闭状态,所有请求正常访问
- 打开状态,所有请求都会被降级。
- Hystrix会对请求情况计数,当一定时间失败请求百分比达到阈值,则触发熔断,断路器完全关闭
- 默认失败比例的阈值是50%,请求次数最低不少于20次
- 半开状态
- 打开状态不是永久的,打开一会后会进入休眠时间(默认5秒)。休眠时间过后会进入半开状态。
- 半开状态:熔断器会判断下一次请求的返回状况,如果成功,熔断器切回关闭状态。如果失败,熔断器切回打开状态。
熔断器的核心解决方案:线程隔离和服务降级。
- 线程隔离。
- 服务降级(兜底方法)。
线程隔离和服务降级之后,用户请求故障时,线程不会被阻塞,更不会无休止等待或者看到系统奔溃,至少可以看到执行结果(熔断机制)。
什么时候熔断:
- 访问超时
- 服务不可用(死了)
- 服务抛出异常(虽然有异常但还活着)
- 其他请求导致服务异常到达阈值,所有服务都会被降级
模拟异常测试:http://localhost:8080/consumer/1失败请求发送10次以上。再请求成功地址http://localhost:8080/consumer/2,发现服务被熔断,会触发消费者fallback方法。
6.5 扩展-服务降级的fallback方法:两种编写方式:编写在类上,编写在方法上。在类的上边对类的所有方法都生效。在方法上,仅对当前方法有效。
-
方法上服务降级的fallback兜底方法
- 使用HystrixCommon注解,定义
- @HystrixCommand(fallbackMethod=“queryByIdFallBack”)用来声明一个降级逻辑的fallback兜底方法
-
类上默认服务降级的fallback兜底方法
-
刚才把fallback写在了某个业务方法上,如果方法很多,可以将FallBack配置加在类上,实现默认FallBack
-
@DefaultProperties(defaultFallback=”defaultFallBack“),在类上,指明统一的失败降级方法;
-
@RestController @RequestMapping("/consumer") @DefaultProperties(defaultFallback = "defaultFallback")//开启默认的FallBack,统一失败降级方法(兜底) public class ConsumerController { @GetMapping("{id}") @HystrixCommand public String queryById(@PathVariable Long id){ //如果参数为1抛出异常,否则 执行REST请求返回user对象 if (id == 1){ throw new RuntimeException("too busy!!!"); } String url = String.format("http://user-service/user/%d", id); return restTemplate.getForObject(url,String.class); } /** * queryById的降级方法 */ public String queryByIdFallback(Long id){ return "对不起,网络太拥挤了!"; } /** * 默认降级方法 */ public String defaultFallback(){ return "默认提示:对不起,网络太拥挤了!"; } }
-
跳转到目录
前面学习中,使用RestTemplate大大简化了远程调用的代码:
String baseUrl = "http://user-service/user/findById?id=1"+ id;
User user = restTemplate.getForObject(baseUrl, User.class)
如果就学到这里,你可能以后需要编写类似的大量重复代码,格式基本相同,无非参数不一样。有没有更优雅的方 式,来对这些代码再次优化呢?
这就是接下来要学的Feign的功能了。
1.1 简介Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,是以Java接口的方式调用Http接口,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
Feign被广泛应用在Spring Cloud 的解决方案中,是学习基于Spring Cloud 微服务架构不可或缺的重要组件。
封装了Http调用流程,更符合面向接口化的编程习惯。 类似Dubbo服务调用。
项目主页:https://github.com/OpenFeign/feign
1.2 入门案例使用Feign替代RestTemplate发送Rest请求。使之更符合面向接口化的编程习惯。
实现步骤:
- 导入依赖feign的starter
- 启动引导类加@EnableFeignClients注解
- 编写FeignClient接口,使用SpringMVC的注解
- 在Controller中注入Feign接口,直接调用,无需实现类
- 访问接口测试
实现过程:
-
导入依赖feign的starter
org.springframework.cloud spring-cloud-starter-openfeign
-
启动引导类加@EnableFeignClients注解
@SpringCloudApplication @EnableFeignClients//开启Feign功能 public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class,args); } }
Feign中已经自动集成Ribbon负载均衡
-
编写FeignClient接口,使用SpringMVC的注解
-
在consumer_service中编写Feign客户端接口UserService
-
@FeignClient("user-service")//指定feign调用的服务 public interface UserService { @RequestMapping("/user/findById") User findById(@RequestParam("id") Integer id); }
- Feign会通过动态代理,帮我们生成实现类。
- 注解@FeignClient声明Feign的客户端接口,需指明服务名称
- 接口定义的方法,采用SpringMVC的注解。Feign会根据注解帮我们逆向生成URL地址然后请求
-
-
在Controller中注入UserService接口,直接调用,无需实现类
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@Autowired
UserService userService;
@Autowired
DiscoveryClient discoveryClient;
@RequestMapping("/consumer/{id}")
public String hello(@PathVariable("id") Integer id){
String url = "http://user-service/user/findById?id="+id;
String jsonResult = restTemplate.getForObject(url, String.class);
return jsonResult;
}
@RequestMapping("/feignconsumer/{id}")
public User hellofeign(@PathVariable Integer id){
return userService.findById(id);
}
}
- 启动测试:访问接口http://localhost:8080/feignconsumer/1,正常获取结果
Feign实现原理简单分析:
负载均衡是远程过程调用必备的要素。Feign本身集成了Ribbon,因此不需要额外引入依赖,也不需要再注册RestTemplate对象。即可无感知使用负载均衡这一特性。
Fegin内置Ribbon默认设置了连接超时,是1000毫秒(1秒)。和读取超时时间。我们可以通过手动配置来修改。Ribbon内部有重试机制,一旦超时,会自动重新发起请求。如果不希望重试,可以修改。
# 连接超时时长
Ribbon.ConnectTimeout: 1000
# 数据通信超时时长
Ribbon.ReadTimeout: 2000
# 当前服务器的重试次数
Ribbon.MaxAutoRetries: 0
# 重试多少次服务
Ribbon.MaxAutoRetriesNextServer: 0
# 是否对所有的请求方式都重试
Ribbon.OkToRetryOnAllOperations: false
1.4 熔断器支持
Feign本身也集成Hystrix熔断器,starter内查看。
服务降级方法实现步骤:
- 在配置文件application.yml中开启feign熔断器支持
- 编写FallBack处理类,实现FeignClient客户端接口
- 在@FeignClient注解中,指定FallBack处理类。
- 测试服务降级效果
实现过程:
-
在配置文件application.yml中开启feign熔断器支持:默认关闭
feign: hystrix: enabled: true # 开启Feign的熔断功能
-
定义一个类UserServiceFallBack,实现刚才编写的UserFeignClient,作为FallBack的处理类
@Component//需要注意:一定要注入Spring 容器 public class UserServiceFallBack implements UserService { @Override public User findById(Integer id) { User user = new User(); user.setId(id); user.setUsername("用户不存在!!!"); return user; } }
-
在@FeignClient注解中,指定FallBack处理类。。
@FeignClient(value = "user-service",fallback = UserServiceFallBack.class) public interface UserService { @RequestMapping("/user/findById") User findById(@RequestParam("id") Integer id); }
-
重启测试:关闭user_service服务,然后在页面访问;http://localhost:8080/feignConsumer/2
SpringCloudFeign支持对请求和响应进行GZIP压缩,以提升通信过程中的传输速度。
通过配置开启请求与响应的压缩功能:
# 开启请求压缩
feign.compression.request.enabled: true
# 开启响应压缩
feign.compression.response.enabled: true
也可以对请求的数据类型,以及触发压缩的大小下限进行设置
# 设置压缩的数据类型
feign.compression.request.mime-types: text/html,application/xml,application/json
# 设置触发压缩的大小下限
feign.compression.request.min-request-size: 2048
1.6 配置日志级别
在发送和接收请求的时候,Feign定义了日志的输出定义了四个等级:这里我们配置测试一下。
级别说明NONE不做任何记录BASIC只记录输出Http 方法名称、请求URL、返回状态码和执行时间HEADERS记录输出Http 方法名称、请求URL、返回状态码和执行时间 和 Header 信息FULL记录Request 和Response的Header,Body和一些请求元数据实现步骤:
- 在application.yml配置文件中开启日志级别配置
- 编写配置类,定义日志级别bean。
- 在接口的@FeignClient中指定配置类
- 重启项目,测试访问
实现过程:
-
在consumer_service的配置文件中设置com.itheima包下的日志级别都为debug
-
# com.itheima 包下的日志级别都为Debug logging.level: com.itheima: debug
-
-
在consumer_service编写配置类,定义日志级别
-
@Configuration public class FeignConfiguration { @Bean public Logger.Level feignLoggerLevel(){ //记录所有请求和响应的明细,包括头信息,请求体,元数据 return Logger.Level.FULL; } }
-
-
在consumer_service的FeignClient中指定配置类
-
@FeignClient(value="user-service",fallback = UserServiceFallBack.class,configuration = FeignConfig.class) public interface UserService { @RequestMapping("/user/{id}") User queryById(@PathVariable("id") Long id); }
-
-
重启项目,即可看到每次访问的日志