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
相关推荐
懒羊羊不懒@6 分钟前
Java基础语法—最小单位、及注释
java·c语言·开发语言·数据结构·学习·算法
ss27310 分钟前
手写Spring第4弹: Spring框架进化论:15年技术变迁:从XML配置到响应式编程的演进之路
xml·java·开发语言·后端·spring
DokiDoki之父21 分钟前
MyBatis—增删查改操作
java·spring boot·mybatis
兩尛38 分钟前
Spring面试
java·spring·面试
Java中文社群1 小时前
服务器被攻击!原因竟然是他?真没想到...
java·后端
Full Stack Developme1 小时前
java.nio 包详解
java·python·nio
零千叶1 小时前
【面试】Java JVM 调优面试手册
java·开发语言·jvm
代码充电宝1 小时前
LeetCode 算法题【简单】290. 单词规律
java·算法·leetcode·职场和发展·哈希表
li3714908901 小时前
nginx报400bad request 请求头过大异常处理
java·运维·nginx
摇滚侠1 小时前
Spring Boot 项目, idea 控制台日志设置彩色
java·spring boot·intellij-idea