目录
1.简要说明
2.服务端设计
2.1 服务端创建
2.2 服务端设计
2.3 服务端业务模块
3.客户端设计-控制台
4.客户端设计-Avalonia桌面程序
5.客户端设计-MAUI安卓端程序
1.简要说明
gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统
项目下载地址:https://download.csdn.net/download/rotion135/90342675
整体架构设计图
解决方案预览
2.服务端设计
2.1 服务端创建
新建项目,搜索grpc 选择下图中的项目,创建;
框架最低版本支持.NET 8.0
2.2 服务端设计
先说下几个重要的文件:
launchSettings.json 部署路径等先关信息的配置文件
appsettings.json 项目设置相关配置文件
手动添加:
"Microsoft.AspNetCore.Hosting": "Information",
"Microsoft.AspNetCore.Routing.EndpointMiddleware": "Information",
greet.proto 通讯交互的模型设计,以及命名空间
收发消息的接口定义等,添加完成后记得保存,重新生成一下
服务类定义,还有收发消息方法重写
寻找对应的服务以及调用的方法,用的反射的机制
参数传过来 ServerName 方法Method 以及入参 Args
从容器中寻找服务,获取到实例后,调用Method,最后返回结果
public class GreeterService : Greeter.GreeterBase
{
private readonly ILogger<GreeterService> _logger;
public GreeterService(ILogger<GreeterService> logger)
{
_logger = logger;
}
public override Task<MessageResult> SendMessage(LSRequest request, ServerCallContext context)
{
return GetResponse(request);
}
private async Task<MessageResult> GetResponse(LSRequest request)
{
return await Task.Run(() =>
{
ResponseModel response = new ResponseModel();
try
{
SendModel send = JsonConvert.DeserializeObject<SendModel>(request.Json);
if (send == null)
{
response.IsSuccess = false;
response.Message = "Request cannot be null";
}
else
{
if (string.IsNullOrEmpty(send.ServerName) || string.IsNullOrEmpty(send.Method))
{
response.IsSuccess = false;
response.Message = "ServerName or Method cannot be null";
}
else
{
response.RequestID = send.RequestID;
// 根据服务名称,寻找对应的服务
var service = BusinessModules.IocContainer.Get<IService>(send.ServerName);
if (service != null)
{
// 使用反射调用方法
MethodInfo methodInfo = null;
if (send.Args != null && send.Args.Length > 0)
{
methodInfo = service.GetType().GetMethod(send.Method, send.Args.Select(arg => arg.GetType()).ToArray());
}
else
{
methodInfo = service.GetType().GetMethod(send.Method, types: new List<Type>().ToArray());
}
if (methodInfo != null)
{
try
{
var res = methodInfo.Invoke(service, send.Args); // 执行方法
response.IsSuccess = true;
response.Content = JsonConvert.SerializeObject(res);
}
catch (Exception ex)
{
LogOperate.Error("Method invocation failed", ex);
response.IsSuccess = false;
response.Message = ex.Message;
}
}
else
{
response.IsSuccess = false;
response.Message = "Method not found";
}
}
else
{
response.IsSuccess = false;
response.Message = "Server not found";
}
}
}
}
catch (Exception ex)
{
LogOperate.Error("GetResponse 发生异常", ex);
response.IsSuccess = false;
response.Message = ex.Message;
}
return new MessageResult()
{
Json = JsonConvert.SerializeObject(response)
};
});
}
}
在程序运行时,将服务注入
一些业务相关的服务启动,用的BusinessModules来管理,下面会介绍。
2.3 服务端业务模块
业务模块,用了我自己写的IOC容器来管理
定义服务,继承IService
业务模块启动:
/// <summary>
/// 模块启动
/// </summary>
public static void OnStart()
{
try
{
IEnumerable<Type> types = GetService();
if (types != null && types.Count() > 0)
{
foreach (Type type in types)
{
string serviceName = type.Name;
// 获取 ServiceNameAttribute 特性
var attribute = type.GetCustomAttribute<ServiceNameAttribute>();
if (attribute != null)
{
serviceName = attribute.Name;
}
//注册平台 基于IService的服务平台
_iocBuilder.RegisterType(type,serviceName,LifeTimeType.Singleton);
}
}
//创建容器
IocContainer = _iocBuilder.Build();
//获取所有注册的服务,基于IService的实现
//然后调用服务的启动方法
var services = IocContainer.GetAllService();
foreach (var service in services)
{
try
{
service.OnStart();
}
catch (Exception ex)
{
LogOperate.Start($"启动容器中的服务发生异常,\r\n" + ex.ToString());
}
}
}
catch(Exception ex)
{
LogOperate.Error("BusinessModules-OnStart", ex);
}
}
业务模块结束:
/// <summary>
/// 模块结束
/// </summary>
public static void OnStop()
{
try
{
var services = IocContainer.GetAllService();
foreach (var service in services)
{
try
{
service.OnStop();
}
catch (Exception ex)
{
LogOperate.Start($"停止容器中的服务发生异常,\r\n" + ex.ToString());
}
}
}
catch (Exception ex)
{
LogOperate.Error("BusinessModules-OnStop", ex);
}
}
3.客户端设计-控制台
客户端设计可以多种方式,项目初始化时,需要Nuget引用下面三个包:
Google.Protobuf
Grpc.Net.Client
Grpc.Tools
添加 Protos 文件夹,添加文件greet.proto
除了命名空间修改为当前项目的之外,其余的与服务中的文件一致
运行时,创建链接,调用服务中的方法
using var channle = GrpcChannel.ForAddress("http://127.0.0.1:5237");
var client = new Greeter.GreeterClient(channle);
SendModel send = new SendModel();
send.RequestID = Guid.NewGuid().ToString();
send.ServerName = "Device";
send.Method = "GetDeviceInfo";
//send.Args = new object[1];
//send.Args[0] = "1231";
var replay = await client.SendMessageAsync(new LSRequest() { Json = JsonConvert.SerializeObject(send) });
Console.WriteLine("Response:" + replay.Json);
Console.ReadKey();
4.客户端设计-Avalonia桌面程序
Avalonia 创建项目
同样需要添加三个包
同样需要添加 Protos 文件夹,添加文件greet.proto
除了命名空间修改为当前项目的之外,其余的与服务中的文件一致
客户端的设计我在这里就不多说了,可以看我的其他文章,或者下载源码来查看
这里边封装了一个gRPC的客户端类
public class GRPC_Control
{
private string url;
private Greeter.GreeterClient client;
private GrpcChannel channle;
public GRPC_Control(string _url)
{
url = _url;
}
public BaseResult Connect()
{
//"http://localhost:5237"
channle = GrpcChannel.ForAddress(url);
client = new Greeter.GreeterClient(channle);
return BaseResult.Successed;
}
public BaseResult SendMessage(string service, string method, object[] args = null)
{
try
{
SendModel send = new SendModel();
send.RequestID = Guid.NewGuid().ToString();
send.ServerName = service;
send.Method = method;
send.Args = args;
var replay = client.SendMessage(new LSRequest() { Json = JsonConvert.SerializeObject(send) });
ResponseModel response = JsonConvert.DeserializeObject<ResponseModel>(replay.Json);
if (response.IsSuccess)
{
return JsonConvert.DeserializeObject<BaseResult>(response.Content);
}
else
{
return new BaseResult(false, response.Message);
}
}
catch (Exception ex)
{
LogOperate.Error("SendMessage", ex);
return new BaseResult(false, ex.Message);
}
}
public async Task<BaseResult> SendMessageAsync(string service, string method, object[] args = null)
{
return await Task.Run(async () =>
{
try
{
SendModel send = new SendModel();
send.RequestID = Guid.NewGuid().ToString();
send.ServerName = service;
send.Method = method;
send.Args = args;
var replay = await client.SendMessageAsync(new LSRequest() { Json = JsonConvert.SerializeObject(send) });
ResponseModel response = JsonConvert.DeserializeObject<ResponseModel>(replay.Json);
if (response.IsSuccess)
{
return JsonConvert.DeserializeObject<BaseResult>(response.Content);
}
else
{
return new BaseResult(false, response.Message);
}
}
catch (Exception ex)
{
LogOperate.Error("SendMessageAsync", ex);
return new BaseResult(false, ex.Message);
}
});
}
}
调用方式:
case "Connect":
if (gRPC == null)
{
gRPC = new GRPC_Control(Url);
gRPC.Connect();
}
VM_MainWindow.Instance.Popup("连接成功");
break;
case "Send":
if (gRPC != null)
{
var res= gRPC.SendMessage(Service, Method);
Respone=JsonConvert.SerializeObject(res);
}
break;
5.客户端设计-MAUI安卓端程序
创建MAUI 项目,MAUI具体的使用可以参考官方文档,这里边就不展开说了
同样也是需要引用三个包
同样需要添加 Protos 文件夹,添加文件greet.proto
除了命名空间修改为当前项目的之外,其余的与服务中的文件一致
封装的gRPC客户端类与Avalonia的一致
如果对此架构感兴趣,欢迎下载源码参考参考,如有更好的建议,欢迎评论区提出