.net core 微服務之Api網關(Api Gateway)
微服務網關目錄
- 1、 微服務引子
- 2、使用Nginx作為api網關
- 3、自創api網關(重復輪子)
- 3.1、構建初始化
- 3.2、構建中間件
- 4、結語
- 引用鏈接
1、 微服務引子
首先恭喜你,進入微服務的開發世界。微服務屬於架構演進中的一種階段,其特點是根據業務模塊水平劃分服務種類,每個服務可以獨立部署並互相隔離,並對外提供輕量的Api調用,服務具有高可用特性。
微服務應遵循的設計原則:
- 單一職責原則: 每個微服務只需要實現自己的業務邏輯
- 服務自治原則: 每個微服務都是獨立的,不依賴其他模塊
- 輕量級通信原則:一般采用Http + Json方式
- 接口明確原則:接口盡量做的更通用,更靈活,從而盡量避免接口參數的來回修改。
我從2017年12月開始接觸微服務概念,並開始著手構建公司的微服務平臺,系統架構采用 .net core webapi方式組織,隨著微服務的增多,越來越需要一個統一入口管理這些微服務。
2、使用Nginx作為api網關
Nginx是由IgorSysoev為俄羅斯訪問量第二的Rambler.ru站點開發的,一個高性能的HTTP和反向代理服務器。2012年,Nginx榮獲年度雲計算開發獎,並成長為世界第二大Web服務器。全世界流量最高的前1000名網站中,超過25%都使用Nginx來處理海量的互聯網請求。
Nginx很牛掰,業界公認的首選,選擇它作為api網關,可以說不用開發介入,只需要運維的同學好好規劃配置即可。
網關配置規劃如下:
/api/ServiceA —> ServiceA
/api/ServiceB —> ServiceB
/api/ServiceC —> ServiceC
外部統一的訪問入口是Nginx,然後根據服務名稱路由到不同的微服務站點。
api網關的路由難題解決了,其他熔斷,灰度發布,線上測試,日誌攔截等功能nginx做起來相對比較吃力,不過對於中小型平臺已經夠用了。
3、自創api網關(重復輪子)
半年後,換了家公司,公司微服務平臺搭建采用Thrift RPC
僅僅是路由,當然是祭出 .net core Web應用。
3.1、構建初始化
MVC框架包就別包含了,輕量的性能才能好!
Main函數初始化線程池大小
//初始化線程池最小大小 為 300
ThreadPool.GetMinThreads(out var wt, out var ct);
if (wt < 300 || ct < 300)
{
ThreadPool.SetMinThreads(Math.Max(wt, 300), Math.Max(ct, 300));
}
**註意:**線程池不是越大越好,最佳選擇約等於 cpu內核的2倍,有個公式參考:
多說一句吧,思考下:
為什麽Nginx只用4個線程發揮出的性能就大大超越了100個進程的Apache HTTPD?回想一下計算機科學的基礎知識,答案其實是很明顯的。
我要支持跨域訪問:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());
});
}
配置下使用中間件
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("AllowAll");
app.UseMiddleware<ProxyMiddleware>();
// app.UseMvc();
}
3.2、構建中間件
路由轉發,一個中間件即可搞定。
public class ProxyMiddleware
{
private readonly RequestDelegate m_Next;
public ProxyMiddleware(RequestDelegate next)
{
this.m_Next = next;
}
public Task Invoke(HttpContext context)
{
if (context.Request.Method?.ToUpper() == "GET")
{
var path = context.Request.Path.HasValue ? context.Request.Path.ToString().ToLower() : string.Empty;
if (string.IsNullOrEmpty(path) || path.Equals("/") || path.Equals("/api") || path.Equals("/api/"))
{
return this.SendConstRespond(context);
}
//增加一個代理網關接口,返回微服務列表
else if (path.EndsWith("/getallservices"))
{
return this.SendServiceListRespond(context);
}
return this.SendStringRespond(context);
}
if (context.Request.ContentType == null || context.Request.ContentType.IndexOf("application/json") < 0)
{
context.Response.StatusCode = 403;
context.Response.ContentType += "application/json;charset=utf-8;";
return context.Response.WriteAsync("Please set ContentType=application/json");
}
return this.SendStringRespond(context);
}
private Task SendStringRespond(HttpContext context)
{
context.Response.StatusCode = 200;
context.Response.ContentType += "application/json;charset=utf-8;";
Task task = new Task(() =>
{
if (ServerSetting.Config.QuantumConfig.RpcService.ServerType==ServerType.HttpWebApi)
{
string constResp = QuantumHttpProxy.SendHttp(context);
using (var strStream = new StreamWriter(context.Response.Body))
{
strStream.Write(constResp);
strStream.Flush();
}
}
else
{
QuantumHttpProxy.Send(context);
}
});
task.Start();
return task;
}
private Task SendConstRespond(HttpContext context)
{
context.Response.StatusCode = 200;
context.Response.ContentType += "application/json;charset=utf-8;";
Task task = new Task(() =>
{
var constResp = new
{
rid = string.Empty,
c = 200,
msg = "Access api gateway success!"
};
using (var strStream = new StreamWriter(context.Response.Body))
{
strStream.Write(JsonConvert.SerializeObject(constResp));
strStream.Flush();
}
});
task.Start();
return task;
}
private Task SendServiceListRespond(HttpContext context)
{
context.Response.StatusCode = 200;
context.Response.ContentType += "application/json;charset=utf-8;";
Task task = new Task(() =>
{
var svrs = ServerSetting.Config?.HttpProxy?.Items?.Select(x => x.Name)?.ToList() ?? new List<string>();
var constResp = new
{
rid = Guid.NewGuid().ToString("N"),
c = 200,
v = svrs,
};
using (var strStream = new StreamWriter(context.Response.Body))
{
strStream.Write(JsonConvert.SerializeObject(constResp));
strStream.Flush();
}
});
task.Start();
return task;
}
}
上面的代碼裏 有 QuantumHttpProxy,是我們封裝的rpc調用,因此各位同學要用此類,請自行修改之。
4、結語
沒有重試,熔斷也沒有降級,是否很low?
這就是實際實踐的力量,不必耗費太多精力在一些非關鍵點上!
好吧,我承認我研究了 **polly**類庫,重試,熔斷,降級都準備好了,你就起航吧~~~
另外:造輪子只適合特定場景,小投入就可以完成的某些特定功能,比如上面的路由功能!如果你是雲服務,可以重點考慮下 阿裏API網關,亞馬遜api網關!
引用鏈接
- 口袋代碼倉庫
- 在線計算器
- 本節源碼:github
.net core 微服務之Api網關(Api Gateway)