今天要厚着脸皮给大家推荐一个自己做的通信中间件——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. TPYBoard V102:能跑Python的stm32开发板

    近来micropython语言做硬件编程很火,随之而来的就开始带动着支持micropython语言编程的开发板也开始火的发烫,今天小编就来和大家介绍一款很经典的micropython开发板-TPYBo ...

  2. java 数据格式验证类

    作为一个前端,懂一点java,php之类的,甚好. 我所在的项目前端采用的就是java的spring mvc框架,所以我们也写java,掐指一算,也快一年了. 前端而言,验证是一个坎,绕不过去的,前面 ...

  3. Pandas库的使用--Series

    一.概念 Series相当于一维数组. 1.调用Series的原生方法创建 import pandas as pd s1 = pd.Series(data=[1,2,4,6,7],index=['a' ...

  4. tomcat配置https协议

    tomcat配置https协议 1.找到本地jdk底下的bin目录,bin目录底下存在文件keytool.exe(在bin目录下空白处,按住shift右击,打开命令窗口,如下图) 2.在cmd的命令窗 ...

  5. Shell和命令基础

    什么是Shell Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口(命令解析器),Shell接收用户输入的命令并把它送入到内核去执行,结构如下图 Shell的功能 Shell最重要的 ...

  6. 利用jquery encoder解决XSS脚本注入所产生的问题

    问题现象:前端接收到后台一个数据(其中包含html)标签,自动转译成html页面元素,且自动执行了脚本,造成了前端页面的阻塞 接受的后台数据为大量重复的如下代码 ");</script ...

  7. addEventListener attachEvent和解决IE 6 7 8 this指向错误

    [JS] addEventListener attachEvent和解决IE 6 7 8 this指向错误   电梯直达 1#    php 发表于 2014/4/13 01:17 | 只看该作者  ...

  8. Spring MVC 详解之废话少说

    <陈翔六点半之废话少说>.... Spring WEB MVC 的请求流程: Spring WEB MVC架构: 开始创建.配置gradle项目 1.在gralde项目中,选择SDK 和框 ...

  9. deeplearning.ai 卷积神经网络 Week 2 深度卷积网络:实例研究 听课笔记

    1. Case study:学习经典网络的原因是它们可以被迁移到其他任务中. 1.1)几种经典的网络: a)LeNet-5(LeCun et al., 1998. Gradient-based lea ...

  10. Codeforces Beta Round #1 A,B,C

    A. Theatre Square time limit per test:1 second memory limit per test:256 megabytes input:standard in ...