zoukankan      html  css  js  c++  java
  • .net core使用RPC方式进行高效的HTTP服务访问

    传统的HTTP接口调用是一件比较繁琐的事情,特别是在Post数据的时候;不仅要拼访问的URL还是把数据序列化成流的方式给Request进行提交,获取Respons后还要对流进行解码。在实际应用虽然可以对HttpClient进行一个简单的封装,一旦到了上层大量的API调用还是不方便和不好维护。但如果在不改变HTTP接口服务的情况可以通过RPC的方式来调用HTTP服务那在使用和修改上都会变得更简单和便于维护了; 接下来讲解一下如何使用FastHttpApi通过接口描述的方式来访问HTTP接口服务!

    引用组件

    在这里简单地介绍一下FastHttpApi,它是一个轻量级高性的能的HTTP通讯组件,除了可以构建高性的HTTP服务外,还可以通过它来实现基于RPC的方式来访问第三方HTTP服务。可以到GitHub了解。如果需要通过接口的方式访问通第三方HTTP服务,首先要在项目用引用FastHttpApi,可以在Nuget上找到它,命令安装如下 Install-Package BeetleX.FastHttpApi -Version 1.0.2.6也可以直接在VS中添加Nuget引用。

    使用组件

    在定义接口前了解第三方的HTTP服务结构是必须的(当然如果选择FastHttpApi构建webapi会得到一下更高效的性能支持),下面主要讲解通过组件定议接口来访问asp.net mvc api的接口服务,先看一下服务的代码

        public class HomeController : Controller
        {
            public DateTime GetTime()
            {
                return DateTime.Now;
            }
            public IActionResult Hello(string name)
            {
                return new JsonResult($"hello {name}");
            }
            public IEnumerable<Order> ListOrders(int employee, string customer)
            {
                Func<Order, bool> exp = o => (employee == 0 || o.EmployeeID == employee)
               && (string.IsNullOrEmpty(customer) || o.CustomerID == customer);
                var result = DataHelper.Orders.Where(exp);
                return result;
            }
            public Employee GetEmployee(int id)
            {
                Employee result = DataHelper.Employees.Find(e => e.EmployeeID == id);
                return result;
            }
            [HttpPost]
            public int AddEmployee([FromBody] List<Employee> items)
            {
                if (items == null)
                    return 0;
                return items.Count;
            }
            [HttpPost]
            public Employee EditEmployee(int id, [FromBody]Employee employee)
            {
                employee.EmployeeID = id;
                return employee;
            }
            public bool Login(string name, string pwd)
            {
                if (name == "admin" && pwd == "123456")
                    return true;
                return false;
            }
        }

    以上是一个简单的asp.net mvc api的代码,接下来用接口来描述对应调用方法

        [JsonFormater]
        [Controller(BaseUrl = "Home")]
        public interface IDataService
        {
            [Get]
            DateTime GetTime();
            [Get]
            string Hello(string name);
            [Get]
            IList<Order> ListOrders();
            [Get]
            IList<Order> ListOrders(int employee, string customer);
            [Get]
            Employee GetEmployee(int id);
            [Post]
            Employee EditEmployee([CQuery]int id, Employee employee);
            [Get]
            bool Login(string name, string pwd);
            [Post]
            int AddEmployee(params Employee[] items);
        }

    是不是非常简单,简单地通过接口方法就可以描述对应HTTP请求,为了达到更好的应用性还可以重载不同版本来访问同一服务接口,这样在使用的时候就变得更方便灵活。再往下看代码了解一下是如何使用这接口的。

                HttpApiClient client = new HttpApiClient(Host);
    
                IDataService service = client.CreateWebapi<IDataService>();
    
                DateTime dt = service.GetTime();
    
                Console.WriteLine($"get time:{dt}");
    
                string hello = service.Hello("henry");
    
                Console.WriteLine($"hello :{hello}");
    
                var orders = service.ListOrders(3, null);
                if (orders != null)
                    Console.WriteLine($"list orders: {orders.Count}");
    
                orders = service.ListOrders();
                if (orders != null)
                    Console.WriteLine($"list orders: {orders.Count}");
    
                var emp = service.GetEmployee(7);
                Console.WriteLine($"get employee id 7:{emp?.FirstName} {emp?.LastName}");
    
                emp = service.EditEmployee(5, new Employee { FirstName = "fan", LastName = "henry" });
                Console.WriteLine($"edit employee :{emp.EmployeeID} {emp?.FirstName} {emp?.LastName}");
    
                var count = service.AddEmployee(null);
                Console.WriteLine($"add employee :{count}");
    
                count = service.AddEmployee(new Employee { EmployeeID = 3 }, new Employee { EmployeeID = 5 });
                Console.WriteLine($"add employee :{count}");
    
                var login = service.Login("admin", "123456");
                Console.WriteLine($"login status:{login}");

    首先是定义一个HttpApiClient对象指向一个服务地址,在这个代码里的访问地址是http://localhost:8080;接下来就可以通过HttpApiClient创建指定接口的操作对象,创建对象后就可以进行方法调用。那在多线程下是怎样处理呢?其实HttpApiClient是线程安全的,所以不用担心多线程下的操作,对于网络连接处理则内部通过连接池实现。

    组件的优势和缺点

    其实dotnet core已经存在这样一个功能的组件Refit,它的功能比较完善支持的版本也比较多,而FastHttpApi则只支持.net core.其实FastHttpApi的定义是针对服务与服务之间的通讯,由于它是基于自有实现的一个轻量化'HttpClient'所以在性能上要出色于'Refit',还有内部集成了基于Host的连接池所以在处理性能和连接管理上相对有着自己的优势;而这些的特点更适合内部服务之间的通讯需求。以下是组件和Refit的性能测试对比,由于网络间的延时会抵销具休处理的效率,所以测试是基于localhost进行,这样BenchmarkDotNet的结果好反映实际代码情况,测试结果如下:

    测试代码

            [Benchmark]
            public void RefitAddEmployee()
            {
                var gitHubApi = Refit.RestService.For<IRefitEmployeeApi>(Host);
                for (int i = 0; i < Count; i++)
                {
                    var octocat = gitHubApi.AddEmployee(Employee.GetEmployee());
                    octocat.Wait();
                    var id = octocat.Result.EmployeeID;
                }
            }
            [Benchmark]
            public void FastApiAddEmployee()
            {
                BeetleX.FastHttpApi.HttpApiClient client = new BeetleX.FastHttpApi.HttpApiClient(Host);
                var api = client.CreateWebapi<IFastHttpEmployeeApi>();
                for (int i = 0; i < Count; i++)
                {
                    var items = api.AddEmployee(Employee.GetEmployee());
                    var id = items.EmployeeID;
                }
            }
            [Benchmark]
            public void RefitGetEmployees()
            {
                var gitHubApi = Refit.RestService.For<IRefitEmployeeApi>(Host);
                for (int i = 0; i < Count; i++)
                {
                    var octocat = gitHubApi.ListEmployees(5);
                    octocat.Wait();
                    var count = octocat.Result.Count;
                }
            }
            [Benchmark]
            public void FastApiGetEmployees()
            {
                BeetleX.FastHttpApi.HttpApiClient client = new BeetleX.FastHttpApi.HttpApiClient(Host);
                var api = client.CreateWebapi<IFastHttpEmployeeApi>();
                for (int i = 0; i < Count; i++)
                {
                    var items = api.ListEmployees(5);
                    var count = items.Count;
                }
                client.Dispose();
            }

    测试结果

    虽然Refit采用静态编译的方式来处理请求,最终测试下来的结构还是FastHttpApi代理调用有着更出色的性能优势。

    实际并发测试

    由于对Refit了解不深入所以并没有把它引入进来做多线程并发测试,接下来进行一个多线程的并发测试,测试的硬件是一台4核发的开发机作为测试服务器。服务测试代码如下:

        [BeetleX.FastHttpApi.Controller(BaseUrl = "Employee")]
        class Program
        {
            static HttpApiServer mApiServer;
            static void Main(string[] args)
            {
                mApiServer = new HttpApiServer();
                mApiServer.ServerConfig.WriteLog = true;
                mApiServer.ServerConfig.LogToConsole = true;
                mApiServer.ServerConfig.Port = 8007;
                mApiServer.ServerConfig.LogLevel = BeetleX.EventArgs.LogType.Warring;
                mApiServer.ServerConfig.UrlIgnoreCase = true;
                mApiServer.Register(typeof(Program).Assembly);
                mApiServer.Open();
                Console.Write(mApiServer.BaseServer);
                Console.WriteLine(Environment.ProcessorCount);
                Console.Read();
            }
            public object Get(int count)
            {
                return new JsonResult(Employee.GetEmployees(count));
            }
            [Post]
            public object Add(Employee item)
            {
                return new JsonResult(item);
            }
            public object GetTime()
            {
                return new JsonResult(DateTime.Now);
            }
        }

    测试的服务并没有使用asp.net core作为服务,而是使用FastHttpApi作为测试服务,主要原因是有着更轻量级的性能优势。接下是看一下测试结果:

    ***********************************************************************
    * https://github.com/IKende/ConcurrentTest.git
    * Copyright ? ikende.com 2018 email:henryfan@msn.com
    * ServerGC:True
    ***********************************************************************
    * AddEmployee test prepping completed
    -----------------------------------------------------------------------
    *                 [10000000/10000000]|threads:[20]
    *       Success:[      0/s]|total:[    10000000][min:60087/s  max:82394/s]
    *         Error:[      0/s]|total:[           0][min:0/s  max:0/s]
    -----------------------------------------------------------------------
    *       0ms-0.1ms:[          203]    0.1ms-0.5ms:[    9,956,907]
    *       0.5ms-1ms:[       15,796]        1ms-5ms:[       26,184]
    *        5ms-10ms:[          681]      10ms-50ms:[           70]
    *      50ms-100ms:[            1]   100ms-1000ms:[          158]
    *   1000ms-5000ms:[             ] 5000ms-10000ms:[             ]
    ***********************************************************************
    
    ***********************************************************************
    * ListEmployees test prepping completed
    -----------------------------------------------------------------------
    *                 [10000000/10000000]|threads:[20]
    *       Success:[      0/s]|total:[    10000000][min:57709/s  max:83524/s]
    *         Error:[      0/s]|total:[           0][min:0/s  max:0/s]
    -----------------------------------------------------------------------
    *       0ms-0.1ms:[          504]    0.1ms-0.5ms:[    9,978,394]
    *       0.5ms-1ms:[        4,114]        1ms-5ms:[       16,732]
    *        5ms-10ms:[           98]      10ms-50ms:[            3]
    *      50ms-100ms:[           20]   100ms-1000ms:[          135]
    *   1000ms-5000ms:[             ] 5000ms-10000ms:[             ]
    ***********************************************************************
    
    ***********************************************************************
    * GetTime test prepping completed
    -----------------------------------------------------------------------
    *                 [10000000/10000000]|threads:[20]
    *       Success:[      0/s]|total:[    10000000][min:65740/s  max:95669/s]
    *         Error:[      0/s]|total:[           0][min:0/s  max:0/s]
    -----------------------------------------------------------------------
    *       0ms-0.1ms:[       77,060]    0.1ms-0.5ms:[    9,904,465]
    *       0.5ms-1ms:[        4,612]        1ms-5ms:[       13,510]
    *        5ms-10ms:[          173]      10ms-50ms:[           31]
    *      50ms-100ms:[             ]   100ms-1000ms:[          149]
    *   1000ms-5000ms:[             ] 5000ms-10000ms:[             ]
    ***********************************************************************

    客户端开启了20个线程同步调用服务,得到的结果峰值大概在8万每秒的http请求响应,这样的性能指标相信完全能满足普通业务的需求,毕竟这台测试服务用的只是一台5-6年前的4核PC机。

  • 相关阅读:
    在Android Studio中用Gradle添加Robolectric
    Windows环境下利用github快速配置git环境
    上传Android代码到Jcenter(解决了字符映射的问题)
    RecyclerView和ScrollView嵌套使用
    Android Java类编写规范+优化建议
    3.0之后在LinearLayout里增加分割线
    【转】Android Studio中通过快捷键来提取提取方法
    为什么Android应该根据屏幕分辨率来加载不同的图片文件
    JSON/XML格式化插件比较
    利用在线工具根据JSon数据自动生成对应的Java实体类
  • 原文地址:https://www.cnblogs.com/smark/p/9983181.html
Copyright © 2011-2022 走看看