传统的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服务访问的更多相关文章

  1. 采用RPC方式和document方式 开发Axis2的WebService客户端

    import javax.xml.namespace.QName; import org.apache.axiom.om.OMAbstractFactory; import org.apache.ax ...

  2. asp.net core 自定义认证方式--请求头认证

    asp.net core 自定义认证方式--请求头认证 Intro 最近开始真正的实践了一些网关的东西,最近写几篇文章分享一下我的实践以及遇到的问题. 本文主要介绍网关后面的服务如何进行认证. 解决思 ...

  3. ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客

    原文:ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客 版权声明:本文为starfd原创文章,转载请标明出处. https://blog.c ...

  4. 使用.NET Core创建Windows服务 - 使用.NET Core工作器方式

    原文:Creating Windows Services In .NET Core – Part 3 – The ".NET Core Worker" Way 作者:Dotnet ...

  5. 浅淡Webservice、WSDL三种服务访问的方式(附案例)

    Webservice Webservice是使应用程序以与平台和编程语言无关的方式进行相互通信技术. eg:站点提供访问的数据接口:新浪微博.淘宝. 官方解释:它是一种构建应用程序的普遍模型,可以在任 ...

  6. C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(上)

    译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(上)),不对的地方欢迎指出与交流. 章节出自<Professional C ...

  7. C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(下)

    译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(下)),不对的地方欢迎指出与交流. 章节出自<Professional C ...

  8. ASP.NET Core 实战:基于 Dapper 扩展你的数据访问方法

    一.前言 在非静态页面的项目开发中,必定会涉及到对于数据库的访问,最开始呢,我们使用 Ado.Net,通过编写 SQL 帮助类帮我们实现对于数据库的快速访问,后来,ORM(Object Relatio ...

  9. [ASP.NET Core 3框架揭秘] 依赖注入[8]:服务实例的生命周期

    生命周期决定了IServiceProvider对象采用怎样的方式提供和释放服务实例.虽然不同版本的依赖注入框架针对服务实例的生命周期管理采用了不同的实现,但总的来说原理还是类似的.在我们提供的依赖注入 ...

随机推荐

  1. Ubuntu 18.04 系统配置 NPM环境和mysql数据库问题解决

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效. 今天我就为大家 使用 Ubun ...

  2. CMD 中常见命令

    引自百度经验:https://jingyan.baidu.com/article/67508eb41d44a09cca1ce4f1.html ipConfig:查询ip ping:查询连接速度: pi ...

  3. docker-maven-plugin插件设置Docker的buildArgs

    docker-maven-plugin是spotify出品的一款针对spring boot项目的docker插件,可将spring boot项目打包到docker镜像中. 如果在编译docker镜像时 ...

  4. vue事件修饰符

    阻止单击事件冒泡 <a v-on:click.stop="doThis"></a>提交事件不再重载页面<form v-on:submit.preven ...

  5. 关于css中为什么要设置html和body的高度?

    1.在怪异模式下,也就是网页的头部不写DOCTYPE的时候,body作为根元素,设置高度为百分百的时候.可以是页面的高度和浏览高度相同,在标准模式下也就是有DOCTYPE的时候,html才是根元素这时 ...

  6. oracle启动服务和监听

    1.故障问题:tomcat显示启动oracle数据库失败,数据库服务启动正常 操作1:重启tomcat查看错误信息 2:重启数据库服务 命令: (1) 启动Oracle服务 C:\Users\Admi ...

  7. Objective-C 优秀文章分享

    1.  Objective-C Runtime 2.KVO + Block 3.Method Swizzling 和 AOP 实践

  8. 关于Java 去除空格,换行的代码

    public static String replaceBlank(String str) { String dest = ""; if (str != null) { //Pat ...

  9. 在C++中,setw(int n)

    setw(int n)用来控制输出间隔例如:cout<<'s'<<setw(8)<<'a'<<endl;则在屏幕显示s        a //s与a之间 ...

  10. MyEclipse 10 报错记录

    1. js文件:右键 >> MyEclipse >> Exclude From Validation 2. Servlet 警告:Window ==> Preferenc ...