RPC远程过程调用,二进制封装,提供高吞吐低延迟的高性能RPC框架
大石头 authored at 2022-08-10 13:26:19
4.13 KiB
NewLife.Remoting
using System;
using System.IO;
using System.Threading.Tasks;
using NewLife;
using NewLife.Data;
using NewLife.Log;
using NewLife.Remoting;
using XCode;
using XCode.Membership;
using Xunit;

namespace XUnitTest.Samples;

/// <summary>ZeroRpcServer 集成测试夹具。启动完整的 ApiServer(端口0自动分配),并初始化临时 SQLite 数据库</summary>
public sealed class ZeroRpcServerFixture : IAsyncLifetime
{
    #region 属性
    /// <summary>服务端监听端口(系统自动分配)</summary>
    public Int32 Port { get; private set; }

    /// <summary>测试用户 ID</summary>
    public Int32 UserId { get; private set; }

    private ApiServer _server = null!;
    private String _tempDir = null!;
    private String _origDataPath = null!;
    private String _origCurrentDir = null!;
    #endregion

    #region 初始化与清理
    public async Task InitializeAsync()
    {
        XTrace.UseConsole();

        // 独立临时目录,测试互不干扰
        _tempDir = Path.Combine(Path.GetTempPath(), "ZeroRpcTest_" + Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(_tempDir);

        // 重定向 XCode 数据目录到临时目录,避免污染工作目录
        _origDataPath = NewLife.Setting.Current.DataPath;
        NewLife.Setting.Current.DataPath = _tempDir;

        _origCurrentDir = Environment.CurrentDirectory;
        Environment.CurrentDirectory = _tempDir;

        // 初始化所有 XCode 实体表(自动建表)
        EntityFactory.InitAll();

        // 预置用户,让 UserController 能正常查询到
        var user = new User
        {
            Name = "TestUser",
            DisplayName = "测试用户",
            Enable = true,
        };
        user.Save();
        UserId = user.ID;

        // 启动 ApiServer(端口0自动分配,同时监听 TCP/UDP/WS/HTTP)
        _server = new ApiServer(0)
        {
            Name = "ZeroRpcTestServer",
            Encoder = new JsonEncoder(),
            Log = XTrace.Log,
#if DEBUG
            EncoderLog = XTrace.Log,
#endif
        };

        _server.Register<MyController>();
        _server.Register<UserTestController>();
        _server.Start();

        Port = _server.Port;

        XTrace.WriteLine("ZeroRpcServerFixture 启动,端口:{0}", Port);

        await Task.CompletedTask;
    }

    public async Task DisposeAsync()
    {
        _server.TryDispose();

        // 恢复工作目录
        if (!_origCurrentDir.IsNullOrEmpty()) Environment.CurrentDirectory = _origCurrentDir;
        if (!_origDataPath.IsNullOrEmpty()) NewLife.Setting.Current.DataPath = _origDataPath;

        // 删除临时目录
        if (Directory.Exists(_tempDir))
        {
            try { Directory.Delete(_tempDir, true); }
            catch { /* 忽略清理异常 */ }
        }

        await Task.CompletedTask;
    }
    #endregion

    #region 内联控制器(镜像 Zero.RpcServer 中的控制器逻辑)
    internal class MyController
    {
        /// <summary>整数加法</summary>
        public Int32 Add(Int32 x, Int32 y) => x + y;

        /// <summary>RC4 加解密往返</summary>
        public IPacket RC4(IPacket pk)
        {
            var data = pk.ToArray();
            var pass = "NewLife".GetBytes();
            return (ArrayPacket)data.RC4(pass);
        }
    }

    [Api("User")]
    internal class UserTestController : IApi, IActionFilter
    {
        public IApiSession Session { get; set; } = null!;

        [Api(nameof(FindByID))]
        public async Task<Object?> FindByID(Int32 id)
        {
            var times = Session["Times"].ToInt();
            times++;
            Session["Times"] = times;

            if (times >= 2)
                throw new ApiException(ApiCode.TooManyRequests, $"调用次数过多!Times={times}");

            await Task.Delay(10);
            var user = User.FindByID(id);
            if (user == null) return null;
            return new { user.ID, user.Name, user.DisplayName, user.Enable };
        }

        public void OnActionExecuting(ControllerContext filterContext) { }
        public void OnActionExecuted(ControllerContext filterContext) { }
    }
    #endregion
}