Feign是一个声明式的web service客户端。(简化接口调用)
Ribbon是客户端负载均衡工具
由于feign集成了ribbon的方法,因此简单搭建时只需要一个openfeign依赖即可
org.springframework.cloud
spring-cloud-starter-openfeign
server:
port: 8200
spring:
application:
name: consumer-server
profiles: dev
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7001/eureka/
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true #开启HYSTRIX,不然降级无法使用
package team.csrj.consumerserver.remote;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
/**
* 接口式定义api
* fallbackFactory:定义降级处理的方法在哪
*
* fallback只是重写了回退方法。
* fallfactory层面比较深,因为它可以调出底层异常。
*/
@Component
@FeignClient(value = "producer-8001",fallbackFactory= ProducerRemoteHystrix.class)
public interface ProducerRemote {
@GetMapping("/hello")
String getPort();
}
package team.csrj.consumerserver.remote;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
@Component
public class ProducerRemoteHystrix implements FallbackFactory {
@Override
public ProducerRemote create(Throwable throwable) {
return new ProducerRemote(){
@Override
public String getPort() {
return "该服务异常,请稍后重试";
}
};
}
}
package team.csrj.consumerserver.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import team.csrj.consumerserver.remote.ProducerRemote;
@RestController
public class ConsumerController {
@Autowired
private ProducerRemote producerRemote;
@RequestMapping("/hello")
public String get(){
return producerRemote.getPort();
}
}
package team.csrj.consumerserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class ConsumerServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerServerApplication.class, args);
}
}
只要在消费者端的application.yml中加入以下配置即可
producer-server: #服务端的注册id
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #在此处输入需要的算法类
Hystrix提供了如下的几个关键参数,来对一个熔断器进行配置:
circuitBreaker.requestVolumeThreshold //滑动窗口的大小,默认为20 circuitBreaker.sleepWindowInMilliseconds //过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟 circuitBreaker.errorThresholdPercentage //错误率,默认50% |
3个参数放在一起,所表达的意思就是:
每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。
这里面有个很关键点,达到熔断之后,那么后面它就直接不去调该微服务。那么既然不去调该微服务或者调的时候出现异常,出现这种情况首先不可能直接把错误信息传给用户,所以针对熔断
我们可以考虑采取降级策略。所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。
@controllerAdvice或者HandlerExceptionResolver是不能直接捕获到FeignException,所以需要在Feign层面拿到具体异常重新封装。即消费者调用服务端时,服务端抛出异常,消费者端无法捕获,只能设置全局的feign异常处理才能接收服务端抛出的异常。
@Configuration
public class FeignExceptionConfig implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
Integer status = null; // 错误状态码
String message = null; // 错误信息
try {
switch (response.status()) {
case 500:
if (response.body() != null) {
JSONObject body = JSONObject.fromObject(Util.toString(response.body().asReader()));
status = body.getInt("status");
message = body.getString("message");
}
break;
}
} catch (Exception e) {
status = 500;
message = "System is analysis body error";
e.printStackTrace();
}
return new Exception(status, message);
}
}