• 基于.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

    今天晚上在写这篇随笔的时候发现自己无从下手,博友有没有支招的啊,非常感谢。

  • 相关阅读:
    VS远程调试亲历
    IIS7.5 配置虚拟目录的经历
    IE 兼容一问题一小记
    寻找 IBatisNet 批量插入(批量复制) 的心路历程
    Linq 多连接及 left join 实例 记录
    easyui treegrid idField 所在属性中值有花括号(如Guid)当有鼠标事件时会报错,行记录一下
    HDU1754
    HDU1166
    线段树模板
    HDU1599(Floyd最小环)
  • 原文地址:https://www.cnblogs.com/yjq-code/p/NetcoreRpc.html
Copyright © 2020-2023  润新知