关于feign:Feign的基础使用模板

Feign是一个申明式的http客户端,官网地址:https://github.com/OpenFeign/feign其作用就是帮忙咱们优雅的实现http申请的发送 一 根本应用1 在消费者中引入依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId></dependency>2 减少启动注解在消费者启动类上增加 @EnableFeignClients 注解,提供者可不增加 3 编写 Feign 接口@FeignClient("userservice") //服务名称public interface UserClient { @GetMapping("/hello/feignTest/{id}") //地址必须与服务中的地址统一 String feignTest(@PathVariable("id") String id);}//提供者中代码 @RestController@RequestMapping("/hello")public class Hello { @GetMapping("/hello") private String hello() { return "userHello"; } @GetMapping("feignTest/{id}") private String feignTest(@PathVariable("id") String id){ return "feignTest, ID:"+id; }}4 注入调用//消费者中注入调用 @RestController@RequestMapping("/hello")public class Hello { @Autowired private UserClient userClient; @GetMapping("/hello") private String hello() { return "orderHello"; } @GetMapping("/feign/{id}") private String feign(@PathVariable("id")String id ){ return userClient.feignTest(id); }}二 自定义配置Feign能够反对很多的自定义配置,如下表所示: ...

April 6, 2023 · 2 min · jiezi

关于feign:open-feign-调用超时与重试

1. 前言在 spring cloud 各种组件中,我最早接触的就是 open feign,但素来没有讲过它。起因是因为感觉它简略,无非就是个服务调用,在代码层面上也很简略,没有啥可说的。 但为什么明天来讲呢: 服务调用看起来简略,但实则是微服务治理中很重要的一环。咱们当初微服务有上百个,如何进步微服务之间调用的稳定性,是老大难的问题。网络或高并发等起因,简直每天都有个别报错是 feign 调用的。open feign 其实是封装了负载平衡、熔断等其余组件的,把握它是有难度的。1. feign 与 openfeignfeign 是 netflix 公司写的,是 spring cloud 组件中的一个轻量级 RESTful 的 HTTP 服务客户端,是 spring cloud 中的第一代负载平衡客户端。起初 netflix 讲 feign 开源给了 spring cloud 社区,也随之停更。 openfeig 是 spring cloud 本人研发的,在 feign的根底上反对了 spring MVC 的注解,如 @RequesMapping 等等。是 spring cloud中的第二代负载平衡客户端。 尽管 feign 停更了,之前我也介绍过 dobbo 这类代替产品。但在服务调用这个畛域,open feign 还是有它的一席之地。 2. spring cloud 版本更迭先讲讲 spring cloud 的版本迭代吧。在2020年之前,spring cloud 等版本号是依照伦敦地铁站号命名的(ABCDEFGH): AngleBrixtonCamdenDalstonEdgwareFinchleyGreenWichHoxton但从2020年开始,版本号开始以年份命名,如:2020.0.1。 spring cloud 与 spring boot 版本的对应关系如下: ...

February 17, 2023 · 5 min · jiezi

关于feign:Spring-CloudFeign调用异常触发降级后如何捕获异常

一、问题背景在Spring Cloud的微服务架构中,通常微服务之间通过feign/openfeign来进行http调用,并且启用hystrix并配置降级策略fallback,能够在http调用异样时触发降级,代码如下 @FeignClient(name = "resourceFeign", fallback = ResourceFeignFallback.class)public interface ResourceFeign { @PostMapping("/resource/list") Map<String, Object> resourceList();}@Componentpublic class ResourceFeignFallback implements ResourceFeign { @Override public Map<String, Object> resourceList() { Map result = new HashMap<>(); result.put("code", 500); result.put("msg", "申请异样"); return result; }}然而这种形式在http调用异样时,会间接执行降级策略,而无奈捕捉到具体的异样信息,这种状况如何解决? 二、解决办法所以咱们通常应用另一种形式,通过配置fallbackFactory来捕捉异样信息,代码如下 @FeignClient(name = "resourceFeign", fallbackFactory = ResourceFeignFallbackFactory.class)public interface ResourceFeign { @PostMapping("/resource/list") Map<String, Object> resourceList();}@Component@Slf4jpublic class ResourceWebFeignFallbackFactory implements FallbackFactory<ResourceFeign> { @Override public ResourceFeign create(Throwable throwable) { // 捕捉异样 log.error(throwable.getMessage()); return new ResourceFeign() { @Override public Map<String, Object> resourceList() { Map result = new HashMap<>(); result.put("code", 500); result.put("msg", "申请异样"); return result; } }; }}这样就能够捕捉到http调用的异样信息 ...

January 30, 2023 · 1 min · jiezi

关于feign:107FeignClient调用携带登录用户信息

定义ContextHolderContextHolder用来寄存和获取线程变量中的 用户id、用户名称、Token等信息。 /** * 获取以后线程变量中的 用户id、用户名称、Token等信息 * 留神: 必须在网关通过申请头的办法传入,同时在Interceptor拦截器设置值。 否则这里无奈获取 * * */public class SecurityContextHolder{ private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>(); public static void set(String key, Object value) { Map<String, Object> map = getLocalMap(); map.put(key, value == null ? StringUtils.EMPTY : value); } public static String get(String key) { Map<String, Object> map = getLocalMap(); return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY)); } public static <T> T get(String key, Class<T> clazz) { Map<String, Object> map = getLocalMap(); return StringUtils.cast(map.getOrDefault(key, null)); } public static Map<String, Object> getLocalMap() { Map<String, Object> map = THREAD_LOCAL.get(); if (map == null) { map = new ConcurrentHashMap<String, Object>(); THREAD_LOCAL.set(map); } return map; } public static void setLocalMap(Map<String, Object> threadLocalMap) { THREAD_LOCAL.set(threadLocalMap); } public static String getUserId() { return get(SecurityConstants.DETAILS_USER_ID); } public static void setUserId(String account) { set(SecurityConstants.DETAILS_USER_ID, account); } public static String getUserName() { return get(SecurityConstants.DETAILS_USERNAME); } public static void setUserName(String username) { set(SecurityConstants.DETAILS_USERNAME, username); } public static String getUserKey() { return get(SecurityConstants.USER_KEY); } public static void setUserKey(String userKey) { set(SecurityConstants.USER_KEY, userKey); } public static void remove() { THREAD_LOCAL.remove(); } public static void setUserType(String userType) { set(SecurityConstants.USER_TYPE, userType); } public static String getUserType() { return get(SecurityConstants.USER_TYPE); }}利用模块的拦截器寄存登录信息@Componentpublic class SjcjInterceptor implements HandlerInterceptor { @Autowired private RemoteUserService remoteUserService; @Autowired private TokenService tokenService; //超管的id=1 @Value("${admin.userId:1}") private String adminUserId; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } String userId = SecurityContextHolder.getUserId(); if(StringUtils.isEmpty(userId)){ userId = adminUserId; R<LoginUser> userResult = remoteUserService.getUserIdInfo(userId, SecurityConstants.INNER); if (R.FAIL == userResult.getCode()) { throw new ServiceException(userResult.getMsg()); } LoginUser userInfo = userResult.getData(); Map<String, Object> rspMap = tokenService.createToken(userInfo, 1); SecurityContextHolder.setUserId(userInfo.getUserid()); SecurityContextHolder.setUserName(userInfo.getUsername()); SecurityContextHolder.set(SecurityConstants.LOGIN_USER, userInfo); SecurityContextHolder.set(SecurityConstants.AUTHORIZATION_HEADER,rspMap.get("access_token")); } return true; } }feign 申请拦截器该拦截器从申请requst取用户信息,或者从SecurityContextHolder取登录信息,并保留到RequestTemplate。 ...

November 7, 2022 · 2 min · jiezi

关于feign:撸了一个-Feign-增强包-V20-升级版

前言大略在两年前我写过一篇 撸了一个 Feign 加强包,过后筹备是利用 SpringBoot + K8s 构建利用,这个库能够相似于 SpringCloud 那样联合 SpringBoot 应用申明式接口来达到服务间通信的目标。 但前期因为技术栈发生变化(改为 Go),导致该我的项目只实现了根本需要后就搁置了。 偶合的时最近外部有局部我的项目又打算采纳 SpringBoot + K8s 开发,于是便着手持续保护;现曾经外部迭代了几个版本比较稳定了,也减少了一些实用功能,在此分享给大家。 https://github.com/crossoverJie/feign-plus 首先是新增了一些 features: 更加对立的 API。对立的申请、响应、异样日志记录。自定义拦截器。Metric 反对。异样传递。示例联合下面提到的一些个性做一些简略介绍,对立的 API 次要是在应用层面: 在上一个版本中申明接口如下: @FeignPlusClient(name = "github", url = "${github.url}")public interface Github { @RequestLine("GET /repos/{owner}/{repo}/contributors") List<GitHubRes> contributors(@Param("owner") String owner, @Param("repo") String repo);}其中的 @RequestLine 等注解都是应用 feign 包所提供的。 这次更新后改为如下形式: @RequestMapping("/v1/demo")@FeignPlusClient(name = "demo", url = "${feign.demo.url}", port = "${feign.demo.port}")public interface DemoApi { @GetMapping("/id") String sayHello(@RequestParam(value = "id") Long id); @GetMapping("/id/{id}") String id(@PathVariable(value = "id") Long id); @PostMapping("/create") Order create(@RequestBody OrderCreateReq req); @GetMapping("/query") Order query(@SpringQueryMap OrderQueryDTO dto);}相熟的滋味,根本都是 Spring 自带的注解,这样在应用上学习老本更低,同时与我的项目中本来的接口写法保持一致。 ...

May 6, 2022 · 2 min · jiezi

关于feign:feign请求返回值反序列LocalDateTime异常记录

前言最近项目组用feign调用近程服务,生产端报了如下一个异样从异样信息能够得出localdatime反序列化出了异样,而这个异样又是因为jackson无奈解决导致。因而咱们能够为jackson的ObjectMapper适配一下 解决办法1、在pom.xml引入 <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jdk8</artifactId> <version>${jackson.version}</version> </dependency>注: jackson-datatype-jsr310这是用来反对jsr310标准的工夫,jackson-datatype-jdk8用来反对新的特定于JDK8的类型,例如Optional 2、替换默认的ObjectMapper@Configurationpublic class LocalDateTimeConfig { public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new Jdk8Module()); objectMapper.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT)); objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); objectMapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule()); return objectMapper; }疑难点:为什么替换了默认的ObjectMapper后,feign就能够解决LocalDateTime答案就在 @Configuration(proxyBeanMethods = false)public class FeignClientsConfiguration { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; @Bean @ConditionalOnMissingBean public Decoder feignDecoder() { return new OptionalDecoder( new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); }}而messageConverters默认的转换器是依据HttpMessageConvertersAutoConfiguration而来 ...

July 27, 2021 · 1 min · jiezi

关于feign:已解决Feign上传文件相关配置

服务构造很简略,消费者通过Feign调用服务提供者的服务。provider有一个上传文件性能,依据接口文档,参数类型为File,与其余参数并列搁置。 关键点:1,路由层File参数应用@RequestPart注解;2,接口层增加consumes = MediaType.MULTIPART_FORM_DATA_VALUE阐明。 路由层 @PostMapping(value = "/notify/upload") @ApiOperation("上传告诉音") public String upload ( AudioRecordScooperReq audioRecordScooperReq, @RequestPart("file") MultipartFile file) { audioRecordScooperReq.setToken(tokenValue); return audioRecordFeign.upload(audioRecordScooperReq, file); }Feign接口层 // 上传告诉音 - @PostMapping(value = "/scooper-record/data/notify/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String upload(@SpringQueryMap AudioRecordScooperReq audioRecordScooperReq, @RequestBody() MultipartFile file);

July 6, 2021 · 1 min · jiezi

关于feign:springCould整合feign出现required-a-bean-of-type-xxx-not-be-found

一、问题springCould整合feign提醒required a bean of type xxx that could not be found Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.2021-05-23 11:51:02.777 ERROR 17160 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : ***************************APPLICATION FAILED TO START***************************Description:Field oAuth2FeignClient in com.quantsmart.service.impl.SysLoginServiceImpl required a bean of type 'com.quantsmart.feign.OAuth2FeignClient' that could not be found.The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true)Action:Consider defining a bean of type 'com.quantsmart.feign.OAuth2FeignClient' in your configuration.Process finished with exit code 1com/quantsmart/feign/OAuth2FeignClient.java ...

May 23, 2021 · 2 min · jiezi

关于feign:Feign系列

Feign - 源码剖析 先占个坑。。。当前再来说应用

December 24, 2020 · 1 min · jiezi

关于feign:Feign-源码分析

例子这次先不写了。。。间接源码走起。局部设计跟Ribbon一样,这边就不在累述,倡议先看Ribbon系列。仍然从spring.factories说起。留神到这里有这几个类:FeignAutoConfiguration、FeignRibbonClientAutoConfiguration。 启动FeignAutoConfiguration加载FeignContext,这里会赋值FeignClientSpecification的汇合,前面说FeignClientSpecification类怎么来的,其实跟Ribbon用的是同一个办法。FeignContext的defaultConfigType是FeignClientsConfiguration.class,这个和Ribboon用法一样,前面会加载。 @Autowired(required = false)private List<FeignClientSpecification> configurations = new ArrayList<>();@Beanpublic FeignContext feignContext() { FeignContext context = new FeignContext(); context.setConfigurations(this.configurations); return context;}另一个加载的是HystrixTargeter,尽管是HystrixTargeter,如果Feign.Builder不是feign.hystrix.HystrixFeign.Builder,前面调用的还是Feign.Builder的target办法。 @Configuration(proxyBeanMethods = false)@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")protected static class HystrixFeignTargeterConfiguration { @Bean @ConditionalOnMissingBean public Targeter feignTargeter() { return new HystrixTargeter(); }}FeignRibbonClientAutoConfiguration加载CachingSpringLoadBalancerFactory,这个类会注入SpringClientFactory,看过了Ribbon,是不是就晓得了他要做什么。没错,他会创立一个ILoadBalancer,用于负载平衡。 @Bean@Primary@ConditionalOnMissingBean@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")public CachingSpringLoadBalancerFactory cachingLBClientFactory( SpringClientFactory factory) { return new CachingSpringLoadBalancerFactory(factory);}FeignRibbonClientAutoConfiguration还import了DefaultFeignLoadBalancedConfiguration类,在这个类,会创立一个LoadBalancerFeignClient,前面须要的Client就是他了。 @Bean@ConditionalOnMissingBeanpublic Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) { return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory);} @EnableFeignClients咱们应用feign的时候,都会应用这个注解,注解里Import了FeignClientsRegistrar这个类,FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,调用registerBeanDefinitions办法。这个办法做了两件事,一件事是注册FeignClientSpecification类,前面会注入到下面的FeignContext中,这个用法跟Ribbon - 几种自定义负载平衡策略提的一样。另外一件事就是创立FactoryBean。 ...

December 24, 2020 · 3 min · jiezi

关于feign:Feignhystrix的配置有了Apollo还用Archaius吗

前言feign是一个杰出的Http申请客户端封装框架,feign-hystrix是整个框架体系里的其中一个模块,用来集成hystrix熔断器的,feign和hystrix这两个我的项目都是Netflix开源的(openfeign已独立迭代)。在spring boot我的项目中,能够应用spring-cloud-starter-openfeign模块,无缝集成feign和hystrix。然而,hystrix默认采纳的Archaius来驱动hystrix的配置零碎,无缝集成的同时,也会把archaius-core给引入进来。archaius是一个配置核心我的项目,相似spring cloud config和apollo,如果archaius只是作为hystrix配置的驱动,我的项目启动时会打印烦人的正告日志,提醒你没有配置任何动静配置源。当我的项目里曾经采纳了apollo时,能够间接剔除掉Archaius,他们的功能定位高度重合了。间接剔除依赖,会导致本来配置在spring中的配置不失效,博主也是在不小心剔除后,遇到了配置不失效的问题,才有了本篇博文,记录下过程。只有稍加改变,联合apollo配置动静下发能力,能够做到hystrix的配置实时动静失效。 feign:https://github.com/OpenFeign/...hystrix:https://github.com/Netflix/Hy...archaius:https://github.com/Netflix/ar...apollo:https://github.com/ctripcorp/...archaius正告日志2020-12-10 11:19:41.766 WARN 12835 --- [ main] c.n.c.sources.URLConfigurationSource : No URLs will be polled as dynamic configuration sources.2020-12-10 11:19:41.766 INFO 12835 --- [ main] c.n.c.sources.URLConfigurationSource : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.2020-12-10 11:19:41.772 WARN 12835 --- [ main] c.n.c.sources.URLConfigurationSource : No URLs will be polled as dynamic configuration sources.2020-12-10 11:19:41.772 INFO 12835 --- [ main] c.n.c.sources.URLConfigurationSource : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.咱们遇到的问题在一次系统优化重构中,博主给整个我的项目来了一个360的大瘦身,把所有的未应用的依赖通通给挪走了。其中就包含了spring-cloud-starter-openfeign模块的archaius-core依赖。因为咱们曾经应用了apollo配置核心,archaius在这个我的项目里显得很多余,而且还会打印烦人的正告日志。所以就间接排除了,如: ...

December 10, 2020 · 1 min · jiezi

关于feign:springcloud中feign的FeignClient应该写在哪里

前言最近项目组拿了友商的springcloud alibaba我的项目来进行革新,在翻阅他们的代码时候,发现他们把@FeignClient写在服务提供方的API上,他们这样的写法胜利的引起我的留神,于是抱着好学的心态求教友商的开发人员,于是一篇水文就这么诞生了 友商开发人员解惑友商服务提供方的API形如下 @FeignClient(name = "feign-provider",path = UserService.INTER_NAME,contextId = "user")public interface UserService { String INTER_NAME = "user"; @GetMapping(value = "/{id}") UserDTO getUserById(@PathVariable("id") Long id);}我过往的经验是@FeignClient是写在生产端上,就是在生产端上会写一个接口继承服务端API接口,再打上@FeignClient,并指明fallback,形如下 @FeignClient(name = "feign-provider",path = UserService.INTER_NAME,contextId = "user",fallback = UserServiceClientFallBack.class)public interface UserServiceClient extends UserService {}我将我过往的写法通知友商开发人员,友商的开发人员对我说,你生产端还要本人写接口啊,那么麻烦。咱们这种写法,生产端仅需pom文件引入API包,在调用方上打个 @Autowired标注,就能够调用服务提供方的接口。额,他们的说法真的很有情理,惋惜没压服我,于是我抛出第二个问题,你们间接把@FeignClient写在服务提供方的API上,那如果生产端要进行熔断降级,要怎么做? 友商给我答案是用sentinel啊,间接在sentinel的控制面板上配置熔断降级策略,形如下 触发的后果形如下看着曾经实现了熔断的成果,然而我这种成果还不是我想要的,于是我又问,如果在面板上进行熔断后,我要记录熔断日志,该怎么做?友商给我的答案是这时候你就得采纳分布式链路追踪组件啊比方skywalking,反正你记录日志,不也是为了排查问题不便,要懂得变通。额,好吧,最初我再抛出一个问题,既然你们间接把@FeignClient写在服务提供方的API上,那如果生产端想直连某台服务提供方进行本地联调,那要怎么做?友商的答复是他们开发的时候不会有这种场景,大家都是直连开发环境联调 如果是我来实现,我会把@FeignClient写在哪里?毋庸置疑的,我会把@FeignClient写在生产端上,因为从职责上,只有生产端能力明确晓得本人要调用哪个服务提供方,比方直连哪个服务提供方进行调试,如果间接把@FeignClient写在服务提供方的API上,生产端就很难按需定制。其次因为本人对sentinel也停留在据说过,也没理论用过,也是因为这次友商的我的项目了用springcloud alibaba的全家桶,才接触了下。前面在和友商探讨@FeignClient的搁置问题后,回来在尝试了一把,发现友商说的在sentinel配置熔断降级不全面,因为我后边尝试让服务提供方超时或者报错,此时拜访页面就会呈现和后边我就按本人的想法,在生产端上会写一个接口继承服务端API接口,再打上@FeignClient,并指明fallback,形如下 @FeignClient(name = "feign-provider",path = UserService.INTER_NAME,contextId = "user",fallback = UserServiceClientFallBack.class)public interface UserServiceClient extends UserService {}@Component@Slf4jpublic class UserServiceClientFallBack implements UserServiceClient{ @Override public UserDTO getUserById(Long id) { log.info("id:{} fallback",id); return UserDTO.builder().id(id).userName("fallback").build(); }}在application.yml激活sentinel对feign的反对 ...

November 28, 2020 · 1 min · jiezi

关于feign:FeignZuul为何不启用HystrixRibbon重试

Feign不举荐启用HystrixFeign 部署地位: 业务服务之间调用增加Hystrix会造成凌乱,故障点难以确认Zuul不举荐启用Ribbon重试Zuul 部署地位: 在最后面作为入口zuul如果启用重试,可能造成后盾多台服务器压力过大

September 30, 2020 · 1 min · jiezi