基于 gRPC 和 .NET Core 的服务器流

原文:https://bit.ly/3lpz8Ll
作者:Chandan Rauniyar
翻译:精致码农-王亮

早在 2019 年,我写过《用 Mapbox 绘制位置数据》一文,详细介绍了我如何通过简单的文件上传,用 Mapbox 绘制约 230 万个位置点。本文介绍我是如何通过使用 gRPC 和 .NET Core 的服务器流来快速获取所有位置历史数据的。

https://chandankkrr.medium.com/mapping-location-data-with-mapbox-9b256f64d569

什么是 gRPC

gRPC 是一个现代开源的高性能 RPC 框架,可以在任何环境下运行。它可以有效地连接数据中心内和跨数据中心的服务,并对负载平衡、跟踪、健康检查和认证提供可插拔的支持。gRPC 最初是由谷歌创建的,该公司使用一个名为 Stubby 的单一通用 RPC 基础设施来连接其数据中心内和跨数据中心运行的大量微服务,使用已经超过十年。2015 年 3 月,谷歌决定建立 Stubby 的下一个版本,并将其开源,结果就是现在的 gRPC,被许多企业或组织使用。

https://grpc.io/

gRPC 服务器流

服务器流式(Server Streaming)RPC,客户端向服务器发送请求,并获得一个流来读取一连串的消息。客户端从返回的流中读取信息,直到没有消息为止。gRPC 保证在单个 RPC 调用中的信息是有序的。

rpc GetLocationData (GetLocationRequest) returns (stream GetLocationResponse);

协议缓冲区(Protobuf)

gRPC 使用协议缓冲区(protocol buffers)作为接口定义语言(IDL)来定义客户端和服务器之间的契约。在下面的 proto 文件中,定义了一个 RPC 方法 GetLocations,它接收 GetLocationsRequest 消息类型并返回 GetLocationsResponse 消息类型。响应消息类型前面的 stream 关键字表示响应是流类型,而不是单个响应。

syntax = "proto3";option csharp_namespace = "GPRCStreaming";package location_data;service LocationData {rpc GetLocations (GetLocationsRequest) returns (stream GetLocationsResponse);
}message GetLocationsRequest {int32 dataLimit = 1;
}message GetLocationsResponse {int32 latitudeE7 = 1;int32 longitudeE7 = 2;
}

创建 gRPC 服务

我们可以使用 dotnet new grpc -n threemillion 命令轻松创建一个 .NET gRPC 服务。更多关于在 ASP.NET Core 中创建 gRPC 服务器和客户端的信息可在微软文档中找到。

Create a gRPC client and server in ASP.NET Core
https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-5.0&tabs=visual-studio-code

在添加了 proto 文件并生成了 gRPC 服务资源文件后,接下来我添加了 LocationService 类。在下面的代码片段中,我有一个 LocationService 类,它继承了从 Location.proto 文件中生成的 LocationDataBase 类型。客户端可以通过 Startup.cs 文件中 Configure 方法中的 endpoints.MapGrpcService<LocationService>() 来访问 LocationService。当服务器收到 GetLocations 请求时,它首先通过 GetLocationData 方法调用读取 Data 文件夹中 LocationHistory.json 文件中的所有数据(未包含在源代码库)。该方法返回 RootLocation 类型,其中包含 List<Location> 类型的 Location 属性。Location 类由两个内部属性 LongitudeLatitude 组成。接下来,我循环浏览每个位置,然后将它们写入 responseStream 中,返回给客户端。服务器将消息写入流中,直到客户在 GetLocationRequest 对象中指定的 dataLimit

using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.Extensions.Logging;
using System.IO;
using System;
using System.Linq;namespace GPRCStreaming
{public class LocationService : LocationData.LocationDataBase{private readonly FileReader _fileReader;private readonly ILogger<LocationService> _logger;public LocationService(FileReader fileReader, ILogger<LocationService> logger){_fileReader = fileReader;_logger = logger;}public override async Task GetLocations(GetLocationsRequest request,IServerStreamWriter<GetLocationsResponse> responseStream,ServerCallContext context){try{_logger.LogInformation("Incoming request for GetLocationData");var locationData = await GetLocationData();var locationDataCount = locationData.Locations.Count;var dataLimit = request.DataLimit > locationDataCount ? locationDataCount : request.DataLimit;for (var i = 0; i <= dataLimit - 1; i++){var item = locationData.Locations[i];await responseStream.WriteAsync(new GetLocationsResponse{LatitudeE7 = item.LatitudeE7,LongitudeE7 = item.LongitudeE7});}}catch (Exception exception){_logger.LogError(exception, "Error occurred");throw;}}private async Task<RootLocation> GetLocationData(){var currentDirectory = Directory.GetCurrentDirectory();var filePath = $"{currentDirectory}/Data/Location_History.json";var locationData = await _fileReader.ReadAllLinesAsync(filePath);return locationData;}}
}

现在,让我们运行该服务并发送一个请求。我将使用一个叫 grpcurl 的命令行工具,它可以让你与 gRPC 服务器交互。它基本上是针对 gRPC 服务器的 curl

https://github.com/fullstorydev/grpcurl

通过 grpcurl 与 gRPC 端点(endpoint)交互只有在 gRPC 反射服务被启用时才可用。这允许服务可以被查询,以发现服务器上的 gRPC 服务。扩展方法 MapGrpcReflectionService 需要引入 Microsoft.AspNetCore.Builder 的命名空间:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{app.UseEndpoints(endpoints =>{endpoints.MapGrpcService<LocationService>();if (env.IsDevelopment()){endpoints.MapGrpcReflectionService();}endpoints.MapGet("/", async context =>{await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");});});
}
grpcurl -plaintext -d '{"dataLimit": "100000"}' localhost:80 location_data.LocationData/GetLocations

一旦服务器收到请求,它就会读取文件,然后在位置列表中循环,直到达到 dataLimit 计数,并将位置数据返回给客户端。

接下来,让我们创建一个 Blazor 客户端来调用 gRPC 服务。我们可以使用 IServiceCollection 接口上的 AddGrpcClient 扩展方法设置一个 gRPC 客户端:

public void ConfigureServices(IServiceCollection services)
{services.AddRazorPages();services.AddServerSideBlazor();services.AddSingleton<WeatherForecastService>();services.AddGrpcClient<LocationData.LocationDataClient>(client =>{client.Address = new Uri("http://localhost:80");});
}

我使用 Virtualize Blazor 组件来渲染这些位置。Virtualize 组件不是一次性渲染列表中的每个项目,只有当前可见的项目才会被渲染。

ASP.NET Core Blazor component virtualization
https://docs.microsoft.com/en-us/aspnet/core/blazor/components/virtualization?view=aspnetcore-5.0

相关代码:

@page "/locationdata"@using Grpc.Core
@using GPRCStreaming
@using threemillion.Data
@using System.Diagnostics
@using Microsoft.AspNetCore.Components.Web.Virtualization@inject IJSRuntime JSRuntime;
@inject System.Net.Http.IHttpClientFactory _clientFactory
@inject GPRCStreaming.LocationData.LocationDataClient _locationDataClient<table class="tableAction"><tbody><tr><td><div class="data-input"><label for="dataLimit">No of records to fetch</label><input id="dataLimit" type="number" @bind="_dataLimit" /><button @onclick="FetchData" class="btn-submit">Call gRPC</button></div></td><td><p class="info">Total records: <span class="count">@_locations.Count</span></p><p class="info">Time taken: <span class="time">@_stopWatch.ElapsedMilliseconds</span> milliseconds</p></td></tr></tbody>
</table><div class="tableFixHead"><table class="table"><thead><tr><th>Longitude</th><th>Latitude</th></tr></thead><tbody><Virtualize Items="@_locations" Context="locations"><tr><td>@locations.LongitudeE7</td><td>@locations.LatitudeE7</td></tr></Virtualize></tbody></table>
</div>@code {private int _dataLimit = 1000;private List<Location> _locations = new List<Location>();private Stopwatch _stopWatch = new Stopwatch();protected override async Task OnInitializedAsync(){await FetchData();}private async Task FetchData(){ResetState();_stopWatch.Start();using (var call = _locationDataClient.GetLocations(new GetLocationsRequest { DataLimit = _dataLimit })){await foreach (var response in call.ResponseStream.ReadAllAsync()){_locations.Add(new Location { LongitudeE7 = response.LongitudeE7, LatitudeE7 = response.LatitudeE7 });StateHasChanged();}}_stopWatch.Stop();}private void ResetState(){_locations.Clear();_stopWatch.Reset();StateHasChanged();}
}

通过在本地运行的流调用,从 gRPC 服务器接收 2,876,679 个单独的响应大约需要 8 秒钟。让我们也在 Mapbox 中加载数据:

@page "/mapbox"@using Grpc.Core
@using GPRCStreaming
@using System.Diagnostics@inject IJSRuntime JSRuntime;
@inject System.Net.Http.IHttpClientFactory _clientFactory
@inject GPRCStreaming.LocationData.LocationDataClient LocationDataClient<table class="tableAction"><tbody><tr><td><div class="data-input"><label for="dataLimit">No of records to fetch</label><input id="dataLimit" type="number" @bind="_dataLimit" /><button @onclick="LoadMap" class="btn-submit">Load data</button></div></td><td><p class="info">Total records: <span class="count">@_locations.Count</span></p><p class="info">Time taken: <span class="time">@_stopWatch.ElapsedMilliseconds</span> milliseconds</p></td></tr></tbody>
</table><div id='map' style="width: 100%; height: 90vh;"></div>@code {private int _dataLimit = 100;private List<object> _locations = new List<object>();private Stopwatch _stopWatch = new Stopwatch();protected override async Task OnAfterRenderAsync(bool firstRender){if (!firstRender){return;}await JSRuntime.InvokeVoidAsync("mapBoxFunctions.initMapBox");}private async Task LoadMap(){ResetState();_stopWatch.Start();using (var call = LocationDataClient.GetLocations(new GetLocationsRequest { DataLimit = _dataLimit })){await foreach (var response in call.ResponseStream.ReadAllAsync()){var pow = Math.Pow(10, 7);var longitude = response.LongitudeE7 / pow;var latitude = response.LatitudeE7 / pow;_locations.Add(new{type = "Feature",geometry = new{type = "Point",coordinates = new double[] { longitude, latitude }}});StateHasChanged();}_stopWatch.Stop();await JSRuntime.InvokeVoidAsync("mapBoxFunctions.addClusterData", _locations);}}private void ResetState(){JSRuntime.InvokeVoidAsync("mapBoxFunctions.clearClusterData");_locations.Clear();_stopWatch.Reset();StateHasChanged();}
}

源代码在我的 GitHub 上 ????:

https://github.com/Chandankkrr/threemillion

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299798.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jQuery入门[2]-选择器[转]

jQuery之所以令人爱不释手&#xff0c;在于其强大的选择器表达式令DOM操作优雅而艺术。jQuery的选择符支持id,tagName,css1-3 expressions,XPath&#xff0c;参见&#xff1a;http://docs.jquery.com/SelectorsDEMO:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tran…

是个狠角色。。 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源洱海弥&#xff0c;侵权删&#xff09;

构建Windows Server 2008虚拟实验室

在阅读新闻稿时&#xff0c;有很多文章和指导&#xff0c;都有一些帮助&#xff0c;但却没有针对Windows Server 2008的手把手经验可供参考。对管理员来说&#xff0c;要熟悉这个服务器系统的最好方法是确定它的特性是如何给他们的Windows环境带来便利。 先前我描述了如何方便地…

eclipse配置tomcat 和JRE环境

配置JRE环境&#xff0c;通俗点讲就是添加一个不同版本的jdk window——preferences——java——installed jres 点击add添加 选择standard VM 择一个本机正确的jdk路径 name和下面lib加载都是系统自动完成的 选择好路径后 点 finish就ok了 配置tomcat window——preferen…

为什么我的 Func 如此之慢?

咨询区 Ricky G&#xff1a;我正在给项目做性能和代码优化&#xff0c;比如将重复的代码提炼成到一个可重用的方法中&#xff0c;为了能够达到可重用目的&#xff0c;我用 Func<T> 作为方法参数。public int Calculate(Func<int> expr){return expr();}当我用 Benc…

[转载MSDN]IIS 7.0中的Live Smooth Streaming -入门

IIS Live Smooth Streaming - 测试版是Internet 信使服务 (IIS) 7.0的一个扩展&#xff0c;它能提供流畅的视频流媒体&#xff0c;并且可以根据客户端的网络带宽和CPU的执行能力的改变&#xff0c;随时的调整视频质量。 IIS Live Smooth Streaming允许Microsoft Silverlight客户…

为啥饮料瓶大都是圆的,牛奶盒却是方的?

全世界只有3.14 % 的人关注了青少年数学之旅一提起喝饮料和牛奶你们是不是就来精神了但是喝了这么久的饮料你有没有想过一个问题为什么饮料瓶一般都是圆的&#xff1f;而牛奶盒却是方的&#xff1f;有的人可能会说饮料瓶要拿在手上当然是圆的舒服啊拿着一个方形的饮料瓶时间长了…

Docker最全教程之使用Docker搭建Java开发环境

前言 Java是一门面向对象的优秀编程语言&#xff0c;市场占有率极高&#xff0c;但是在容器化实践过程中&#xff0c;发现官方支持并不友好&#xff0c;同时与其他编程语言的基础镜像相比&#xff08;具体见各语言镜像比较&#xff09;&#xff0c;确实是非常臃肿。 Java [J…

OrganicsDelivery总结

一、UserControl调用所在页面的其他内容&#xff08;代理实现&#xff09; UserControl代码 Codeusing System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.HtmlControls;…

面试官:Redis 内存数据满了,会宕机吗?

背景Redis 数据库内存数据满了&#xff0c;会宕机吗&#xff1f;答案是&#xff1a;不会让它出现存满的情况&#xff0c;在使用Redis的时候我们要配置Redis能使用的最大的内存大小&#xff0c;存到一定容量的时候还有Redis的内存淘汰策略呢&#xff0c;还有LRU算法进行淘汰&…

你爱我吗? | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源你这样真的很机车诶&#xff0c;侵权删&#xff09;

Java开发和嵌入式开发该如何选择

首先&#xff0c;Java开发和嵌入式开发都是目前IT行业内比较常见的开发岗位&#xff0c;也都有大量的从业人员&#xff0c;所以从就业的角度来看&#xff0c;学习Java开发和嵌入式开发都是不错的选择。Java语言的应用领域包括Web开发、Android开发和大数据开发等领域&#xff0…

小米和腾讯的.NET笔面试题哪个更难?可自测附答案

统计了下&#xff0c;当下正在招.NET的大厂有腾讯、京东、唯品会、阿里、百度、华为、小米、网易、特斯拉等。进大厂、拿高薪、享福利&#xff0c;应该是.NET程序员普世梦想了。金九银十将至&#xff0c;还不赶紧刷题准备&#xff1f;即使等金三银四&#xff0c;下面这些笔面试…

熬夜变傻有科学依据,人类睡觉时会被“洗脑”,科学家首次拍下全程

全世界只有3.14 % 的人关注了青少年数学之旅你睡着的时候&#xff0c;真的被洗了脑。这次&#xff0c;波士顿大学的科学家们&#xff0c;史无前例地拍下了清洗过程&#xff1a;红色是血液&#xff0c;蓝色是脑脊液。厉害的是从前没有发现过&#xff0c;血液会周期性地大量流出大…

程序员的人生

今天在网上看到这么一张图片&#xff0c;让人吃惊不小&#xff0c;我也是一名典型的学生出身的程序员在这个行业做了也接近三年了&#xff0c;也曾经犹豫过&#xff0c;曾经迷茫过&#xff0c;曾经思考过自己以后的人生&#xff0c;也是几乎和下面这张图片的经历一致&#xff0…

到家自动充电,拜拜充电线!东京大学开发自动充电房间,能量成为“与空气一样的东西”...

全世界只有3.14 % 的人关注了青少年数学之旅每天下班回家第一件事&#xff0c;是不是给手机充电&#xff1f;但是充电线在哪&#xff0c;你找得到吗&#xff1f;如果现在告诉你&#xff0c;一回到家&#xff0c;手机就能自动充电&#xff0c;简直就是整理苦手的救星&#xff01…

JS图片放大查看效果!

http://bbs.blueidea.com/thread-2812683-1-1.htmlhttp://bbs.blueidea.com/thread-2814846-1-1.html应用效果<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html><head><title>Javascript Images zoom-out view sample.…

从零开始学习jQuery (九) jQuery工具函数 【转】

一.摘要 本系列文章将带您进入jQuery的精彩世界, 其中有很多作者具体的使用经验和解决方案, 即使你会使用jQuery也能在阅读中发现些许秘籍. 我们经常要使用脚本处理各种业务逻辑, 最常见的就是数组和对象的操作. jQuery工具函数为我们操作对象和数组提供了便利条件. 二.前言 大…

设计模式之装饰器

装饰器模式介绍俄罗斯套娃装饰器的核心就是在不改原有类的基础上给类新增功能。可以避免继承导致的子类过多&#xff0c;可避免AOP带来的复杂性。装饰器主要解决的是直接继承下因功能的不断横向扩展导致子类膨胀的问题&#xff0c;而是用装饰器模式后就会比直接继承显得更加灵活…

zookeeper集群部署监控与选举同步流程等工作原理

部署一个zookeeper集群&#xff0c;要多简单就能有多简单&#xff08;下载压缩包&#xff0c;解压&#xff0c;修改配置文件zoo.cfg,执行启动脚本&#xff09;&#xff0c;但是想要真的把这套东西玩好了&#xff0c;还是需要费些功夫研究一番的。就跟自己搭建一个lnmp的站点&am…