先前公司使用的是在开源微服务框架的基础上二次开发的,后来伴随着业务不断增多,逐渐发现项目规模大到一定程度,其实天天解决的并不是BUG,而是执行效率问题,整天被time-out问题困扰,我逐渐有了需要重新捋一遍框架的冲动,这里多说一句,现在开源微服务框架很多,很多公司项目工期紧张,会直接购买或者拿开源的框架搭建业务平台,我对这点不是很认同,因为别人的框架,咱们学习一下还行,直接用的话,出了问题都不清楚如何解决,特别是微服务类平台,就是java技术及相关组件的大杂烩,如果项目当天晚上要发版,突然出现个问题,因为使用别人的框架,不晓得问题出在哪?咋办呢?跑路都来不及,所以还是需要一点一点搭建框架,虽然慢点,但心里踏实。最近研究微服务间通信,涉及feign、hystrix、ribbon,原来一直用,但一直没过多关注,反正就是在yml里配一山的东西,这么多配置信息,就属它的配置多。打算好好看一看,至于看它的原因,是因为日常工作中服务间通信效率问题实在是把我坑死了。这次又是搞了一天才搞定,踩了很多坑,担心后面忘了,记录一下,分享出来给大家看看
一、第一坑
一般网上的资料或教程在配置openfeign的时候,都是直接在启动类上直接加上注解@EnableFeignClients,由于我的框架场景不是这样,说个题外话哈,我玩微服务也有些时间了,根据以往经验,我认为微服务框架中的服务可以大体分为三类,第一类是工具服务,比如说网关、zipkin、springadmin、nacos等,第二类是可以写成通用功能的微服务,最典型的就是认证中心、鉴权中心还有管理客户信息的客户中心,这类服务就是固定的业务,就拿认证中心来说把,其实就是干一件事,用户是谁的问题吗?其实就是搞一些如何生成token以及如何解析token的那些事,但有了网关之后,这部分功能就放在网关层了,因为token是无状态的,如果是jwttoken,还要判断token本身的失效时间以及缓存刷新时间,说实话,代码不复杂,逻辑有点复杂,后面我会拿单独的一篇文章聊聊。像这类服务如果肯用点心的话,完全可以写成通用的,暴露出接口之后,可以远程支撑其他业务服务,相信我,只要用心一定是可以做到的。另外还有一类服务就是业务服务,这就得根据项目现用现写了,带过团队的人都知道,你不可能保证团队的所有开发人员都是高手,大部分人还是整天干CRUD的活的,但像网关、认证中心、鉴权中心等核心服务,最好不要让他们接触到,不定谁手贱给你改了代码,查问题很困难,所以我搭建架构的原则是:1、最核心的代码封装在jar包里依赖(spring的自动注入是个好东西,哈哈);2、核心的微服务要在独立的一个maven工程里的,除了核心骨干人员,其他人就不要接触了;3、普通的业务微服务也是一个独立的maven工程,这样一般的开发人员就老老实实干CRUD的活吧,干成狗屎也影响不了大局。
正因为上述原因,我将提供端的feign接口单独在放在一个jar文件里,通过maven把依赖暴露出来,然后在调用端里引入依赖,这样就实现了彻底解耦,而且再有其他调用端调用的话,直接引入依赖调用接口就行了,但也因为如此,我的坑也就随之而来了.......
1、提供端代码片段
2、调用端代码片段
在调用端测试类调用的时候,日志报feign接口没有对应的实现bean,日志我看明白了,但问题不晓得出在哪,最后才发现,原来由于feign接口提供端以及调用端已经不再一个工程目录了,openfeign根本就扫描不到,也就没法进行生成bean进行注入,于是我就在提供端的启动类和调用端的启动类上加上了扫描路径:
1、接口提供端启动类
@RefreshScope
@EnableCircuitBreaker
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"cn.**.*","com.**.*"})
@SpringBootApplication
public class AuthStarterApplication {
public static void main(String[] args) {
SpringApplication.run(AuthStarterApplication.class, args);
}
}
2、接口调用端启动类
/**
* 微服务业务服务启动类
* @author gaoh
*/
@RefreshScope
@EnableCircuitBreaker
@EnableFeignClients(basePackages = {"cn.**.*","com.**.*"})
@EnableDiscoveryClient
@SpringBootApplication
public class DemoStarterApplication {
public static void main(String[] args) {
SpringApplication.run(DemoStarterApplication.class, args);
}
}
问题立马解决,这里为啥是全路径扫描呢,这就来源于生活了,哈哈,在搭建架构的过程中,大家要考虑一件事,那就是团队里开发人员干活,有些人干活会时刻以学习的状态进行工作,会以“为什么”的态度去工作;但有些人干活就是整口饭吃,这类人与其说是编码,倒不如说是在copy编码,我搭建架子的过程中把扫描目录写死,我是知道啥意思的,但有些人他们就不在乎,在新建微服务创建启动类时直接copy,最后还以一副研究高深问题的样子,惦着脸问我为啥他写的代码老是调不通,很费解,既浪费我时间也浪费我精力,所以我索性就写个模糊扫描的目录吧,省心........
二、第二坑
第二个坑就是,我在调用端调用feign接口时,日志给我打印出一个带有注册中心服务ID的URL,说404错误调不通,我知道这是feign封装的restURL,我看了看路径也没错,调用方式也没错,咋就调不通呢,搞得我心力交瘁,最后看了下网上的帖子,又试了一下,才发现,是因为我的工程yml里配置了上下文路径 context-path:
server:
port: 9002
servlet:
context-path: /admin
我去掉前缀后重启提供端服务,feign和ribbon就起作用了,其实配上下文路径这事并不是我手残,是故意为止,因为在配置网关路由转发时,如服务有个上下文路径的话,网关路由的配置能方便点,就在路由里配个“"pattern": "/admin/**"”就完事了,没想到就因为这个事feign接口还调不通,我也没多少时间解决这个问题,只能把上下文配置去掉了,网关路由配置麻烦点就麻烦点吧
三、第三坑
这个坑是最贱的一个坑,上面两个坑填上之后,feign接口终于调通了,开始集成hystrix,本来希望一把就过,谁知道坑又来了,先把代码贴上看看:
@FeignClient(
contextId = "ExternalPermissionCheckClient",
value = "hcbycloud-platform-service-auth-starter",
fallbackFactory = ExternalPermissionCheckClientFallback.class)
public interface ExternalPermissionCheckClient {
/**
* 根据认证信息获取权限列表
*
* @param loginInfoDTO
* @return
*/
@PostMapping(value = "/external/permissions")
List<String> getPermissionsByFeign(@RequestBody LoginInfoDTO loginInfoDTO);
/**
* 根据认证信息获取角色列表
*
* @param loginInfoDTO
* @return
*/
@PostMapping(value = "/external/roles")
List<String> getRolesByFeign(@RequestBody LoginInfoDTO loginInfoDTO);
}
@Slf4j
@Component
public class ExternalPermissionCheckClientFallback implements FallbackFactory<ExternalPermissionCheckClient> {
@Override
public ExternalPermissionCheckClient create(Throwable throwable) {
log.warn("ExternalPermissionCheckClient error " + "(Hystrix FallbackFactory): [{}]", throwable.getMessage(), throwable);
return new ExternalPermissionCheckClient() {
@Override
public List<String> getPermissionsByFeign(LoginInfoDTO loginInfoDTO) {
return CollUtil.newArrayList();
}
@Override
public List<String> getRolesByFeign(LoginInfoDTO loginInfoDTO) {
return CollUtil.newArrayList();
}
};
}
}
大家看看代码是不是很标准,典型的openfeign接口配置,但是调用端调接口时,程序流程怎么一直会跳到Fallback(后备模式)的代码里呢,我确定一定以及肯定:提供端的微服务没问题啊,开始扒拉帖子资料一通查、试、查、试.........,最后发现是hystrix的超时配置时间太短,但我没配置hystrix呀,就只配了个feign开启hystrix,搞了半天人家是默认配置,擦!最后在网上copy了一段代码,把超时时间延长了不少,问题解决,对了有个事我还要记录一下,就是在配置yml文件的过程中,我有个习惯,就是在idea的ide里习惯性的用鼠标点点yml上配置的key和value,能点进去至少说明单词没有打错,配置的没有问题,但hystrix.command.default.xxxxxxx.这段配置鼠标点上去不高亮也点不进去,其实你没有配错,它就是点不进去,哎!强迫症害死人啊,我把配置的代码贴上,大家可以看看是不是遇到过相同的问题
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 600000
semaphore:
maxConcurrentRequests: 1000
shareSecurityContext: true
ribbon:
ReadTimeout: 100000
ConnectTimeout: 100000
over!!