浅谈【Feign Client】 开发规范化

背景

随着公司业务的快速发展,项目微服务化已经成为了一个普遍常态。一个项目涉及到的微服务的少说三五个,多则几十个甚至几十个不等,而服务之间的调用所使用的技术进几年来也是层出不穷,例如我们熟知常见的 Http Restful、dubbo、motan、grpc、还有Spring 的Feign等。每个公司都有自己的技术栈,而我们公司大部分Java服务普遍使用的都是Spring的Feign调用,由于公司Java微服务众多,每个部门甚至每个团队都有自己负责的维护的微服务,由于feign Client的开发还没有统一的规范,因此每个项目的实现也是层出不穷,因此给整个Feign Client的维护也带来了诸多的不变。

一、现状

  • 部分服务提供Client依赖包,部分服务没有完善Client包甚至没有。
  • 每个微服务定义Client包的方式不进相同。
  • 每个服务都调用其他的微服务都有自己的熔断,导致Client升级新增了接口的时候,调用方必须更新自己的业务代码,重写熔断接口采能避免编译报错。
  • 每个服务的调用方不明确,也就是说一个微服务上线时间久了,随着业务的快速发展,到了后期不清楚自己的服务有哪些系统在调用,导致重构或者升级过程中对风险的评估不准确带来潜在风险。

    相信做Java开发的大佬们都隐隐约约的遇到了以上提到的难题,那么针对以上问题,我们应该怎么来规范是定义Client或者解决以上问题呢?

二、思考

1.为何要做Feign Hystrix。
什么是服务熔断,我这里在百度百科上找到定义如下


服务熔断按照层级来划分也分为很多种,比如服务之间调用的熔断,网关调用的熔断等。从定义上来看可能比较难理解,我们举个例子。

假设A服务的某个接口依赖B服务的某个接口,当某时刻A服务的这个接口流量成倍数增长,假设这个时候A服务依赖的B服务这个接口因为mysql、或者缓存 或其他原因发生异常,试想会发生什么?很显然此时B服务将会接收大量 的无效请求,那么就会占用B服务的系统资源从而造成资源浪费。
那么有了熔断会怎么处理呢?其实如果A服务对B服务做了熔断处理,那么当大量请求B服务的时候,当第一次请求B服务异常后,那么后续的其他请求将在一段时间内将不会连续再次请求B服务了,这样就给B服务节省了大量的资源。除此之外,在A服务对B服务做熔断的过程中在业务允许范围内也可以对异常响应做mock default处理。
2.统一规范Feign Client会带来哪些好处。
大家都晓得在对接一些三方服务的时候都会有对应开发语言的一些SDK,例如我们熟知的腾讯微信有自己的微信相关SDK、腾讯对象存储COS有自己的 SDK、阿里相关功能也都会有自己的sdk。其实 Feign Client包从某种意义上来说也是属于我们给下游系统提供的sdk。
那么对于下游系统来说会带来哪些好处呢?服务接入将会变得简单,原来没有一sdk可能需要自己要写大量的http请求相关代码,如果你的请求需要有签名、加密相关要求,那么每个客户端都需要重复的开发一套逻辑去进行对接,如果对你们的服务接口进行升级,那么对下游的系统将会影响巨大,甚至可能还需要重新写一份。
那么对于服务端会带来哪些好处呢?这里好处不再一一列举,例如可以对所有的客户端进行统一的升级维护,可以规范下游系统调用服务的规范,例如加固定的请求头信息,统一的签名处理,统一的加密处理等,这样既方便的自己,也给下游系统带来了便利,何乐而不为呢?

三、实现

包结构说明
└─plan
├─client
├─config
│ ├─fallback
│ │ └─plan
│ ├─interceptor
│ └─log
├─request
└─response
client:接口定义,定义提供服务的接口。
config: 配置定义
fallback: 定义熔断支持。
interceptor:定义拦截器,处理统一的请求头、签名、加密等。
log:定义统一的日志搜集打印。
request: 定义统一的请求dto参数定义。
response:定义统一的响应体参数定义。
Client接口定义

定义Client接口指定fallback熔断配置,指定configuration 配置(包含log日志打印,拦截器配置、参数类型转换器配置、请求统一签名、加密处理等);
熔断配置
整体熔断相关包接口定义如下

AbstractFallbackFactory:为抽象的熔断工厂。
由于一个Client SDK可能存在有多个API Client接口定义,如 XxxAClient、XxxBClient....等,而每个Client都有自己的熔断定义,所以这里在fallback包下又为每个Client单独定义一个包名,如这里的plan就是为PlanClient定义的熔断配置。
XxxFallback 熔断处理接口定义需要继承XxxClient接口,例如这里的 PurchasePlanFallback定义如下。

public interface PurchasePlanFallback extends PurchasePlanClient {

}

AbstractFallbackFactory抽样工厂配置

@Slf4j
public abstract class AbstractFallbackFactory<T> implements FallbackFactory<T> {

    private static final String SERVICE_NAME = "plan-service";

    @Override
    public T create(Throwable error) {
        log.error("[{}]服务调用异常", SERVICE_NAME, error);
        T fallbackInstance = getFallbackInstance();
        log.info("Purchase Plan Service Feign FallBack Instance[{}]", fallbackInstance);
        return fallbackInstance;
    }

    /**
     * 获取熔断处理实例
     *
     * @return 熔断处理实例
     */
    public abstract T getFallbackInstance();
}

XxxFallbackFactory具体的熔断工厂配置类,主要负责定义和实现具体的熔断实例UML图如下

具体实现如下

@Slf4j
@Component
public class PurchasePlanFallbackFactory extends AbstractFallbackFactory<PurchasePlanClient> {
    @Autowired
    private PurchasePlanFallback fallbackInstance;

    /**
     * PurchasePlanClient 默认熔断配置
     */
    @Bean
    @ConditionalOnMissingBean
    public PurchasePlanFallback getPurchasePlanFallback() {
        return new DefaultPurchasePlanFallback();
    }

    @Override
    public PurchasePlanClient getFallbackInstance() {
        return fallbackInstance;
    }
}

DefaultPurchasePlanFallback 为默认的熔断处理配置。如想自定义熔断异常处理可以有如下方案
1.如果想自定义全部Client接口熔断可以直接定义类实现 PurchasePlanFallback 接口全部实现类即可。

//实例化到IOC中
@Component
public class MyFallBack implements PurchasePlanFallback{
    @Override
    public ... A(... request) {
        你的实现
    }

    @Override
    public ... B(... request) {
    }
}

2.如果想自定义部分接口熔断,可直接继承 DefaultPurchasePlanFallback 类,重写你需要自定义的方法接口即可。
例如:

//实例化到IOC中
@Component
public class myFallBack extends DefaultPurchasePlanFallback{
        @Override
        public ... A(... request) {
            你的实现
        }
}

默认熔断定义

public class DefaultPurchasePlanFallback implements PurchasePlanFallback {

    private static final String SERVICE_NAME = "plan-service";

    @Override
    public ResponseBaseVo<Boolean> cancelPurchasePlan(CancelPlanRequest request) {
        ResponseBaseVo<Boolean> responseBaseVo = new ResponseBaseVo<>();
        responseBaseVo.setCode(-1);
        responseBaseVo.setData(false);
        responseBaseVo.setMsg(String.format("[%s]服务[cancelPurchasePlan]接口调用异常", SERVICE_NAME));
        return responseBaseVo;
    }
}

拦截器配置:添加调用来源

@Slf4j
public class PurchasePlanFeignInterceptor implements RequestInterceptor {

    @Value("${app.name:}")
    private String appId;

    private static final String HEADER_APP_SOURCE_ID = "App-Source-Id";

    @Override
    public void apply(RequestTemplate template) {
        //处理定义请求头添加 调用方应用名称
        template.header(HEADER_APP_SOURCE_ID, appId);
    }
}

四、参考
https://docs.spring.io/spring-cloud-openfeign/docs/2.2.5.RELEASE/reference/html/#spring-cloud-feign

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容