查看原文
其他

.NET Core 用RPC方式进行高效的HTTP服务访问

DotNet 2019-08-03

(给DotNet加星标,提升.Net技能


转自:smark

cnblogs.com/smark/p/9983181.html




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是线程安全的,所以不用担心多线程下的操作,对于网络连接处理则内部通过连接池实现。



[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();
}


测试结果




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


推荐阅读

(点击标题可跳转阅读)

 C# 爬虫爬取商品信息

.NET Core 开发日志配置

 深入浅出 ASP.NET Core 基于JWT的认证和授权 


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存