这是一个用于本机多进程进行IPC通讯的库,此库的顶层API是采用ASP.NETCore的MVC框架,其底层通讯不是传统的走网络的方式,而是通过dotnetCampus.Ipc开源项目提供的基于NamedPipeStream命名管道的方式进行通讯。相当于替换掉ASP.NETCore的底层通讯方式,从走网络换成命名管道的方式。本库的优势是可以使用设计非常好的ASP.NETCore的MVC框架作为顶层调用API层,底层通讯采用可提升传输性能的命名管道,如此可以做到不走网络通讯从而极大减少网络端口占用问题和减少用户端网络环境带来的问题
背景
本机内多进程通讯IPC不同于跨设备系统的RPC通讯方式,大多数的IPC通讯都需要处理复杂的用户端环境问题。对于RPC通讯来说,大部分时候,服务端都在开发者完全管控的环境下运行。但IPC通讯则无论是服务端还是客户端都可能是在用户端运行的。然而用户端上,无论是系统还是其他环境都是十分复杂的,特别是在国内的,魔改的系统,凶狠的杀*软件,这些都会让IPC通讯受到非预期的打断
传统的dotnet系的IPC手段有很多个,提供给开发使用的顶层框架也有很多,如.NETRemoting和WCF等。但是在迁移到dotnetcore时,由于底层运行时机制的变更,如透明代理不再支持类对象只能支持接口的行为变更,就让.NETRemoting从机制性不受支持。为了方便将应用迁移到dotnetcore框架上,可采用dotnetcampus组织基于最友好的MIT协议开源的dotnetCampus.Ipc开源库进行本机内多进程通讯
此dotnetCampus.Ipc开源库底层可基于命名管道进行通讯,经过了约万台设备近半年的测试,发现通过此方式的通讯稳定性极高。开源仓库 //忽略代码varipcCore=Services.GetRequiredServiceIpcPipeMvcServerCore();IpcPipeMvcServerCore=ipcCore;}TaskIServer.StartAsyncTContext(IHttpApplicationTContextapplication,CancellationTokencancellationToken){//忽略代码IpcPipeMvcServerCore.Start();}privateIpcPipeMvcServerCoreIpcPipeMvcServerCore{get;}//忽略代码}
而IpcPipeMvcServerCore和IpcServer对象都是在调用builder.WebHost.UsePipeIpcServer(xxx);被注入,如以下代码
publicstaticclassWebHostBuilderExtensions{///summary///Enablestheseecref="IpcServer"/service.启用命名管道IPC服务////summary///paramname="builder"Theseecref="IWebHostBuilder"/./param///paramname="ipcPipeName"设置Ipc服务的管道名/param///returnsTheseecref="IWebHostBuilder"/./returnspublicstaticIWebHostBuilderUsePipeIpcServer(thisIWebHostBuilderbuilder,stringipcPipeName){returnbuilder.ConfigureServices(services={ //忽略代码services.AddSingletonIServer,IpcServer();services.AddSingletonIpcPipeMvcServerCore(s=newIpcPipeMvcServerCore(s,ipcPipeName));});}}
依靠ASP.NETCore的机制,将会在主机启动,调用IServer的StartAsync方法。通过IpcServer的StartAsync方法启动IpcPipeMvcServerCore的逻辑
在IpcPipeMvcServerCore里,将初始化IpcProvider服务。这里的IpcProvider服务是dotnetCampus.Ipc提供的服务对外的接口,通过IpcProvider可以和dotnetCampus.Ipc层的其他Peer进行通讯。刚好在客户端也相同的初始化IpcProvider服务,通过ipcPipeName管道名可以将客户端和服务端关联
classIpcPipeMvcServerCore{publicIpcPipeMvcServerCore(IServiceProviderserviceProvider,string?ipcServerName){ipcServerName??="IpcPipeMvcServer"+Guid.NewGuid().ToString("N");IpcServer=newIpcProvider(ipcServerName,newIpcConfiguration(){DefaultIpcRequestHandler=newDelegateIpcRequestHandler(asynccontext={//核心代码})});}publicvoidStart()=IpcServer.StartServer();publicIpcProviderIpcServer{set;get;}}
在dotnetCampus.Ipc层提供了请求响应框架,可以通过传入DefaultIpcRequestHandler对象用来接收其他端发送过来的请求,处理完成之后返回给对方。上面代码的核心就是DelegateIpcRequestHandler的处理逻辑,在context里读取客户端的请求信息,反序列化为HttpRequestMessage对象,通过内部逻辑进入到ASP.NETCore层,再通过MVC框架之后拿到请求的返回值,将返回值封装为IpcResponseMessageResult返回给客户端
IpcServer=newIpcProvider(ipcServerName,newIpcConfiguration(){DefaultIpcRequestHandler=newDelegateIpcRequestHandler(asynccontext={ //将请求反序列化为HttpRequestMessage对象 //用于传入到ASP.NETCore层System.Net.Http.HttpRequestMessage?requestMessage=HttpMessageSerializer.DeserializeToRequest(context.IpcBufferMessage.Body);//创建虚拟的请求,进入到ASP.NETCore框架里varserver=(IpcServer)serviceProvider.GetRequiredServiceIServer();varclientHandler=(ClientHandler)server.CreateHandler();varresponse=awaitclientHandler.SendInnerAsync(requestMessage,CancellationToken.None);//拿到的返回值序列化为IpcResponseMessageResult放入dotnetCampus.Ipc层用来返回客户端varresponseByteList=HttpMessageSerializer.Serialize(response);returnnewIpcResponseMessageResult(newIpcMessage($"[Response][{requestMessage.Method}]{requestMessage.RequestUri}",responseByteList));})});
创建虚拟的请求,进入ASP.NETCore框架里的逻辑是服务端最复杂的部分。在IpcServer的CreateHandler方法里面,将创建ClientHandler对象。此ClientHandler对象是用来构建虚拟的请求,相当于在当前进程内发起请求而不是通过网络层发起请求,代码如下
publicclassIpcServer:IServer{///summary///Createsacustomseecref="HttpMessageHandler"/forprocessingHTTPrequests/responseswiththetestserver.////summarypublicHttpMessageHandlerCreateHandler(){ //忽略代码returnnewClientHandler(BaseAddress,Application){AllowSynchronousIO=AllowSynchronousIO,PreserveExecutionContext=PreserveExecutionContext};}}
在也是继承HttpMessageHandler的ClientHandler里,也重写了SendInnerAsync方法,此方法将会负责创建HttpContextBuilder对象,由HttpContextBuilder执行具体的调用ASP.NETCore层的逻辑
publicasyncTaskHttpResponseMessageSendInnerAsync(HttpRequestMessagerequest,CancellationTokencancellationToken){//创建HttpContextBuilder对象varcontextBuilder=newHttpContextBuilder(_application,AllowSynchronousIO,PreserveExecutionContext);varrequestContent=request.Content;if(requestContent!=null){ //以下是对HttpContextBuilder的初始化逻辑//ReadcontentfromtherequestHttpContentintoapipeinabackgroundtask.Thiswillallowtherequest//delegatetostartbeforetherequestHttpContentis