一、前言

前一阵子关于.NET的各大公众号都发表了关于gRpc的消息,而随之而来的就是一波关于.NET Core下如何使用的教程,但是在这众多的教程中基本都是泛泛而谈,难以实际在实际环境中使用,而该篇教程以gRpc为主,但是使用了其SSL/TLS,这样更加符合实际的生产使用,期间也会配套的讲解Docker、openssl等。

二、服务端

a.准备工作

笔者的项目分为三个部分分别如下所示:

Sino.GrpcService.Host(控制台):宿主程序

Sino.GrpcService.Impl(类库):实现协议

Sino.GrpcService.Protocol(类库):生成协议

最终的项目如下图所示:

每个项目的project.json如下所示:

{

"version": "1.0.0-*",

"buildOptions": {

"emitEntryPoint": true,

"copyToOutput": [ "server.crt", "server.key", "appSettings.json", "appSettings.*.json" ]

},

"dependencies": {

"Microsoft.NETCore.App": {

"type": "platform",

"version": "1.0.0"

},

"Sino.GrpcService.Impl": "1.0.0-*",

"Microsoft.Extensions.Configuration.Json": "1.0.0",

"Microsoft.Extensions.Configuration.Binder": "1.0.0",

"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0"

},

"frameworks": {

"netcoreapp1.0": {

"imports": [ "dnxcore50", "net452" ]

}

},

"publishOptions": {

"include": [ "server.crt", "server.key", "appSettings.json", "appSettings.*.json" ]

}

}

其中“buildOptions”和“publishOptions”中我们将后面我们需要的证书包含到输出和发布中,其中我们还利用了“Configuration”相关组件去读取配置信息。

Sino.GrpcService.Impl:

其中我们安装了“MongoDb.Driver”,为了能够贴近真实的情况,笔者这里采用MongoDb作为数据源来提供数据,当然读者为了能够快速上手可以硬编码一些数据。

Sino.GrpcService.Protocol:

至此项目的初始化结束。

b.编写协议

首先我们打开Sino.GrpcService.Protocol项目,在其中新建一个msg.proto文件,打开msg.proto文件,我们将在其中编写基于proto3语言的协议,以便后面自动生成到各语言,如果读者需要更深入的学习可以打开该网站Proto3语言指南。

这里我们定义我们当前使用的是proto3语言并且包名(生成为C#则为命名空间)为:

syntax = "proto3";
package Sino.GrpcService;

笔者为该服务定义了1个服务,且有4种方法:

service MsgService{

rpc GetList(GetMsgListRequest) returns (GetMsgListReply){}

rpc GetOne(GetMsgOneRequest) returns (GetMsgOneReply){}

rpc Edit(EditMsgRequest) returns (EditMsgReply){}

rpc Remove(RemoveMsgRequest) returns (RemoveMsgReply){}

}

对应到其中每个方法的接收参数和返回参数的定义如下:

message GetMsgListRequest {

int64 UserId = 1;

string Title = 2;

int64 StartTime = 3;

int64 EndTime = 4;

}

message GetMsgListReply {

message MsgItem {

string Id = 1;

string Title = 2;

string Content = 3;

int64 UserId = 4;

int64 Time = 5;

}

repeated MsgItem Items = 1;

int64 Count = 2;

bool IsSuccess = 3;

string ErrorMsg = 4;

}

message GetMsgOneRequest {

string Id = 1;

}

message GetMsgOneReply {

string Id = 1;

string Title = 2;

string Content = 3;

int64 UserId = 4;

int64 Time = 5;

bool IsSuccess = 6;

string ErrorMsg = 7;

}

message EditMsgRequest {

string Id = 1;

string Title = 2;

string Content = 3;

}

message EditMsgReply {

bool IsSuccess = 1;

string ErrorMsg = 2;

}

message RemoveMsgRequest {

string Id = 1;

}

message RemoveMsgReply {

bool IsSuccess = 1;

string ErrorMsg = 2;

}

到这为止我们就完成了协议的编写。

c.将协议生成为C#代码

相对于网站的很多关于C#使用gRpc的教程都是基于.NET项目框架下的,所以可以安装gRpc.Tools,但是.NET Core安装后是找不到工具的,所以读者可以新建一个.NET项目安装该类库,然后将其中的工具复制到Sino.GrpcService.Protocol中,这里读者需要根据你当前的系统去选择,复制完成之后在该项目中新建一个名为“ProtocGenerate.cmd”的文件,在其中输入以下指令:

protoc -I . --csharp_out . --grpc_out . --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe msg.proto

然后读者直接双击运行,就会看到项目下生成了“Msg.cs”和“MsgGrpc.cs”两个文件,这样就完成了所有协议部分的工作了,最终的项目结构如下所示:

d.编写实现代码

有了协议层之后我们就可以开始编写实现了,因为笔者这里使用了MongoDb提供数据所以下文篇幅会较长。

首先打开Sino.GrpcService.Impl项目在其中新建Model文件,然后在该文件夹下新建MsgDM.cs文件,该文件主要是定义MongoDb存储的数据结构,具体内容如下所示:

/// <summary>

/// 消息体

/// </summary>

public sealed class MsgDM

{

/// <summary>

/// 编号

/// </summary>

public ObjectId Id { get; set; }

/// <summary>

/// 标题

/// </summary>

public string Title { get; set; }

/// <summary>

/// 内容

/// </summary>

public string Content { get; set; }

/// <summary>

/// 用户编号

/// </summary>

public long UserId { get; set; }

/// <summary>

/// 时间

/// </summary>

public long Time { get; set; }

}

紧接着我们新建Repositories文件夹,在其中新建四个文件分别为“IDataContext.cs”、“DataContext.cs”、“IMsgRepository.cs”和“MsgRepository.cs”。打开IDataContext.cs文件在其中编写如下内容:

/// <summary>

/// 数据库上下文

/// </summary>

public interface IDataContext

{

IMongoDatabase Database { get; set; }

}

打开DataContext.cs文件进行数据库初始化相关工作:

public class DataContext : IDataContext

{

public IMongoDatabase Database { get; set; }

public DataContext(IConfigurationRoot config)

{

var client = new MongoClient(config.GetConnectionString("mongodb"));

Database = client.GetDatabase("aSQ0cWkEshl8NiVn");

}

}

打开IMsgRepository.cs,我们需要在其中定义仓储提供的操作:

/// <summary>

/// 消息仓储

/// </summary>

public interface IMsgRepository

{

/// <summary>

/// 获取列表

/// </summary>

Task<List<MsgDM>> GetList(long userId, string title, long startTime, long endTime);

/// <summary>

/// 获取实体

/// </summary>

Task<MsgDM> Get(string id);

/// <summary>

/// 更新实体

/// </summary>

Task<bool> Update(MsgDM data);

/// <summary>

/// 添加实体

/// </summary>

Task<string> Insert(MsgDM data);

/// <summary>

/// 删除实体

/// </summary>

Task<bool> Delete(string id);

}

对应的我们还需要打开MsgRepository.cs文件实现该接口:

public class MsgRepository : IMsgRepository

{

private IDataContext _dataContext;

private IMongoCollection<MsgDM> _collection;

public MsgRepository(IDataContext dataContext)

{

_dataContext = dataContext;

_collection = _dataContext.Database.GetCollection<MsgDM>("msg");

}

public async Task<bool> Delete(string id)

{

var filter = Builders<MsgDM>.Filter.Eq(x => x.Id, new ObjectId(id));

var result = await _collection.DeleteOneAsync(filter);

return result.DeletedCount == 1;

}

public Task<MsgDM> Get(string id)

{

var objectId = new ObjectId(id);

var result = (from item in _collection.AsQueryable()

where item.Id == objectId

select item).FirstOrDefault();

return Task.FromResult(result);

}

public Task<List<MsgDM>> GetList(long userId, string title, long startTime, long endTime)

{

IQueryable<MsgDM> filter = _collection.AsQueryable();

if (userId != 0)

filter = filter.Where(x => x.UserId == userId);

if (!string.IsNullOrEmpty(title))

filter = filter.Where(x => x.Title.Contains(title));

if (startTime != 0)

filter = filter.Where(x => x.Time > startTime);

if (endTime != 0)

filter = filter.Where(x => x.Time < startTime);

return Task.FromResult(filter.ToList());

}

public async Task<string> Insert(MsgDM data)

{

await _collection.InsertOneAsync(data);

return data.Id.ToString();

}

public async Task<bool> Update(MsgDM data)

{

var filter = Builders<MsgDM>.Filter.Eq(x => x.Id, data.Id);

var update = Builders<MsgDM>.Update.Set(x => x.Title, data.Title).Set(x => x.Content, data.Content);

var result = await _collection.UpdateOneAsync(Builders<MsgDM>.Filter.Eq(x => x.Id, data.Id), update);

return result.ModifiedCount == 1;

}

}

完成了上面关于数据库的工作,下面我们就进入正题,开始实现gRpc服务了,首先我们在项目根目录下新建MsgServiceImpl.cs文件,在其中实现我们协议中的服务:

public class MsgServiceImpl : MsgService.MsgServiceBase

{

private IMsgRepository _msgRepository;

public MsgServiceImpl(IMsgRepository msgRepository)

{

_msgRepository = msgRepository;

}

public override async Task<GetMsgListReply> GetList(GetMsgListRequest request, ServerCallContext context)

{

var result = new GetMsgListReply();

var list = await _msgRepository.GetList(request.UserId, request.Title, request.StartTime, request.EndTime);

result.IsSuccess = true;

result.Items.AddRange(list.Select(x => new GetMsgListReply.Types.MsgItem

{

UserId = x.UserId,

Title = x.Title,

Time = x.Time,

Content = x.Content

}).ToList());

return result;

}

public override async Task<EditMsgReply> Edit(EditMsgRequest request, ServerCallContext context)

{

var result = new EditMsgReply();

result.IsSuccess = await _msgRepository.Update(new MsgDM

{

Id = new MongoDB.Bson.ObjectId(request.Id),

Title = request.Title,

Content = request.Content

});

return result;

}

public override async Task<GetMsgOneReply> GetOne(GetMsgOneRequest request, ServerCallContext context)

{

var msg = await _msgRepository.Get(request.Id);

return new GetMsgOneReply

{

IsSuccess = true,

Id = msg.Id.ToString(),

UserId = msg.UserId,

Title = msg.Title,

Content = msg.Content,

Time = msg.Time

};

}

public override async Task<RemoveMsgReply> Remove(RemoveMsgRequest request, ServerCallContext context)

{

var result = new RemoveMsgReply();

result.IsSuccess = await _msgRepository.Delete(request.Id);

return result;

}

}

三、证书生成

a.安装openssl

首先读者需要从该网站下载openssl安装程序:

Openssl下载

笔者的系统是Win10 64所以下载的是“Win64 OpenSSL v1.1.0b”。

b.制作证书

网上有很多的教程,但是对于新手来说直接给绕晕了,有的有ca、client和service有的没有,这里笔者提供一个全面的cmd脚本(默认CA是自己):

@echo off

set OPENSSL_CONF=c:\OpenSSL-Win64\bin\openssl.cfg

echo Generate CA key:

openssl genrsa -passout pass:1111 -des3 -out ca.key 4096

echo Generate CA certificate:

openssl req -passin pass:1111 -new -x509 -days 365 -key ca.key -out ca.crt -subj  "/C=CN/ST=JS/L=ZJ/O=sino/OU=test/CN=root"

echo Generate server key:

openssl genrsa -passout pass:1111 -des3 -out server.key 4096

echo Generate server signing request:

openssl req -passin pass:1111 -new -key server.key -out server.csr -subj  "/C=CN/ST=JS/L=ZJ/O=sino/OU=test/CN=root"

echo Self-sign server certificate:

openssl x509 -req -passin pass:1111 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

echo Remove passphrase from server key:

openssl rsa -passin pass:1111 -in server.key -out server.key

echo Generate client key

openssl genrsa -passout pass:1111 -des3 -out client.key 4096

echo Generate client signing request:

openssl req -passin pass:1111 -new -key client.key -out client.csr -subj  "/C=CN/ST=JS/L=ZJ/O=sino/OU=test/CN=root"

echo Self-sign client certificate:

openssl x509 -passin pass:1111 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt

echo Remove passphrase from client key:

openssl rsa -passin pass:1111 -in client.key -out client.key

以上的脚本也会生成我们下面Demo中使用的证书。

四、完善服务端

用了上面的证书之后我们需要继续把服务端启动gRpc服务部分的代码书写完毕,这里笔者是采用命令行形式运行的,所以gRpc的启动是独立放在一个文件文件中,如下RpcConfiguration所示:

其中我们使用了server.crtserver.key这两个证书,所以在Host项目中需要将这个两个证书文件copy到项目根目录下,如果需要发布的时候包含则需要在project.json中配置如下节:

  "publishOptions": {    "include": [ "server.crt", "server.key", "appSettings.json", "appSettings.*.json" ]}

最后我们需要在Program中启动对应的gRpc即可。

五、客户端编写

完成了服务端的编写剩下的就是客户端的编写,当然客户端的编写相对容易很多,笔者这里直接把Sino.GrpcService.Protocol项目包含到客户端解决方案中了(在正式开发中建议采用nuget包进行管理),为了简单起见,所以只调用了其中一个服务接口:

需要注意下其中“ChannelOptions.SslTargetNameOverride”这部分是必须的,因为我们是自己生成的证书,所以域名是root,如果是生产环境可以不需要。

六、利用Docker运行

a.安装Docker For Windows

这里需要win10的系统,这样可以直接在ps中直接利用docker指令了。

b.编写Dockerfile

因为1.1版本出来了,但是经过本人的验证,如果你的应用不升级是无法使用该镜像的,默认使用1.1,所以这里我们的Dockerfile需要指定下特定的版本,否则是无法构建的,我们首先在解决方案的根目录下新建Dockerfile文件,然后在其中放入以下命令:

FROM microsoft/dotnet:1.0-sdk-projectjson

ADD ./ /usr/local/src

WORKDIR /usr/local/src/Sino.GrpcService.Host/

RUN cd /usr/local/src/

RUN dotnet restore -v http://api.nuget.org/v3/index.json

RUN dotnet build

EXPOSE 9007

CMD ["dotnet","run"]

c.生成镜像并运行

我们打开ps,然后cd到解决方案的文件夹下利用:

docker build -t gRpcService:1.0 .

开始构建,基于国内的情况建议大家将docker默认拉取镜像的地址调整下。生成好之后,利用以下指令去启动即可:

docker run -d –name -p 9007:9007 gRpcService gRpcService:1.0

当然客户端连接的地址和端口也要根据-p指定的情况去调整。

七、其他

对应的源码可以访问以下地址:

https://github.com/Vip56/Sino.GrpcService

https://github.com/Vip56/Sino.GrpcClient

相关文章:

  • gRPC .NET Core跨平台学习

  • 谷歌发布的首款基于HTTP/2和protobuf的RPC框架:GRPC

  • C#中使用gRPC

  • Google高性能RPC框架gRPC 1.0.0发布

原文地址:http://www.cnblogs.com/yaozhenfa/p/gRpc_with_ssl.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

.NET Core下使用gRpc公开服务(SSL/TLS)相关推荐

  1. .NET Core 下使用 gRPC

    gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架. https://grpc.io/docs/guides/ https://github.com/grpc/grpc-dotnet h ...

  2. .net core 下的 grpc网关——http到grpc的网关源码剖析

    我曾经有段时间,特别想写一款从Http的json请求到grpc协议的网关,以便顺应asp.net core3.1的grpc微服务支持,grpc虽然从性能上稍微逊色于其他基于tcp的rpc,但由于是微软 ...

  3. .NET Core使用gRPC打造服务间通信基础设施

    一.什么是RPC rpc(远程过程调用)是一个古老而新颖的名词,他几乎与http协议同时或更早诞生,也是互联网数据传输过程中非常重要的传输机制. 利用这种传输机制,不同进程(或服务)间像调用本地进程中 ...

  4. 如何在.NET Core中为gRPC服务设计消息文件(Proto)

    如何在.NET Core中为gRPC服务设计消息 使用协议缓冲区规范定义gRPC服务非常容易,但从需求转换为.NET Core,然后管理服务的演变时,需要注意几件事. 创建gRPC服务的核心是.pro ...

  5. 如何在 ASP.NET Core 中为 gRPC 服务添加全局异常处理 ?

    咨询区 Dmitriy 我在 ASP.NET Core 中使用 GRPC.ASPNETCore 工具包写 gRPC 服务,现在我想实现 gRPC 的异常全局拦截,我的代码如下: app.UseExce ...

  6. .net core下简单构建高可用服务集群

    一说到集群服务相信对普通开发者来说肯定想到很复杂的事情,如zeekeeper ,反向代理服务网关等一系列的搭建和配置等等:总得来说需要有一定经验和规划的团队才能应用起来.在这文章里你能看到在.net ...

  7. 【gRPC】 在.Net core中使用gRPC

    最近在学习.net core的微服务体系架构.微服务之间的通信常常通过gRPC进行同步通信,但是需要注意的是,大多数微服务之间的通信是通过事件总线进行异步通信.在微软介绍.net微服务体系架构的项目e ...

  8. gRPC 的 SSL/TLS 加密认证

    文章目录 简介 生成自签证证书 服务端 客户端 单向认证 双向认证 基于 Token 的单向认证 基于 Token 的双向认证 简介 传输层安全性协议(Transport Layer Security ...

  9. dotnet core高吞吐Http api服务组件FastHttpApi

    简介 是dotNet core下基于Beetlex实现的一个高度精简化和高吞吐的HTTP API服务开源组件,它并没有完全实现HTTP SERVER的所有功能,而是只实现了在APP和WEB中提供数据服 ...

最新文章

  1. ASP.NET前台代码绑定后台变量方法总结
  2. 如何知道交换机的缓存大小_网络基本功之细说交换机
  3. (转载)Qt中MOC的一些限制
  4. [置顶]动态网页开发基础【笔记】
  5. Spark应用日志级别设置
  6. Ubuntu18.04 安装Nvidia驱动
  7. JavaScript(四)—— JavaScript 内置对象/JavaScript 简单数据类型与复杂类型
  8. TMS320C55x的指令系统
  9. 算法和数据结构最全最易懂总结,再也不怕面试了~
  10. 王校长一分钟能吃多少热狗?| 小游戏 1
  11. 高德地图---已知两点经纬度坐标求距离和角度
  12. 推荐两套大型医院信息管理系统,代码完整,免费分享
  13. 子龙山人Learn Emacs in 21 Days: day 6 学习笔记
  14. 计算机仿真课程的心得体会,数学建模心得体会
  15. 水星网卡 linux驱动,水星网卡驱动下载_硬件驱动下载
  16. 哪种编程语言好?大神为你分析 Go、Java、C、C++ 等主流编程语言
  17. jpg格式图片怎么压缩?jpg图片如何压缩到最小?
  18. vue项目中金额小写转换为汉字大写的功能封装
  19. python、pygame开发的太空大战游戏源代码,供学习参考
  20. UI-平面构成-ai-快捷键-1

热门文章

  1. Ansible 一步一步从入门到精通(一)
  2. 安装Wamp时出现无法启动此程序,因为计算机中丢失MSVCR110.dll的解决方法
  3. C++STL之string (转)
  4. 深入理解Magento-第十章-数据操作数据收集器
  5. NDK/JNI01--NDK下载配置
  6. 同事都说有SQL注入风险,我非说没有
  7. C# 基于事件的异步模式
  8. WPF 实现火炬效果
  9. 解决ASP.NET Core部署到IIS,更新项目另一个程序正在使用此文件,进程无法访问...
  10. C#使用iTextSharp操作PDF文件