Feign远程接口调用

概述

目的:解决微服务调用问题。如何从微服务A调用微服务B提供的接口。

特性:

  • 声明式语法,简化接口调用代码开发。
  • 像调用本地方法一样调用其他微服务中的接口。
  • 集成了Eureka服务发现,可以从注册中心中发现微服务。
  • 集成了Spring Cloud LoadBalancer,提供客户端负载均衡。
  • 从调用发起方控制微服务调用的请求时间,防止服务雪崩。

使用Feign进行微服务调用

xml 复制代码
       
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

导入依赖

xml 复制代码
       <!--     springboot web start   -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--   引入nacos 注册中心依赖  注册服务   -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.qf</groupId>
            <artifactId>common</artifactId>
        </dependency>

<!--        feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--   引入nacos配置中心依赖     -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    </dependencies>

yml文件

yml 复制代码
spring:
  application:
    name: feign-demo #跟配置的前缀名字
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      config:
        file-extension: yaml
  profiles:
    active: dev #跟配置的后缀 设备名匹配
logging:
  level:
    com.qf.feignconsumer.feign.UserFeignClient: debug
server:
  port: 9090
feign:
  client:
    config:
      default:
        # 建立连接的超时时间
        connectTimeout: 5000
        # 发送请求后等待接口响应结果的超时时间
        readTimeout: 3000

创建FeignClient接口

java 复制代码
package com.qf.feignconsumer.feign;

import com.qf.common.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@FeignClient("service-user")
public interface UserFeignClient {

    /**
     * 注意,如果少加了@RequestParam,会抛出如下异常
     * Caused by: java.lang.IllegalStateException: Method has too many Body parameters
     * @param pagenum
     * @param pagesize
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/user/page")
    public List<User> getUserByPage(@RequestParam("pagenum") Integer pagenum,@RequestParam("pagesize")Integer pagesize) throws InterruptedException;

    @GetMapping("/user/getall")
    public List<User> getAll();

    @PostMapping("/user/update")
    public User updateUser(@RequestBody User user);

    @DeleteMapping("/user/delete/{id}")
    public User deleteUser(@PathVariable("id") Integer id);

}

使用主启动类

java 复制代码
package com.qf.feignconsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient//注册到nacos中
@EnableFeignClients//注意:使用feign时 需要添加该注解
public class FeignApp9090 {
    public static void main(String[] args) {
        SpringApplication.run(FeignApp9090.class,args);
    }
}

FeignController

java 复制代码
package com.qf.feignconsumer.controller;

import com.qf.common.entity.User;
import com.qf.common.vo.ResultVo;
import com.qf.feignconsumer.feign.ProviderFeigngClient;
import com.qf.feignconsumer.feign.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;


@RestController
public class FeignController {

    @Autowired
    UserFeignClient userFeignClient;
    @Autowired
    ProviderFeigngClient providerFeigngClient;

    @GetMapping("/u/test4")
    public ResultVo utest4() throws InterruptedException {
        List<User> userByPage = userFeignClient.getUserByPage(1, 2);
        return ResultVo.ok(1,"ok",userByPage);
    }




    @GetMapping("/echo")
    public ResultVo echo(String msg){
        String echo = providerFeigngClient.echo(msg);

        return ResultVo.ok(1,"asd",echo);
    }

    @GetMapping("/u/test1")
    public ResultVo utest1(){
        //使用feignclient发起微服务用
        List<User> users = userFeignClient.getAll();
        System.out.println(users);
        return ResultVo.ok(1,"ok",users);
    }

    @GetMapping("/u/test2")
    public ResultVo utest2(){
        //使用feignclient发起微服务用
        System.out.println("utest2");
        User user = new User(100, "luffy", "123");
        User user1 = userFeignClient.updateUser(user);
        return ResultVo.ok(1,"ok",user1);
    }

    @GetMapping("/u/test3")
    public ResultVo utest3(){
        //使用feignclient发起微服务用
        System.out.println("utest3");
        User user1 = userFeignClient.deleteUser(100);
        return ResultVo.ok(1,"ok",user1);
    }


}

日志配置类

java 复制代码
package com.qf.feignconsumer.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {
    @Bean
    public Logger.Level liver(){
        /*
         * NONE:默认的,不显示任何日志
         * BASIC:仅记录请求方法、RUL、响应状态码及执行时间
         * HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
         * FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
         */
        return Logger.Level.FULL;
    }
}

提供服务方的controller

java 复制代码
package com.qf.userprovider.controller;

import com.qf.common.entity.User;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/page")
    public List<User> getUserByPage(@RequestParam("pagenum") Integer pagenum,@RequestParam("pagesize")Integer pagesize) throws InterruptedException {
        User user = new User(1, "zhangsan1", "1234567");
        User user2 = new User(2, "lisi", "12345");
        Thread.sleep(5000);
        List<User> users = Arrays.asList(user, user2);
        return users;
    }




    @GetMapping("/getall")
    public List<User> getAll(){
        User user1 = new User(1, "zhangsan", "12345677");
        User user2 = new User(2, "lisi", "asdasdaas");

        List<User> users = Arrays.asList(user1, user2);

        return users;
    }


    @PostMapping("/update")
    public User updateUser(@RequestBody User user){

        System.out.println(user);
        //根据id更新用户
        user.setPassword("88889888");

        return user;
    }


    @DeleteMapping("/delete/{id}")
    public User deleteUser(@PathVariable("id") Integer id){

        System.out.println("要删除用户的id="+id);
        //去数据库里删除

        User luffy = new User(id, "luffy", "12345");

        return luffy;

    }


}

Feign负载均衡

注册中心中观察,发现Micro1微服务在Eureka注册中心中有两个服务节点

当发起feign调用时,究竟调用的是哪个节点呢?

java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {

    @Value("${server.port}")
    String port;

    @GetMapping("/findAll")
    List<User> findAll(){
        System.out.println(port);
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        List<User> users = Arrays.asList(new User(1,"zhangsan", "123456", 29), new User(2,"lisi", "12345", 19));
        return users;
    }

}

Feign使用的是轮询负载均衡算法,当有多个节点时,采用轮询的方式依次调用。

Feign调用超时时间设置

yaml 复制代码
feign:
  client:
    config:
      default:
        # 建立连接的超时时间
        connectTimeout: 5000
        # 发送请求后等待接口响应结果的超时时间
        readTimeout: 10000

将微服务的接口响应时间延长,观察接口调用,超时抛异常

java 复制代码
    @GetMapping("/findAll")
    List<User> findAll(){
        System.out.println(port);
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        List<User> users = Arrays.asList(new User(1,"zhangsan", "123456", 29), new User(2,"lisi", "12345", 19));
        return users;
    }

添加全局异常处理

java 复制代码
@RestControllerAdvice
public class ExHandler {

    @ExceptionHandler(Exception.class)
    public String handleEx(Exception e){
        return e.getMessage();
    }

}

Feign配置日志

添加一个配置类

java 复制代码
@Configuration
public class FeignConfig {

    @Bean
    public Logger.Level liver(){
        /*
         * NONE:默认的,不显示任何日志
         * BASIC:仅记录请求方法、RUL、响应状态码及执行时间
         * HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
         * FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
         */
        return Logger.Level.FULL;
    }
}

yml文件中为FeignClient接口配置日志级别

yml 复制代码
logging:
  level:
    com.qf.feign.XXXClient: debug
相关推荐
binishuaio1 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE3 分钟前
【Java SE】StringBuffer
java·开发语言
老友@3 分钟前
aspose如何获取PPT放映页“切换”的“持续时间”值
java·powerpoint·aspose
wrx繁星点点18 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
Upaaui21 分钟前
Aop+自定义注解实现数据字典映射
java
zzzgd81621 分钟前
easyexcel实现自定义的策略类, 最后追加错误提示列, 自适应列宽,自动合并重复单元格, 美化表头
java·excel·表格·easyexcel·导入导出
友善的鸡蛋22 分钟前
解决:使用EasyExcel导入Excel模板时出现数据导入不进去的问题
java·easyexcel·excel导入
星沁城23 分钟前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵
NoneCoder35 分钟前
Java企业级开发系列(1)
java·开发语言·spring·团队开发·开发
一只爱好编程的程序猿37 分钟前
Java后台生成指定路径下创建指定名称的文件
java·python·数据下载