当前位置: 首页 > news >正文

.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机。

相关文章:

  • 手机里删除的照片怎么恢复,如何恢复
  • Java并发编程之AQS
  • 浅谈常见的七种加密算法及实现
  • PHP消息队列学习
  • 实验05博客园总结
  • 华为S5700系列交换机配置通过Telnet登录设备
  • 4g伪基站如何实现的
  • logStash安装
  • 在树莓派上安装Ubuntu Core
  • Java国际化处理
  • 撩课大前端-面试宝典-第九篇
  • C# 后台获取前台交互判断
  • 《转》完美解决微信video视频隐藏控件和内联播放问题
  • 微信浏览器播放音频的问题:preload属性
  • Java并发编程之锁机制之(ReentrantLock)重入锁
  • 5、React组件事件详解
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • Java的Interrupt与线程中断
  • JS基础之数据类型、对象、原型、原型链、继承
  • JS题目及答案整理
  • leetcode46 Permutation 排列组合
  • python大佬养成计划----difflib模块
  • Rancher如何对接Ceph-RBD块存储
  • SpiderData 2019年2月13日 DApp数据排行榜
  • SQLServer之索引简介
  • Vue.js-Day01
  • vue数据传递--我有特殊的实现技巧
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 反思总结然后整装待发
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • - 概述 - 《设计模式(极简c++版)》
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 算法---两个栈实现一个队列
  • 小而合理的前端理论:rscss和rsjs
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 鱼骨图 - 如何绘制?
  • 怎么把视频里的音乐提取出来
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • ######## golang各章节终篇索引 ########
  • $.proxy和$.extend
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (2)STL算法之元素计数
  • (8)STL算法之替换
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (delphi11最新学习资料) Object Pascal 学习笔记---第14章泛型第2节(泛型类的类构造函数)
  • (javascript)再说document.body.scrollTop的使用问题
  • (Python第六天)文件处理
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (一)python发送HTTP 请求的两种方式(get和post )