源宝导读:微服务架构与传统的单体式方案的最大不同是微服务将应用的核心功能拆分成多项服务。每项服务可以单独构建和部署。服务之间需要互相通信。假设服务间每次通信都需要在调用方编码操作,那么必定会增加很大的工作量,并且造成代码冗余并且无法维护。
一、背景
集成平台在整体架构上抽象了连接中心核心服务来负责对外的服务通信。而在内部的服务通信中。接口中心,事件中心,服务流等都需要在接收到外部请求和指令后将对应的请求通过连接中心转发到第三方服务/厂商。那么,在系统内部就存在连接中心会不断的被其他微服务调用。连接中心在被调用时目前需要经历如下几个步骤,服务发现->负载均衡->APM->ZIP->身份认证处理->服务质量。如果这些步骤在此调用的时候都需要被重复编码,那结果肯定是灾难性的。所以,我们给我们的核心服务连接中心提供了专门的调用SDK。
二、实现思路
2.1 行业实现思想
SDK定义:指一些被软件工程师用于为特定的软件包、软件框架、硬件平台及操作系统等创建应用软件的开发工具之集合。
在单体应用早期,我们往往使用SDK封装一些对外公开的功能代码,这样需要使用此功能就可以直接引用SDK,而不需要从头开发。在SOA时代服务SDK的概念其实就已经出现在我们使用的技术中。比如,WCF的客户端在某种程度上也是一种服务SDK,使用客户端可以快捷的调用WCF服务端。而通信,序列化,编码等过程对我们来说是隐式的。我们不需要去关心这些特定的实现细节。
现阶段在对于行业而言,我们熟知的厂商提供了许多的软件服务。比如百度的地图服务,讯飞的语音服务。从他们的技术文档可以得知,基本上每个厂商在提供核心服务的同时。一定会给我们提供配套的服务SDK来简化我们调用服务的过程。在使用的过程中,我们往往只需要使用SDK进行简单的配置。遵循这一思想,在核心服务就绪后,我们可以给核心服务配套对应的SDK来简化外部调用的过程。
2.2 实现过程分析
整体架构分析 :
遵循行业服务端SDK设计思想,我们需要给MIP核心服务连接中心设计配套的调用SDK。虽然连接中心不对外提供服务,但是它在架构中需要对其他微服务提供服务。对应其他服务而言,也需要简化对连接中心的调用。
内部服务只需要引用连接中心SDK,提供连接对应的Code(标识)和Path(调用路径)。就可以驱动连接中心访问第三方服务。
SDK功能分析:
支持链式调用,在调用链中现有业务需要支持链路追踪,请求压缩,服务发现,负载均衡,身份认证,服务质量。
连接数据缓存,并实时更新最新连接数据。
内部服务调用连接中心连接服务需要经历如图所示步骤
缓存能力:在SDK加载启动时,SDK应具有实时更新连接数据的能力。
设计思路:通过上述分析得知,SDK支持的功能实际上是一个链式调用的过程。链中的处理中间件是根据需求组装,改变HttpRequest。通过分析有我们有三种链式调用实现方案。
使用GOF设计模式中的责任链模式构建链式调用通道。
模仿ASP.NET中间件模式,构建链式调用通道。
使用DelegatingHandler为HttpClient创建附加消息处理程序从而构建链式调用通道。
缓存设计思路:使用集成平台中比较成熟的Consul Blocking Query组件实时监控Consul中连接Flag的更改并实时获取最新的连接配置数据。
三、实现方案
经过对三种链式调用方案的对比,我们决定使用HttpClient的HttpHandler构建连接中心SDK的链式通道,那么为什么使用DelegatingHandler呢?第一和第二种方案需要我们自己实现链式调用的头尾相连的过程。这个过程比较繁琐而且维护成本较高。而HttpClient天生就支持创建附加消息处理程序。这样HttpClient的调用链就是一个调用通道。我们可以根据需求在这个通道中插入自己的逻辑。
关键实现代码
定义通道中的SDK功能实现
/// <summary>
/// 连接中心SDK链路追踪实现
/// </summary>
publicclassTracingHandler : DelegatingHandler
{
//声明变量
private readonly ConnectionProvider _provider;
private readonly MipDeployConfigProvider _deployConfigProvider;
protected static readonly DiagnosticListener _diagnosticListenerEvent = new DiagnosticListener(MIPDiagnosticStrings.EventActionInvokeListenerName);
protected static readonly DiagnosticListener _diagnosticListenerApigateway = new DiagnosticListener(MIPDiagnosticStrings.ApiGatewayActionInvokeListenerName);
protected static readonly DiagnosticListener _diagnosticListenerFlow = new DiagnosticListener(MIPDiagnosticStrings.Flow_ApiConnectionListenerName);
/// <summary>
/// 构造函数
/// </summary>
/// <param name="provider">连接中心运行时提供者</param>
/// <param name="deployConfigProvider">部署信息提供者</param>
public TracingHandler(ConnectionProvider provider, MipDeployConfigProvider deployConfigProvider)
{
_provider = provider;
_deployConfigProvider = deployConfigProvider;
}
/// <summary>
/// 将MIP连接中心请求,相应链路追踪加入到HTTPClient扩展通道中
/// </summary>
/// <param name="requestMessage">发送到连接API的Http请求</param>
/// <param name="cancellationToken">令牌</param>
/// <returns>连接中心API请求结果</returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken)
{
try
{
//省略代码
ExecuteHttpAfter(requestMessage, response, carrier);
return response;
}
else
{
return await base.SendAsync(requestMessage, cancellationToken);
}
}
catch (Exception ex)
{
ExecuteHttpError(requestMessage, ex);
throw;
}
}
}
编排通道调用链
public static class HttpHandleExtension
{
public static void AddHttpClinet(this IServiceCollection services)
{
//编排HttpClient的调用通道链条
services.TryAddTransient<ConnectivityTokenHandler>();
services.TryAddTransient<AuthenticationHandler>();
services.TryAddTransient<LoadBalancerHandler>();
services.TryAddTransient<TracingHandler>();
services.TryAddTransient<CompatibleGzipHandler>();
services.TryAddTransient<PollyCircuitBreakingHandler>();
services.TryAddTransient<WebRequestHandler>();
services.TryAddSingleton<MipDeployConfigProvider>();
AddConnectivityApiClient(services);
AddConnectionApiClient(services);
AddFakeConnectionApiClient(services);
}
/// <summary>
/// 配置HTTP行为
/// </summary>
/// <returns></returns>
private static HttpMessageHandler CreateDefaultHttpClientHandler()
{
var handler = new HttpClientHandler
{
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.None,
UseCookies = false,
UseProxy = false,
ServerCertificateCustomValidationCallback = Const.DangerousAcceptAnyServerCertificateValidator,
};
if (handler.MaxConnectionsPerServer < Const.DefaultMaxConnectionsPerServer)
handler.MaxConnectionsPerServer = Const.DefaultMaxConnectionsPerServer;
//return handler;
return new WebRequestHandler {InnerHandler = handler};
}
}
至此,集成平台中的其他内部服务,只需要调用SDK的注册就可以通过连接Code/Path来调用连接中心中连接实例提供的Rest服务了。
四、后续思考
目前而言,连接中心SDK并未公开任何扩展点。虽然我们构建了调用通道,但是对通道的扩展能力并未对外公开。在后续的过程中。SDK可以根据需求变更进行如下两个优化。
1、支持通道扩展能力。
2、缓存目前只支持从Consul实时更新数据,这也可以在后续过程中根据需求增加更多的基础设施。
------ END ------
作者简介
刘同学: 集成平台的开发工程师,目前负责天际平台相关开发工作。
也许您还想看:
技术分享 | Java SDK 元数据驱动的事件通信架构
技术分享|Hangfire深度实践
技术分享 | APT结合JavaPoet生成模板化Java源代码文件
更多明源云·天际开放平台场景案例与开发小知识,可以关注明源云天际开发者社区公众号: