本篇文章仅作为近日参考其他文章后,自己实践的记录和总结,场景到细节尚有很多不足,有待补充和修正。
Feign集成了Ribbon、RestTemplate实现了负载均衡的执行Http调用,只不过对原有的方式(Ribbon+RestTemplate)进行了封装,开发者不必手动使用RestTemplate调服务,而是定义一个接口,在这个接口中标注一个注解即可完成服务调用,这样更加符合面向接口编程的宗旨,简化了开发。
Feign现在停止迭代了,OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
在启动类上添加 @EnableFeignClients 注解以启用OpenFeign
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients//*启用Feign
public class ConsumerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ConsumerApplication.class).web(true).run(args);
}
}
创建Feign类,这样就为目标服务springcloud-producer提供的接口生成了一个代理客户端,可以直接调用feignClient的方法来访问目标接口。
//value为要调用的服务名(spring.application.name)
@FeignClient(value = "springcloud-producer")
public interface UserClient {
//和要调用的服务方法描述一致
@RequestMapping("/queryUsers")
List<User> queryUsers();
}
与ribbon配置类似,feign也可以通过Configuration类或yaml来实现策略配置。
方式一:
feign有一个默认的配置org.springframework.cloud.netflix.feign.FeignClientsConfiguration.class,我们可以使用@Configuration创建一个配置类,使用@Bean注解来定义同名bean以覆盖默认的配置。
@Configurable
@ConditionalOnClass(Feign.class)
@AutoConfigureAfter(EnableFeignClients.class)
public class FeignAutoConfiguration {
/**
* 使用自定义 feign client 客户端 注意:在使用 SentinelFeign.builder() 来创建 SentinelFeign 客户端作为 Feign
* 客户端
* @return
*/
@Bean
@Primary
public Feign.Builder client() {
return new Feign.Builder();
}
/**
* 配置 feign 日志打印等级
* @return
*/
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
然后在每个@FeignClient注解中,即可以指定自定义的Configuration
@FeignClient(value = CommonConstant.SERVICE_PRODUCER,configuration = {FeignAutoConfiguration.class})
也可以在@EnableFeignClients注解中将之设置为默认配置。
方式二:
在yaml文件中添加配置
#常规的配置
feign:
sentinel:
enabled: true
okhttp:
enabled: true
httpclient:
enabled: false
client:
config:
default:
connectTimeout: 100000
readTimeout: 1000000
LoggerLevel: FULL
compression:
request:
enabled: true
min-request-size: 2048
mime-types: text/xml,application/xml,application/json
response:
enabled: true
日志级别
在feign.Logger.class类中内嵌了一个枚举类型Level,包括了四个日志级别
日志级别 | 说明 |
---|---|
NONE | 不打印日志 |
BASIC | 只记录请求方法、URL、响应代码以及执行时间等基本信息 |
HEADERS | 打印请求和响应的头部信息 |
FULL | 打印详尽日志 |
自定义日志级别首先要在yaml添加配置,以启用DEBUG模式
logging:
level:
#Feign client的类名或所在包名,这里的值只有DEBUG可选
com.ffcs.common.business.org.feign.MyFeign: DEBUG
至于日志级别的配置可以通过方式一提供Bean的方式
/**
* 配置 feign 日志打印等级
* @return
*/
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
或者方式二在yaml中配置
feign:
client:
config:
#这里default就是全局配置,如果是写服务名称,则是针对某个微服务的配
default:
LoggerLevel: FULL
其他待补充
Hystrix意为“断路器”,就和我们生活中的保险丝,开关一个道理。Hystrix是由Netflix开源的一个服务隔离组件,通过服务隔离来避免由于依赖延迟、异常,引起资源耗尽导致系统不可用的解决方案。主要作用有:
服务降级
在访问某个服务接口时,如果发生超时等不可知的异常时,可以断开访问并直接返回一个有好的返回,虽然结果不是正常想要的,但是可以避免异常带来的连锁反应,以此可以保障分布式环境中微服务间相互访问的容错性。
服务熔断
在分布式系统中,就是调用一个系统时,在一定时间内,这个服务发生的错误次数达到一定的值时, 我们就打开这个断路器,不让调用过去,而是让他直接去调用降级方法。再过一段时间后,当一次调用时,发现这个服务通了,就将这个断路器改为“半开”状态,让调用一个一个的慢慢过去,如果一直没有发生错误,就将这个断路器关闭,让所有的服务全部通过。
服务限流
服务限流就容易理解多了,顾名思义,这是对访问的流量进行限制,就比如上边的场景,我们还可能通过服务限流的方法来解决高并发以及秒杀等问题。主流的限流算法主要有:漏桶算法和令牌算法
引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
在启动类上添加 @EnableCircuitBreaker 注解来开启服务熔断
在服务提供端可以使用@HystrixCommand注解来实现
PS: 实际上commandProperties 属性的作用还有待探讨,这里只是简单占个位置
@HystrixCommand(
fallbackMethod = "getIdError",
commandProperties = {
@HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds" ,value ="3000" )
}
)
public String getId(String id) {
return id;
}
public String getIdError(String id) {
return id;
}
# hystrix 配置
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE #THREAD|SEMAPHORE
thread:
timeoutInMilliseconds: 1200000 #超时时间,单位ms,默认为1000
semaphore:
maxConcurrentRequests: 300000 #最大并发请求量,默认10
circuitBreaker:
requestVolumeThreshold: 10 #触发熔断的最小请求次数,默认20
errorThresholdPercentage: 10000 #触发熔断的失败请求最小占比,默认50%
sleepWindowInMilliseconds: 100000 #触发熔断后的服务休眠时长,休眠结束服务接口将再次启用,默认是5000毫秒
shareSecurityContext: true
资源隔离策略
隔离的目的限制对共享资源的并发访问量(服务限流)
通过yaml文件配置,路径如下
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE #THREAD|SEMAPHORE
Hystrix的资源隔离策略有信号量(SEMAPHORE) 和线程池(THREAD)
1.信号量隔离: 主要是使用一个原子的计数器来记录当前的值,请求来临之前,首先判断计数器的值是否已经达到了设置的最大值,如果没有,则继续运 行,计数器+1;反之,请求处理结束返回后,计数器进行-1,和令牌桶有点像,但是无法处…
2.线程池隔离: 针对不同的服务设置不同的线程池,这样如果其他服务的线程阻塞的时候不会对其他服务造成影响。
二者比较
比较项 | THREAD | SEMAPHORE |
---|---|---|
线程 | 与调用线程非相同线程 | 与调用线程相同(jetty线程) |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 可以是异步,也可以是同步。看调用的方法 | 同步调用,不支持异步 |
并发支持 | 支持(最大线程池大小hystrix.threadpool.default.maximumSize) | 支持(最大信号量上限maxConcurrentRequests) |
是否超时 | 支持,可直接返回 | 不支持,如果阻塞,只能通过调用协议(如:socket超时才能返回) |
是否支持熔断 | 支持,当线程池到达maxSize后,再请求会触发fallback接口进行熔断 | 支持,当信号量达到maxConcurrentRequests后。再请求会触发fallback |
隔离原理 | 每个服务单独用线程池 | 通过信号量的计数器 |
资源开销 | 大,大量线程的上下文切换,容易造成机器负载高 | 小,只是个计数器 |
其他待补充
在Hystrix的简单使用中,是Hystrix在服务提供端的使用,而Feign+Hystrix是作为请求端设置的,是最为经典的使用方式。
首先是引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
feign和hystrix的基础配置可以参考前面的内容,这里重要的是feign启用hystrix
feign:
hystrix:
enabled: true
首先需要一个feign client,并指定TestFeignFallbackFactory(服务降级实现类工厂)
@FeignClient(contextId = "testFeign1", value = “TEST-APP”, configuration = FeignAutoConfiguration.class, fallbackFactory = TestFeignFallbackFactory.class)
public interface TestFeign {
@PostMapping("/test/testReq")
public String testReq(@RequestBody TestVo testVo);
}
定义FeignFallbackImpl,提供服务降级的方法
@Component
@Slf4j
public class TestFeignFallbackImpl implements TestFeign {
@Setter
private Throwable cause;
@Override
public String testReq(TestVo testVo) {
log.error("feign接口异常", cause);
return "feign接口异常";
}
}
定义FeignFallbackFactory,实现FallbackFactory的create方法来提供服务降级实现类对象的创建
@Component
public class TestFeignFallbackFactory implements FallbackFactory<TestFeign> {
@Override
public TestFeign create(Throwable throwable) {
TestFeignFallbackImpl testFeignFallback = new TestFeignFallbackImpl();
testFeignFallback .setCause(throwable);
return testFeignFallback ;
}
}