1.负载均衡Ribbon
1.1.案例
在user-service中设置端口:
复制一个新的application,端口号设为9092
启动Eureka:
1.2.开启负载均衡
在consumer中加入LoadBalanced注解:
修改controller,不再手动获取ip和端口,而是直接通过服务名称调用:
1.3.负载均衡的策略
类名:RibbonLoadBalanceClient。
方法名:choose()
默认方法是轮询。
修改负载均衡配置规则(将轮询修改为随机):
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
2.熔断器Hystix
Hystix是Netflflix开源的一个延迟和容错库,用于隔离访问远程服务,防止出现级联失败。
Hystrix解决雪崩问题的手段,主要包括:
- 线程隔离
- 服务降级
线程隔离
Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,加 速失败判定时间。
用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超 时,则会进行降级处理,什么是服务降级?
服务降级:可以优先保证核心服务
用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回 友好的提示信息) 。
服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它
服务没有响应。 触发Hystrix服务降级的情况:
- 线程池已满
- 请求超时
服务降级实操:
在consumer中进行下列操作:
导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在application中加入注解:
package com.java.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//@SpringBootApplication
//@EnableDiscoveryClient //开启Eureka客户端
//@EnableCircuitBreaker //开启熔断
@SpringCloudApplication //可以代替上方三个注解
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
在controller中添加服务降级逻辑:
主要是加入queryByIdFallback方法以及加入@HystrixCommand(fallbackMethod = “queryByIdFallback”)注解。@HystrixCommand(fallbackMethod = “queryByIdFallBack”):用来声明一个降级逻辑的方法
package com.java.consumer.controller;
import com.java.consumer.pojo.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @Author: YNB
* @Description:
* @Date Created in 2021-01-11 20:56
* @Modified By:
*/
@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallback") //设置为下方的queryByIdFallback方法名
public String queryById(@PathVariable Long id) {
// String url = "http://localhost:9091/user/" + id; // http://localhost:9091/user/ 是userservice的地址
// List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("user-service");
// ServiceInstance serviceInstance = serviceInstanceList.get(0);
// String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/user/" + id;
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
public String queryByIdFallback(Long id) {
log.error("查询用户信息失败。id:{}", id);
return "对不起,网络太拥挤了!";
}
}
效果:
设置默认fallback:
将Fallback配置加载类上
package com.java.consumer.controller;
import com.java.consumer.pojo.User;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @Author: YNB
* @Description:
* @Date Created in 2021-01-11 20:56
* @Modified By:
*/
@RestController
@RequestMapping("/consumer")
@Slf4j
@DefaultProperties(defaultFallback = "defaultFallback")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
// @HystrixCommand(fallbackMethod = "queryByIdFallback") //设置为下方的queryByIdFallback方法名
@HystrixCommand
public String queryById(@PathVariable Long id) {
// String url = "http://localhost:9091/user/" + id; // http://localhost:9091/user/ 是userservice的地址
// List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("user-service");
// ServiceInstance serviceInstance = serviceInstanceList.get(0);
// String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/user/" + id;
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
public String queryByIdFallback(Long id) {
log.error("查询用户信息失败。id:{}", id);
return "对不起,网络太拥挤了!";
}
public String defaultFallback() {
return "默认提示:对不起,网络太拥挤了!";
}
}
效果:
超时设置:
设置yml文件:
spring:
application:
name: consumer-demo
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
registry-fetch-interval-seconds: 30
#超时设置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000
3.服务熔断
3.1.熔断原理
在服务熔断中,使用的熔断器,也叫断路器,其英文单词为:Circuit Breaker 熔断机制与家里使用的电路熔断原理类似;当如果电路发生短路的时候能立刻熔断电路,避免发生灾难。在分布式系统中应用服务熔断后;服务调用方可以自己进行判断哪些服务反应慢或存在大量超时,可以针对这些服务进行主动熔断,防止整个系统被拖垮。Hystrix的服务熔断机制,可以实现弹性容错;当服务请求情况好转之后,可以自动重连。通过断路的方式,将后续请求直接拒绝,一段时间(默认5秒)之后允许部分请求通过,如果调用成功则回到断路器关闭状态,否则继续打开,拒绝请求的服务
Hystrix的熔断状态机模型:
3.2.实操
consumer中进行修改:
加入一个id=1就会抛出异常的逻辑。
package com.java.consumer.controller;
import com.java.consumer.pojo.User;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @Author: YNB
* @Description:
* @Date Created in 2021-01-11 20:56
* @Modified By:
*/
@RestController
@RequestMapping("/consumer")
@Slf4j
@DefaultProperties(defaultFallback = "defaultFallback")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
// @HystrixCommand(fallbackMethod = "queryByIdFallback") //设置为下方的queryByIdFallback方法名
@HystrixCommand
public String queryById(@PathVariable Long id) {
// String url = "http://localhost:9091/user/" + id; // http://localhost:9091/user/ 是userservice的地址
// List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("user-service");
// ServiceInstance serviceInstance = serviceInstanceList.get(0);
// String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/user/" + id;
if (id == 1){
throw new RuntimeException("!!!!!!");
}
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
public String queryByIdFallback(Long id) {
log.error("查询用户信息失败。id:{}", id);
return "对不起,网络太拥挤了!";
}
public String defaultFallback() {
return "默认提示:对不起,网络太拥挤了!";
}
}
执行:
id=1:
id=2:
当执行多次id=1失败之后,在执行id=2:
等待几秒后恢复正常。
spring:
application:
name: consumer-demo
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
registry-fetch-interval-seconds: 30
#超时设置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000 #服务降级超时时间
circuitBreaker:
errorThresholdPercentage: 50 # 触发熔断错误比例阈值,默认值50%
sleepWindowInMilliseconds: 10000 # 熔断后休眠时长,默认值5秒
requestVolumeThreshold: 10 # 熔断触发最小请求次数,默认值是20