前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >服务之美-读《微服务设计》笔记全集(一)

服务之美-读《微服务设计》笔记全集(一)

作者头像
ImportSource
发布2018-12-19 10:43:52
5010
发布2018-12-19 10:43:52
举报
文章被收录于专栏:ImportSourceImportSourceImportSource

最近在微信读书上读《微服务设计》一书,目前读了30%多了,其间想法有点多,现分享给大家。

微服务设计

Sam Newman

阅读笔记

共享库不要轻易的使用?

>> 但是你还是需要很小心,如果使用共享代码来做服务之间的通信的话,那么它会成为一个耦合点。

模块化在为服务中并不是很好的实践?

>> 除了把系统划分为不同的服务之外,你可能也想要在一个进程内部使用模块进行划分,但是仅仅使用模块划分不能解决所有的问题。如果你只使用Erlang,可能会花很长时间才能把Erlang的模块化做好,但是我怀疑大部分人不会这么做。对于剩下的人来说,模块能够提供的好处与共享库比较类似。

调用链监控

>> 能够清晰地描绘出跨服务系统的健康状态非常关键。

代码治理

>> 尽量通过技术的手段把更多的规则固化。比如代码风格,各种工具和插件,所采用的技术方案。

技术债务

不光走捷径会引入技术债务。有时候系统的目标会发生改变,并且与现有的实现不符,这种情况也会产生技术债务。

一个系统中或多或少都会存在一些债务,只要这些债务在某个点得到遏制和升级。亡羊补牢 犹未迟也。就像我们每个人一样谁身上还没有一两个伤疤。比如,有的代码实现,逻辑比较混乱,直到几个迭代下来,你才真正理清了内的的逻辑,此时,你要在新的实现中果断的,对之前的代码进行固化,或者叫隔离,新的实现就基于新的思路来实现。这样你的代码中相当于有两个思路的代码实现存在,一种是之前的现在看来有问题的代码,一种是更成熟的代码实现。之前的那点存在的代码就是伤疤。技术债务随着迭代的持续,得到了升级,从而得到有效的遏制。

然后你可以把这个伤疤标记todo,待日后进行彻底迁移。这算是技术债务列表的一种实现方式。

服务自治和易修改

>> 赢得世界的方法是,保证自己很容易对应用进行修改。这正是微服务的用武之地!

谨慎过早的划分微服务

曾经遭遇过过早划分微服务,过早划分一般很难定义好边界。因为需求什么的根本就不稳定。除非需求非常明确,或者有一个非常资深的架构师甚至专家级别的大佬坐阵。否则,会比较费力。

>> 很多时候,将一个已有的代码库划分成微服务,要比从头开始构建微服务简单得多。

为别人提供微服务的正确姿势

我们很多时候为别人提供微服务,首先问对方一句话:你要哪些数据?其实这样的出发点也许是不对的,是不是可以从提供功能的角度来提供一个更加通用的微服务?

同样的,当你在思考组织内的限界上下文时,不应该从共享数据的角度来考虑,而应该从这些上下文能够提供的功能来考虑。

逐步划分上下文

>> 通常很难说哪种规则更合理,但是你应该根据组织结构来决定,到底是使用嵌套的方法还是完全分离的方法。

嵌套上下文还是隔离上下文,取决于组织架构和团队边界。如果几个上下文都是由一个小组的人负责,你把这些独立开来后,反倒不好管理。但这种思考角度似乎有点问题,虽然表象上可以讲通。应该是根据上下文内的服务的负荷程度来决定才更具本质。

技术边界

按照技术去划分服务的后果基本不可想象。但按照技术接缝对服务边界进行建模也并不总是错误的。比如,我见过当一个组织想要达到某个性能目标时,这种划分方式反而更合理。然而一般来讲,这不应该成为你考虑的首要方式。

寻找理想的集成技术

>> 如果你从事IT业已经超过15分钟,不用我说也应该知道,你工作的领域在不断地变化,而唯一不变的就是变化。新的工具、框架、语言层出不穷,它们使我们的工作更高效。现在你在做.NET,那一年之后呢,或者五年之后呢?说不定什么时候,你会想要尝试一个能够让你的工作更有效率的技术栈。 我很喜欢保持开放的心态,这也正是我喜欢微服务的原因。因此我认为,保证微服务之间通信方式的技术无关性是非常重要的。这就意味着,不应该选择那种对微服务的具体实现技术有限制的集成方式。

?,作为一个RPC的日常使用者,曾经也主张这种技术无关性。也就是微服务应该采用更加标准的,与具体技术和语言无关的集成技术,比如REST。但,我目前使用过的微服务集成技术中,除了中途使用过一段时间Spring Cloud(算是REST吧),其他的比如Dubbo以及现在公司自研的服务框架都是基于RPC的。当然了,基于thrift的RPC算是一种更加通用的,更加“技术无关性”的集成技术了。但大部分时候,还是使用基于RPC,然后生成客户端桩代码进行远程调用。使用客户端会造成耦合,但对于开发者来说确实是很方便,很多时候一个技术能不能流行起来取决于使用者到底欢不欢迎它。

共享数据库

数据库是一个很大的共享API。多个不同的组织以及服务共享一个数据库,一个数据库表,真的是一场无法预期的灾难。

>> 数据库是一个很大的共享API,但同时也非常不稳定。

编排vs协同

服务组装有两种方式,一种是通过编排,一种通过协同(事件订阅)。前者偏重,导致上帝节点出现,而其他节点变为贫血,只是crud;而协同模式采用事件机制,则有效的解耦,但需要一个额外的监控系统来跟踪每个订阅后的行为是否正常结束,如果失败了,那么要考虑补偿和善后。

>> 通常来讲,我认为使用协同的方式可以降低系统的耦合度,并且你能更加灵活地对现有系统进行修改。但是,确实需要额外的工作来对业务流程做跨服务的监控。我还发现大多数重量级的编排方案都非常不稳定且修改代价很大。基于这些事实,我倾向于使用协同方式,在这种方式下每个服务都足够聪明,并且能够很好地完成自己的任务。

REST

json的轻量级让人们不假思索就用了起来,包括我们绝大多数,但其实有的场景下偶尔使用xml或者html会有更好的效果。但json依然是主流。xml和html作为返回格式,只在特定场景下才适用。比如富媒体。

>> 不过我个人来讲还是很喜欢XML的,因为在工具上有很好的支持。举个例子,如果我只想提取负载中某个特定部分的话,可以使用XPATH,而支持XPATH标准的工具相当多。CSS选择器也可以,并且用起来还更简单。使用JSON的话,也有JSONPATH可用,但是目前支持该标准的工具很有限。我感觉很奇怪的是,很多人选择JSON是因为它很轻量,但是又想方设法把超媒体控制之类的概念添加进去,而这些概念是在XML中早已存在的。

Reactive

在微服务中使用reactive是一件让人听起来就略微激动的事情,可以基于spring提供的reactive来做push。得看场景。

>> 响应式扩展(Reactive extensions, Rx)提供了一种机制,在此之上,你可以把多个调用的结果组装起来并在此基础上执行操作。调用本身可以是阻塞或者非阻塞的。Rx改变了传统的流程。以往我们会获取一些数据,然后基于此进行操作,现在你可以做的是简单地对操作的结果进行观察,结果会根据相关数据的改变自动更新。一些Rx的实现允许你对这些被观察者应用某种函数变换,比如在RxJava中就可以使用类似map或者filter这样的经典函数。

微服务世界中的DRY和代码重用的危险

服务内部遵循dry,跨服务之间可以适当反dry,就好比以前我们搞代码脚手架,如果是通过maven依赖来搞模版,那么后果就是一旦你的公共库改掉,所有的项目都跟着改了,这显然是一个巨大的common包的共享,强耦合。所以此时大家一般采用复制冗余的方式来保证接耦合,然后通过脚手架约定来做到模版化。

>> 跨服务共用代码很有可能会引入耦合。但使用像日志库这样的公共代码就没什么问题,因为它们对外是不可见的。Realestate.com.au使用了很多深度定制化的服务模板来快速创建新服务。他们不会在服务之间共用代码,而是把这些代码复制到每个新的服务中,以防止耦合的发生。

版本管理

不同接口共存是一个很棒的做法。事实上包括jdk都是这样做的,然后通过@Deprecate。

>> 如果已经做了可以做的所有事情来避免对接口的修改(但还是无法避免),那么下一步的任务就是限制其影响。我们不想强迫客户端跟随服务端一起升级,因为希望微服务可以独立于彼此进行发布。我用过的一种比较成功的方法是,在同一个服务上使新接口和老接口同时存在。所以在发布一个破坏性修改时,可以部署一个同时包含新老接口的版本。

用户界面

有时候,你可以让服务端负责提供一部分UI,看起来看棒。但依然存在一些问题,比如一致的用户体验。往往前端和服务端有可能不是一个团队,此时就比较麻烦,但如果是面向业务的团队则这方面的问题就会稍微好一点,但即使一个团队内部,也会因为前端和后端不是一个人,而导致风格会不一致。

>> 这种方式有一个工作得很好的变种,即将一系列粗粒度的UI部分组装起来。也就是说不再创建小部件,而是对胖客户端应用的所有内容或一个网站的所有页面进行组装。

与第三方软件集成

很多产品买之前说得天花乱坠,一旦付钱后,事情就变得复杂了。所谓定制是对系统架构设计非常合理的前提下暴露了的api定制接口,对技术要求比较高。一旦不合理,你定制的那些扩展。等他们下个版本稍微升级一下就可能变得不可用。

>> 很多企业购买的工具都声称可以为你做深度定制化。一定要小心!这些工具链的定制化往往会比从头做起还要昂贵!如果你决定购买一个产品,但是它提供的能力不完全适合你,也许改变组织的工作方式会比对软件进行定制化更加合理。

最好的定制是加个自己开发系统的一个链接?哈哈,我感觉是,这样是可控的。

>> 那么到底应该怎么办呢?你可以选择在CMS上面套一层自己的服务作为对外的网站,如图4-11所示。这时CMS就成为了一个服务,其职责是管理内容的创建和获取。在自己写的那个前端服务中,你可以按照自己的方式来写代码和做集成。你对网站的扩展具有很好的控制力(很多商业CMS提供了自己专用的插件来处理负载),那么就可以使用更合理的模板系统。

善用绞杀者模式

绞杀者模式就是把老系统接口拦截,然后决定是调用新系统接口还是继续走老系统。

>> 与在CMS系统前面套一层自己的代码非常类似,绞杀者可以捕获并拦截对老系统的调用。这里你就可以决定,是把这些调用路由到现存的遗留代码中还是导向新写的代码中。这种方式可以帮助我们逐步对老系统进行替换,从而避免影响过大的重写。

增量的方式拆分

其实万事都是这种做法。得分而治之,一点点增量来搞。时间长了,慢慢就变得越来越好。然而现实经常看到各种拉倒重来的做法,每做一件事情像是搞运动,看似热闹,其实是每隔一点时间归零后又重新起步。从这点讲,那些搞运动式的所谓项目最终只是“政客”们短期实现自己仕途目标的一个垫脚石。从这点延伸开来,一个人活着的意义究竟是仰望,还是俯视。有的人喜欢和比自己强的人在一起,而有的人则喜欢身边的人都不如自己。这种两种不同的选择,看到的东西也自然不一样。

>> 决定把单块系统变小是一个很好的开始。但我强烈建议你慢慢开凿这些系统。增量的方式可以让你在进行的过程中学习微服务,同时也可以限制出错所造成的影响(相信我,你一定会犯错的!)。

打破外键关系

自从微服务观念注入进来后,各种表关联的SQL就离我远去。

分布式事务

再做一次。其实也就是补偿。

>> 其实,对我们来说知道订单被捕获并被处理就足够了,因为可以后面再对仓库的提取表做一次插入操作。我们可以把这部分操作放在一个队列或者日志文件中,之后再尝试对其进行触发。对于某些操作来说这是合理的,但要保证重试能够修复这个问题。很多地方会把这种形式叫作最终一致性。相对于使用事务来保证系统处于一致的状态,最终一致性可以接受系统在未来的某个时间达到一致。这种方法对于长时间的操作来说尤其管用。在第11章中我们会讨论扩展(scaling)模式,届时会对该话题做进一步讨论。

可以把处理中的订单全部管理起来。包括处理状态。比如,异常状态,正常状态,未处理状态。异常状态中包含异常码,然后根据不同的异常码跳转至不同的补偿逻辑。等。支持手动和自动重试补偿。

>> 举个例子,你可以创建一个叫作“处理中的订单”的概念,围绕这个概念可以把所有与订单相关的端到端操作(及相应的异常)管理起来。

微服务中怎么实现报表

在传统的单块应用中,我们通过使用使用简单的cron定时对slave数据库进行查询然后生成报表。但在微服务中,你的数据分布在各个不同的库里。此时已经再使用传统的方式显然不能搞定。如果通过对方提供的服务来进行查询然后聚合,想想都可怕,显然也是无法实现的。此时推荐你通过事件源的方式,也就是通过消息订阅的方式来生成视图。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ImportSource 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云 BI
腾讯云 BI(Business Intelligence,BI)提供从数据源接入、数据建模到数据可视化分析全流程的BI能力,帮助经营者快速获取决策数据依据。系统采用敏捷自助式设计,使用者仅需通过简单拖拽即可完成原本复杂的报表开发过程,并支持报表的分享、推送等企业协作场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档