共计 5658 个字符,预计需要花费 15 分钟才能阅读完成。
作者:小傅哥
博客: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