Ocelot+Naco搭建的.net core 微服务下封装通用的服务之间的调用中间件
在 Ocelot + Nacos 搭建的 .NET Core 微服务中,可以封装一个通用的服务调用中间件,该中间件可以使用 Ocelot 作为 API 网关路由微服务请求,并通过 Nacos 服务发现来实现微服务的动态调用。
构建中间件ServiceProxyMiddleware
以下是一个构建通用服务调用中间件的示例代码:
public class ServiceProxyMiddleware
{
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private readonly IServiceDiscoveryProvider _serviceDiscoveryProvider;
public ServiceProxyMiddleware(RequestDelegate next, IConfiguration configuration, IServiceDiscoveryProvider serviceDiscoveryProvider)
{
_next = next;
_configuration = configuration;
_serviceDiscoveryProvider = serviceDiscoveryProvider;
}
public async Task InvokeAsync(HttpContext context)
{
// 获取请求服务名称
var serviceName = context.Request.Headers["ServiceName"];
if (string.IsNullOrEmpty(serviceName))
{
// 如果请求中没有ServiceName头,则直接跳过
await _next(context);
return;
}
// 获取服务实例地址
var serviceInstances = await _serviceDiscoveryProvider.GetInstancesAsync(serviceName);
// 轮询方式选择一个服务实例
string serviceInstance = null;
if (serviceInstances != null && serviceInstances.Count() > 0)
{
serviceInstance = serviceInstances.RandomOrDefault()?.ServiceUrl;
}
if (string.IsNullOrEmpty(serviceInstance))
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return;
}
// 转发请求到微服务
await ForwardRequestToServiceAsync(context, serviceInstance);
await _next(context);
}
private async Task ForwardRequestToServiceAsync(HttpContext context, string serviceInstance)
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(serviceInstance);
var requestMessage = new HttpRequestMessage();
requestMessage.Method = new HttpMethod(context.Request.Method);
requestMessage.RequestUri = new Uri(httpClient.BaseAddress + context.Request.Path);
requestMessage.Content = new StreamContent(context.Request.Body);
if (context.Request.Headers != null)
{
foreach (var h in context.Request.Headers)
{
requestMessage.Headers.TryAddWithoutValidation(h.Key, h.Value.ToArray());
}
}
HttpResponseMessage response = await httpClient.SendAsync(requestMessage);
// 将微服务的响应直接返回
context.Response.StatusCode = (int)response.StatusCode;
foreach (var header in response.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
var stream = await response.Content.ReadAsStreamAsync();
await stream.CopyToAsync(context.Response.Body);
}
}
}
在上述代码中,ServiceProxyMiddleware 是微服务调用中间件的主体。当请求到达 API 网关时,该中间件会从传入请求的头部信息中获取服务名称,并使用 IServiceDiscoveryProvider 接口封装的服务发现功能获取服务的实例地址,并使用 HttpClient 将请求转发到微服务的实例地址,最后将微服务的响应直接返回到 API 网关。
我们还可以通过创建一个包含 IServiceDiscoveryProvider 接口的默认实现的服务集合,将 ServiceProxyMiddleware 用于微服务调用中间件的引用注入到程序的服务容器中。这样,在需要发出调用的应用程序中,可以轻松地使用相同的控件调用通用的服务调用中间件。
注入服务
例如,在 ConfigureServices 方法中,我们可以注册该服务:
public void ConfigureServices(IServiceCollection services)
{
// 注入Nacos服务发现功能
services.AddSingleton<IServiceDiscoveryProvider, NacosServiceDiscoveryProvider>(sp =>
{
var configuration = sp.GetService<IConfiguration>();
var options = new NacosDiscoveryOptions();
configuration.GetSection("Nacos").Bind(options);
return new NacosServiceDiscoveryProvider(options);
});
// 注入服务调用中间件
services.AddSingleton<ServiceProxyMiddleware>();
}
最后,我们将 ServiceProxyMiddleware 用于微服务调用中间件的引用注入到程序的服务容器中,并在 Configure 方法中添加以下代码:
使用中间件
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 省略代码...
// 使用服务调用中间件
app.UseMiddleware<ServiceProxyMiddleware>();
}
这样,在需要连接到任何微服务的应用程序中。
调用中间件
在需要连接到任何微服务的应用程序中,可以通过以下代码来调用通用服务调用中间件:
public class MyService
{
private readonly HttpClient _httpClient;
public MyService(HttpClient httpClient)
{
_httpClient = httpClient;
}
// 向指定微服务发起请求,其中 serviceName 是服务名称、apiPath 是 API 的地址,返回响应消息体 string 类型
public async Task<string> GetMicroserviceResponseAsync(string serviceName, string apiPath)
{
var request = new HttpRequestMessage(HttpMethod.Get, apiPath);
request.Headers.Add("ServiceName", serviceName);
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
IServiceDiscoveryProvider接口的实现
IServiceDiscoveryProvider 接口定义了从服务发现源中获取微服务实例的方法。在 Ocelot + Nacos 架构中,我们可以创建一个接口实现,用于从 Nacos 服务注册中心中获取服务实例。
以下是一个基于 Nacos 的 IServiceDiscoveryProvider 接口实现:
public class NacosServiceDiscoveryProvider : IServiceDiscoveryProvider
{
private readonly INacosNamingClient _namingClient;
public NacosServiceDiscoveryProvider(NacosDiscoveryOptions options)
{
_namingClient = new NacosNamingClient(Options.Create(options));
}
public async Task<List<ServiceInstance>> GetInstancesAsync(string serviceName)
{
// 从 Nacos 服务注册中心获取指定服务名称的所有实例
var instances = await _namingClient.ListInstances(serviceName);
// 将 Nacos 服务注册中心返回的实例数据映射为 ServiceInstance 实体
var serviceInstances = new List<ServiceInstance>();
if (instances != null)
{
foreach (var instance in instances)
{
var serviceInstance = new ServiceInstance
{
ServiceName = serviceName,
ServiceUrl = instance.ToUrlString()
};
serviceInstances.Add(serviceInstance);
}
}
return serviceInstances;
}
}
在上述代码中,NacosServiceDiscoveryProvider 是接口 IServiceDiscoveryProvider 的实现。在构造函数中,我们使用 NacosDiscoveryOptions 配置项初始化 NacosNamingClient,并将其保存为类成员变量。在 GetInstancesAsync 方法中,我们使用 _namingClient.ListInstances 方法从 Nacos 服务注册中心获取特定服务名称的所有实例,然后将实例数据转换为 ServiceInstance 实体,并使用实体列表封装的所有实例返回结果。
在上述代码中,ServiceInstance 是一个映射到服务实例的实体类,可以根据自己的需要自定义它的属性和逻辑。
以下是 ServiceInstance 类的一个示例定义:
public class ServiceInstance
{
public string ServiceName { get; set; }
public string ServiceUrl { get; set; }
}
以上是基于 Nacos 的 IServiceDiscoveryProvider 接口实现,它可以从 Nacos 服务注册中心中获取服务实例,并在需要的时候使用这些实例作为目标微服务的地址。

浙公网安备 33010602011771号