乐趣区

关于后端:自研API-网关-媲美美团这套Shepherd网关架构

作者:小傅哥
博客:https://bugstack.cn

积淀、分享、成长,让本人和别人都能有所播种!😄

我说:” 很多互联网大厂,很少基于 SpringMVC 模块对外提供 WEB 服务的 HTTP 接口!” 一下炸窝了,你说,哪个厂不必,你说。还,还不必 SpringMVC 我天天用! 哈哈哈,好在我最近浏览到了美团的这篇技术文章《百亿规模 API 网关服务 Shepherd 的设计与实现》

他说:在没有 Shepherd API 网关之前,美团业务研发人员如果要将外部服务输入为对外的 HTTP API 接口。通常要搭建一个 Web 利用,用于实现根底的鉴权、限流、监控日志、参数校验、协定转换等工作,同时须要保护代码逻辑、根底组件的降级,研发效率绝对比拟低。此外,每个 Web 利用都须要保护机器、配置、数据库等,资源利用率也十分差。

他说:美团外部一些业务线苦于没有现成的解决方案,依据本身业务特点,研发了业务相干的 API 网关。放眼业界,亚马逊、阿里巴巴、腾讯等公司也都有成熟的 API 网关解决方案。

他说的 我说的 ,是同一个事件。 并且咱们所设计的 API 网关架构模型也都是相似的! 相似的架构设计,并不会让我多诧异。因为 API 网关所实现的指标统一,在同一指标下如果研发思考高度一致,那么就不须要太多技术认知对其。—— 所以,少和臭棋篓子下棋!

接下来,小傅哥就给大家分享下。两套 API 网关的架构设计,以及你该怎么学习能力把握这些技术技能和进步这些技术认知。

一、技术设计与实现

API 网关 来说,咱们能够先形象出一个最简略的模型来了解。它的外围指标是对立提供 HTTP 申请服务,也就是说你能够在不须要额定开发 WEB 利用的前提下,对外把本身的服务通过 HTTP 申请协定暴漏出去。因为在互联网大厂中,各个微服务零碎的交互次要以 RPC 为主,同时为了提供带有根底性能 (鉴权、监控、限流等) HTTP 服务,所以有了API 网关 服务。

这就是一套最根底的 API 网关 设计模型构造,从左到右,从 HTTP 申请到协定转换解决,调用到具体的 RPC 服务。而 RPC 服务的接口变动由 SDK 上报到注册核心,注册核心再告诉给协定转换服务。有了这样一个根底认知当前,咱们在来解说几个重要的外围模块设计和实现,包含;整体架构、注册核心、服务发现、协定转换。

1. 整体架构

这里有 2 张 API 网关架构图,一张是美团技术团队的,一张是小傅哥设计的。

1.1 API 网关架构图 - 美团

Shepherd API 网关的数据面也就是 Shepherd 服务端。一次残缺的 API 申请,可能是从挪动利用、Web 利用,合作伙伴或外部零碎发动,通过 Nginx 负载平衡零碎后,达到服务端。服务端集成了一系列的根底性能组件和业务自定义组件,通过泛化调用申请后端 RPC 服务、HTTP 服务、函数服务或服务编排服务,最初返回响应后果。

留神:美团的这张技术架构图图应该是简化的,整体架构并不会比小傅哥设计的简略。

1.2 API 网关架构图 - 小傅哥

这是一整套 API 网关的外围通信模型结构图,以 API 网关算力的多套服务注册到网关核心开始,拉取 RPC 利用接口并实现映射 HTTP 调用操作。最终容许用户通过 Nginx 拜访和门路重写的负载平衡治理,调用到具体的网关算力中执行协定解析和 RPC 接口的泛化调用并最终返回后果数据。

2. 注册核心

API 网关为什么要有一个注册核心呢?

其实这个注册核心,最外围治理就是 RPC 接口映射成一个 HTTP 申请地址,并把这个信息下发给对应的协定转发服务上进行应用。

  • 如图所示,api-gateway-core 是最外围的通信层。那么它还须要把注册的网关接口在通信外围服务中启动起来。那么怎么启动呢?
  • 这个启动过程首先来自于 api-gateway-sdk 向 api-gateway-center 推送注册接口,之后在通过网关引擎 api-gateway-engin 拉取接口并在本地服务实现注册。最初再调用到网关接口时,则是通过 api-gateway-core 调用到对应的 RPC 利用中。
  • 那么 api-gateway-sdk 并不是次要工程,没有它的是能够通过 api-gateway-admin 配置。所以 在整个流程中 api-gateway-center、api-gateway-core 是两个外围工程,能更好的串联流程。

3. 服务发现

什么叫服务发现呢?发现谁呢?

服务发现,发现的是用于注册到 API 网关注册核心的 RPC 服务,通过 SDK 配置的形式,采集到 RPC 服务中的接口信息。因为这些接口的定义如果都是手动配置到 API 网关注册核心,那么就会十分麻烦。所以通过 SDK 采集的形式进行主动注册,当有接口变更的时候也会及时的变更接口信息。

  • 在 api-gateway-center 工程中增加 Redis 公布音讯模块,并提供应用服务注册后的事件告诉操作。这个告诉只会告诉给对应的网关算力服务,不会全局告诉。
  • 在 api-gateway-assist 工程中开发 Redis 订阅音讯模块,当收到注册核心的音讯推送时,则依据零碎的标识信息进行拉取服务。留神这里你能够进行细化,只把变更的信息一条条推送给网关注册,缩小接口的拉取
  • 在 api-gateway-sdk 工程中增加对网关注册核心接口的调用,当所有的服务注册实现后,调用接口进行告诉。

4. 协定转换

这是最外围的服务!

所有的 HTTP 申请协定转发,到最终的 RPC 泛化调用,这些操作都在这一个服务中实现。而整个这一块服务的实现,其实就是一套会话模型的架构分层设计。

一次网络申请通过 Netty 解决能够分为三段;音讯接管、申请鉴权、音讯解决。这样就由原来咱们只是在接管音讯后间接把音讯协定转换后申请到 RPC 服务,转换为多增加二层来解决简略的音讯接管和申请鉴权。这里的申请鉴权就是基于引入的 Shrio + JWT 实现。

二、内容构造和目录

当你须要学习编程常识,进步编程思维和编码能力的阶段时候,你须要看到什么材料?🤔 不晓得大家是否有想过这样一个问题。

每当我看到那些十分牛皮的架构或者框架的时候,我就心愿把他们吃透,并拿捏成本人的常识体系。但往往这些框架源码有太多的繁枝末节,也因为随着一直的需要迭代,让一些旁路细节流程的大量代码覆盖了外围流程。所以 当你想学习时候,往往也是有心无力,基本不晓得从哪开始。

当初我来为你铺路!

为了解决这样的学习问题,小傅哥把一个 API 网关我的项目,以一直接需要迭代的视角,一点点渐进式的实现整套代码开发。那么这样就能够让大家有清晰的学习编码路线,把一整套这样的货色学习实现。—— 跟着小傅哥学习,你能够 不浪费时间 少走弯路、指标明确的把技术学习到手。

三、设计模式与编码

每每在公司经验一个大我的项目时,其实不只是看重这块业务场景,还看重对应这套我的项目的架构和编码,跟着各路大牛晋升教训。可能就这样一个我的项目的学习,就能把一个人的编码思维晋升到一个新的台阶。

那么小傅哥再做这套架构和编码时,特地重视整体的架构设计和编码实现。接下来我给大家举例看看这套代码中的代码实现。

1. 会话模型

源码cn.bugstack.gateway.core.session.defaults.DefaultGatewaySessionFactory

public class DefaultGatewaySessionFactory implements GatewaySessionFactory {

    private final Configuration configuration;

    public DefaultGatewaySessionFactory(Configuration configuration) {this.configuration = configuration;}

    @Override
    public GatewaySession openSession(String uri) {
        // 获取数据源连贯信息:这里把 Dubbo、HTTP 形象为一种连贯资源
        DataSourceFactory dataSourceFactory = new UnpooledDataSourceFactory();
        dataSourceFactory.setProperties(configuration, uri);
        DataSource dataSource = dataSourceFactory.getDataSource();
        // 创立执行器
        Executor executor = configuration.newExecutor(dataSource.getConnection());
        // 创立会话:DefaultGatewaySession
        return new DefaultGatewaySession(configuration, uri, executor);
    }

    public Configuration getConfiguration() {return configuration;}

}
  • 会话模型是网关算力中十分重要的一环,所有的 HTTP 申请都能够被形象为会话模型。通过会话模型封装出 HTTP 到 RPC 的解决,两头再通过执行器和 RPC 形象的数据源进行连接。

2. 形象模板

源码cn.bugstack.gateway.core.socket.BaseHandler

public abstract class BaseHandler<T> extends SimpleChannelInboundHandler<T> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, T msg) throws Exception {session(ctx, ctx.channel(), msg);
    }

    protected abstract void session(ChannelHandlerContext ctx, final Channel channel, T request);

}
  • 网关会话中还须要协定的解决,而协定的接管、解析、转换,就须要通过 Netty 实现的 Socket 服务来封装。通过为了更好的体现出会话的构造,这里小傅哥通过一个抽象类模板,定义出 session 办法。—— 好的代码,就是好的文档。有了这样的约定,也就不须要太多的口口相传。

3. 映射代理

源码cn.bugstack.gateway.core.bind.MapperProxyFactory

public class MapperProxyFactory {

    private String uri;

    public MapperProxyFactory(String uri) {this.uri = uri;}

    private final Map<String, IGenericReference> genericReferenceCache = new ConcurrentHashMap<>();

    public IGenericReference newInstance(GatewaySession gatewaySession) {
        return genericReferenceCache.computeIfAbsent(uri, k -> {HttpStatement httpStatement = gatewaySession.getConfiguration().getHttpStatement(uri);
            // 泛化调用
            MapperProxy genericReferenceProxy = new MapperProxy(gatewaySession, uri);
            // 创立接口
            InterfaceMaker interfaceMaker = new InterfaceMaker();
            interfaceMaker.add(new Signature(httpStatement.getMethodName(), Type.getType(String.class), new Type[]{Type.getType(String.class)}), null);
            Class<?> interfaceClass = interfaceMaker.create();
            // 代理对象
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Object.class);
            // IGenericReference 对立泛化调用接口
            // interfaceClass    依据泛化调用注册信息创立的接口,建设 http -> rpc 关联
            enhancer.setInterfaces(new Class[]{IGenericReference.class, interfaceClass});
            enhancer.setCallback(genericReferenceProxy);
            return (IGenericReference) enhancer.create();});
    }

}
  • 为了更好的连接 HTTP 申请的地址【/wg/activity/sayHi】与 RPC 服务的映射关系,这里咱们像 ORM 框架一样做了 bind 绑定关系。有了这样一层绑定关系的形象设计,就会变得十分好保护代码实现关系。—— 代码就是一块砖头🧱,怎么搭建摆放,那是设计师的能力体现。

4. 畛域驱动

不只是代码,小傅哥也心愿各个实现的工程构造也是洁净整洁的。永远不是应用设计模式耽误时间,是不具备这样的教训的人员耽误时间。不是当初耽搁开发工夫,就是耽搁当前的迭代工夫。

  • 举例:如何设计出畛域驱动的四层架构,会用 DDD 其实 DDD 也就没那么难。驾驭不了才难。
  • 同时到处都能看到设计模式的身影,用设计模式的思维解决各类场景实现问题。

四、技术我的项目与生态

其实小傅哥所构建的是一整套我的项目生态,以 API 网关 所提供的 HTTP 服务为枢纽,连接星球中的各类我的项目进行组合构建。目前星球中包含; 4 个业务我的项目 3 个组件我的项目,它们能够被如下关系构造展现;我的项目链接:https://bugstack.cn/md/zsxq/introduce.html

退出移动版