今天要厚着脸皮给大家推荐一个自己做的通信中间件——ServiceAnt,目前已经在我们团队的两个产品线上投入了使用。

ServiceAnt是什么


它最初的定位是ESB(企业服务总线),但目前还没有达到这个高度,主要是还是没有提供分布式的实现,有机会会补上。

现在它只能工作于进程内,与 Mediator 的角色非常类似。

可能有同学不知道 Mediator, Automapper 总该听过吧?它们的作者是一个人。

ServiceAnt 部分的设计也参考了 Mediator,当然还有别的一些框架,比如 Abp中的 EventBus, eShopOnContainer的 integation event 以及 NServiceBus

可能有人会问,通信中间件的作用是什么?这里我们先用现在火热的微服务场景来举个例子,假设我们拥有ABCDEF六个微服务,A需要与DE服务通信来获得某些信息,而B则是与EF,C与DF,D与AB,E与BC,F与AC。

那么它们之间的通信拓扑图将会是如下的样子:

看上去相当地混乱,对吧?

在真实的互联网应用中,拆分的服务数量和关联度多数都要比上面这张图更加复杂。

如果采用RESTful API 的方式来通信,这会造成每个服务都需要管理不同的多方连接信息,给开发带来了相当大的复杂度。

为了解决这样的问题,我们在其间引入了一个中介者的角色来负责分发请求,传递结果,这就是通信中间件。

引入之后的拓扑图如下:

无论你需要与多少方通信,最终你只需要告诉中间件:目标地址、通信内容以及通信模式(Pub Or Request,如果支持的话)。

而不需要关心是谁,以什么方式来处理通信内容,中间件会帮你处理好这些事情。

这样一来我们获得以下的优点:

  1. 解除了服务间的直接耦合,提高了扩展性
  2. 降低了开发的复杂度,避免管理通信相关的内容,如通信协议,安全性,以及监控等。

有同学忍不住要说了:你说的都是微服务啊,这些我都懂,比如SpringCloud就是这样的,你刚刚说过 ServiceAnt 还没有分布式的实现,那介绍微服务有锤子用啊?

别急,听我慢慢解释。我们知道在大型企业应用中都会把程序拆分为多个模块对吧?

把以上两图的ABCDEF视作模块,模块间的直接通信看作引用,就可以将把进程内的单体应用看作特别的“分布式”应用。

事实上,大多数设计良好的单体应用都具备清晰的业务模块边界,而如何让这些模块以更加灵活的方式协作完成业务逻辑是设计中需要仔细考量的一个点,通信中间件就是一种不错的解决方案。

现在大家应该对 ServiceAnt 是什么有点认识了吧。

注:上面关于模块如何划分,我们团队是采用的DDD,有兴趣的同学可以移步我的另一篇博文,里面分享了我们实践DDD的一些经验与基础架构。传送门点我

ServiceAnt 的现状


如上所述,目前 ServiceAnt 已经投入到我们团队所负责的两个线下产品线中使用,发现的坑也都填完了。

为了响应c#的开源氛围,我重构了一下原有的代码,并且补充了多版本的支持,然后上传到了Gihub上。

目前版本号:1.0

支持 .net 版本:.net 4.5、net standard2.0

Github地址:点我进入

Github上有非常详细的文档,这里我只简单介绍,ServiceAnt 支持的两种工作方式:

  1.Pub/Sub(发布/订阅)模式,使用这种模式你可以把它视作事件总线。

  2.Req/Resp(请求/响应)模式,这种模式是我们工作中使用最频繁的模式了吧,他跟普通的Http请求类似,发起一个请求然后可以由一个或多个处理函数(这些函数可能位于同一个模块也可能位于多个模块)来处理这个请求并返回结果。

ServiceAnt正在完善例子和英文文档,现在是起步阶段,而且线下应用的需求也较为简单,所以有很多功能都没有实现(比如重试机制,流量监控以及可视化仪表等等)。

如果你有这样的需求,欢迎在Issue上提出,我会在工作之余第一时间回复你。

为什么会有ServiceAnt


起因是这样的,我们团队在开发一个企业应用时采用了DDD,然后将我们的业务逻辑拆分为了复数个限界上下文,每个上下文低耦合高内聚的.

但无论再怎么低耦合,总会有一些高层次的交互,这些被称为“边界点”,通常在分布式部署中,我们会选择Webapi 或者 WebServie 等远程通信手段来进行交互

遗憾的是,我们的应用是线下的,并发量也并不需要到集群这样重量级的解决方案,所以我们使用Abp的插件加载机制为基础设施, 将每个上下文都实现成了一个个独立的项目模块.

项目初期我们使用 Abp 提供的事件总线作为模块之间交互的方式, 但它有一个很不好的地方是, 它的事件引用必须是显式的原对象引用。
这也就意味着,你为了在A模块中使用B模块发布的事件,你必须让两个上下文都引用这个事件对象,这显然加深了模块间的耦合。

在参考了Abp, Medirator, NServerBus以及微软的示例项目 EShopContainer 后,我决定自己实现一个服务总线, 它要具有以下特点:

  • 支持以委托的方式注册处理函数
  • 支持 Req/Resp 模式
  • 事件的接收与发布对象是非引用的(指你可以在不同模块间建立各自的事件类,只需要保证它们名称与结构相同即可)

所以ServiceAnt出现了, ServiceAnt 的初期目标是一个进程内的消息中介者, 后期有时间会逐步完善它。

Req/Resp 模式在上面已经介绍过了,可能很多同学比较有疑问的地方是:以委托的方式注册处理函数这一点,请看下以下的代码。

        static void Main(string[] args)
{
var serviceBus = InProcessServiceBus.Default;
serviceBus.AddRequestHandler<TestRequest>((requestParam, handlerContext) =>
{
Console.WriteLine($"Request Handler get value: {requestParam.RequestParameter}");
handlerContext.Response = "First handler has handled. \r\n";
return Task.FromResult();
}); // it used when you do not want to create trigger class, you can handle it with a dynamic parameter
serviceBus.AddDynamicRequestHandler("TestRequest", (eventParam, handlerContext) =>
{
Console.WriteLine($"DynamicRequest Handler get value: {eventParam.RequestParameter}");
handlerContext.Response += "Second handler has handled. \r\n"; // set IsEnd flag to true then directly return response and ignore the rest handlers
handlerContext.IsEnd = true;
return Task.FromResult();
}); // this handler will not be excuted
serviceBus.AddRequestHandler<TestRequest>((requestParam, handlerContext) =>
{
Console.WriteLine($"Third Request Handler get value: {requestParam.RequestParameter}");
handlerContext.Response += "Third handler has handled. \r\n";
return Task.FromResult();
}); var publishEvent = new TestRequest() { RequestParameter = "HelloWorld" };
Console.WriteLine($"Send request parameter value: { publishEvent.RequestParameter }");
var response = serviceBus.Send<string>(publishEvent);
Console.WriteLine("The response is : \r\n" + response); Console.ReadLine();
} class TestRequest : IRequestTrigger
{
public string RequestParameter { get; set; }
}

这段代码是从Github上的示例代码上复制过来的,可以看到它的所有处理函数都是以匿名委托的方式注册的,并且演示了Req/Resp的管道工作方式。

Github上的介绍中也简单写了一些与其他类似组件的不同之处,有兴趣的同学可以自行查看。

写在最后的话


ServiceAnt 离最初所定位的ESB还有很长的一段路要走,但因为目前公司的主产品是线下的自助系统及其支撑系统,所以一直没有场景需求去开发支持分布式甚至是支持异构系统。

如果有哪些同学项目正好有这样的场景又想使用 ServiceAnt,我很乐意与你一起分析需求然后完善 ServiceAnt 的功能,当然你也可以直接开发完之后发起PR给我。

目前互联网的天下都被 Java, NodeJs, PHP等占了大半江山,导致新出的 .Net Core 生存空间和生态都发展迟缓,虽然我不介意使用其他语言,但我更看好 .net core 和 c# 这个组合一些天生优势(当然也有一点自己使用c#较多的情怀在里面,呵呵),特别是它的设计和性能表现都可以称得上后起之秀了,特别是2.0之后。

关于 .net core 我这里就不多言了,已经偏题了,随手转发一下最近在博客看到的关于.net core 特性的文章吧。

英文版原版点我

热心园友翻译版点我

只希望通过为c#的开源生态多贡献一些东西,尽自己绵薄之力去改善它的生态。

这样做不仅是为了大家其实也是为了自己,现在平均待遇偏低不说,更可气的是整个大环境都让人有些难受。

比如现在一个完全没干过编程,毕业五年的销售,经过某些培训机构培训Java半年,简历包装一下,背背面试题,混进一个互联网公司,他的待遇就要比很多.net的同等经验工作者高。

为什么?就是因为业界很多人都觉得 .net 还是那个无法做互联网,封闭的老式技术,所以大环境下一说起线上应用就是 SprintBoot, SSM, SSH,导致目前来看待遇更好,挑战更多的互联网公司都下意识选择了Java。

我曾与公司的Java组同事做过一些集成应用,自己也私下鼓捣过 SprintBoot,也了解过Java多数主流框架。

同时自己现在是web组的牵头人,更多的时候是在做前端的技术工作,对比使用过的这些技术,我觉得现在的 c# 在线上应用方向的能力被很多人都看低了。

c#语言的优势,我只说一点,ES2015添加的箭头函数早在c#3.0就已经有了,它就是lambda表达式,而java是在 java8之后才有的,c#语言由于诞生较晚所以吸取很多前车之鉴,加上设计者也很厉害,所以c#相较其他语言会更加优雅。Nuget也一点不比Maven,Npm差。IDE我就不多说了,用过Eclipse和Vs都懂,最新的Idea没用过,但这里不多做评论,只是想说其他语言有的,c# 都不会差。

加之现在微软大力推动开源与跨平台,我们有理由相信c#是可以在线上应用争得一席之地的。

所以如果你想作为一个c#的开发者能拥有更好的待遇与更多的挑战,除了提升自己能力之外,多多贡献自己力量去推广它, 完善它的生态,让整个业界重新认识它,也不失为良策,对吧。

使用 ServiceAnt 更好地解耦你的程序的更多相关文章

  1. 一个更好用的.NET Core程序瘦身器,减小程序尺寸到1/3

    一.为什么要开发.NET Core程序瘦身器? .NET Core具有[剪裁未使用的代码]的功能,但是由于它是使用静态分析来实现的,因此它的剪裁效果并不是最优的.它有如下两个缺点: 不支持Window ...

  2. 【Yom框架】漫谈个人框架的设计之二:新的IRepository接口+搜索和排序解耦(+基于Castle实现)

    经过了上篇IRepository和IRepository<T>的讨论[文章地址为:http://www.cnblogs.com/yomho/p/3296759.html] 我选择了IRep ...

  3. 新的IRepository接口+搜索和排序解耦(+基于Castle实现)

    新的IRepository接口+搜索和排序解耦(+基于Castle实现) 经过了上篇IRepository和IRepository<T>的讨论[文章地址为:http://www.cnblo ...

  4. 让Docker功能更强大的10个开源工具

    让Docker功能更强大的10个开源工具 更好的管理.Web前端程序.更深入地了解容器应用程序,Docker生态系统正在迅速发展,这还得归功于其充满活力的开源社区. 软件项目的成功常常根据其催生的生态 ...

  5. mysql的SQL_CALC_FOUND_ROWS 使用 类似count(*) 使用性能更高

    mysql的SQL_CALC_FOUND_ROWS 使用 类似count(*) 使用性能更高 在很多分页的程序中都这样写: SELECT COUNT(*) from `table` WHERE ... ...

  6. 黄聪:mysql的SQL_CALC_FOUND_ROWS 使用 类似count(*) 使用性能更高

    mysql的SQL_CALC_FOUND_ROWS 使用 类似count(*) 使用性能更高 在很多分页的程序中都这样写: SELECT COUNT(*) from `table` WHERE ... ...

  7. 让 Python 代码更易维护的七种武器——代码风格(pylint、Flake8、Isort、Autopep8、Yapf、Black)测试覆盖率(Coverage)CI(JK)

    让 Python 代码更易维护的七种武器 2018/09/29 · 基础知识 · 武器 原文出处: Jeff Triplett   译文出处:linux中国-Hank Chow    检查你的代码的质 ...

  8. MindSpore模型精度调优实战:如何更快定位精度问题

    摘要:为大家梳理了针对常见精度问题的调试调优指南,将以"MindSpore模型精度调优实战"系列文章的形式分享出来,帮助大家轻松定位精度问题,快速优化模型精度. 本文分享自华为云社 ...

  9. Python窗口学习之使窗口变得更高清

    初学tkinter发现窗口并不像成熟软件那么清楚 在实例化window后加这一行代码 #使窗口更加高清 # 告诉操作系统使用程序自身的dpi适配 ctypes.windll.shcore.SetPro ...

随机推荐

  1. J2EE 项目 org.apache.jasper.JasperException: 解决方法

    项目从一个电脑转移到另一台电脑总是有各种意外qaq~ 刚放假把从实验室的项目拷回自己的电脑回家继续coding,结果出了这个错误.... 各个地方都调试原来是Tomcat版本问题!!!我电脑上的是6. ...

  2. STM32f103x IAP远程升级小结

    最近在面试的时候遇到一个关于IAP远程程序升级的问题,由于之前所做的项目没有涉及到远程升级需求,当时一脸懵呆,不过回答的还是不错的,今天针对STM32F103系列调试了IAP的程序,这里做一下小结,如 ...

  3. vscode运行前端代码

    vscode 可安装静态web服务器  Live Server用于运行前端代码,方便调试.最新的 Live Server已支持php动态页面. 安装方法是,点击左侧第五个图标,打开"扩展&q ...

  4. tensorflow mnist read_data_sets fails

    下载处理mnist数据时出现如下错误 VisibleDeprecationWarning: converting an array with ndim > 0 to an index will ...

  5. Ubuntu 搭建简单的git server

    Git 可以使用四种主要的协议来传输资料:本地协议(Local),HTTP 协议,SSH(Secure Shell)协议及 Git 协议. 在此,我们将会讨论那些协议及哪些情形应该使用(或避免使用)他 ...

  6. asp.net mvc 5 蛋疼的问题

    看图,debugger显示匹配路径没有错.    html开源码看 显示没有实现IController. 然而我的确实现了.  关闭vs 重启

  7. PE文件详解(五)

    在前面几节中经常提到相对虚拟地址RVA,在这篇博客中主要说明这个概念.本来是想接着转载小甲鱼的,但是我自己根据这篇文章和他的视频来学习的时候,发现在RVA与文件的相对偏移地址进行转化的时候,那块我看不 ...

  8. 算法8 五大查找之:二叉排序树(BSTree)

    上一篇总结了索引查找,这一篇要总结的是二叉排序树,又称为二叉搜索树(BSTree) . 构造一棵二叉排序树的目的,其实并不是为了排序,而是为了提高查找和插入删除的效率. 什么是二叉排序树呢?二叉排序树 ...

  9. ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器)

    前言 本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期. 这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度. 目录 ...

  10. zoj 3228:Searching the String

    Description Little jay really hates to deal with string. But moondy likes it very much, and she's so ...