IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 2.Feign -> 正文阅读

[Java知识库]2.Feign

Open Feign学习

什么是Feign

? Feign 是Spring Cloud Netflix组件中的一量级Restful的 HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了RibbonRestTemplate, 实现了WebService的面向接口编程,进一步降低了项目的耦合度。后期不在维护后 Cloud 就出了OpenFeign,在之前的框架中在服务调用中使用熔断hystrix 新的alibaba 框架使用了Sentinel 来替换之前的服务熔断

什么是服务调用

? 顾名思义,就是服务之间的接口互相调用,在微服务架构中很多功能都需要调用多个服务才能完成某一项功能。

? 个人理解,在微服务概念中,服务分布在不同的环境下,我们可以将所有的服务都注册到注册中心,然后利用我们的Http请求的方式对某一个服务进行请求。

为什么要使用Feign

? Feign 旨在使编写 JAVA HTTP 客户端变得更加简单,Feign 简化了RestTemplate代码,实现了Ribbon负载均衡,使代码变得更加简洁,也少了客户端调用的代码,使用 Feign 实现负载均衡是首选方案,只需要你创建一个接口,然后在上面添加注解即可。
Feign 是声明式服务调用组件,其核心就是:像调用本地方法一样调用远程方法,无感知远程 HTTP 请求。让开发者调用远程接口就跟调用本地方法一样的体验,开发者完全无感知这是远程方法,无需关注与远程的交互细节,更无需关注分布式环境开发。

Feign vs OpenFeign

Feign 内置了Ribbon,用来做客户端负载均衡调用服务注册中心的服务。
Feign 支持的注解和用法参考官方文档:https://github.com/OpenFeign/feign官方文档,使用 Feign 的注解定义接口,然后调用这个接口,就可以调用服务注册中心的服务。

Feign本身并不支持Spring MVC的注解,它有一套自己的注解,为了更方便的使用Spring Cloud孵化了OpenFeign。并且支持了Spring MVC的注解,如@RequestMapping@PathVariable等等。
OpenFeign@FeignClient可以解析Spring MVC@RequestMapping注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。

如何使用

  • 添加依赖

    <!-- spring cloud openfeign -->
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  • 新建服务调用接口 主要是用来调用其他服务的接口

    package com.ruoyi.system.api;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import com.ruoyi.common.core.constant.ServiceNameConstants;
    import com.ruoyi.common.core.domain.R;
    import com.ruoyi.system.api.factory.RemoteUserFallbackFactory;
    import com.ruoyi.system.api.model.LoginUser;
    
    /**
     * 用户服务
     * 
     * @author ruoyi
     * 使用FeignClient 里面的参数主要有下面的几种
     * centextId 因为不想将所有服务的接口放在一起,可以使用这个注释 来重新命名
     * value=name 如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
     * decode404:当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException
     * configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
     * fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
     * fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
     */
    
    @FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
    public interface RemoteUserService
    {
        /**
         * 通过用户名查询用户信息
         *
         * @param username 用户名
         * @return 结果
         */
        // 两个坑:1. @GetMapping不支持   2. @PathVariable得设置value
        @GetMapping(value = "/user/info/{username}")
        public R<LoginUser> getUserInfo(@PathVariable("username") String username);
    }
    
  • 新建RemoteUserFallbackFactory.java降级实现

    package com.ruoyi.system.api.factory;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import com.ruoyi.common.core.domain.R;
    import com.ruoyi.system.api.RemoteUserService;
    import com.ruoyi.system.api.model.LoginUser;
    import feign.hystrix.FallbackFactory;
    
    /**
     * 用户服务降级处理
     * 实现上面的方法,并且将返回新的内容或者抛出异常
     * 除了写法不同没有发现fallBackFactory和fallback 有啥区别
     * 
     * @author ruoyi
     */
    @Component
    public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserService>
    {
        private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class);
    
        @Override
        public RemoteUserService create(Throwable throwable)
        {
            log.error("用户服务调用失败:{}", throwable.getMessage());
            return new RemoteUserService()
            {
                @Override
                public R<LoginUser> getUserInfo(String username)
                {
                    return R.fail("获取用户失败:" + throwable.getMessage());
                }
            };
        }
    }
    
  • 调用者消费者TestUserController.java新增info查询用户方法

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestUserController
    {
        @Autowired
        private RemoteUserService remoteUserService;
    
        /**
         * 获取当前用户信息
         */
        @GetMapping("/user/{username}")
        public Object info(@PathVariable("username") String username)
        {
            return remoteUserService.getUserInfo(username);
        }
    }
    
  • 启动类添加@EnableRyFeignClients注解,默认的@EnableRyFeignClients扫描范围com.ruoyi。 这个注解是若依大大根据之前的**@EnableFeignCilents** 进行封装的

  • 启动后访问http://localhost:8888/user/admin,返回正确数据表示测试通过。

  • 提示

    目前已经存在ruoyi-api-system系统接口模块,用于服务调用

负载均衡

Feign默认集成了RibbonNacos也很好的兼容了Feign,默认实现了负载均衡的效果。

请求传参

Get方式传参,使用@PathVariable@RequestParam注解接收请求参数

@GetMapping(value = "/user/info/{username}")
public R<LoginUser> getUserInfo(@PathVariable("username") String username);

Post方式传参,使用@RequestBody注解接收请求参数。

@PostMapping("/operlog")
public R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog);

性能优化

Gzip压缩

gzip是一种数据格式,采用deflate算法压缩数据。gzip大约可以帮我们减少70%以上的文件大小。

全局配置

server:
  compression:
    # 是否开启压缩
    enabled: true
    # 配置支持压缩的 MIME TYPE
    mime-types: text/html,text/xml,text/plain,application/xml,application/json

局部配置

feign:
  compression:
    request:
      # 开启请求压缩
      enabled: true
      # 配置压缩支持的 MIME TYPE
      mime-types: text/xml,application/xml,application/json 
      # 配置压缩数据大小的下限
      min-request-size: 2048   
    response:
      # 开启响应压缩
      enabled: true  

提示

开启压缩可以有效节约网络资源,但是会增加CPU压力,建议把最小压缩的文档大小适度调大一点。

Http连接池

两台服务器建立HTTP连接的过程涉及到多个数据包的交换,很消耗时间。采用HTTP连接池可以节约大量的时间提示吞吐量。

FeignHTTP客户端支持3种框架:HttpURLConnectionHttpClientOkHttp

默认是采用java.net.HttpURLConnection,每次请求都会建立、关闭连接,为了性能考虑,可以引入httpclientokhttp作为底层的通信框架。

例如将FeignHTTP客户端工具修改为HttpClient

1、添加依赖

<!-- feign httpclient -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

2、全局配置

feign:
  httpclient:
    # 开启httpclient
    enabled: true

3、测试验证

// RemoteUserService FeignClient
@GetMapping("/user/pojo")
public Object selectUser(SysUser user);


// 消费端
@Autowired
private RemoteUserService remoteUserService;

@GetMapping("/user/pojo")
public Object UserInfo(SysUser user)
{
	return remoteUserService.selectUser(user);
}

// 服务端
@GetMapping("/pojo")
public R<SysUser> selectUser(@RequestBody SysUser user)
{
	return R.ok(userService.selectUserByUserName(user.getUserName()));
}

4、启动后访问http://localhost:8888/user/pojo?userName=ry,返回正确数据表示测试通过。

日志配置

浏览器发起的请求可以通过F12查看请求和响应信息。如果想看微服务中每个接口我们可以使用日志配置方式进行查看详细信息。

配置文件logback.xml设置com.ruoyi日志级别为debug

全局配置

@Bean
public Logger.Level getLog()
{
	return Logger.Level.FULL;
}

局部配置

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

/**
 * Feign 客户端配置
 *
 * @author ruoyi
 */
@Configuration
public class FeignConfiguration
{
    @Bean
    Logger.Level feignLoggerLevel()
    {
        return Logger.Level.FULL;
    }
}

// ====== 在客户端接口指定此配置 ======

/**
 * 用户服务
 * 
 * @author ruoyi
 */
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class, configuration = FeignConfiguration.class)
public interface RemoteUserService
{
} 

请求超时

Feign的负载均衡底层用的就是Ribbon,所以请求超时其实就只需要配置Ribbon参数。

全局配置 一般都是在全局里面进行修改 局部的没怎么用过

# 请求处理的超时时间
ribbon:
  ReadTimeout: 10000
  ConnectTimeout: 10000

局部配置

# ruoyi-xxxx 为需要调用的服务名称
ruoyi-xxxx:
  ribbon:
    ReadTimeout: 10000
    ConnectTimeout: 10000

异常配置

1、配置开启 基本不用了

使用sentinel 进行熔断了 基本都是一样的

feign:
  hystrix:
    enabled: true

2、FeignClient接口服务加入fallbackFactory

@FeignClient(fallbackFactory = RemoteUserFallbackFactory.class)

3、添加接口实现异常类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import feign.hystrix.FallbackFactory;

/**
 * 用户服务降级处理
 * 
 * @author ruoyi
 */
@Component
public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserService>
{
    private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class);

    @Override
    public RemoteUserService create(Throwable throwable)
    {
        log.error("用户服务调用失败:{}", throwable.getMessage());
        return new RemoteUserService()
        {
            @Override
            public Object getUserInfo(String username)
            {
                return "获取用户失败:" + throwable.getMessage();
            }
        };
    }
}

请求拦截器

在微服务应用中,通过feign的方式实现http的调用,可以通过实现feign.RequestInterceptor接口在feign执行后进行拦截,对请求头等信息进行修改。

例如项目中利用feign拦截器将本服务的userIduserNameauthentication传递给下游服务

package com.ruoyi.common.security.feign;

import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils;
import feign.RequestInterceptor;
import feign.RequestTemplate;

/**
 * feign 请求拦截器
 * 
 * @author ruoyi
 */
@Component
public class FeignRequestInterceptor implements RequestInterceptor
{
    @Override
    public void apply(RequestTemplate requestTemplate)
    {
        HttpServletRequest httpServletRequest = ServletUtils.getRequest();
        if (StringUtils.isNotNull(httpServletRequest))
        {
            Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest);
            // 传递用户信息请求头,防止丢失
            String userId = headers.get(CacheConstants.DETAILS_USER_ID);
            if (StringUtils.isNotEmpty(userId))
            {
                requestTemplate.header(CacheConstants.DETAILS_USER_ID, userId);
            }
            String userName = headers.get(CacheConstants.DETAILS_USERNAME);
            if (StringUtils.isNotEmpty(userName))
            {
                requestTemplate.header(CacheConstants.DETAILS_USERNAME, userName);
            }
            String authentication = headers.get(CacheConstants.AUTHORIZATION_HEADER);
            if (StringUtils.isNotEmpty(authentication))
            {
                requestTemplate.header(CacheConstants.AUTHORIZATION_HEADER, authentication);
            }
        }
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-26 10:01:22  更:2021-09-26 10:04:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:14:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码