Abp + gRpc 如何實現用戶會話狀態傳遞

0.背景

在實際項目當中,咱們採用的是 Abp 框架,可是 Abp 框架官方並無針對 Grpc 進行模塊封裝。基於此我結合 Abp 與 MagicOnion 封裝了一個 Abp.Grpc 模塊,它包括服務端和調用端兩部分的包。經過這兩個包,你能夠很方便地在 Abp 框架當中集成 Grpc 實現服務內部通信。git

可是在實際使用當中會出現一個問題,當 A 服務調用 B 服務的時候,A 服務當前登陸用戶爲 admin,調用 B 服務的 IAbpSession 的值仍然爲空,這個時候當 B 服務內部實現使用了 IAbpSession 時會出現問題。github

這是由於經過 Grpc 接口調用時,並無傳遞諸如 Token 之類的東西,而在 B 服務內部的 IAbpSession 自己附加的數據是從 HttpContext 裏面獲取的,因此 B 服務當前是沒有用戶狀態的。session

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 差。code

2.1 服務定義

服務定義接口時,必須附加一個 GrpcSession 參數,這個參數用於調用方傳遞其 IAbpSession 值所使用。例如我有一個接口方法以下,用於返回服務方接收到的用戶 Id 值。blog

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 狀態。rpc

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 傳遞到服務端,這樣服務端就可以共享客戶端的繪畫狀態。get

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

相關文章
相關標籤/搜索