
首先,讓我們簡單了解下什么是API網關?
API網關是一個服務器,是系統的唯一入口。從面向對象設計的角度看,它與外觀模式類似。API網關封裝了系統內部架構,為每個客戶端提供一個定制的API。它可能還具有其它職責,如身份驗證、監控、負載均衡、緩存、請求分片與管理、靜態響應處理。
API網關方式的核心要點是,所有的客戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。通常,網關也是提供REST/HTTP的訪問API。服務端通過API-GW注冊和管理服務。
其次,我們了解下Ocelot框架
Ocelot的目標是使用.NET運行微服務/面向服務架構,我們需要一個統一的入口進入我們的服務,提供監控、鑒權、負載均衡等機制,也可以通過編寫中間件的形式,來擴展Ocelot的功能。 Ocelot是一堆特定順序的中間件。
Ocelot框架內部集成了IdentityServer和Consul(服務注冊發現),還引入了Polly來處理進行故障處理,關於Polly,可以在這篇博客中了解更多《已被.NET基金會認可的彈性和瞬態故障處理庫Polly介紹》
Ocelot開源地址:https://github.com/TomPallister/Ocelot
接下來,我們就針對Ocelot的具體用法展開介紹。
這里使用的Ocelot版本為2.0,.Net Core版本2.0
1、搭建Asp.net Core WebApi項目,引用Ocelot.dll。

Nuget控制台,執行Ocelot安裝。
PM>Install-Package Ocelot
GET https://api.nuget.org/v3/registration3-gz-semver2/ocelot/index.json OK https://api.nuget.org/v3/registration3-gz-semver2/ocelot/index.json 104 毫秒 GET https://api.nuget.org/v3/registration3-gz-semver2/ocelot/page/1.5.0-unstable0134/2.0.0.json OK https://api.nuget.org/v3/registration3-gz-semver2/ocelot/page/1.5.0-unstable0134/2.0.0.json 108 毫秒 正在還原 J:\Demo\RichCodeBox\APIGatewayApp\APIGatewayApp.csproj 的包... GET https://api.nuget.org/v3-flatcontainer/ocelot/index.json OK https://api.nuget.org/v3-flatcontainer/ocelot/index.json 476 毫秒 GET https://api.nuget.org/v3-flatcontainer/ocelot/2.0.0/ocelot.2.0.0.nupkg OK https://api.nuget.org/v3-flatcontainer/ocelot/2.0.0/ocelot.2.0.0.nupkg 125 毫秒 GET https://api.nuget.org/v3-flatcontainer/identityserver4.accesstokenvalidation/index.json GET https://api.nuget.org/v3-flatcontainer/cachemanager.core/index.json GET https://api.nuget.org/v3-flatcontainer/cachemanager.microsoft.extensions.configuration/index.json GET https://api.nuget.org/v3-flatcontainer/cachemanager.microsoft.extensions.logging/index.json GET https://api.nuget.org/v3-flatcontainer/consul/index.json GET https://api.nuget.org/v3-flatcontainer/polly/index.json GET https://api.nuget.org/v3-flatcontainer/identityserver4/index.json OK https://api.nuget.org/v3-flatcontainer/identityserver4.accesstokenvalidation/index.json 133 毫秒 GET https://api.nuget.org/v3-flatcontainer/identityserver4.accesstokenvalidation/2.1.0/identityserver4.accesstokenvalidation.2.1.0.nupkg OK https://api.nuget.org/v3-flatcontainer/cachemanager.microsoft.extensions.logging/index.json 286 毫秒 OK https://api.nuget.org/v3-flatcontainer/polly/index.json 287 毫秒 OK https://api.nuget.org/v3-flatcontainer/identityserver4.accesstokenvalidation/2.1.0/identityserver4.accesstokenvalidation.2.1.0.nupkg 160 毫秒 GET https://api.nuget.org/v3-flatcontainer/cachemanager.microsoft.extensions.logging/1.1.1/cachemanager.microsoft.extensions.logging.1.1.1.nupkg
2、修改Startup程序。
/// <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();
}
/// <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())
//{
// 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文件,配置如下:
{
"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) |
| APIUserServiec | 8801 |
| APIProductService | 8802 |
解決方案如下:

5、配置VS啟動端口:

依次類推,分別設置端口。
6、啟動項目。
配置多個項目啟動。

F5啟動項目。

再通過API網關,訪問商品服務:http://localhost:5000/api/product。

常見問題:
首次在啟動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();
這樣,就解決了問題,API網關啟動成功。另外,官方例子https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/Program.cs中,寫法是正確的,大家自己寫的時候,注意把IWebHostBuilder換成var會引發錯誤。
這樣,使用Ocelot框架搭建API網關的工作已經完成,嘗試通過訪問API網關,來訪問下游的服務。
此實例源碼:https://gitee.com/lichaoqiang/RichCodeBox.git
歡迎大家一起研究探討,開啟你的微服務之路。
