MQTT CS架构设计

MQTT CS架构设计
目录1.项目背景2.项目下载3.架构介绍4.细节讲解4.1 消息接收处理4.2 网关处理4.3 接口方法定义4.4 IOC容器加载 服务组件/客户端组件4.5 数据库操作1.项目背景项目是针对线体项目进行设计线体流程管理系统包含人工工位20个工位以上和串联的单机设备5台以上管理系统负责工位数据的收集以及流程的控制人员的操作响应等拓扑图2.项目下载下载链接https://download.csdn.net/download/rotion135/930113593.架构介绍设计模式以 MQTT为主要通讯协议同时支持与PLC的ModbusTCP测试设备的RS232通讯等采用CS的设计框架客户端与服务端是多对一的模式。 架构设计上也支持多对多的模式就是数据同步上需要重新设计一下接口调用参考了MVC的模式实现以定义控制器的方式采用反射将接口与主题绑定决定请求路径的执行与返回4.细节讲解解决方案目录4.1 消息接收处理MQTT服务接收到客户端的请求后在下面的事件中响应处理/// summary/// 消息接收/// /summary/// param namearg/param/// returns/returnsprivate Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs arg)获取ClientId知道来源客户端再获取主题与携带的参数把主题和参数都传入到 网关API 中处理再等待网关回复数据再通过主题返回给客户端4.2 网关处理网关收到消息后根据主题路径获取需要调用的目标方法通过反射的机制实现方法调用再返回结果。参数都是Json的数据结构目标方法是空参数的话传入的参数就需要时空Json对象即{}/// summary /// 请求操作 /// /summary /// param nametoptic主题内容/param /// param nameparam参数/param /// returns/returns public static async Taskstring RequestOperation(string clientId, string toptic, object param) { string msgModel {}; try { //根据主题获取请求路径 // 1.根据主题获取请求路径/反射所需信息 var item TopticCache.TopticCaches.Find(x x.RequestToptic toptic); if (item null) { return msgModel; } // 2.将入参JSON字符串转为JObject(核心方便根据参数名取值自动类型转换) string paramJson param?.ToString() ?? {}; JObject paramJObj JObject.Parse(paramJson); // 3.反射拼接完整类名【命名空间.类名】获取类的Type对象 string fullClassName ${item.NameSpace}.{item.Controller}; Type targetClassType AppDomain.CurrentDomain.GetAssemblies() .Select(asm asm.GetType(fullClassName)) // 从每个程序集中获取类型 .FirstOrDefault(type type ! null); // 取第一个非空的匹配类型 //Type targetClassType Type.GetType(fullClassName); if (targetClassType null) { return msgModel; } // 4.创建类的实例(如果是单例/注入的服务这里可以替换为IOC容器获取实例) object classInstance Activator.CreateInstance(targetClassType); // 5.反射获取类中指定的异步方法对象 MethodInfo targetMethod targetClassType.GetMethod( item.Method, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if (targetMethod null) { return msgModel; } // 6.核心解析JSON参数匹配目标方法的入参生成参数数组 ParameterInfo[] methodParams targetMethod.GetParameters(); object[] invokeParams new object[methodParams.Length]; for (int i 0; i methodParams.Length; i) { var paramInfo methodParams[i]; // 从JSON中根据【参数名】取值兼容参数名大小写不一致问题 JToken paramValue paramJObj.GetValue(paramInfo.Name, StringComparison.OrdinalIgnoreCase); if (paramValue null || paramValue.Type JTokenType.Null) { // 无传参时赋值默认值 invokeParams[i] paramInfo.HasDefaultValue ? paramInfo.DefaultValue : null; } else { // 自动类型转换JSON值 转为 方法入参的实际类型 invokeParams[i] paramValue.ToObject(paramInfo.ParameterType); } } //赋值ClientId (classInstance as BaseAPIController).ClientId clientId; // 7.核心反射调用【异步方法】并await执行获取返回结果 var methodResult targetMethod.Invoke(classInstance, invokeParams); // 异步方法必须await获取Task的实际返回值 if (methodResult is Task realTask) { await realTask; // 通过反射获取TaskT的Result属性拿到真实返回数据 var resultProperty realTask.GetType().GetProperty(Result); if (resultProperty ! null) { var realData resultProperty.GetValue(realTask); // 8.将方法返回值 赋值给泛型返回对象 if (realData ! null) { msgModel JsonConvert.SerializeObject(realData); } } } return msgModel; } catch (Exception ex) { MsgModel err new MsgModel(false, MsgErrorType.Internal_Error, ex.Message); return JsonConvert.SerializeObject(err); } }4.3 接口方法定义接口类都需要继承基类 BaseAPIController特性APIRoute 定义主题的前部分方法再用特性定义主题的后半部分加起来访问目标方法的主题就是后面再填充各个客户端的ClientId就能请求方法了4.4 IOC容器加载 服务组件/客户端组件服务主程序 和 客户端主程序运行起来时通过配置参数来决定IOC容器加载的引用项目服务端客户端4.5 数据库操作此处定义数据库的操作方法定义实体定义数据模型数据库ORM使用 SqlSugar我的这篇文章有详细介绍SqlSugar 数据库操作通用类设计_c# sqlsugar通用类-CSDN博客Domain里边定义访问数据库的方法增删查改等如果有实现缓存的先从缓存中获取。DBControl 定义了数据库初始化的代码包含生成数据库生成表添加索引以及上线后新增字段等功能