“终有一天,你会静下心来,像个局外人一样 看着自己的故事,笑着摇摇头”


feign与openfeign

feign和openfeign都可以实现微服务间的相互调用。

  • feign是spring提供的一种RESTful的HTTP服务客户端的组件,通过发送http请求实现微服务间的调用
  • openfeign,名字和feign很像,那一定和feign有关系(可能是私生子),openfeign在feign的基础上集成了springMVC,也就意味着我们可以使用springMVC注解的方式调用微服务。

微服务调用

我这里有三个微服务,comuser调用provider1和provider2,可以理解为消费者和提供者,他们本质都是微服务,这样命名只是方便理解

微服务 使用其他服务的mapper层 微服务间的调用_ide


-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();
    }

访问

微服务 使用其他服务的mapper层 微服务间的调用_ide_02


没问题,可以调用。

但是我们的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,我们不需要再引依赖

微服务 使用其他服务的mapper层 微服务间的调用_微服务 使用其他服务的mapper层_03


但是我们需要在配置里面开启openfeign的熔断

feign:
  hystrix:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 5000

默认超时时间是3秒,我们可能需要设置久一点

然后将服务提供者provider1停掉

访问

微服务 使用其他服务的mapper层 微服务间的调用_微服务_04


发现并没有报错,而是返回了我们在熔断类里面定义的返回信息,至此熔断就完成了、

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)

然后我调用了两次,发现就有日志输出了

微服务 使用其他服务的mapper层 微服务间的调用_微服务 使用其他服务的mapper层_05

openfeign就介绍到这里咯!!!