分布式部署_业务模块化打造单体和分布式部署同步支持方案

af068e001ea8076e94f1c1eab4f8eaed.png

我在2019年中国.NET开发者峰会上为大家分享了我们的微服务电商安全工程实践,那次会议分享的高清录播已经上传到我的腾讯课堂,大家可以通过底部的小程序打开直接观看(复习)。

在大会上跟大家提到,我们当时只有4个人的创业团队。追求的是一个既可以单体部署,又可以进行分布式部署的架构方式。我们需要同时满足云上SaaS部署(流量偏大)和私有部署(流量小,看重服务器成本)。当然这种架构方式我们也是经过好几次的框架迭代才实现的,框架暂时没有开源的打算,但是现在利用ABP VNext的几个核心组件已经完全可以方便的实现业务拼接组装,同时实现单体和分布式部署。今天我将这种架构风格推荐给所有中小企业。

微服务架构再理解

组织结构的重要性 

人们在谈到微服务的时候必须会提到康威定律:“Communication dictates design(组织沟通方式决定系统设计)”如果大家想更详细的了解一下这个定律可以自己百度一下。但也许因为它是定律,所以大家总把它说的很玄乎,导致我们一下子很难真正理解到它的精髓。当然这很大的原因其实在于我们从技术的角度出发是不能理解组织结构对企业效率的重要性

微软的现任CEO 萨蒂亚·纳德拉,执掌微软的第一件事情就是对组织结构的调整。在下面的图中你可以看到以前大家是如何看待微软的组织结构的, 腾讯这两年动不动就做事业部的调整也是出于这个考量 

任何的组织架构之所有存在必然有其特殊性, 苹果的绝对中心化那个时候能成功是因为中心是乔布斯,现在中心灵魂人物没有了,这种组织结构还适用吗?

9ffd698e7f8f1de32eddd8d4ca85752b.png

既然在微服务架构中"康威定律"被如此广泛的引用,我就用大白话给大家解释一下:“如果你是一个团队在一起维护好几十个服务,那你们就没有必要用微服务架构。”

一个服务一个团队

请大家记住上面的这个铁律,如果是整个团队维护很多微服务,哪怕是要用这些微服务拼成多个产品,那也不是微服务的最佳实践。微服务强调:一个服务一个完整团队(这个团队中包括产品开发和测试) 。

0274036e29f564a21f3783d28216e659.png

微服务按DDD中的界限上下文来进行拆分之后, 每一个服务都可以为这个领域业务的客户提供最小化服务,直到业务做到足够复杂,某些业务长大成为另一个独立的上下文。这也完全符合敏捷和精益创业的思路。

充分授权小团队

一个服务一个团队还不能完全发挥这种架构的威力,这个团队需要充分的授权。任正非说:“让听得见炮火的人指挥战斗”就是这个原因。大部分情况下直接和客户打交道的前线最了解实际情况,知道客户要什么以及如何去改进产品。微服务架构将组织结构拆分之后,每个团队都可以集中力量解决自己团队所在领域内客户最关心的问题,快速做也改进,快速发布将改进送到客户面前拿到正确的反馈。所以“每个服务独立部署,独立交付” 如此重要。 

中小企业不建议采用微服务架构

资源不够,最严重紧缺的就是产品经理。没有人去正确、快速地从用户那里拿到反馈来快速改进产品。最坏的情况可能用更快的速度堆了更多不需要也不那么正确的“功能” 。 

资源不够,即使我们使用k8s降低和解除了很多运维的功能,依旧还有配置中心、分布式追踪、和集中日志的问题解决的不是很好。这是一个很“重”的话题。 

资源不够,这是一个永恒的话题, 孙子兵法以及很多战略思想中都提到要集中兵力,中小企业的资源只够集中优势资源优先解决关键问题,企图想要快速把所有问题都解决到最后基本都是失败。 

利用模块化打造单体与分布式都适用的架构

中小企业愿意去深度微服务架构的两个初衷:

  • 希望最大化避免重复开发,降低开发成本 

  • 防止将来业务做大要整体重构

但是实施微服务框架会给企业带来比较高的运维成本,即使在使用K8S的情况下依然会比单体的运维成本要高出很多。并且如果是属于客户订制化项目需要部署到客户现场,维护费用会更高。

业务模块化可以让团队把特定的业务封装在一个模块内,由多个业务模块组装完成一个系统 。比如我们常见的租户、用户、商品、论坛、博客、订单、客户等等。

1671f0a07fd8157acb013ccddeb1f25e.png

业务模块化如果部署成分布式那就是微服务架构,需要面对同样的挑战。如果部署成单体那将可以收获以下好处

  • 服务器成本更低 

  • 不需要考虑服务治理(比如:服务发现与注册、分布式追踪等)

  • 不需要考虑分布式配置中心

当然,由于我们同时需要考虑兼容分布式部署。所以以下问题还是一样的

  • 由于数据库按模块独立,数据一致性依旧是一个挑战(实在不得已集中提供一些面向报表库的查询服务也是可以接受的) 

  • 不允许跨模块join数据查询,所以复杂数据报表展示也同样需要找到相应的解决办法

模块拆分原则

  • 微服务拆分的大部份原则依旧适用

  • 一个业务模块对应一个数据库,不能直接查另一个业务模块的数据库

  • 模块之间的调用通过抽象契约接口来完成 

  • 模块之间互相依赖只能依赖于抽象契约

模块项目模板 

abp默认的项目模板中将DDD也引入进来,但是由于应用DDD对大多数开发者来说依旧是一个问题。所以我推荐的项目模板中没有引入DDD领域实体和领域服务的概念。也比较简单,只包括两层:

  • ModuleName.Contracts 契约层

  • Module 实现层 

契约层

契约层中主要包括ApplicationService接口和DTO。

实现层

实现层主要是ApplicationServer的实现以及数据库的Entity。如果你对DDD有一定的理解的话可以把这里理解为失血模型的领域分层 。在ApplicationService中通过仓储IRepository来完成领域实体Entity的持久化操作。需要注意的是这里不是简单的BLL和DAL三层架构的思路,而是DDD领域分层中的六边形架构。

39a556d55ec93e71b29e4fd1f65152fa.png

你可以在我的github上找到demo源码:https://github.com/jessetalk/Galaxy (请一定要切换到abp分支,master分支是grpc的demo) 

模块间调用/通信 

遵循“依赖于接口而不依赖于实现”原则。业务模块化之后,各个模块之间的通信通过契约接口来完成。

85c0c0358bceb609e234eb8ab0fcf9c2.png

在我们的场景中,OrderService在创建订单时需要使用到IProductService的查询接口,那么Galaxy.Order项目需要添加Galaxy.Product.Contacts的引用。

private IProductService productService { get; set; }public OrderService(IProductService productService){    this.productService = productService;}public async TaskGetAsync(string id){    var items = await this.productService.GetListAsync(new ProductQueryDto());    var task = await Task.Run(() =>                        {    Thread.Sleep(100);       return new OrderDto() { Id = id, OrderNo = "1234", ProductItems = items };    });    return task;}

单体部署 

153847f9b61afc779328ed0befe28424.png

单独创建一个Api项目,通过nuget引用所有业务模块包。通过对外暴露模块内的ApplicationService来实现后端的业务处理。

各个模块之间由于注入的都是本地类,所以模块之间的调用也会是本地方法调用。

342eda3650f2494d7329d11681b1259a.png

分布式部署

4087151caa770c28c159f14f291dd015.png

在分布式部署的场景下,需要为每一个业务模块建立一个对应的api项目,并将对应的业务模块实现包引入到项目。我们在上面的模块间通信上讲到了具体模块之间的调用是通过抽象接口来完成的,所以在分布式部署的情况下需要注入一个远程代理来替代原来的本地调用。 

42b7fa8b2b2fd7eb5cd097f5cdb01496.png

比如我们可以实现一个ProductServiceProxy,并将它注入到OrderService中。但如果是这样,我们需要为每一个业务模块都实现一个Proxy模块。好消息是ABP VNext为我们实现了动态客户客户端代理。

关键点

ABP 动态客户端代理

ABP提供的动态客户端代理功能,可以让我们只需要添加一行代码即拥有通过 http实现远程模块调用的功能 。 

[DependsOn(typeof(AbpAspNetCoreMvcModule))][DependsOn(typeof(AbpAutofacModule), typeof(GalaxyOrderModule))]public class GalaxyOrderWebModule : AbpModule{      public override void ConfigureServices(ServiceConfigurationContext context)    {      //创建动态客户端代理      context.Services.AddHttpClientProxies(typeof(GalaxyProductContractModule).Assembly, remoteServiceConfigurationName: "ProductStore");    }}

ABP框架按约定式的方式默认注册服务实例,所以如果项目添加了对Galaxy.Product模块的依赖则ProductService会自动成为IProductService的实现(即本地方法调用)。  如果在模块配置手动添加了HttpClientProxies则会自动为IProductService添加一个动态的实现(采用http的方式调用远程服务)。

关于如何配置远程服务的地址可以参考abp官方文档(abp.io)。 

ABP动态Controller 

大量使用业务模块化的时候,单体的情况下所有的Controller都在单体的那个api项目中,如果我们要转成微服务的方式部署则需要把原来在单体中的Controller都转移到对应的微服务api项目中。如果要同时在这两种架构下切换则每添加一个Action都需要在两个地方都做对应的调整 。

ABP提供的动态Controller功能,支持通过ApplicationService直接暴露成API Controller。这样我们就可以只写业务的契约接口和本地的实现, 在对应的API项目中只需要引用对应业务模块的nuget包即可。

我们只需要在order api项目的web module中将对应业务模块添加到 ConventionalControllers即可。

[DependsOn(typeof(AbpAspNetCoreMvcModule))][DependsOn(typeof(AbpAutofacModule), typeof(GalaxyOrderModule))]public class GalaxyOrderWebModule : AbpModule{    public override void ConfigureServices(ServiceConfigurationContext context)    {        Configure(options =>                                           {          options.ConventionalControllers.Create(typeof(GalaxyOrderModule).Assembly);        });    }}

ABP会将ApplicationService中的所有公有方法按照约定自动生成Action,具体的生成规则可以参考abp相关的文档。当然你也可以在方法上加Route或者HttpVerb的标签来定制路由。

9252293036c393cbaaeff8e546144e5a.png

以上是我们在Order的API项目中,只需要把Order相关的实现暴露为Web API。如果想通过一个API项目把所有的业务模块都通过web api 暴露出来,也只需要多添加一行代码。

public class GalaxyWebModule : AbpModule{    public override void ConfigureServices(ServiceConfigurationContext context)    {      Configure(options =>      {        options.ConventionalControllers.Create(typeof(GalaxyOrderModule).Assembly);        options.ConventionalControllers.Create(typeof(GalaxyProductModule).Assembly);      });  }}

由此在Swagger文档中即可以看到Product和Order两个模块的Web API。

37b8848bd7050ff0151c72469902ab9d.png

也就是通过这种方式,我们只需要开发相关的业务模块然后通过动态Controller的方式来灵活控制是进行单体部署还是分布式部署。也许你已经发现了,这种情况下我们没有Controller层,也就意味着我们所有的逻辑都需要写在ApplicationService层,包括验证等。

结尾

在这种架构风格下,你可以很灵活的决定系统的部署方式。 既没有必要被技术捆绑,也可以进行最大化的复用同时免除未来业务快速增长的后顾之忧。

由于篇幅有限,如果有描述不清或者疑问的地方欢迎给我留言。希望这些实践能给广大中小企业的研发团队一些启发。 我将继续关注中小企业信息化以及产品研发中的技术架构、团队管理、运维等领域。如果你有相关的问题或者新思路,欢迎沟通 :) 

这是关于中小企业产品研发体系建设的第二篇,你也可以查看我的上一篇《中小企业团队敏捷产品开发流程最佳实践》

我已将去年2019年中国.NET开发者峰会上为大家的分享上传到我的腾讯课堂,以及之前为大家录制的《ASP.NET Core核心模块》 以及《.Net Core ON K8S快速入门》 两个系列都可以免费加入。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/347893.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机二级web题目(4)--CSS基础

1 [单选题] CSS指的是下列(B)的缩写。 A. Computer style Sheets B. Cascading style Sheets C. Creative stye Sheets D. Colerful siyle Sheets 2 [单选魃] 在HTML文档中,应该在下列哪个部分引用外部样式表?(D) A. 文档的末尾 …

dozer bean_使用Dozer框架进行Bean操作

dozer bean介绍 如您所知,您可以在任何操作系统中将文件或文件夹从源位置复制到目标位置。 您是否考虑过复制基本上是POJO的java对象? 在很多情况下,您需要将源bean的内容复制到目标bean。 我不关心对象的拷贝构造函数,浅拷贝或深…

英语作文计算机主板,(完整版)电脑主板bios英文版的中英文对照翻译.pdf

电脑主板 BIOS 英文版的中英文对照翻译让你的电脑 BIOS 知识迅速提高滴。 Time/System Time时间 / 系统时间Date/System Date 日期/ 系统日期Level 2 Cache 二级缓存System Memory 系统内存Video Controller 视频控制器Panel Type 液晶屏型号Audio Controller 音频控制器Modem …

怎么修改提交git是的用户名_git 修改提交邮箱以及用户名-亲测

一.查看git配置$ git config --list 此命令会列出所有GIT当时能找到的配置...查看 列表项[email protected] 邮箱user.name123 用户名二.修改邮箱和用户名1.重新设置邮箱和用户名$ git config --global user.email"[email protected]&…

简而不漏,300字Java标识符命名规则规范,看一遍就可以记住,包名类名变量名常量名,让命名有迹可循

1.标识符命名规则 (如果不遵循,编译不通过,需要严格遵守) 由26个英文字母大小写,0-9,_,$组成不可以数字开头不可使用关键字和保留字,可以包含关键字和保留字严格区分大小写不能包含…

html文字依次显示,利用定时器和css3动画制作文字依次渐变显示的效果

如上图,这种效果看着是不是挺“柔”的附代码文字一次渐变出现body{background-color: #333333;padding: 20px;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;}div,span{color: #ffffff;font-size: 30px;}.hidden{display: no…

面向Java程序员的20大Spring REST面试问题答案

大家好,过去两周来,我一直在与Spring教程共享一些REST,今天,我将向申请Web开发人员角色的Java开发人员共享一些常见的Spring和REST面试问题。 由于Spring Framework是用于开发Java Web应用程序和RESTful Web Services的最受欢迎的…

你还认识变量吗?Java基础学习不可略过的基本语法,简而不漏,变量的定义赋值和分类,系统认识Java中的变量有哪些?

文章目录变量的声明和赋值变量分类变量的声明和赋值 class VariableTest{public static void main(String[] args) {//变量的定义int myAge 21;//变量的使用System.out.println(myAge);//变量的声明int myNumber;//变量的赋值myNumber 9090;} }特别注意: ①定义变…

地图统计_庄园地图“出镜率”排名,500场数据统计,红教堂第一

在11赛季的时候很多玩家都私信趣游君,他们感觉自己排位经常会遇到一些让人讨厌的地图,也是因为经常遇到这些地图,所以导致自己的排位赛迟迟无法上分,因为要求我们做一下地图出镜率的排名。为了完成这个刷剧统计,趣游君…

2018安徽省计算机一级试题答案,2018年计算机等一级考试试题100题及参考答案.docx...

2018年计算机等一级考试试题100题及参考答案.docx文档编号:395397文档页数:14上传时间: 2018-09-03文档级别:精品资源文档类型:docx文档大小:28.84KB2018 年 计算机等一级考试试题 100 题 及参考答案 1. 在…

基于Spring Boot Profile的日志记录示例样本

我们希望在Spring Boot中为不同的配置文件使用不同的日志记录配置,例如在本地运行中,我们只希望控制台日志记录和用于生产,我们希望文件记录日志支持每天滚动日志文件。 我提出了一个示例logback配置,该配置将在所有应用程序中使…

简自动类型提升,精度损失类型强制转换,常用转义字符,简单帮你回顾Java基本数据类型整形浮点型字符型布尔型Boolean及其运算规则

文章目录整形浮点型字符型布尔型boolean自动类型提升强制类型转换注意整形 bit是计算机中的最小存储单位。 byte是计算机中的基本存储单元。 1MB1024KB——1KB1024B(byte) public class JavaTest {public static void main(String[] args) {byte b1 1…

linux posix 线程池_linux多线程--POSIX Threads Programming

linux多线程自己从接触很久也有不少实践,但总是觉得理解不够深刻,不够系统。借这篇文章试着再次系统学习一下linux多线程编程,理解编程的concept,细致看一下POSIX pthread API的实现。还是凭借强大的google search,找到…

计算机相关货品类别,工业计算机的种类以及产品供应链

(文章来源:OFweek)因为工业计算机几乎是针对某种特定需求而生,它的型态可说是五花八门,如果要简单做个分类,可以工业计算机的类型与层级来区分。大致上有板卡、子系统与系统整合解决方案三种。板卡:又有单板计算机(Sin…

Java基本语法(6)--算术运算符

除号(/)要注意的是除数与被除数的数据类型。如果都是整型,那么结果还是一个整型,因此结果相当于是取整;如果至少有一浮点型,那么结果是浮点型,也就可以除到完整彻底,带小数点的。 前…

soapui 证书_SoapUI入门之附件上传和配置Https请求

已经欠了一堆的作业了,吼吼~拖延症是个大bug,废话不多说,马上进入咱们SoapUI的学习中来吧~现在开始我们的SoapUI集成测试学习之道:一、如何使用SoapUI进行附件上传做人还是不能这样,这不欺负银嘛。首先,我们…

pptv手机端html,影视资源持续更新,PPTV手机化身看片神器

移动终端的快速发展为人们的生活带来了多元化的娱乐享受,用手机观赏电影逐渐成为人们放松身心的重要选择之一。虽然在手机品牌呈井喷发展的态势下,支持观影的智能手机不在少数,要想突出重围,仅仅在硬件配置上做文章是远远不够的&a…

Java基本语法(7)--赋值运算符及其扩展

int a1b11; int a12; //相当于a1a12 int a1-2; //相当于a1a1-2 //乘除取余均类似short s1 1; s1 2; //第一种 int s2 s1 2; //第二种 //以上两种都能实现2功能,但是第一种不会改变数据类型,更推荐。 int i1 1; i1 2.2; //i13int n 9; n (n) (n)…

java neo4j_Neo4j Java REST绑定–第2部分(批处理)

java neo4j在第1部分中 ,我们讨论了使用Java REST绑定建立与Neo4j Server的连接。 现在让我们详细了解事务,批处理以及REST请求的实际情况。确保org.neo4j.rest.logging_filter to true) as described in Part 1打开日志记录(将系统属性org.n…

计算机算法设计与分析考试题,《计算机算法设计与分析》习题及答案

《计算机算法设计与分析》习题及答案一.选择题1、二分搜索算法是利用( A )实现的算法。A、分治策略 B、动态规划法 C、贪心法 D、回溯法2、下列不是动态规划算法基本步骤的是( A )。A、找出最优解的性质 B、构造最优解 C、算出最优解 D、定义最优解3、最大效益优先是…