前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >我所知道的那点微服务

我所知道的那点微服务

作者头像
ImportSource
发布2018-04-03 14:04:12
6490
发布2018-04-03 14:04:12
举报
文章被收录于专栏:ImportSourceImportSource

忽然一夜春风来,人间处处微服务。这真是一个相当火的概念啊。笔者第一次知道微服务这个概念是在15年4月份,应该是。

铺垫

有一日翻到martin fowler的博客。发现微服务这个概念是此人发明的。而且他写了一堆博客。从前端说到数据库。

相信你对上面的这个图应该不陌生,国内的博客中经常会引用到这张图。

没错,这张图透露了两个主要点:

一个就是 拆分。

一个就是 replication。

要想搞微服务,基本上这两点都要做到。如果你所在的公司做到了只做到了其中一点,都应该算是假的微服务(当然以概念论概念另当别论)。

先说拆分。

拆分这事你可以移步这篇文章:微服务到底有多微?How big is a microservice?

关于这个问题,有人说用代码行数来衡量微服务到底有多微,我们都知道不同语言写的微服务行数肯定都不统一,这个显然行不通;还有人说用重写时间来衡量,什么意思呢?就是说一个微服务如果拉倒重来得多长时间,这个显然不是一个衡量标准。既然有的书籍提到了,我们在这里就提一下。 那么究竟用什么来划分微服务的边界呢? 我们认为应该从 具体的业务来考虑。其实还是和我们传统的一体化架构思维角度是一样的。总是先从业务功能去考虑一定不会出错的。 我们划分微服务首先应该要保证微服务的业务对立性。 那么这个独立性怎么去保证呢?也有很多的做法。 1、领域模型角度: 其中一种就是从领域模型角度考虑,就是将一个个领域模型作为独立的模块(或者叫单元)。比如产品、订单、客户。 2、业务行为角度: 还有一种角度是从业务行为考虑,比如 单点登录,生成全局唯一序列号,发送邮件等等。就是从业务使用场景来考虑。 关于这个从业务独立性划分, Sam Newman强调说我们划分微服务应该基于Domain-Driven Design里的Bounded Context的概念。

那么我们的每个微服务对应的人力是多少呢? 关于这个,martin folwer说了这些话: 根据我们和一些微服务的从业者的交谈后得出,我们看到了很多不同size的微服务。根据Amazon的概念,微服务的最大尺寸遵循两个比萨团队(即整个团队可以用两个比萨饼喂食),意思是不超过12人。比较小的微服务size的规模是,我们看到有公司是这样的配置:一个6人团队支持6个服务。

再说replication。如果你只是拆分了。然后继续把这些微服务搞个一主一丛,或者xx主从,还需要找机器然后去人工部署。那么这也许是假的微服务,充其量这算是soa化了。因为你的实例并不是动态分配和启停。所以,这是一个假的微服务,并没有为你的开发和运维和线上使用带来特别大的价值。

然后就涌现devops这个概念,docker顺势要为微服务做容器,让一切隔离和统一,于是“开发即生产”,于是。。。。

微服务体系所应该具备的能力

1、服务注册与发现

服务注册。每个微服务应用在启动的时候向服务注册中心,例如eureka server上注册自己的信息,叫作renewal,续约。定期进行续约。

服务发现。某个服务要调用另外一个服务。那么该服务本地也要同时保持一份服务列表。定期与服务注册中心同步。调用时是从本地的服务列表中通过客户端负载均衡策略选择一个实例来完成一次请求。

屏蔽底层细节。通过使用服务ID作为映射的根。比如:

http://192.168.1.25:8080/users/12。这样的真实信息应该做抽象,将192.168.1.25:8080替换成User-Service类似这样。

然后url就变成了这样:

http://User-Service/users/12

然后客户端发现组件负责在本地的服务列表中查找User-Service对应的host列表,然后通过负载均衡选择某一个实例完成请求。

2、负载均衡

微服务负载均衡分为服务端负载均衡和客户端负载均衡两种。二者的区别也非常简单,就是把负载均衡的执行放在本地还是某个服务端节点,

仅此而已。由于服务端负载均衡所带来的性能损耗,客户端负载均衡是一种比较合适的选择,至少在微服务之间的调用中客户端负载均衡是一个不错的选择。

服务端负载均衡则主要用在提供最终服务的API网关层。这里指的最终是指最终面向微服务空间外部的那些调用和访问。比如Android、IOS、Web前端等等的调用。至于API网关层的内容后面会介绍到。

3、熔断弹性能力

在单体应用中,多个service放在一个块里似乎没什么毛病,也是可靠的。但在微服务中,涉及到远程调用多个微服务。我们可以做一个假设:在IO型服务中,假设服务A依赖服务B和服务C,而B服务和C服务有可能继续依赖其他的服务, 继续下去会使得调用链路过长,技术上称1->N扇出。如果在A的链路上某个或几个被调用的子服务不可用或延迟较高,则会导致调用A服务的请求被堵住,堵住的请求会消耗占用掉系统的线程、io等资源,当该类请求越来越多,占用的计算机资源越来越多的时候,会导致系统瓶颈出现,造成其他的请求同样不可用,最终导致业务系统崩溃,又称:雪崩效应。

其实在单体应用也存在此问题。单体应用的某个service不可用意味着整个单体都是不可用,只是在单体中,这个事情你是认为整个应用down掉了。而且微服务的“雪崩” 其实也就是单体中的所谓“应用挂了”。

要不要每个服务都引入弹性能力?

为了解决微服务中的雪崩问题,我们可以模仿 电路中的电闸来开发弹性的微服务。 而且这样的弹性能力自然是每个微服务都要用到,而且理论是应该是必须要用到的,除非你可以确保你这个微服务是最前端的那个,也就是没有其他服务在依赖调用。

事实上市面上已经有了这样的框架。那就是netflix的hystrix。Spring Cloud已经集成了hystrix,也集成了基于注解的hystrix -javanica。让你可以轻松地通过注解的方式注入熔断条件以及熔断后的fallback。

当然了上面的做法主要是异常出现后进行相应fallback。还有一种做法是让服务永远处于理想状态,就是限流模式。其实限流从根本也是当条件满足后进行后续处理。

限流其实是一种简单粗暴但又经济实惠的做法,相信现在的绝大多数互联网公司主要还是采用这种模式。

传统限流模式适合微服务吗?

但限流模式也许并不适合微服务。只适用于那些“最终的”微服务。一个成熟而富有忍耐力的微服务应该还是要做好充足的熔断条件设置和条件满足后的处理逻辑。

否则,那个“乐观的微服务”也许就是未来某天整个集群雪崩的导火索。

4、前端API网关

API网关主要是面向最终的前端调用者。 API 网关也是前后端的分界点。至此,微服务体系也算是完成了自己的使命。

API网关一般以httpserver的形式示人。虽然这有损耗。但似乎你也没有什么别的好的办法了。

API网关的基本逻辑

某终端开发者,通过一个url(比如:GET http://api.xxxx.com/USER-SERVICE/users/1234)发送request到网关server。网关拿到request url对于url进行解析,得到微服务应用的SERVICE_ID,本例中就是USER-SERVICE,然后去本地的注册列表中检索到USER-SERVICE下的server list,然后通过服务端负载均衡算法选择一个server完成本次请求。

API网关的请求风格问题

关于这个问题速来是一个容易引起讨论的点。总的来说,推荐你主要使用RESTful风格,然后“基于操作”的配合使用。

你可以参照以下的内容来考虑自己的网关规范到底是什么样子:

你真的知道你喜欢REST而不是RPC的原因吗?

API负载均衡

只要涉及到服务实例的选择都要用到负载均衡。API的负载均衡自然算是一种服务端负载均衡。当然这也是一个相对概念,至少对于外部调用者来说。

5、微服务监控

监控一般分为三个部分。第一步是收集数据,第二步是条件满足,第三步是应对策略。

先说第一步收集数据。关于收集数据,无非就是两种做法。要么你主动去拿,要么每个实例node发送给你。

然后是条件满足。这一步也好理解。

最后是应对策略。应对策略包括各种动作和通知。各种动作包括启动,关闭,out of service等等。

通知自然是指通知到责任相关人,做后续的手动操作。

监控可以借助一些微服务框架来进行。比如可以借助Spring Boot的endpoint来做。Spring Cloud中的微服务实时监控Turbine框架就是通过读取spring boot actuator的stream来封装的。

6、服务管理

这里所说的管理大概是指的更强的对微服务的管理。包括启动、outofservice、停止、下线等等。以及动态启动、停止服务实例,也就是所谓的扩容和缩容的概念,以及健康检查等等。

动态繁衍和动态裁减都需要一个标准的底层环境支撑。比如预先通过标准镜像生成底层虚拟机环境数台。每个机器节点都具备接受任务并完成任务的能力。

在这里就是需要具备接收到启动微服务实例的命令后,能够根据端口自增自动启动指定数量的实例。也就说,需要有一个底层机器的集群,这个集群应该具备集群的基本沟通能力,保证相互协作,具备调度能力,资源管理能力,这也是任何一个分布式计算或存储的集群所应该具备的基本能力。就好比单机上的各个进程能够彼此协调配合一样,总之,集群要像一台电脑那样显得从容可靠,有求必应。

7、链路跟踪

请求埋点

每一个业务所发起的请求都要携带请求编号(RequestID),涉及到所有的被调用服务都要携带该RequestID。服务内部跟踪通过Java 的trace类来做跟踪。如果途径其他非java语言实现的微服务,则采用服务id(这里可以是RestApi标示,比如apps/list)与RequestID来保证唯一性(此方案待定)。

同时还要在rest调用时,携带调用关系,这个很重要。通过这个我们就可以绘制出调用关系链的图。

RequestID生成时机:当请求来到微服务,该微服务首先判断有没有RequestID,如果有,那么就认为该片段不是头片段,只需要获取该RequestID,然后进行存储;如果没有,则认为是第一次,那么就需要生成RequestID,然后进行存储;同时在本微服务中如涉及到调用其他微服务,则在rest模板中传入RequestID,同时传入(携带)调用关系。

链路信息收集

通过在微服务框架中配置开关的方式,来设置是否需要链路跟踪;如果需要链路跟踪,那么开发人员可以通过配置链路监控平台的host信息来实现向平台发送链路片段信息。发送支持同步和异步两种。异步有两种做法,一种是通过消息中间件发送信息,一种是采用本地缓存堆积数据,然后定期发送。开发者可以配置来选择具体的使用方式。

在把链路片段信息拿到后,需要把这些内容迅速的堆积到本地的内存中,当超过指定大小时发送至数据库)。

发送后然后清空本地缓存。继续堆积下一波。

本地缓存池要监测。一旦达到某值就开始发送。

链路片段数据结构设计

链路片段是指一个业务请求所经过的多个微服务,每个微服务我们认为就是这个业务请求的链路片段。链路片段实体应该包含如下属性:请求编号、微服务信息(包含:该服务所在host的信息、restapi、花费时间、编程语言

开发者信息甚至是所包含的异常信息)等。

链路监控

通过在链路监控平台中对每个业务请求的链路片段通过RequestID进行拼接从而展现一个request的完整链路。要展示出该请求整个链路所消耗的时间,以及每个片段的具体片段信息,具体属性见上一段:链路片段数据结构设计。

同时链路监控支持发送警报,发送方式支持邮箱、手机短信等格式,支持定时生成日报和周报对对应开发人员以及相关人发送告警。

链路数据存储

由于数据量过大,应该选择分布式数据库。考虑到链路片段都是以RequestID为key进行存储,也就是简单的key value型存储,故可以使用Redis或其他的nosql数据库比如hbase等来存储数据。

哪些项目适合用微服务?

长期演进的项目:

如果你的项目是一个长期演进的,那么你可以迁移到微服务来。至少拆分这一点可以让你的代码和人员不至于在一个目录下无限扩张。

技术裸奔:

我们总是喜欢最潮流的技术,特别是作为技术人员来讲。如果从这一点来讲的话,在你构建的微服务体系的过程中,会成长一批新型技术人才。

哪些项目不适合微服务?

工具类项目:

如果你的项目只是一个工具类的项目。那么也许微服务并不适合你。除非你已经有了一套成熟的微服务体系。对于这样的项目也许简单的soa就够了,把前端和后端分离,已经算是够前卫了。甚至都不需要这些,直接一个单体应用就可以了。

访问量不是很大的项目:

对于这类项目也许soa就够用了。当然了,如果你预见到在未来的一年内,用户访问量将会迅速飙升的话,那么你可以去尝试微服务。

单体应用究竟是不是微服务化的一部分?

个人认为单体应用并不是微服务的一个阶段。单体应用和微服务是两个不同的方向。

微服务对编程语言的挑战

毫无疑问,微服务对于那些过去在单体应用中占统治地位的语言来说是一次挑战。由于微服务的特性,传统的web容器成为了应用包的一部分。而且很多语言启动一个httpserver只需要很少的几行代码就可以实现。

微服务下数据是如何存储的?

没错,就像上面这张图所展现的一样。过去在单体应用中,基本上是围绕“一个”数据库来做文章,即使数据的物理分布分散在一个以上节点上。但在微服务下,却有可能一个微服务就独占一个数据库。其实这个也很容易想来,微服务应用本来就是一个单体应用的样子。只是里边只做了一件事情而已。只不过宏观上看,过去一个库搞定的事情,现在拆分成微服务可能多了很多个库。其实这也许是一种去中心化的体现,这样一来,微服务也分散了数据库层面的访问压力,也就是微服务下更强调“微存储”!有关微服务下的数据库后面也会讲到有关NoSql的内容。

关系

微服务与RESTful的关系

无论你如何的选择,RESTful无疑是你在微服务中用来交互的一种选择。也许你的微服务与微服务之间内部的交互使用了RPC的请求风格,但对外开放的最终API还是离不开http的请求方式,这时候RESTful也许能起到标准规范的作用,让开发人员之间的沟通成本更低。当然了,RESTful风格请求方式,强调以资源的方式来定位一个微服务,对于底层一点的原子服务来说也是不错的暴露方式,他的http method看起来和数据库的CRUD是那么的匹配。

你真的知道你喜欢REST而不是RPC的原因吗?

微服务与容器

我们这里说到容器,无非就是为了标准化。那么如果有一种方式能够实现这种能力,那么我们认为就适合做微服务的容器,而不仅仅是docker,比如spring boot现在都是以jar包的方式启动,配置信息又都是基于配置中心。那么底层的虚拟机或物理机只要有了java环境就足够了。所以这时候你可能并不需要运行docker就能实现标准化和无状态化。

微服务与标准集群的关系

微服务的动态增减能力需要一个强大的底层集群支撑。就像上面的服务管理中所说的那样:

需要有一个底层机器的集群,这个集群应该具备集群的基本沟通能力,保证相互协作,具备调度能力,资源管理能力,这也是任何一个分布式计算或存储的集群所应该具备的基本能力。就好比单机上的各个进程能够彼此协调配合一样,总之,集群要像一台电脑那样显得从容可靠,有求必应。

微服务与NoSql的关系

毫无疑问,在微服务的世界你需要转变观念了。一言不合微服务,微服务作为一种快捷的能力提供方式,无疑给大数据注入了一个友好而标准的API。

如果说在单体应用中,关系型数据库基本上还是处于统治地位;那在微服务架构中,底层存储变得多样,而且替换和迁移变得更容易。

你好好想想,当你一个劲的告诫开发者们:“现在是搞微服务了,不要给我做表关联,要单表操作!”。这时候其实告诉了你一个事实,关系数据库可能正在变得力不从心了,再加上关系数据库对于集群能力的支持的局限和牵强(个人观点)。你想想,你都只能用单表了,为什么还不去使用下那些集群扩展能力更强的NoSql数据库呢?

这里只是说了一种场景。并不是说关系数据库就没落了。关系数据库依然还会在未来的多年里占有重要角色。但在微服务的架构下,也许NoSql数据库能够为你在某一些特定的场景下提供更高效的读取或更强大的存储能力,多了选择,何乐而不为呢?

16篇NoSQL底层原理合集-两个月的翻译成果

微服务与DevOps的关系

当你把一整块的东西打乱后,如果还是用传统的开发运维流程人肉进行CI和CD简直就是一件不可能的事情,除非你的“微服务”只是做了简单的SOA化而且一个应用下没有几个实例。我个人理解的DevOps并不是说让开发连带运维的事情一块干了,full stack到把运维的活都干了,不是这样的,而是运维的大部分事情被自动化取代了,也就是基础设施自动化了。这样开发相当于还是干开发的事情,运维由自动化逐渐取代。回到微服务,如果你按照上面的流程做出了一个真的微服务架构体系,那么没有DevOps几乎就是假的微服务,换句话说,没有基础设施自动化,几乎就是假的微服务。好吧,这只是个人观点,如果你认为服务只要拆分了就是微服务的话,那么上面的观点也许并不符合你的胃口。

有关持续集成的内容你可以移步:CI漫谈

总结

总之,个人认为一个真的微服务架构总是应该以两个抽象动作为前提,即拆分和replication这两个动作。二者也许缺一不可。如果你只是拆分,也许只完成了微服务进程中的一小步而已。忽如一夜春风来,人间处处微服务。微服务到底适不适合你,你到底适不适合微服务,除了上面所说到的一些普通“标准”外,全看您个人的抉择了!

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
负载均衡
负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档