背景

进程间通讯属于老生常谈的话题,可能已经有很多的通信示例代码,但在实际使用中需要做的东西还比较多。例如协议定制、消息收发、进程管理等都需要实现,进阶需求可能还需要实现回调函数、取消等。

个人在工作中恰好遇到了相关需求,然后在网上搜了一下,只找到两个.net下多进程相关的库(有可能是搜索方式不对)

https://github.com/tmds/Tmds.ExecFunction

https://github.com/CyAScott/AppDomainAlternative/

但并不满足需求,不能进程复用,或者不能方便的进行回调、取消。于是乎进行手写相关代码。

在手写了两三个相似的项目后,觉悟到重复的劳动是可以进一步省略的,于是进行了封装,产生了该项目 - Juxtapose 基于 SourceGenerator 的硬编码 .Net进程运行库。只需要几行额外的代码就能使现有代码(*)支持多进程运行,进程复用,支持LinuxWindows,支持回调委托CancellationToken,支持调试子进程(*)。无显式的运行时反射调用和动态构造类。

好处很多,那么代价是什么呢?

  • 简单的一次方法调用将会经历数据传输以及至少四次序列化或反序列化;对比进程内方法调用来说是巨大的损耗。
  • 方法参数只能是简单的数据类型(委托和CancellationToken除外,它们进行了特化处理),不能传递有行为的对象。
  • 跨进程的异常信息将被包裹,需要自行解析;
  • 调用堆栈将会变复杂;

综上,使用时需要能够接受和处理上述情况。

Juxtapose早期版本已经在生产环境进行了长时间的使用验证,但新的Release版本暂时还没有;

相关技术

SourceGenerator

源生成器 - SourceGenerator 已经推出有一段时间了,参见官方公告 Introducing C# Source Generators

官方简介:

源生成器是一项 C# 编译器功能,使 C# 开发人员能够在编译用户代码时进行检查,并动态生成新的 C# 源文件,以添加到用户的编译中。 通过这种方式,你的代码可以在编译过程中运行并检查你的程序以生成与其余代码一起编译的其他源文件。

使用SourceGenerator可以在编译时生成代码,避免运行时反射,提高运行效率,使FullAOT成为可能,代码运行逻辑也更加直观。不在此处进行过多描述,已经有不少的文章对其进行了介绍,可以随便搜索到,官方也有完善的示例代码。

相关资源:

进程间通讯

通讯方式很多,可以参考文章 c#多进程通讯,今天,它来了

Juxtapose默认实现了命名管道通信(有需求可以自行实现其他方式进行替换),默认使用命名管道的主要原因是它由.net原生集成、支持双工通信、跨平台。

使用场景

以下为实际使用场景,更多场景请自行结合实际需求。

  • 某组件不允许在一个进程内创建多个实例,无法并发;
  • 某组件使用了过多内存、内存泄漏、内存释放不及时,导致进程被杀、Pod重启、服务中断;
  • 某组件执行某耗时操作时,不支持取消操作,且占用大量CPU/内存资源,影响程序运行;
  • 某组件直接操作内存,会偶发性出现非法访问,导致进程退出;

Nuget包介绍

1. Intro

基于 SourceGenerator 的硬编码 .Net进程运行库。

包列表

名称 介绍
Juxtapose 运行时库,封装通用的功能
Juxtapose.SourceGenerator 源代码生成器,用于代码生成
Juxtapose.VsDebugger VisualStudio调试附加包,用于子进程调试

2. Features

  • 可以为类型接口静态类生成代理,无需手动编写RPC相关代码,即可多进程运行;
  • 编译时生成所有代码,运行时无显式的反射调用和动态构造;
  • 支持委托CancellationToken类型的方法参数(其余类型未特殊处理,将会进行序列化,目前回调委托不支持嵌套和CancellationToken);
  • 支持LinuxWindows(其它未测试);
  • 支持调试子进程(Windows&&VisualStudio Only);

注意事项

  • 目前参数不支持定义为父类型,实际传递子类型,序列化时将会按照定义的类型进行序列化和反序列化,会导致具体类型丢失;
  • 目前所有的参数都不应该在方法完成后进行保留,CancellationToken委托等在方法完成后会被释放;

3. Requirement

  • .Net5.0+(其它版本没有尝试过)

4. 使用方法

4.1 引用包

<ItemGroup>
<PackageReference Include="Juxtapose" Version="1.0.0" />
<PackageReference Include="Juxtapose.SourceGenerator" Version="1.0.0" />
</ItemGroup>

4.2 建立上下文

4.2.1 创建上下文类型,并使用 [Illusion] 特性指定要生成的类型

[Illusion(typeof(Greeter), typeof(IGreeter), "Juxtapose.Test.GreeterAsIGreeterIllusion")]
public partial class GreeterJuxtaposeContext : JuxtaposeContext
{
}

示例代码将为Greeter生成IGreeter接口的代理类型Juxtapose.Test.GreeterAsIGreeterIllusion

Note!!!

  • 必须继承JuxtaposeContext
  • 必须标记partial关键字;

4.2.2 [Illusion] 的多种用法

  • 直接为类型生成代理,如下示例生成 Juxtapose.Test.GreeterIllusion 类型,且不继承接口(静态类型相同用法)
[Illusion(typeof(Greeter))]
  • 为类型生成代理,并继承指定接口,如下示例生成 Juxtapose.Test.GreeterAsIGreeterIllusion 类型且继承IGreeter接口
[Illusion(typeof(Greeter), typeof(IGreeter))]
  • 生成类型,并指定类型名称,如下示例生成 Juxtapose.Test.HelloGreeter 类型
[Illusion(typeof(Greeter), generatedTypeName: "Juxtapose.Test.HelloGreeter")]
  • 生成从IoC容器获取的接口代理类型,如下示例生成 Juxtapose.Test.IGreeterIllusionFromIoCContainer 类型(此时Context类需要实现IIoCContainerProvider接口,并提供有效的IServiceProvider
[Illusion(typeof(IGreeterFromServiceProvider), generatedTypeName: "Juxtapose.Test.IGreeterIllusionFromIoCContainer", fromIoCContainer: true)]

4.3 添加入口点

Main方法开始处添加入口点代码,并使用指定上下文

await JuxtaposeEntryPoint.TryAsEndpointAsync(args, GreeterJuxtaposeContext.SharedInstance);

到此已完成开发,创建类型Juxtapose.Test.GreeterAsIGreeterIllusion的对象,并调用其方法,其实际逻辑将在子进程中运行;

5. 调试子进程(Windows&&VisualStudio Only)

5.1 引用调试包

在Host项目文件中添加包引用

<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="Juxtapose.VsDebugger" Version="1.0.0" />
</ItemGroup>

建议和示例代码一样,添加条件引用,只在Debug环境下引用调试包

5.2 添加入口点

Main方法开始处添加调试入口点代码

JuxtaposeDebuggerAttacher.TryAttachToParent(args);

在代码中打上断点,运行时将会正确命中断点(只在 VisualStudio2022 17.0.5 && Win11 21TH2 中进行了测试,理论上是通用)

6. 工作逻辑

SourceGenerator在编译时生成代理类型,封装通信消息。在创建代理类型对象时,会自动创建子进程,并在子进程中创建目标类型的对象,使用命名管道进行进程间通信,使用System.Text.Json进行消息的序列化与反序列化。更多技术细节可以参考源代码。

链接

源代码: https://github.com/stratosblue/juxtapose

Nuget: https://www.nuget.org/packages/Juxtapose

SourceGenerator的应用: .Net多进程开发库 - Juxtapose的更多相关文章

  1. [资料搜集狂]D3.js数据可视化开发库

    偶然看到一个强大的D3.js,存档之. D3.js 是近年来十分流行的一个数据可视化开发库. 采用BSD协议 源码:https://github.com/mbostock/d3 官网:http://d ...

  2. 计算机图形学——OpenGL开发库开发库

    vc++6.0 有 OpenGL 的东西.有头文件 GL.H, GLAUX.H, GLU.H 但没有 GLUT 软件包/工具包 如果想使用glut.h的话必须自己添加. 首先下载 OpenGL开发库, ...

  3. ubuntu下安装 openssl 开发库

    ubuntu下安装 openssl 开发库 检查是否已安装openssl: sudo apt-get install openssl 如果已安装执行以下操作:sudo apt-get install ...

  4. JavaScript模块化开发库之SeaJS

    SeaJS是一个很好的前端模块化开发库,源码不到1500行,压缩后才4k,质量极高.

  5. 跨平台的C++应用和UI开发库 QT

    跨平台的C++应用和UI开发库 QT 运行环境: 授权方式:BSD 软件大小:M 下载量:3912 更新日期:2012-04-05 来源地址: 联系作者:Linux     Qt是诺基亚开发的一个跨平 ...

  6. 分享一个嵌入式httpdserver开发库 - boahttpd library

    http://sourceforge.net/projects/boahttpd/ 一个C接口的开发库,适用于 windows/linux/或其它嵌入式平台,支持CGI扩展,支持多线程.採用面向对象开 ...

  7. 基于.NET Core的Hypertext Application Language(HAL)开发库

    HAL,全称为Hypertext Application Language,它是一种简单的数据格式,它能以一种简单.统一的形式,在API中引入超链接特性,使得API的可发现性(discoverable ...

  8. 多媒体开发库 之 SDL 详解

    SDL 简介 SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成.SDL提供了数种控制图像.声音.输出入的函数,让开发者只要用相同或是相似的 ...

  9. 接口测试入门(5)----新框架重构,使用轻量级的HTTP开发库 Unirest

    之前的第一套框架在使用过程中发现存在以下问题: 一.  框架1缺点: 1.登陆的账号每个人写的不一样,一旦用户在测试环境被修改,则导致用例失败 2.每次读取访问网址,需要在同一个java文件下切换测试 ...

随机推荐

  1. Excel转Json升级版-Python

    Excel转Json升级版 将excel文件夹中所有xslx文件全部转换json文件,存放在data文件夹中: excel中的格式,从序号为2的行开始,2行为key:1行可以自由写注释: 使用时用双击 ...

  2. ByteBuddy代码生成技术

    简介 如官网所说Byte Buddy 是一个代码生成和操作库,用于在Java应用程序运行时创建和修改Java类,而无需编译器的帮助.除了Java类库附带的代码生成实用程序外,Byte Buddy还允许 ...

  3. Android 运行C可执行程序

    p.p1 { margin: 0; font: 12px "Helvetica Neue"; color: rgba(69, 69, 69, 1) } p.p2 { margin: ...

  4. 【LeetCode】235. Lowest Common Ancestor of a Binary Search Tree 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 [LeetCode] https://leet ...

  5. 【LeetCode】513. Find Bottom Left Tree Value 解题报告(Python & C++ & Java)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS DFS Date 题目地址:https:// ...

  6. 登陆认证框架:SpringSecurity

    最近想给自己的小系统搭建一个登录认证服务,最初是想着一套oauth2权鉴就可以,但是发现这个oauth2只是权鉴,具体的登录认证需要由 SpringSecurity来进行实现. 也就是说SpringS ...

  7. Java中的关键字有哪些?「Java中53个关键字的意义及使用方法」

    Java中的关键字有哪些? 1)48个关键字:abstract.assert.boolean.break.byte.case.catch.char.class.continue.default.do. ...

  8. 揭开“QUIC”的神秘面纱

    作者:赵咏 QUIC的发音类似于Quick,实际上也确实很快.它可以很好地解决应用在传输层和应用层面临的各种需求,包括处理更多的连接.安全性以及低延迟. 目前在互联网领域,QUIC可以说刮起了新一代互 ...

  9. RabbitMQ使用 prefetch_count优化队列的消费,使用死信队列和延迟队列实现消息的定时重试,golang版本

    RabbitMQ 的优化 channel prefetch Count 死信队列 什么是死信队列 使用场景 代码实现 延迟队列 什么是延迟队列 使用场景 实现延迟队列的方式 Queue TTL Mes ...

  10. wordpress搭建网站更改域名后打开网页排版显示错乱解决办法

    发生的原因: 我本来已经搭建好了网站,也测试了没问题.后来更改了网站的域名,出现了这种情况. 解决办法: 需要修改数据库的options表里面的 siteurl 和 home 这两个表的内容为最新的域 ...