今天要厚着脸皮给大家推荐一个自己做的通信中间件——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. windows系统下使用cd命令

    如果要切换到D:\Program Files目录下,大多数人会想当然的在命令行窗口输入 cd D:\Program Files回车. 如下所示: 发现并没有切换到D:\Program Files. 正 ...

  2. SQL Server之LEFT JOIN、RIGHT LOIN、INNER JOIN的区别

    很多人刚入门的时候分不清LEFT JOIN.RIGHT LOIN 和 INNER JOIN的区别,对它们的定义比较模糊,今天就简单的介绍一下它们的区别,对于入门的人来说,应该能够帮助你们理解. lef ...

  3. CentOS配置SSH免密登录

    假如我们有两台CentOS机器,192.168.199.101,192.168.199.102,要想在101上远程连接102可以通过ssh命令来实现 ssh 192.168.199.102 如果没有配 ...

  4. shiro真正项目中的实战应用核心代码!!!

    欢迎转载!!!请注明出处!!! 说道shiro的学习之路真是相当坎坷,网上好多人发的帖子全是简单的demo样例,核心代码根本没有,在学习过程中遇到过N多坑. 经过自己的努力,终于整出来了,等你整明白之 ...

  5. .net整理

    CLR via C# 1 关于CLI,CTS,CLS,CIL,.Net Framework,CLR,FCL图 CLI:Common Language Infrastructure,是公共语言架构: C ...

  6. Git分支-分支简介

    源地址:https://git-scm.com/book/zh/ch3-1.html 几乎所有的版本控制系统都以某种形式支持分支. 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线 ...

  7. Gym 100952J&&2015 HIAST Collegiate Programming Contest J. Polygons Intersection【计算几何求解两个凸多边形的相交面积板子题】

    J. Polygons Intersection time limit per test:2 seconds memory limit per test:64 megabytes input:stan ...

  8. Gym 100952D&&2015 HIAST Collegiate Programming Contest D. Time to go back【杨辉三角预处理,组合数,dp】

    D. Time to go back time limit per test:1 second memory limit per test:256 megabytes input:standard i ...

  9. POJ 1163 The Triangle【dp+杨辉三角加强版(递归)】

    The Triangle Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 49955   Accepted: 30177 De ...

  10. hdu_1037(水题水疯了。。。史上最水)

    #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int ma ...