我的编程空间,编程开发者的网络收藏夹
学习永远不晚

自定义feignClient的常见坑及解决

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

自定义feignClient的常见坑及解决

自定义feignClient的常见坑

自定义feignClient 踩过的坑,因为spring cloud 需要spring 4 以上的版本,所以对于低版本工程想要使用feign就需要自定义,在定义过程中遇到了很多问题,整理总结一下。(有需要的结合github请慢慢看,真的是手写的,但是有些东西不能全部粘贴出来抱歉了,全部的代码放在 第四点里面)

整体的过程分为两个部分:

  • 一、从eureka上拉取服务地址,
  • 二、feignClient 发送请求到目标服务器(其实feignClient 最终是使用httpClient 发送一个rest的请求,这就是官网给出httpclient和feign-okhttp的原因,这里使用okthhp 因为需要支持path请求)。

一、从eureka上拉取相关服务的配置信息

这里使用的是加载eureka的默认配置,初始化时使用单例。代码如下

1,2的目的是加载项目中的配置,常量定义如下


private static final String CLIENT_CONFIG_FILE_NAME = "eureka";
private static final String CLIENT_RIBBON_CONFIG_FILE_NAME = "ribbon";

resource下定义两个文件:eureka.properties和ribbon.properties (名称可以需要改动),内容是声明服务必要的配置,具体配置如下:

ribbon.properties


aa.ribbon.DeploymentContextBasedVipAddresses=aa //aa 为feign中使用的服务名称
aa.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList //服务调用策略,轮询等
aa.ribbon.ServerListRefreshInterval=60000 //客户端请求eureka 刷新aa 服务节点列表时间
ribbon.ConnectTimeout=50000000 //服务的超时时间
ribbon.ReadTimeout=50000000
eureka.properties
eureka.registration.enabled=false //服务是否注册到eureka上
eureka.serviceUrl.default=http://discovery.ingageapp.com:9401/eureka //eureka地址
#eureka.preferSameZone=true(其余可以百度下cans参数太多不一一列举
#eureka.shouldUseDns=false

具体代码如下,看下代码的具体解释:

1,2两步分别shi是加载ribbon和eureka配置,

3 通过DiscoveryManager加载配置信息。


private XsyServiceLocator() {
    try {
        ConfigurationManager.loadCascadedPropertiesFromResources(CLIENT_RIBBON_CONFIG_FILE_NAME); //1
        ConfigurationManager.loadCascadedPropertiesFromResources(CLIENT_CONFIG_FILE_NAME);   //2
    } catch (IOException e) {
        throw new IllegalStateException("Xsy client config load error! Please check your client.properties");
    }
    DiscoveryManager.getInstance().initComponent(new MyDataCenterInstanceConfig(), new DefaultEurekaClientConfig()); //3
}

二、feignClient 发送请求到目标服务器

1,2两步是自定义了一个@FeignClient 注解,通过传经来的class拿去请求的服务名称即serviceId(如果你不会这个我也没办法了,略有尴尬)

3 Feign.builder() .client(new RibbonClient(new OkhttpClient())) (其实feign的负载均衡,发送请求都是通过ribbon完成的)

这里是初始化ribbonClient,最后的restclient 用的是okhttpclient。

4,5是用来编码和解码 (不要用goson的那个有坑)

6 是用来记录log的 关于log这个 ,feign默认打印的是debug级别的这个是因为他在代码里面写死的可以重写feign的Slf4jLogger类修改。

7 是设置log级别(具体哪些级别打印什么东西,自己搜下吧)

8 FeignInterceptor 是将一些请求header向下传递的(实现RequestInterceptor 接口重写即可)

9 拼接参数发送信息 拼接完 请求是 "http://aa(服务名称)/info (最后会根据eureka上的服务名称拼接成对应的ip+端口号,他自己底层实现的)


public <T> T lookup(Class<T> clazz) {
    FeignClient feignClient = clazz.getAnnotation(FeignClient.class);// 1
    String serviceId = feignClient.value();//2
    T service = Feign.builder()
            .client(new RibbonClient(new OkhttpClient()))//3
            .encoder(new JacksonEncoder)//4
            .decoder(new JacksonDecoder)//5
            .logger(logger)//6
            .logLevel(Logger.Level.HEADERS)//7
            .requestInterceptor(new FeignInterceptor())//8
            .target(clazz, "http://" + serviceId);//9
    return service;
}

三、一些坑

1.源码的坑,实现过程中发现ribbon的配置并未生效,是因为feign-core源码问题,他总是会new一个 config 然后传进去,所以你得配置是无效的,这里重写(整个ribbonClient包copy下来改掉然后引用自己的)

2 这个类好像也是有问题的(忘记了)

四 、以下是现有全部的代码粘贴出来看一下


public class XsyServiceLocator {
    private static final String CLIENT_CONFIG_FILE_NAME = "eureka";
    private static final Object synRoot = new Object();
    private static final String CLIENT_CONFIG_CUSTOM_FILED_NAME = "eureka.name";
    private static final String CLIENT_RIBBON_CONFIG_FILE_NAME = "ribbon";
    private static final JacksonEncoder jacksonEncoder = new JacksonEncoder();
    private static final JacksonDecoder jacksonDecoder = new JacksonDecoder();
    private static final RibbonClient ribbonClient = new RibbonClient(new OkHttpClient());
    private static String ipAddress = null;
    private static boolean isLoadEureka = true;//为true表示需要加载默认eureka 配置文件如 crm,false则加载自定义eureka配置文件如paas-aggregator-service
    private static XsyFeignLogger logger = null;
    private XsyServiceLocator() {
        try {
            ConfigurationManager.loadCascadedPropertiesFromResources(CLIENT_RIBBON_CONFIG_FILE_NAME);
            ConfigurationManager.loadCascadedPropertiesFromResources(CLIENT_CONFIG_FILE_NAME);
            Object eurekaName = ConfigurationManager.getConfigInstance().getProperty(CLIENT_CONFIG_CUSTOM_FILED_NAME);
            if (eurekaName != null) {
                isLoadEureka = false;
            }
        } catch (IOException e) {
            throw new IllegalStateException("Xsy client config load error! Please check your client.properties");
        }
        while (isLoadEureka && ipAddress == null) {
            DiscoveryManager.getInstance().initComponent(new MyDataCenterInstanceConfig(), new DefaultEurekaClientConfig());
            ipAddress = DiscoveryManager.getInstance().getEurekaInstanceConfig().getIpAddress();
        }
    }

    public <T> T lookup(Class<T> clazz) {
        if (isLoadEureka && ipAddress == null) {
            DiscoveryManager.getInstance().initComponent(new MyDataCenterInstanceConfig(), new DefaultEurekaClientConfig());
            ipAddress = DiscoveryManager.getInstance().getEurekaInstanceConfig().getIpAddress();
        }
        FeignClient feignClient = clazz.getAnnotation(FeignClient.class);
        String serviceId = feignClient.value();
        if(logger == null){
            synchronized (synRoot){
                if(logger == null){
                    logger = new XsyFeignLogger(clazz);
                }
            }
        }
        T service = Feign.builder()
                .client(ribbonClient)
                .encoder(jacksonEncoder)
                .decoder(jacksonDecoder)
                .logger(logger)
                .logLevel(Logger.Level.HEADERS)
                .requestInterceptor(new FeignInterceptor())
                .target(clazz, "http://" + serviceId);
        return service;
    }

feignClient的使用

服务提供端代码


@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.UPMS_SERVICE)
public interface RemoteUserService {
 
 @GetMapping("/user/info/{username}")
 R<UserInfo> info(@PathVariable("username") String username
   , @RequestHeader(SecurityConstants.FROM) String from);
}
@GetMapping("/user/info/{username}") 是服务Controller包中的(@Inner注解代表内部方法,不用权限直接调用,不会被网管拦截)
 
 @Inner
 @GetMapping("/info/{username}")
 public R info(@PathVariable String username) {
  SysUser user = userService.getOne(Wrappers.<SysUser>query()
    .lambda().eq(SysUser::getUsername, username));
  if (user == null) {
   return R.failed(null, String.format("用户信息为空 %s", username));
  }
  return R.ok(userService.findUserInfo(user));
 }

服务调用端

(SecurityConstants.FROM_IN是系统内部服务调用的一个标识 值为IN)


@Slf4j
@AllArgsConstructor
public class HzUserDetailsServiceImpl implements HzUserDetailsService {
 private final RemoteUserService remoteUserService;
 private final CacheManager cacheManager;
 
 
 @Override
 @SneakyThrows
 public UserDetails loadUserByUsername(String username) {
  Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
  if (cache != null && cache.get(username) != null) {
   return (HzUser) cache.get(username).get();
  }
 
  R<UserInfo> result = remoteUserService.info(username, SecurityConstants.FROM_IN);
  UserDetails userDetails = getUserDetails(result);
  cache.put(username, userDetails);
  return userDetails;
 } 
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

自定义feignClient的常见坑及解决

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

vue cli使用iview自定义主题遇到的坑及解决

vue cli使用iview自定义主题遇到的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-05-16

Spring中使用自定义ThreadLocal存储导致的坑及解决方法是什么

本篇文章为大家展示了Spring中使用自定义ThreadLocal存储导致的坑及解决方法是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Spring自定义ThreadLocal存储导致的坑Spr
2023-06-21

Java异常--常见方法--自定义异常--增强try(try-with-resources)详解

这篇文章主要介绍了Java异常--常见方法--自定义异常--增强try(try-with-resources)的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-03-14

golang中自定义函数实现的常见错误

go 中自定义函数实现的常见错误包括:定义过长的函数,导致代码难以理解。缺少文档说明,使函数难以理解和使用。不处理错误,可能导致程序出现意外行为。产生副作用,使代码难以理解和测试。过度使用函数指针,增加代码复杂度。Go 中自定义函数实现的常
golang中自定义函数实现的常见错误
2024-04-26

常见绝对定位问题及解决方法详解

绝对定位故障一览:你应该知道的常见问题及解决方法,需要具体代码示例摘要:绝对定位是前端开发中经常使用的一种排版方式,但在使用过程中常常会遇到一些问题。本文将介绍几种常见的绝对定位故障,并给出相应的解决方法和具体的代码示例,帮助读者更好地理
常见绝对定位问题及解决方法详解
2024-01-23

MySQL存储过程自定义异常怎么解决

在MySQL存储过程中,可以通过SIGNAL语句来自定义异常并解决。SIGNAL语句用于在存储过程中抛出异常,它的语法如下:SIGNAL SQLSTATE value SET message_text其中,SQLSTATE是异常状态码,
MySQL存储过程自定义异常怎么解决
2024-02-29

Nacos的常见问题及解决方法

本篇内容主要讲解“Nacos的常见问题及解决方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Nacos的常见问题及解决方法”吧!如何依赖最新的 Nacos 客户端?很多用户都是通过 Sprin
2023-06-02

自定义注解和springAOP捕获Service层异常并处理自定义异常的示例分析

这篇文章主要为大家展示了“自定义注解和springAOP捕获Service层异常并处理自定义异常的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“自定义注解和springAOP捕获Serv
2023-06-15

Oracle数据库中表锁定的常见场景及解决方案

Oracle数据库中表锁定的常见场景及解决方案在Oracle数据库中,表锁定是一个常见的数据库性能问题,当多个会话同时访问同一个表时,可能会导致表出现锁定,进而影响系统的性能和稳定。本文将会讨论一些常见的表锁定场景,并提供相应的解决方案和
Oracle数据库中表锁定的常见场景及解决方案
2024-03-03

Android常用的AlertDialog对话框及自定义对话框

常用的Dialog有确认对话框,单选按钮对话框,多选按钮对话框,复选按钮对话框另外还有自定义的对话框 AlertDialog的常用方法 setTitle:为对话框设置标题 setMessage:为对话框设置内容 setIcon:为对话框设置
2022-06-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录