0.背景 在實際項目當中,我們採用的是 Abp 框架,但是 Abp 框架官方並沒有針對 Grpc 進行模塊封裝。基於此我結合 Abp 與 MagicOnion 封裝了一個 Abp.Grpc 模塊,它包括服務端和調用端兩部分的包。通過這兩個包,你可以很方便地在 Abp 框架當中集成 Grpc 實現服務 ...
0.背景
在實際項目當中,我們採用的是 Abp 框架,但是 Abp 框架官方並沒有針對 Grpc 進行模塊封裝。基於此我結合 Abp 與 MagicOnion 封裝了一個 Abp.Grpc 模塊,它包括服務端和調用端兩部分的包。通過這兩個包,你可以很方便地在 Abp 框架當中集成 Grpc 實現服務內部通訊。
但是在實際使用當中會出現一個問題,當 A 服務調用 B 服務的時候,A 服務當前登錄用戶為 admin,調用 B 服務的 IAbpSession
的值仍然為空,這個時候當 B 服務內部實現使用了 IAbpSession
時會出現問題。
這是因為通過 Grpc 介面調用時,並沒有傳遞諸如 Token 之類的東西,而在 B 服務內部的 IAbpSession
本身附加的數據是從 HttpContext
裡面獲取的,所以 B 服務當前是沒有用戶狀態的。
1.解決
所幸 IAbpSession
提供了一個 Use
方法,通過這個方法我們可以臨時地改變 IAbpSession
內部的值,當 。定義如下:
IDisposable Use(int? tenantId, long? userId);
使用方法如下:
public class TestAppService : ITransientDependency
{
private readonly IAbpSession _abpSession;
public TestAppService(IAbpSession abpSession)
{
_abpSession = abpSession;
}
public void TestMethod()
{
using(_abpSession.Use(10,20))
{
// 其他操作
}
// 出去 using 語句之後會自動釋放之前的值
}
}
2.Grpc 介面改造
這裡 Abp.Grpc 庫使用的是 MagicOnion 庫實現 Grpc 介面的,底層序列化使用的是 MessagePack,速度也不比 Protocol Buffer 差。
2.1 服務定義
服務定義介面時,必須附加一個 GrpcSession
參數,這個參數用於調用方傳遞其 IAbpSession
值所使用。例如我有一個介面方法如下,用於返回服務方接收到的用戶 Id 值。
public interface ITestGrpcService : IService<ITestGrpcService>
{
// 普通的 Grpc 介面定義
UnaryResult<int> Sum(int x, int y);
// 帶有 GrpcSession 的介面定義
UnaryResult<long?> TestGrpcSession(GrpcSession session);
}
2.2 服務提供方
服務提供方在實現 ITestGrpcService
的時候,需要在代碼起始點就開始使用 using
語句包裹代碼。
public class TestGrpcService : ServiceBase<ITestGrpcService>, ITestGrpcService
{
private readonly IAbpSession _abpSession;
public TestGrpcService()
{
_abpSession = IocManager.Instance.Resolve<IIocManager>().Resolve<IAbpSession>();
}
public UnaryResult<int> Sum(int x, int y)
{
return UnaryResult(x + y);
}
public UnaryResult<long?> TestGrpcSession(GrpcSession session)
{
// 賦值前 Session 的值
Console.WriteLine(_abpSession.UserId);
// 臨時改變 Session 值
using (_abpSession.Use(session.TenantId, session.UserId))
{
Console.WriteLine(_abpSession.UserId);
}
// 離開 using 語句時 Session 的值
Console.WriteLine(_abpSession.UserId);
return new UnaryResult<long?>(1000);
}
}
2.3 服務調用方
服務調用方則直接在調用 Grpc 介面的時候,傳遞給介面當前服務的 Session 狀態。
public class TestApplicationService : ApplicationService
{
private readonly IGrpcConnectionUtility _utility;
public TestApplicationService(IGrpcConnectionUtility utility)
{
_utility = utility;
}
public void TestAction()
{
// 獲得指定的 Grpc 服務
var service = _utility.GetRemoteService<ITestGrpcService>("Grpc 服務名稱");
// 調用測試方法,傳遞當前調用方的 Session 值
var userId = service.TestGrpcSession(AbpSession as AbpSessionBase).GetAwaiter().GetResult();
Console.WriteLine("TestGrpcSession 方法結果:" + userId);
}
}
2.4 最後的效果
當客戶端調用 GRPC 介面時,會將自身的 Session 狀態通過 GrpcSession 傳遞到服務端,這樣服務端就能夠共用客戶端的繪畫狀態。
3.Abp.Grpc 項目地址
Abp.Grpc 庫地址:https://github.com/GameBelial/Abp.Grpc
4.實現的 DEMO 地址
服務端:https://github.com/GameBelial/Abp.Grpc.Server.Demo
客戶端:https://github.com/GameBelial/Abp.Grpc.Client.Demo