“终有一天,你会静下心来,像个局外人一样 看着自己的故事,笑着摇摇头”
feign与openfeign
feign和openfeign都可以实现微服务间的相互调用。
- feign是spring提供的一种RESTful的HTTP服务客户端的组件,通过发送http请求实现微服务间的调用
- openfeign,名字和feign很像,那一定和feign有关系(可能是私生子),openfeign在feign的基础上集成了springMVC,也就意味着我们可以使用springMVC注解的方式调用微服务。
微服务调用
我这里有三个微服务,comuser调用provider1和provider2,可以理解为消费者和提供者,他们本质都是微服务,这样命名只是方便理解
-Restful的方式
之前如果我们想要在项目里面发送http请求,可以使用http client,以及其他的一些技术,spring提供了Restemplate发送http请求,能够大幅度提高我们的编程效率。我们也可以使用他调用微服务
添加controller类
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@AccessLog
@RequestMapping(value = "/test1",method = RequestMethod.GET)
public String test1() {
String url = "http://192.168.1.197:9001/test1";
ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
return forEntity.getBody();
}
- 注入Resttemplate bean
- 发送http请求,第一个参数为url,第二个参数为返回的类型,可以根据自己的需求更改,getForEntity是发送一个get请求,restemplate提供了很多的方法给我们发送请求,有需要的可以去了解。
添加bean,可以在主类加,也可以通过@configuration添加bean
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
// @LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
访问
没问题,可以调用。
但是我们的url使用了ip地址,这样就很愚蠢了,难道我ip变了,我就要来修改代码吗?这样显然不行,不符合java的编程规范
我们将restemplate bean的@LoadBalanced注解放开,此注解是提供负载均衡的,负载的方式是轮询,也就是如果访问的微服务有多个实例,他会以轮询的方式访问
然后修改controller类,将ip改为微服务名称,使用ip会报错
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/test1",method = RequestMethod.GET)
public String test1() {
String url = "http://ts-provider1/test1";
ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
return forEntity.getBody();
}
访问发现也没问题
- openfeign方式
使用springMVC注解方式调用微服务,相比于restTemplate,你会更喜欢这种方式,可读性强,代码也简洁,而且可以配置熔断和日志
引入依赖
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '2.0.2.RELEASE'
编写feign接口
@FeignClient(name = "ts-provider1", fallback = testHys.class, configuration = FeignInterceptorConfig.class)
public interface TestFeign {
@RequestMapping(value = "/test1", method = RequestMethod.GET)
String testFn1();
@GetMapping(value = "/test2/{id}")
String testFn2(@PathVariable("id") int id);
}
@Component
class testHys implements TestFeign {
@Override
public String testFn1() {
return "server error";
}
@Override
public String testFn2(int id) {
return "server error id=" + id;
}
}
- 使用@FeignCilent注解标识feign类
- name:表示微服务的名称
- fallback:熔断的处理类
- configuration :feign的配置,配置在日志的时候介绍
使用springMVC注解,接口是服务提者相同的接口,返回类型和提供者返回的一致
传参方式和接受参数使用的注解一样 - testHys :为熔断类,使用@Component注解将他注册为一个bean提供调用,然后实现我们的feign client接口,编写熔断后我们要干的事情,我这里就返回了一个字符串
- feigncilent注解还提供了很多参数,比如url,我们可以不使用微服务名,直接使用url=ip:port的形式调用微服务
controller调用feign
public class ConsumerController {
@Autowired
TestFeign testFeign;
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/test1",method = RequestMethod.GET)
public String test1() {
// String url = "http://ts-provider1/test1";
// ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
// return forEntity.getBody();
return testFeign.testFn1();
}
@RequestMapping(value = "/test2/{id}",method = RequestMethod.GET)
public String test2(@PathVariable("id") int id) {
return testFeign.testFn2(id);
}
访问发现也没问题。
附上服务提供者provider1的接口
@GetMapping("/test1")
public String test1() {
String info = "uri=test1,我是服务提供者一。欢迎光临。";
logger.info(info);
return info;
}
@GetMapping("/test2/{id}")
public String test2(@PathVariable int id) {
return "test2: id=" + id;
}
然后我们测试熔断
openfeign内部已经集成了hystrix,我们不需要再引依赖
但是我们需要在配置里面开启openfeign的熔断
feign:
hystrix:
enabled: true
client:
config:
default:
connectTimeout: 5000
默认超时时间是3秒,我们可能需要设置久一点
然后将服务提供者provider1停掉
访问
发现并没有报错,而是返回了我们在熔断类里面定义的返回信息,至此熔断就完成了、
openfeign日志
默认日志是不开启的,需要我们自己配置
@Configuration
public class FeignInterceptorConfig {
@Bean
Logger.Level feignLevel() {
return Logger.Level.BASIC;
}
// @Bean
// public RequestInterceptor requestInterceptor() {
// RequestInterceptor requestInterceptor = template -> {
// //传递日志traceId
// String traceId = MDC.get("traceId");
// template.header("traceId", traceId);
// };
// return requestInterceptor;
// }
@Bean
Logger FeignLog(){
return new FeignLogger();
}
- 第一个bean表示feign日志的级别,注意不是log日志的级别,默认为NONE,所有我们斌不能看见调用日志
- NONE:不输出日志
- BASIC:输出请求方法、URL、响应状态码、执行时间
- HEADERS:基本信息以及请求和响应头
- FULL:请求和响应的heads、body、metadata,建议使用这个级别
- 第二个注释的bean是用于向调用的服务传递参数的,有需求可以加上,我这里注释了
- 第三个bean是加载自己配置的日志类,正常这个应该可以不用配置,但由于我的项目使用的日志是log4j2,所有需要自己配置,不然即使配置了,也不会有日志输出,如果不需要可以跳过这个配置
重写feign的logger,使用自己的logger
package com.byb.consumer.config;
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class FeignLogger extends feign.Logger {
Logger logger;
public FeignLogger() {
this(feign.Logger.class);
}
public FeignLogger(Class<?> clazz) {
this(LoggerFactory.getLogger(clazz));
}
public FeignLogger(String name) {
this(LoggerFactory.getLogger(name));
}
FeignLogger(Logger logger) {
this.logger = logger;
}
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
if (logger.isInfoEnabled()) {
super.logRequest(configKey, logLevel, request);
}
}
@Override
protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime)
throws IOException {
if (logger.isInfoEnabled()) {
return super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);
}
return response;
}
@Override
protected void log(String configKey, String format, Object... args) {
if (logger.isInfoEnabled()) {
logger.info(String.format(methodTag(configKey) + format, args));
}
}
}
这里的logger导入你自己使用的日志框架的logger就行
然后修改配置文件
logging:
level:
com.byb.consumer: info
level下面是你自己的包名,后面info是log日志的级别
然后在@Feigncilent注解里面加入我们的配置
@FeignClient(name = "ts-provider1", fallback = testHys.class, configuration = FeignInterceptorConfig.class)
然后我调用了两次,发现就有日志输出了
openfeign就介绍到这里咯!!!