OpenFeign笔记

0 环境

系统环境:win10
编辑器:idea
springcloud版本:H版

1 前言

之前使用的eureka/hystrix 都是调用RestTemplate(繁琐 重复高) OpenFeign对请求进行简化。Feign停更了 OpenFeign是在Feign基础上开发出来的

  • 常用的几种接口调用方法
  • Httpclient 易用 灵活
  • Okhttp 处理网络请求 轻量级 支持多协议。。
  • HttpURLConnection 使用复杂
  • RestTemplate Rest服务的客户端 提供多种便携访问HTTP服务的方法

2 尝鲜

2.1 创建springboot项目

在这里插入图片描述

2.2 yml配置

spring:
  application:
    name: openfeign


eureka:
  client:
    service-url:
      defaultZone: http://localhost:1234/eureka

server:
  port: 5000

2.3 启动类配置

@EnableFeignClients --> 开启Feign在这里插入图片描述

2.4 接口配置

使用的是之前的eureka server和provider以及如今使用的openfeign

// openfeign service
// 对比之前xxx.getForObject("http://provider/hello1", String.class)
// 现在只需要抽取provider hello1 拼接不需要我们操心了
@FeignClient("provider")
public interface HelloService {
	@GetMapping("/hello")
    // 方法名无所谓 无参调用
    String hello();
}    

2.5 接口调用

@RestController
public class HelloController {
    @Autowired
    HelloService helloService;

    // 无参测试
    @GetMapping("/hello")
    public String hello(){
        return helloService.hello();
    }
}    

2.6 测试结果

开启eureka server provider openfeign
在这里插入图片描述

3 参数传递

3.1 导入依赖模块

因为要用到类 之前新建一个模块 现在该opfeign需要引入依赖 可以在opfeign中定义类(随意)

		<dependency>
            <groupId>xxx</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

3.2 接口配置

// eureka provider
@RestController
public class HelloController {
	
	@GetMapping("/hello1")
    public String hello1(String name){
        return "hello provider: " + name;
    }
	
	@PostMapping("/user1")
    public User addUser1(@RequestBody User user){
        return user;
    }
	
	@DeleteMapping("/user1/{id}")
    public void delUser1(@PathVariable Integer id){
        System.out.println("json形式:" + id);
    }

    @GetMapping("/user2")
    public void getUserByName(@RequestHeader String name) throws UnsupportedEncodingException {
        // 解码
        System.out.println(URLDecoder.decode(name, "utf-8"));
    }
}    
// openfeign service配置
@FeignClient("provider")
public interface HelloService {
    // 参数传递一定要绑定参数名
    @GetMapping("/hello1")
    String hello1(@RequestParam("name") String name);

    // json
    // 注:key/value形式的参数 一定要标记参数的名称
    @PostMapping("/user1")
    User user1(@RequestBody User user);

    // 删除id
    // /user1/{id}
    @DeleteMapping("/user1/{id}")
    void delUser1(@PathVariable Integer id);

    // 通过header来传参 中文要转码
    @GetMapping("/user2")
    void getUserByName(@RequestHeader String name);

}

3.3 接口调用

	// openfeign controller 传参
    @GetMapping("/hello1")
    public void hello1() throws UnsupportedEncodingException {
        String s = helloService.hello1("你好呀");
        System.out.println("hello1:" + s);
        System.out.println("---------------------------------------");
        User user = new User();
        user.setId(1);
        user.setName("小个");
        user.setNickName("萨达过");
        User user1 = helloService.user1(user);
        System.out.println("user:" + user1);
        System.out.println("---------------------------------------");
        helloService.delUser1(1);
        System.out.println("---------------------------------------");
        // 放在heard中的中文参数 一定要先编码在传递
        helloService.getUserByName(URLEncoder.encode("方便热土", "utf-8"));

    }

3.4 测试结果

开启eureka server provider openfeign
在这里插入图片描述

3.5 小结

  • 参数传递
  • 参数一定要绑定参数名
  • 若通过header来传递参数 中文需转码 编码后在传递(URLEncoder.encode(xxx, "utf-8"))
    .provider解码(URLDecoder.decode(xxx, "utf-8"))

4 继承特性

4.1 新建maven子模块

这个包被其他模块依赖 需要springmvc依赖

<dependencies>
        <!-- 涉及到springmvc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>

        <!-- 存储类模块 -->
        <dependency>
            <groupId>xxx</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

4.2 封装接口

public interface IUserService {
    @GetMapping("/hello")
    String hello();

    // 参数传递一定要绑定参数名 若是参数最好用@RequestParam
    // 最好别用map 其问题是可以随意传参
    @GetMapping("/hello1")
    String hello1(@RequestParam("name") String name);

    // json
    // 注:key/value形式的参数 一定要标记参数的名称
    @PostMapping("/user1")
    User addUser1(@RequestBody User user);

    // 删除id
    // /user1/{id}
    // 添加@PathVariable("id") 注意了一定要把("id")添加进去 不然会报错
    @DeleteMapping("/user1/{id}")
    void delUser1(@PathVariable("id") Integer id);

    // 通过header来传参 中文要转码
    // 添加@RequestHeader("name") 注意了一定要把("name")添加进去 不然会报RequestHeader0参数错
    @GetMapping("/user2")
    void getUserByName(@RequestHeader("name") String name) throws UnsupportedEncodingException;
}

4.3 消费者和openfeign添加依赖

		<dependency>
            <groupId>com.sundown</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.sundown</groupId>
            <artifactId>hi-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

4.4 提供者实现接口

@RestController
//@RequestMapping("/test")
public class Hello1Controller implements IUserService {

    @Value("${server.port}")
    Integer port;
	
	// 重写
    @Override
    public String hello(){
        return "hello provider:" + port;
    }


    /**
    * @Description: consumer访问该接口 调用RestTemplate的get请求
    * @Param: [name]
    * @return: java.lang.String
    * @Author: 水面行走
    * @Date: 2020/3/4
    */
    @Override
    public String hello1(String name){
        return "hello provider: " + name;
    }

    @GetMapping("/hello2")
    public String hello2(String name){
        System.out.println(new Date() + "--->" + name);
        return "hello " + name;
    }

    // 在provider 提供2个post接口

    /**
    * @Description: key:value形式传参
    * @Param: [user]
    * @return: model.User
    * @Author: 水面行走
    * @Date: 2020/3/7
    */
    @PostMapping("/user")
    public User addUser(User user){
        return user;
    }


    /**
     * @Description: json形式传参
     * @Param: [user]
     * @return: model.User
     * @Author: 水面行走
     * @Date: 2020/3/7
     */
    @Override
    public User addUser1(@RequestBody User user){
        return user;
    }

    /**
    * @Description: k/v形式 因为是更新操作 put方法返回为void 所以返回值为void就行 有返回值不会报错
    * @Param:
    * @return:
    * @Author: 水面行走
    * @Date: 2020/xx/xx
    */
    @PutMapping("/update-user")
    public void updateUser(User user){
        System.out.println("k/v形式:" + user);
    }

    /**
     * @Description: json形式 别忘了传参添加注解 因为是更新操作 put方法返回为void 所以返回值为void就行 有返回值不会报错
     * @Param:
     * @return:
     * @Author: 水面行走
     * @Date: 2020/xx/xx
     */
    @PutMapping("/update-user1")
    public void updateUser1(@RequestBody User user){
        System.out.println("json形式:" + user);
    }

    /**
    * @Description: k/v形式的删除 xxx?id=1
    * @Param:
    * @return:
    * @Author: 水面行走
    * @Date: 2020/3/8
    */
    @DeleteMapping("/deluser")
    public void delUser(Integer id){
        System.out.println("k/v形式:" + id);
    }

    /**
     * @Description: PathVariable(参数放在路径中 xxx/1)形式的删除
     * @Param:
     * @return:
     * @Author: 水面行走
     * @Date: 2020/3/8
     */
    @Override
    public void delUser1(@PathVariable Integer id){
        System.out.println("json形式:" + id);
    }

    @Override
    public void getUserByName(@RequestHeader String name) throws UnsupportedEncodingException {
        // 解码
        System.out.println(URLDecoder.decode(name, "utf-8"));
    }
}

4.5 openfeign配置

// 继承接口
// 继承特性的好处:抽出公共模块 provider和consumer代码一致 只需更改公共接口即可 减少出错率
// 坏处就是耦合度变高
@FeignClient("provider")
public interface Hello1Service extends IUserService {

}
// 调用Hello1Service
// 其他这个类没有变化 调用结果和上次是一致的
@RestController
public class Hello1Controller {
    @Autowired
    Hello1Service hello1Service;

    // 无参测试
    @GetMapping("/hello")
    public String hello(){
        return hello1Service.hello();
    }

    // 传参
    @GetMapping("/hello1")
    public void hello1() throws UnsupportedEncodingException {
        String s = hello1Service.hello1("你好呀");
        System.out.println("hello1:" + s);
        System.out.println("---------------------------------------");
        User user = new User();
        user.setId(1);
        user.setName("小个");
        user.setNickName("萨达过");
        User user1 = hello1Service.addUser1(user);
        System.out.println("user:" + user1);
        System.out.println("---------------------------------------");
        hello1Service.delUser1(1);
        System.out.println("---------------------------------------");
        // 放在heard中的中文参数 一定要先编码在传递
        hello1Service.getUserByName(URLEncoder.encode("个百分点", "utf-8"));

    }

}

4.6 小结

  • 继承特性
  • 代码简洁 服务者和消费者指向同一目标 一改都改 出错烦恼大减(既是优点也是缺点(耦合度高) 类似赤壁之战 曹操的战船相连)
  • 无论是否继承 参数(无参还是传参方式依然不变)

5 数据压缩

开启压缩 节省资源 提升性能

feign:
  compression:
    request:
      # 开启数据压缩请求
      enabled: true
      # 压缩数据类型
      mime-types: text/xml, application/xml, application/json
      # 数据压缩下限 2048表示传输数据大于2048 才会进行数据压缩(最小压缩值标准)
      min-request-size: 2048
    # 开启数据压缩响应
    response:
      enabled: true

在这里插入图片描述在这里插入图片描述

6 日志配置

  • 配置日志 分4种
  1. NONE: 不开启日志(默认)
  2. BASIC: 记录请求方法、URL、响应状态、执行时间
  3. HEADERS: 在BASIC基础上 加载请求/响应头(+2)
  4. FULL: 在HEADERS基础上 增加body和请求元数据(+3)

6.1 在yml中配置日志级别

# 可以在yml feign.client.config.xxx 配置超时时间 拦截器等配置
logging:
  level:
    com.sundown.openfeign:  debug

6.2 两种配置日志bean方式

  • 在applicaton类中配置bean
import feign.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(OpenfeignApplication.class, args);
    }

    // 配置日志 分4种
    // 1. NONE: 不开启日志(默认)
    // 2. BASIC: 记录请求方法、URL、响应状态、执行时间
    // 3. HEADERS: 在BASIC基础上 加载请求/响应头(+2)
    // 4. FULL: 在HEADERS基础上 增加body和请求元数据(+3)
    // 通过bean配置
    @Bean
    Logger.Level loggerLevel(){
        return Logger.Level.FULL;
    }
}
  • Configuration中配置

正好将超时和自定义拦截器加入

@Configuration
public class FeignConfig {
     // 超时时间配置(通过Options可配置连接超时时间和读取超时时间)
    // Options第一个参数连接超时时间(ms 默认1000*10)
    // Options第二个参数取超时时间(ms 默认1000*60)
    @Bean
    public Request.Options options(){
        return new Request.Options(3000, 8000);
    }

    /**
     * 日志级别
     * 在这里配置日志级别
     * @return
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
	
	 // 配置basic认证
    // 通常: 调用有权限控制的接口 可能认证的值通过传参/请求头去传认证信息
    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("user", "password");
    }

    // 自定义拦截器配置
    @Bean
    public FeignBasicAuthRequestInterceptor feignBasicAuthRequestInterceptor() {
        return new FeignBasicAuthRequestInterceptor();
    }
}    
// OpenFeign自定义拦截器(自定义认证方式) 实现RequestInterceptor
// 自定义一个请求拦截器 在请求之前做认证操作 在往请求头中配置认证后的信息
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {

    // 业务逻辑
    @Override
    public void apply(RequestTemplate requestTemplate) {
        System.err.println("欢迎进入拦截器: " + requestTemplate);
    }
}
// 在openfeign service层配置
//@FeignClient(value = "provider",configuration= FeignConfig.class) 配置类-->拦截器啥的
@FeignClient(value = "provider",configuration= FeignConfig.class)
public interface Hello1Service extends IUserService {

}

启动eureka server和provider 还有openfeign http://localhost:5000/hello1
在这里插入图片描述
在这里插入图片描述

7 openfeign+hystrix配合使用

降级@FeignClient+value+fallback/fallbackFactory属性(都是在openfeign模块中实现 且要开启hystrix)

  hystrix:
    # 开启hystrix
    enabled: true
  • 降级fallback属性
// fallback属性实现类 
@Component
@RequestMapping("/milk") // implements Hello1Service相当于调用了2次 避免重复的请求地址 不然会报错
public class HelloServiceFallback implements Hello1Service{

    @Override
    public String hello() {
        return "error-hello";
    }

    @Override
    public String hello1(String name) {
        return "error-hello1";
    }

    @Override
    public User addUser1(User user) {
        return null;
    }

    @Override
    public void delUser1(Integer id) {

    }

    @Override
    public void getUserByName(String name) throws UnsupportedEncodingException {

    }
}
@FeignClient(value = "provider",fallback= HelloServiceFallback.class)
public interface Hello1Service extends IUserService {

}

启动/重启eureka server和openfeign 断开provider http://localhost:5000/hellohttp://localhost:5000/hello1
在这里插入图片描述
在这里插入图片描述

  • 降级fallbackFactory属性
@Component
public class HelloServiceFallFactory implements FallbackFactory<Hello1Service> {

    @Override
    public Hello1Service create(Throwable throwable) {
        return new Hello1Service() {
            @Override
            public String hello() {
                return "error1---------";
            }

            @Override
            public String hello1(String name) {
                return "error2---------";
            }

            @Override
            public User addUser1(User user) {
                return null;
            }

            @Override
            public void delUser1(Integer id) {

            }

            @Override
            public void getUserByName(String name) throws UnsupportedEncodingException {

            }
        };
    }
}

//@FeignClient(value = "provider",fallback= HelloServiceFallback.class) fallback和fallbackFactory不能同时使用
@FeignClient(value = "provider",fallbackFactory= HelloServiceFallFactory.class)
public interface Hello1Service extends IUserService {

}

启动/重启eureka server和openfeign 断开provider http://localhost:5000/hellohttp://localhost:5000/hello1
在这里插入图片描述
在这里插入图片描述

8 小结

  • openfeign只需要我们提供关键的value就行了 自行拼接
  • openfeign环境: 依赖eureka连接依赖 web openfeign
  • yml eureka连接配置
  • 注解开启openfeign
  • 无参 无需参数直接调用即可
  • 有参 --> 绑定参数、header传递 中文要解码、多参数的话 建议@RequestParam("xxx")
  • 特性继承 --> provider和openfeign公用一个接口 特别注意(一定要定义名字@RequestHeader("xxx") @PathVariable("xxx")。。。不然会报错) 它的好处既是坏处
  • 数据压缩 yml配置 开启压缩请求和响应 最小压缩值标准还有压缩类型
  • 日志配置 四种级别NONE BASIC HEADERS FULL
  • yml --> logging: level: com.sundown.openfeign: debug
  • 在bean配置 要么在application中或是在config中配置好(还可以超时配置和认证配置或自定义拦截器) 在@FeignClient中配置configuration属性 例如@FeignClient(value = "provider",configuration= FeignConfig.class)
  • 另外 也可在yml feign.client.config.xxx 配置超时时间 拦截器等
  • openfeign+hystrix降级操作 在yml中开启hystrix 在@FeignClient中实现fallbackFactory属性(需要implements FallbackFactory<T>)或fallback(实现接口implements Hello1Service添加@RequestMapping("/xx")作为区分) 2种实现方式都要添加@Component注解
posted @ 2020-03-18 17:00  焜掱玚  阅读(7508)  评论(0编辑  收藏  举报
levels of contents