VS2022 开发Grpc ,使用Docker支持

使用VS2022开发遇到了不少坑,记录一下

  1. VS貌似没有类似WebService添加接口的模板,需要手动添加.proto文件,而且.proto文件,也没有对应模板,需要添加一个文本文件,改成需要的名字

  2. 添加完.proto文件,需要用Grpc.Tools编译生成对应所需的文件,使用dotnet add package Grpc.Tools
    这里我遇到一个坑,就是添加完了chatGPT告诉我,Grpc会在项目路径下的package里面,但最后查的实际在C盘的全局路径下
    C:\Users{用户名}.nuget\packages\grpc.tools\2.58.0\tools\windows_x64

  3. 安装完之后,将我们找到路径添加到系统环境变量PATH里面

  4. 接着我们执行编译命令,chatGPT最开始给我的有问题,经过几次反馈最终终于执行成功了
    这是最终给我的命令:
    protoc --proto_path="D:\{项目路径,包含.csproj文件那个}\Protos" --csharp_out=. --grpc_out=. --plugin=protoc-gen-grpc="C:\Users\{用户名}\.nuget\packages\grpc.tools\2.58.0\tools\windows_x64\grpc_csharp_plugin.exe" "D:\{项目路径,包含.csproj文件那个}\Protos\inventory.proto"

  5. 添加Service文件
    Service文件需要继承刚才工具生成的文件
    以我的代码为例

using Grpc.Core;

namespace YourName.Services
{
    public class InitialInventoryServiceImpl : InitialInventoryService.InitialInventoryServiceBase
    {
        public override Task<InitialInventoryResponse> InitialInventory(InitialInventoryRequest request, ServerCallContext context)
        {
			// ToDo
			return Task.FromResult(new InitialInventoryResponse { Result = true });
        }
    }
}
  1. 注册服务
    app.MapGrpcService<InitialInventoryServiceImpl>();
    就这一句
public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);


            // Add services to the container.
            builder.Services.AddGrpc();

            // Initialize IOC modules
            InitializeIOC.Initialize();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            app.MapGrpcService<InitialInventoryServiceImpl>();

            app.Run();
        }
    }
  1. 测试GRpc调用
    新建控制台程序,然后安装nuget包。
    dotnet add package Grpc.Tools
    dotnet add package Grpc.Core
    dotnet add package Google.Protobuf
    接着像service一样通过proto文件生成类文件。这个proto生成的文件可能会导致警告冲突,调整一下结构就好了
using var channel = GrpcChannel.ForAddress("https://localhost:5001");

            // 创建gRPC客户端
            var client = new InitialInventoryServiceClient(channel);
            var testList = FileHelper.ReadTestAsIds();
            var sucList = FileHelper.ReadSucAsIds();
            testList = testList.Except(sucList).ToList();
            foreach(var asId in testList)
            {
                // 调用服务方法
                var reply = client.InitialInventory(
                    new InitialInventoryRequest { AsId = asId }
                    );
                ConsoleHelper.ShowMessage($"账套{asId}处理{(reply.Result ? "成功" : "失败")}");
                if(reply.Result)
                {
                    FileHelper.WriteSucAsId(asId);
                }
                else
                {
                    FileHelper.WriteErrorAsId(asId);
                }
            }

8 使用Docker
8.0 启动Docker Desktop
8.1 右键项目添加docker支持
8.2 将生成的docker文件复制到解决方案的根目录,因为dockerfile只能访问他的根目录及子目录
8.3 找到 NuGet.Config,因为公司通常会有私有的nuget包,nuget.config在%AppData%\NuGet\NuGet.Config中应该可以找到
8.4 修改dockerfile,注意大小写,文件名路径会区分大小写

...
# Copy the NuGet configuration file.
COPY NuGet.Config /root/.nuget/NuGet/

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["NuGet.Config", "./"]
...

8.5 执行命令
docker build -t my-service-name .
注意不要把 .丢了
8.6 我在生成的过程中报了个错,因为,我有两个项目都有相同的配置文件,需要先取消一个项目的配置文件输出
error NETSDK1152: Found multiple publish output files with the same relative path:
8.7 保存镜像文件
docker save -o D:\Images\my-service.tar my-service

  1. 启动docker联调
    联调后出现很多问题,主要是https协议引起的,本身,我这个不需要https协议,只是内部调用。所以直接使用http协议就行
    本地直接调用grpc没问题,但调用docker的时候,就不行了,需要gRPC 服务配置为允许非加密的 HTTP/2 连接。默认情况下,许多 gRPC 实现只允许通过 HTTPS 使用 HTTP/2。
    需要调整一下代码:
 	var builder = WebApplication.CreateBuilder(args);
            builder.WebHost.UseUrls("http://localhost:5001");
			//增加下面这句就可以用http调用grpc了
            builder.WebHost.ConfigureKestrel(options =>
            {
                options.ListenAnyIP(5001, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http2;
                });
            });

docker 常用命令

>docker stop my_container
>docker rm my_container
>docker build -t inv-s-2 .
>docker stop my_container
>docker run -d --name=my_container -p 5001:5001 inv-s-2:latest tail -f /dev/null
>docker exec -it my_container /bin/bash

posted @ 2023-09-25 10:29  花茶冰糖  阅读(260)  评论(0编辑  收藏  举报