NetCoreAPI⽹关Ocelot
业务的飞速发展,产⽣的⾮常多的对外的服务接⼝,分散在组织的各个地⽅需要进⾏统⼀的管理,⽽且我们的环境是linux和windows的混合环境,我们的⽬标是统⼀在公司的Linux环境,.NET Core对于.NET 技术团队来说是⼀个⾮常棒的技术,⽽且.NET Core本⾝的架构⾮常好,性能就更好了。
这⾥列出了Ocelot⽬前⽀持的特性:
Routing
⽤户可以指定上游请求之间的映射,并将其转发到下游服务上的不同URL。
Service Discovery
Ocelot可以查看你的服务发现,并到它应该转发下游请求的服务。它可以在这些服务之间进⾏负载平衡。.
Authentication using IdentityServer
您可以将端点标记为已认证,并使⽤IdentityServer承载标记对您的⽤户进⾏⾝份验证.
Authorisation using Claims
如果使⽤ bearer tokens, 可以使⽤ claims 标记特定 endpoints是授权的
Claims Transformation
Ocelot提供了⼀种语法来转换给下游请求,并将声明数据添加到标题,URL参数,其他声明等等
Quality of service
Retries, circuit breaker, timeouts etc.
Request / Correlation Ids
Caching
Logging
Custom Middleware
上⾯介绍了Ocelot的功能特性,接下来我们进⼊介绍Ocelot 的实现原理剖析,核⼼是是ASP.NET Core Middleware 以及 ASP.NET Core DependencyInjection:
ASP.NET Core 传统的ASP.NET 在架构上有很⼤的改进,更加的模块化,下图形象的说明了他们之间区别,Application 和 Middleware 是平等的,⽐如ASP.NET Core MVC也是⼀个Middleware,通过Middleware这样的结构我们⾮常容易的扩展我们的应⽤程序。
具体内容参考:和,我们在Middleware的编程过程中需要关注HttpContext 以及管道的注册者和构建者 ApplicationBuilder。
注册 Service 有分三种⽅式:
Transient 每次注⼊时,都重新 new ⼀个新的实体。
Scoped 每个 Request 都重新 new ⼀个新的实体。
Singleton 程序启动后会 new ⼀个实体。也就是运⾏期间只会有⼀个实体。
A 为 Singleton
B 为 Scoped
C 为 Transient
上⾯介绍完了Ocelot开发的基本原理,⽬前Ocelot 由17 个Middleware 来完成,在每个Middleware的内部实现上还有涉及到很多业务的知识,本篇⽂章先不做展开,后续写具体的⽂章详细解析。接下来我们来说说如何⾃定义扩展,在我们的项⽬中主要在三个⽅⾯进⾏了扩展:
1、⾃定义扩展API 接⼝验证
2、⾃定义扩展下游通讯协议
Ocelot 默认⽀持Http的通讯,在我们的实际项⽬中有很多⽼的服务是RPC调⽤,使⽤的是私有的Relay通讯框架,在API⽹关上需要做协议转换,⾃动将Http的请求转换成Relay的tcp通讯。
3、⾃定义管理控制台
⾸先,让我们简单了解下什么是API⽹关?
API⽹关是⼀个服务器,是系统的唯⼀⼊⼝。从⾯向对象设计的⾓度看,它与外观模式类似。API⽹关封装了系统内部架构,为每个客户端提供⼀个定制的API。它可能还具有其它职责,如⾝份验证、监控、负载均衡、缓存、请求分⽚与管理、静态响应处理。
API⽹关⽅式的核⼼要点是,所有的客户端和消费端都通过统⼀的⽹关接⼊微服务,在⽹关层处理所有的⾮业务功能。通常,⽹关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
其次,我们了解下Ocelot框架
Ocelot的⽬标是使⽤.NET运⾏微服务/⾯向服务架构,我们需要⼀个统⼀的⼊⼝进⼊我们的服务,提供监控、鉴权、负载均衡等机制,也可以通过编写中间件的形式,来扩展Ocelot的功能。 Ocelot是⼀堆特定顺序的中间件。
Ocelot框架内部集成了IdentityServer和Consul(服务注册发现),还引⼊了Polly来处理进⾏故障处理,关于Polly,可以在这篇博客中了解更多
接下来,我们就针对Ocelot的具体⽤法展开介绍。
这⾥使⽤的Ocelot版本为2.0,.Net Core版本2.0
1、搭建Asp Core WebApi项⽬,引⽤Ocelot.dll。
Nuget控制台,执⾏Ocelot安装。
1PM>Install-Package Ocelot
1
2
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22GET /v3/registration3-gz-semver2/ocelot/index.json
OK /v3/registration3-gz-semver2/ocelot/index.json 104 毫秒
GET /v3/registration3-gz-semver2/ocelot/page/1.5.0-unstable0134/2.0.0.json
OK /v3/registration3-gz-semver2/ocelot/page/1.5.0-unstable0134/2.0.0.json 108 毫秒
正在还原 J:\Demo\RichCodeBox\APIGatewayApp\APIGatewayApp.csproj 的包...
GET /v3-flatcontainer/ocelot/index.json
OK /v3-flatcontainer/ocelot/index.json 476 毫秒
GET /v3-flatcontainer/ocelot/2.0.0/ocelot.2.0.0.nupkg
OK /v3-flatcontainer/ocelot/2.0.0/ocelot.2.0.0.nupkg 125 毫秒
GET /v3-flatcontainer/identityserver4.accesstokenvalidation/index.json
GET //index.json
GET /v3-flatcontainer/figuration/index.json
GET /v3-flatcontainer/sions.logging/index.json
GET /v3-flatcontainer/consul/index.json
GET /v3-flatcontainer/polly/index.json
GET /v3-flatcontainer/identityserver4/index.json
OK /v3-flatcontainer/identityserver4.accesstokenvalidation/index.json 133 毫秒
GET /v3-flatcontainer/identityserver4.accesstokenvalidation/2.1.0/identityserver4.accesstokenvalidation.2.1.0.nupkg
OK /v3-flatcontainer/sions.logging/index.json 286 毫秒
OK /v3-flatcontainer/polly/index.json 287 毫秒
OK /v3-flatcontainer/identityserver4.accesstokenvalidation/2.1.0/identityserver4.accesstokenvalidation.2.1.0.nupkg 160 毫秒
GET /v3-flatcontainer/sions.logging/1.1.1/sions.logging.1.1.1.nupkg
2、修改Startup程序。1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16/// <summary>
///
/// </summary>
/// <param name="environment"></param>
public Startup(IHostingEnvironment environment)
{
var builder = new Microsoft.Extensions.Configuration.ConfigurationBuilder();
builder.SetBasePath(environment.ContentRootPath)
.AddJsonFile("appsettings.json", false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: false, reloadOnChange: true) .AddJsonFile("configuration.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
1
2
3
4
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31/// <summary>
///modified:配置
/// </summary>
public IConfigurationRoot Configuration { get; }
/// <summary>
/// 配置服务
/// </summary>
/// <param name="services"></param>
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
}).WithDictionaryHandle();
};
services.AddOcelot(Configuration, settings);
//services.AddMvc();
}
/
// <summary>
/// 配置Ocelot
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
public async void Configure(IApplicationBuilder app, IHostingEnvironment env) {
//if (env.IsDevelopment())
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 //{
// app.UseDeveloperExceptionPage();
//}
await app.UseOcelot();
/
/app.UseMvc();
}
/// <summary>
/// ⼊⼝程序
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
IWebHostBuilder builder = new WebHostBuilder();
builder.ConfigureServices(s =>
{
s.AddSingleton(builder);
});
builder.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights();
var host = builder.Build();
host.Run();
}
3、配置Ocelot。
我们新建⼀个名为configuration的json⽂件,配置如下:1
2
3
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/values", "DownstreamScheme": "http",
"DownstreamHost": "localhost",
"DownstreamPort": 8801,
"UpstreamPathTemplate": "/api/values",
"UpstreamHttpMethod": [ "Get"],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 10,
"TimeoutValue": 5000
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
"UseCookieContainer": false
},
"AuthenticationOptions": {
}
},
{
"DownstreamPathTemplate": "/api/product", "DownstreamScheme": "http",
"DownstreamPort": 8802,
"DownstreamHost": "localhost",
"UpstreamPathTemplate": "/api/product", "UpstreamHttpMethod": [ "Get"],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 10,
"TimeoutValue": 5000
},
"AuthenticationOptions": {
}
}
]
,
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/admin"
}
}
在这⾥,我们配置了两个服务,端⼝分别为8801和8802的。
Ocelot⽀持负载均衡(提供轮询、最少访问)。Ocelot⼤部分功能,都可以通过中间件来完成,也可以实现和重写中间件。
Ocelot原理⾮常简单,这⾥的配置⽂件,体现了上游请求和下游服务间的映射关系,你可以理解为,上游是客户端直接调⽤的URL ,下游,则是对应我们开发的服务。
4、新增两个WebApi项⽬,分别为APIUserServiec和APIProductService。
API服务端⼝(Port)
APIUserServiec8801
APIProductService8802
解决⽅案如下:
5、配置VS启动端⼝:
依次类推,分别设置端⼝。
6、启动项⽬。
配置多个项⽬启动。
F5启动项⽬。
常见问题:
⾸次在启动API⽹关时,触发以下错误。
Sequence contains no matching element
根据错误详细信息,可知原因是由于系统调⽤AddIdentityServer⽅法时,触发异常。
刚开始,怀疑是配置⽂件configuration.json⽂件配置导致的,Ocelot2.0版,采⽤官⽅配置仍然触发该异常,由此排除这种可能。接下来,直接从github上克隆源代码,查看。
到触发错误的地⽅,
private static void AddIdentityServer(this IServiceCollection services, IIdentityServerConfiguration identityServerConfiguration, IConfigurationRoot configurationRoot)
{
services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
services.TryAddSingleton<IHashMatcher, HashMatcher>();
var identityServerBuilder = services
.AddIdentityServer(o => {
o.IssuerUri = "Ocelot";
})
.AddInMemoryApiResources(Resources(identityServerConfiguration))
.AddInMemoryClients(Client(identityServerConfiguration))
.AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
//todo - refactor a method so we know why this is happening
var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder));//这个地⽅触发了错误
var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
var baseSchemeUrlAndPort = urlFinder.Find();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.
AddIdentityServerAuthentication(o =>
{
var adminPath = configurationRoot.GetValue("GlobalConfiguration:AdministrationPath", string.Empty);
o.Authority = baseSchemeUrlAndPort + adminPath;
o.ApiName = identityServerConfiguration.ApiName;
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
o.SupportedTokens = SupportedTokens.Both;
微服务网关和注册中心区别o.ApiSecret = identityServerConfiguration.ApiSecret;
});
//todo - refactor naming..
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword)) {
identityServerBuilder.AddDeveloperSigningCredential();
}
else
{
//todo - refactor so calls method?
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
identityServerBuilder.AddSigningCredential(cert);
}
var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder));
这就代码触发了错误,是由于表达式条件不成⽴,导致First发⽣异常,这就简单了,我们修改Main函数,把这句代码:
var builder = new WebHostBuilder();
改成:
IWebHostBuilder builder = new WebHostBuilder();
这样,使⽤Ocelot框架搭建API⽹关的⼯作已经完成,尝试通过访问API⽹关,来访问下游的服务。
欢迎⼤家⼀起研究探讨,开启你的微服务之路。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论