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

一、前言

 

      前一阵子关于.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跨平台或扫描二维码关注

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

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

相关文章

HashMap jdk1.7源码阅读与解析

转载自 HashMap源码阅读与解析 一、导入语 HashMap是我们最常见也是最长使用的数据结构之一&#xff0c;它的功能强大、用处广泛。而且也是面试常见的考查知识点。常见问题可能有HashMap存储结构是什么样的&#xff1f;HashMap如何放入键值对、如何获取键值对应的值以及如何…

java实现加密电话号码,有具体的加密流程注释

闲着没事做&#xff0c;正好有一位哥们让帮他看个写个逻辑题&#xff0c;我就顺便写了下&#xff01; 此题主要是加密一个数字类型的电话号码&#xff0c;具体加密流程如下&#xff1a; * 将一串数字进行加密 * 加密规则&#xff1a;先把这串数字降序&#xff0c;然后将每个…

.NET项目版本号的小随笔

【题外话】 一直以来都对.NET项目中的几个版本号&#xff08;AssemblyVersion、AssemblyFileVersion、AssemblyInformationalVersion&#xff09;以及版本号中的Revision和Build有疑问&#xff0c;今儿抽了点时间看了几篇文章&#xff0c;整理一下与大家一起分享下。 【一、Ass…

Windows.etc\hosts文件

Windows.etc\hosts文件 ZC&#xff1a;就是将 后面的项 重定位到 前面的项 1、目录&#xff1a;"C:\Windows\System32\drivers\etc" 文件&#xff1a;"C:\Windows\System32\drivers\etc\hosts" 2、c__Windows_System32_drivers_etc_hosts的作用 - Sharpe…

java实现邮件发送准备工作(前期配置)

本文主要用的邮件客户端是&#xff1a;office 2007的outlook,服务器是apache-james-2.3.2&#xff0c;首先我们来配置一下这个james服务器: 1.将james服务器解压到硬盘目录下&#xff0c;注意目录不能有中文&#xff0c;如e:盘下 2.修改apps/james/sar-inf目录下的confi…

win10关闭“Windows安全中心”功能的两种方法

win10系统怎么将windows安全中心关闭&#xff1f; 听语音 原创|浏览&#xff1a;10407|更新&#xff1a;2020-03-24 10:541 2 3 4 5 6 7 分步阅读 一些软件需要将Windows安全中心关闭。 方法/步骤 1 首先打开开始菜单。 2 在开始菜单中点击设置按钮。 3 在设置界…

ASP.NET Core 中间件Diagnostics使用

ASP.NET Core 中间件(Middleware)Diagnostics使用。对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware)。 Diagnostics中间件&#xff0c;主要功能是用于报告和处理ASP.NET Core中的异常和错误信息&#xff0c;以及诊断Entity Framework核心迁移错误。 其…

使用java底层实现邮件的发送(含测试,源码)

直接上代码&#xff1a;3个类&#xff0c;两个主要的类&#xff0c;一个测试类&#xff1a; 主类&#xff08;Mail&#xff09;&#xff1a; /** * Title: Mail.java * Package org.service.impl * Description: TODO该方法的主要作用&#xff1a; * author A18ccms A18ccms_…

Java多线程:线程状态

转载自 Java多线程:线程状态 一. 线程状态类型 1. 新建状态&#xff08;New&#xff09;&#xff1a;新创建了一个线程对象。 2. 就绪状态&#xff08;Runnable&#xff09;&#xff1a;线程对象创建后&#xff0c;其他线程调用了该对象的start()方法。该状态的线程位于可运行…

Win10怎么关闭开机启动项

Win10怎么关闭开机启动项 我们可以首先打开电脑的运行对话框&#xff0c;按下键盘的WINR组合键&#xff0c;打开运行。 然后这里我们在运行对话框中输入命令msconfig确定&#xff0c;打开系统配置程序。 系统配置窗口&#xff0c;启动里面点击这里的任务管理器打开。 这时…

TypeScript 2.1发布

TypeScript是微软开发的一个JavaScript的超集&#xff0c;提供了最新的JavaScript特性以及可选的静态类型。近日&#xff0c;TypeScript 2.1发布。该版本提供了功能更为强大的类型检查器&#xff0c;并且让开发人员可以编写出更简洁的代码。以下是该版本带来的主要新特性&#…

使用spring实现邮件的发送(含测试,源码,注释)

此篇主要讲的是使用spring配置实现邮件发送&#xff0c;与之前的底层实现简便了不少&#xff0c;只需要几个配置就可以了&#xff0c;那么请往下看&#xff1a; 先写个接口 /** * Title: IMailserdService.java * Package org.service * Description: TODO该方法的主要作用&a…

你当前无权访问该文件夹 解决你当前无权访问该文件夹拒绝你访问该文件夹

我 这样就完成了 http://www.xitonghe.com/jiaocheng/windows7-5642.html https://jingyan.baidu.com/article/4b52d702aa01b3fc5c774b1b.html Win10正式版提示你当前无权访问该文件夹怎么办 https://jingyan.baidu.com/article/4b52d702aa01b3fc5c774b1b.html 1407345人看了…

SpringBoot+Vue博客系统---后端接口开发

Java后端接口开发 从零开始搭建一个项目骨架&#xff0c;最好选择合适&#xff0c;熟悉的技术&#xff0c;并且在未来易拓展&#xff0c;适合微服务化体系等。所以一般以Springboot作为我们的框架基础&#xff0c;这是离不开的了。 然后数据层&#xff0c;我们常用的是Mybati…

图说世界编程语言排行

TIOBE编程语言社区排行榜是编程语言流行趋势的一个指标&#xff0c;每月更新&#xff0c;这份排行榜排名基于互联网上有经验的程序员、课程和第三方厂商的数量。排名使用著名的搜索引擎&#xff08;诸如Google、MSN、Yahoo!、Wikipedia、YouTube以及Baidu等&#xff09;进行计算…

中国有超级计算机的大学,计算机专业排名看超算实力,ASC竞赛五大高校排名,中山大学第一...

ASC竞赛五大高校计算机专业的实力主要体现在算法与编程的逻辑运算上&#xff0c;因此计算机专业必须掌握大量基础数学知识&#xff0c;甚至很多是离散数学、模糊数学等人工智能逻辑数学&#xff0c;简单的程序软件应用和O2O程序实现其实都不是计算机专业实力的体现&#xff0c;…

IntelliJ IDEA设置JDK版本

IntelliJ IDEA设置JDK版本 临渊行 2019-06-13 13:59:13 46888 收藏 30 分类专栏&#xff1a; 这里有个坑 版权 一、背景 即使我电脑安装的JDK版本是8&#xff0c;然而在idea运行中常常提示xxjdk1.5已过时之类的&#xff0c;why?明明是我装的JDK8啊 二、解决 鼠标点击f…

从抵触到力推,.Net Core的成功让微软正视开源

微软在两年前做了一件当时非常令人惊讶的事情&#xff1a;将 .Net Core 开源。在此之前&#xff0c;微软曾表示开源是对软件经济的威胁。或许是为了更好的销售工具和云服务&#xff0c;亦或许是为了吸引更多的开发者到其平台&#xff0c;微软开始开源。到目前为止&#xff0c;这…

线程池的原理及实现

转载自 线程池的原理及实现 1、线程池简介&#xff1a; 多线程技术主要解决处理器单元内多个线程执行的问题&#xff0c;它可以显著减少处理器单元的闲置时间&#xff0c;增加处理器单元的吞吐能力。 假设一个服务器完成一项任务所需时间为&#xff1a;T1 创建线…

计算机考试行高怎么设置,Excel隔行调整行高的四种有效方法

领导要求把一份Excel表格的偶数行行高调整一下。这份表格可是有上百行的&#xff0c;逐一调整行高显然是不科学的。几经周折&#xff0c;费了我N多的口舌四处讨教&#xff0c;这个任务还是顺利地完成了。一、直接定位法先在表格的最后增加一个辅助列。在该列的第一行的单元格中…