0 前言
在物聯網領域中,mqtt消息一直是海量設備連接到平臺的標配協議,而平臺向移動端開放的操作接口往往是http協議,這就要求平臺爲兩種協議作消息一一適配。在某些情況下,這些設備是有操作系統的linux或安卓設備,如果我們換個思路,讓這些設備直接提供http協議的操作接口(httpd服務器),平臺端使用隧道打通設備與移動端的傳輸層,做爲平臺開發人員,就可以省去時間做這些繁瑣的消息級別的一一適配工作了。
1 面臨的問題
1.1 海量連接
mqtt默認基於tcp傳輸,一臺設備與平臺維持一個tcp連接即可高效的上下並行傳輸消息。而http/1.1在協議層限制了單個連接串行請求響應,但我們可以使用http/2.0或http/3.0做爲http/1.1的傳輸層,也能達到一個物理鏈接多路傳輸http/1.1的目的。這樣下來內網http/1.1穿透也和mqtt一樣,只需要一個物理長連接。
1.2 身份認證
我嘗試使用frp來承載物聯網設備的http內網穿透,但至今還沒成功完成海量設備的一機一密的身份認證,它裏面的token
認證方式只適合一平臺對一個設備的安全要求,而OIDC驗證我至今仍然看不明白怎麼使用。對於一款內網穿透組件或應用,能像asp.netcore的身份驗證強大,對平臺端而言是非常需要的。
1.3 安全傳輸
設備內置的http服務器,一般都是沒有https,因爲它本身就不考慮公網傳輸的能力。但在內網穿透之後,我們必須要考慮設備到公網平臺這段的傳輸安全。
1.4 開放協議
內網穿透組件涉及到平臺的服務端和集成在設備裏的客戶端庫,平臺端一般只有二次開發的需求。而設備端由於芯片、系統和指令集、內存限制等等因素,只提供客戶端庫或二進制可執行文件是不夠的,還可能需要客戶端開發者根據交互協議來自行開發客戶端組件。這就要求內網穿透組件提供客戶端與服務端的交互協議,且最好是設計爲非常簡單的協議。
2 CYarp出場
CYarp基於Yarp的http內網穿透中間件,支持tcp、http/2.0或http/3.0作爲http/1.1的傳輸層,其具有以下特徵
- 是一個標準的asp.netcore中間件
- 能完整使用asp.netcore框架和其它中間件
- 使用服務端的tls(https)做爲安全傳輸層
- 單連接多路複用,無需客戶端提供http2服務器
- 協議透明簡單,參考了WebSocket升級和Bootstrapping WebSockets with HTTP/2
2.1 服務端開發
CYarp中間件其依賴於Authentication身份認證中間件,使用如下方法進行註冊和中間件的配置。
builder.Services.AddCYarp(cyarp=>
{
...
});
中間件配置順序如下:
...
app.UseAuthentication();
...
app.UseCYarp();
...
最後在Controller、minapi的處理者或中間件中處理http轉發
// 請求者的授權驗證
[Authorize(Roles = "Mobile")]
public class CYarpController : ControllerBase
{
private static readonly string clientIdClaimType = "ClientId";
/// <summary>
/// 處理cyarp
/// 核心操作是從請求上下文獲取clientId
/// 然後使用clientId從clientManager獲取client來轉發http
/// </summary>
/// <param name="clientManager"></param>
/// <returns></returns>
[Route("/{**cyarp}")]
public async Task InvokeAsync([FromServices] IClientManager clientManager)
{
var clientId = this.User.FindFirstValue(clientIdClaimType);
if (clientId != null && clientManager.TryGetValue(clientId, out var client))
{
this.Request.Headers.Remove(HeaderNames.Authorization);
await client.ForwardHttpAsync(this.HttpContext);
}
else
{
this.Response.StatusCode = StatusCodes.Status502BadGateway;
}
}
}
2.2 客戶端開發
使用CYary.Client.CYarpClient
很方便完成客戶端開發
using var client = new CYarpClient();
while (true))
{
await client.TransportAsync(this.clientOptions.CurrentValue, stoppingToken).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
}
2.3 客戶端協議
由於篇幅有限,不在這裏展開,可以查閱CYarp握手協議
3 同源產品
Yarp是dotnet平臺下堪比nginx的一個組件,其它融入asp.netcore框架作爲裏面的中間件共享asp.netcore生態。
davidfowl大神也曾經小手一揮造就了YarpTunnelDemo項目,也是實現了http內網穿透的能力。但是他的實現方案要求客戶端方能要運行asp.netcore監聽做爲客戶端組件,由於asp.netcore的runtime只適配了桌面系統,且編譯出的二進制文件很大,此方法自然無法在物聯網設備中運行了。