.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 == || 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 ;
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 == "")
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(, 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();
Console.WriteLine($"get employee id 7:{emp?.FirstName} {emp?.LastName}"); emp = service.EditEmployee(, 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 = }, new Employee { EmployeeID = });
Console.WriteLine($"add employee :{count}"); var login = service.Login("admin", "");
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 = ; 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 = ; 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 = ; i < Count; i++)
{
var octocat = gitHubApi.ListEmployees();
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 = ; i < Count; i++)
{
var items = api.ListEmployees();
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 = ;
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 email:henryfan@msn.com
* ServerGC:True
***********************************************************************
* AddEmployee test prepping completed
-----------------------------------------------------------------------
* [/]|threads:[]
* Success:[ /s]|total:[ ][min:/s max:/s]
* Error:[ /s]|total:[ ][min:/s max:/s]
-----------------------------------------------------------------------
* 0ms-.1ms:[ ] .1ms-.5ms:[ ,,]
* .5ms-1ms:[ ,] 1ms-5ms:[ ,]
* 5ms-10ms:[ ] 10ms-50ms:[ ]
* 50ms-100ms:[ ] 100ms-1000ms:[ ]
* 1000ms-5000ms:[ ] 5000ms-10000ms:[ ]
*********************************************************************** ***********************************************************************
* ListEmployees test prepping completed
-----------------------------------------------------------------------
* [/]|threads:[]
* Success:[ /s]|total:[ ][min:/s max:/s]
* Error:[ /s]|total:[ ][min:/s max:/s]
-----------------------------------------------------------------------
* 0ms-.1ms:[ ] .1ms-.5ms:[ ,,]
* .5ms-1ms:[ ,] 1ms-5ms:[ ,]
* 5ms-10ms:[ ] 10ms-50ms:[ ]
* 50ms-100ms:[ ] 100ms-1000ms:[ ]
* 1000ms-5000ms:[ ] 5000ms-10000ms:[ ]
*********************************************************************** ***********************************************************************
* GetTime test prepping completed
-----------------------------------------------------------------------
* [/]|threads:[]
* Success:[ /s]|total:[ ][min:/s max:/s]
* Error:[ /s]|total:[ ][min:/s max:/s]
-----------------------------------------------------------------------
* 0ms-.1ms:[ ,] .1ms-.5ms:[ ,,]
* .5ms-1ms:[ ,] 1ms-5ms:[ ,]
* 5ms-10ms:[ ] 10ms-50ms:[ ]
* 50ms-100ms:[ ] 100ms-1000ms:[ ]
* 1000ms-5000ms:[ ] 5000ms-10000ms:[ ]
***********************************************************************
客户端开启了20个线程同步调用服务,得到的结果峰值大概在8万每秒的http请求响应,这样的性能指标相信完全能满足普通业务的需求,毕竟这台测试服务用的只是一台5-6年前的4核PC机。
.net core使用RPC方式进行高效的HTTP服务访问的更多相关文章
- 采用RPC方式和document方式 开发Axis2的WebService客户端
import javax.xml.namespace.QName; import org.apache.axiom.om.OMAbstractFactory; import org.apache.ax ...
- asp.net core 自定义认证方式--请求头认证
asp.net core 自定义认证方式--请求头认证 Intro 最近开始真正的实践了一些网关的东西,最近写几篇文章分享一下我的实践以及遇到的问题. 本文主要介绍网关后面的服务如何进行认证. 解决思 ...
- ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客
原文:ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客 版权声明:本文为starfd原创文章,转载请标明出处. https://blog.c ...
- 使用.NET Core创建Windows服务 - 使用.NET Core工作器方式
原文:Creating Windows Services In .NET Core – Part 3 – The ".NET Core Worker" Way 作者:Dotnet ...
- 浅淡Webservice、WSDL三种服务访问的方式(附案例)
Webservice Webservice是使应用程序以与平台和编程语言无关的方式进行相互通信技术. eg:站点提供访问的数据接口:新浪微博.淘宝. 官方解释:它是一种构建应用程序的普遍模型,可以在任 ...
- C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(上)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(上)),不对的地方欢迎指出与交流. 章节出自<Professional C ...
- C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(下)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(下)),不对的地方欢迎指出与交流. 章节出自<Professional C ...
- ASP.NET Core 实战:基于 Dapper 扩展你的数据访问方法
一.前言 在非静态页面的项目开发中,必定会涉及到对于数据库的访问,最开始呢,我们使用 Ado.Net,通过编写 SQL 帮助类帮我们实现对于数据库的快速访问,后来,ORM(Object Relatio ...
- [ASP.NET Core 3框架揭秘] 依赖注入[8]:服务实例的生命周期
生命周期决定了IServiceProvider对象采用怎样的方式提供和释放服务实例.虽然不同版本的依赖注入框架针对服务实例的生命周期管理采用了不同的实现,但总的来说原理还是类似的.在我们提供的依赖注入 ...
随机推荐
- FunData — 电竞大数据系统架构演进
电竞出现在人们视野中的频率越来越高了,此次选取FunData作为电竞数据平台,v1.0 beta版本主要提供由Valve公司出品的顶级MOBA类游戏DOTA2相关数据接口(详情:open.varena ...
- 通过Ajax来简单的实现局部刷新(主要为C#中使用的UpdatePanel控件和ScriptManager控件)
1. ScriptManager和UpdatePanel控件联合使用可以实现页面局部异步刷新的效果.UpdatePanel用来设置页面中局部异步刷新的区域,它必须依赖于ScriptManager,因为 ...
- 单个 js 文件禁用 ESLint 语法校验
在代码顶部添加一行注释 /* eslint-disable */ ESLint 在校验的时候就会跳过后面的代码 还可以在注释后加入详细规则,这样就能避开指定的校验规则了 /* eslint-disab ...
- 支付宝红包口令自动复制到剪贴板脚本js,安卓,IOS通用版
有客户找到涛舅舅,要求开发一个可以自动支付宝红包口令的js脚本,经过大量探索和优化,目前此脚本功能已经测试成功! 预期效果: 只要来访用户在当前网页的任意位置点击一下,支付宝红包口令即可复制到用户手机 ...
- hrbust 2384 相同的不相同的字符串
Description 研究证明,汉的字阅读序顺是不会影响你明白这句话的意思的. 但是如果顺序差的太多也是会影响的,比如汉阅读顺的序字你就看不懂什么意思了. 那么多少算多呢?科学家芦苇给出证明,当两个 ...
- over(partition by..) 的运用(转)
oracle的分析函数over 及开窗函数一:分析函数overOracle从8.1.6开始提供分析函数,分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是对于每个组返回多行,而聚合函数对于每 ...
- React的类型检测PropTypes
React.propTypes:React.PropTypes 提供很多验证器来验证传入数据的有效性,当向props传入无效数据时,JavaScript 控制台会抛出警告. ; class MyTit ...
- Flask的上下文源码剖析
先写一段Flask程序 from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return ...
- elasticsearch目录
快速入门篇(基于版本5.4) Elasticsearch入门 Elasticsearch和Kibana安装 Elasticsearch索引和文档操作 Elasticsearch文档查询 安装和配置(基 ...
- Winform 窗体获得焦点
给窗体添加Shown事件 public void Form_Shown(object sender, EventArgs e) { this.Activate(); this.Focus(); //定 ...