基於.netcore 開發的輕量Rpc框架


Rpc原理詳解

博客上已經有人解釋的很詳細了,我就不在解釋了。傳送門

項目簡介

項目是依賴於.net core2.0版本,內部都是依靠IOC來實現的,方便做自定義擴展。底層的通信是采用socket,sokcet的代碼參考Enode的socket代碼。類的序列化目前只支持自帶的BinarySerializer和Json.net,也可以自定義,擴展也很方便。也支持zookeeper的服務協調。

框架傳輸及序列化邏輯

當客戶端發起請求時,根據建立的客戶端代理,獲取當前的請求方法信息(名字、所屬類型、參數值),通過自帶的BinarySerializer將其序列化,所以在傳輸的方法中的自定義的類就必須加上【Serializable】可序列化標記,不然會報錯。客戶端將請求的方法信息序列化成字節數組之后傳輸到服務端,服務端獲取到信息后根據方法信息獲取到要執行的方法,然后執行該方法,並將結果返回給客戶端。返回結果的序列化可以采用自定義的標記來進行,比如在方法或者類上面打上【BinarySerializer】標記,則采用BinarySerializer序列化,打上【JsonSerializer】標記則采用json.net來序列化話,后期可以支持protobuf來序列化。

服務端代碼

首先定義一個接口和一個實現類

namespace NetCoreRpc.Application
{
    public interface IStudentApplication
    {
        int Age();

        bool IsYongPeople(int age);

        void Say(string msg);

        Task Sleep();

        Task<int> RunAsync(int sleepTime);

        void Say(byte[] msg);

        byte[] Say();

        [BinarySerializer]
        TestModel Test();
    }

    public class StudentApplication : IStudentApplication
    {
        public int Age()
        {
            return 10;
        }

        public bool IsYongPeople(int age)
        {
            return age < 18;
        }

        public async Task<int> RunAsync(int sleepTime)
        {
            await Task.Delay(sleepTime);
            return sleepTime;
        }

        public void Say(string msg)
        {
            Console.WriteLine($"Say:{msg}");
        }

        public Task Sleep()
        {
            return Task.Delay(10);
        }

        public void Say(byte[] msg)
        {
            Console.WriteLine(Encoding.UTF8.GetString(msg));
        }

        public byte[] Say()
        {
            return Encoding.UTF8.GetBytes("Good Job!");
        }

        public TestModel Test()
        {
            return new TestModel
            {
                Age = 10,
                Msg = Encoding.UTF8.GetBytes("Hello")
            };
        }
    }

    [Serializable]
    public class TestModel
    {
        public int Age { get; set; }

        public byte[] Msg { get; set; }

        public override string ToString()
        {
            return $"{Age}|{Encoding.UTF8.GetString(Msg)}";
        }
    }
}
IStudentApplication

不基於zookeeper的服務端版本

    internal class Program
    {
        public static IConfigurationRoot Configuration;

        private static void Main(string[] args)
        {
            Console.WriteLine("請輸入監聽端口:");
            var strPort = Console.ReadLine();
            var builder = new ConfigurationBuilder();
                //.SetBasePath(Path.Combine(AppContext.BaseDirectory)).AddJsonFile("NetCoreRpc.json", optional: true);
            Configuration = builder.Build();
            var servicesProvider = BuildDi();
            DependencyManage.SetServiceProvider(servicesProvider, Configuration);
            NRpcServer nrpcServer = new NRpcServer(int.Parse(strPort));
            nrpcServer.Start("NetCoreRpc.Application");
            Console.WriteLine("Welcome to use NetCoreRpc!");
            Console.WriteLine("Input exit to exit");
            var str = Console.ReadLine();
            while (!string.Equals(str, "exit", StringComparison.OrdinalIgnoreCase))
            {
                str = Console.ReadLine();
            }
            nrpcServer.ShutDown();
        }

        private static IServiceProvider BuildDi()
        {
            IServiceCollection services = new ServiceCollection();

            services.AddSingleton<ILoggerFactory, LoggerFactory>();
            services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
            services.AddSingleton<IStudentApplication, StudentApplication>();
            services.UseRpc();
                //.UseZK();
            var serviceProvider = services.BuildServiceProvider();

            var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
            
            loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true });
            loggerFactory.ConfigureNLog("NLog.config");

            return serviceProvider;
        }
    }
Server

基於zookeeper的服務端版本

    internal class Program
    {
        public static IConfigurationRoot Configuration;

        private static void Main(string[] args)
        {
            Console.WriteLine("請輸入監聽端口:");
            var strPort = Console.ReadLine();
            var builder = new ConfigurationBuilder()
                .SetBasePath(Path.Combine(AppContext.BaseDirectory)).AddJsonFile("NetCoreRpc.json", optional: true);
            Configuration = builder.Build();
            var servicesProvider = BuildDi();
            DependencyManage.SetServiceProvider(servicesProvider, Configuration);
            NRpcServer nrpcServer = new NRpcServer(int.Parse(strPort));
            nrpcServer.Start("NetCoreRpc.Application");
            Console.WriteLine("Welcome to use NetCoreRpc!");
            Console.WriteLine("Input exit to exit");
            var str = Console.ReadLine();
            while (!string.Equals(str, "exit", StringComparison.OrdinalIgnoreCase))
            {
                str = Console.ReadLine();
            }
            nrpcServer.ShutDown();
        }

        private static IServiceProvider BuildDi()
        {
            IServiceCollection services = new ServiceCollection();

            services.AddSingleton<ILoggerFactory, LoggerFactory>();
            services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
            services.AddSingleton<IStudentApplication, StudentApplication>();
            services.UseRpc()
                .UseZK();
            var serviceProvider = services.BuildServiceProvider();

            var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
            
            loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true });
            loggerFactory.ConfigureNLog("NLog.config");

            return serviceProvider;
        }
    }
基於zookeeper的版本

客戶端代碼

首先要引用剛剛定義的接口和Model

    internal class Program
    {
        public static IConfigurationRoot Configuration;

        private static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder().SetBasePath(Path.Combine(AppContext.BaseDirectory)).AddJsonFile("NetCoreRpc.json", optional: true);
            Configuration = builder.Build();

            var servicesProvider = BuildDi();
            DependencyManage.SetServiceProvider(servicesProvider, Configuration);

            Console.WriteLine("Welcome to use NetCoreRpc!");
            var studentApplication = ProxyFactory.Create<IStudentApplication>();
            Console.WriteLine(studentApplication.Age());
            Console.WriteLine(studentApplication.IsYongPeople(15));
            var runTask = studentApplication.RunAsync(111);
            studentApplication.Say("Hello world");
            studentApplication.Say(Encoding.UTF8.GetBytes("Hi!"));
            Console.WriteLine(Encoding.UTF8.GetString(studentApplication.Say()));
            var test = studentApplication.Test();
            Console.WriteLine(test.ToString());
            studentApplication.Sleep();
            Console.WriteLine(runTask.Result);

            Console.WriteLine("Input exit to exit");
            var str = Console.ReadLine();
            while (!string.Equals(str, "exit", StringComparison.OrdinalIgnoreCase))
            {
                str = Console.ReadLine();
            }
        }

        private static IServiceProvider BuildDi()
        {
            IServiceCollection services = new ServiceCollection();
            services.AddOptions();
            services.Configure<RemoteEndPointConfig>(Configuration.GetSection("NetCoreRpc"));
            services.AddSingleton<ILoggerFactory, LoggerFactory>();
            services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
            services.UseRpc().UseZK();
            var serviceProvider = services.BuildServiceProvider();

            var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();

            //configure NLog
            loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true });
            loggerFactory.ConfigureNLog("NLog.config");

            return serviceProvider;
        }
    }
客戶端基於zookeeper
{
  "NetCoreRpc": {
    "Default": "192.168.129.194:12346,192.168.129.194:12347,192.168.129.194:12348",
    "Group": [
      {
        "NameSpace": "",
        "Address": "127.0.0.1:12345"
      }
    ],
    "Zookeeper": {
      "Connection": "192.168.100.34:2181",
      "ParentName": "/NetCoreRpc/ClientTest"
    }
  }
}
NetCoreRpc.json
    internal class Program
    {
        public static IConfigurationRoot Configuration;

        private static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder().SetBasePath(Path.Combine(AppContext.BaseDirectory)).AddJsonFile("NetCoreRpc.json", optional: true);
            Configuration = builder.Build();

            var servicesProvider = BuildDi();
            DependencyManage.SetServiceProvider(servicesProvider, Configuration);

            Console.WriteLine("Welcome to use NetCoreRpc!");
            var studentApplication = ProxyFactory.Create<IStudentApplication>();
            Console.WriteLine(studentApplication.Age());
            Console.WriteLine(studentApplication.IsYongPeople(15));
            var runTask = studentApplication.RunAsync(111);
            studentApplication.Say("Hello world");
            studentApplication.Say(Encoding.UTF8.GetBytes("Hi!"));
            Console.WriteLine(Encoding.UTF8.GetString(studentApplication.Say()));
            var test = studentApplication.Test();
            Console.WriteLine(test.ToString());
            studentApplication.Sleep();
            Console.WriteLine(runTask.Result);

            Console.WriteLine("Input exit to exit");
            var str = Console.ReadLine();
            while (!string.Equals(str, "exit", StringComparison.OrdinalIgnoreCase))
            {
                str = Console.ReadLine();
            }
        }

        private static IServiceProvider BuildDi()
        {
            IServiceCollection services = new ServiceCollection();
            services.AddOptions();
            services.Configure<RemoteEndPointConfig>(Configuration.GetSection("NetCoreRpc"));
            services.AddSingleton<ILoggerFactory, LoggerFactory>();
            services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
            services.UseRpc();//.UseZK();
            var serviceProvider = services.BuildServiceProvider();

            var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();

            //configure NLog
            loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true });
            loggerFactory.ConfigureNLog("NLog.config");

            return serviceProvider;
        }
    }
不基於zookeeper的代碼

NetCoreRpc.json中的Zookeeper節點可以不用配置

調用測試結果

服務端輸出如下:

客戶端輸出如下:

項目中感覺不足之處

1、傳輸時采用的序列化采用的是自帶的BinarySerializer,需要在每個Model打上可序列化標記,后期希望改成不需要打標記就可以序列化的

2、采用zookeeper時,獲取可用IP是獲取當前第一個可用的IP,沒有有任何的算法

3、其它目前還沒有想到,如果各位博友有什么建議可以提一下,幫助我一下,謝謝

項目源碼地址

GitHub

今天晚上在寫這篇隨筆的時候發現自己無從下手,博友有沒有支招的啊,非常感謝。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM