《极简微服务》

1 概述

1.1 写作的目的

《极简微服务》的原始动机是为了总结自己所学,但后来发现很多同学在学习微服务后仍然一头雾水,处于一种知道但是尚未深刻的阶段。我希望能够通过《极简微服务》这篇文章,用尽量少的语言将各部分的知识串联起来,帮助刚入门的同学们重新梳理一遍,希望能够让大家对微服务形成一个更清晰的认识。因为是“极简”,所以本文不会涉及到任何代码的具体实现。

我想要为大家带来的思路是:

  • 微服务是什么?
  • 微服务包含了哪些东西?
  • 组件之间是如何相互作用,并构建成为一个整体?
  • 微服务会对我们的开发带来何种影响?

首先要说明的是,我入门微服务走的是Spring Cloud相关技术栈,所以我可能很难做到完全的脱离这个技术栈去总结。我对于微服务的了解可能也是肤浅的,某些方面上的理解可能也会存在一些偏差,我更多的把这看作是一种记录和交流,因为我也不过是通过阅读书籍和一些博文后做了一些梳理,所以希望读者朋友们能够有批判地阅读我的文字。

当大家阅读我的文字,然后再提起微服务的时候,如果能说出更多自己想法,那这恐怕就是对我莫大的鼓励了。

因为叫做 “极简”,所以其实我已经尽量想要控制好篇幅不要太长了,但是很无奈到最后还是超过了自己所认为的最理想篇幅,因为我觉得有些东西是不得不去了解的,望读者朋友能够见谅。

1.2 我们的目标

其实就是微服务的目标啦。

终极目标

通过将大的项目拆分成不同子服务,不同子服务相互协作,创造出一个分布式的、稳定的、高可用的、可控制的系统。

管理对象

服务。为了实现终极目标管理好各个服务,让他们之间的沟通无障碍。

因此,接下来我将针对我们的服务与服务之间的关系来描述微服务。

2 什么是微服务

2.1 微服务

微服务是一种架构风格 ,就是将业务系统拆分为不同的服务,然后每个服务是独立的。不同的服务通过轻量级的协议通信(如HTTP)。部署的时候,一个服务可能会有多个实例。服务与服务的合作,构成了一个完整的系统。

通过微服务我们可以达到一些理想的效果。

  • 组件化
  • 可伸缩
  • 面向错误设计
  • 职责分明
  • 技术多样性

    ...

2.2 什么是好服务

  • 高内聚:相关的东西都扔一块儿,否则出BUG不知道哪里找问题。
  • 松耦合:不强依赖其他服务,减少每次改动所影响的范围。
  • 独立性:可以开发、测试、部署,不受其他因素影响。
  • 好看:指的是接口好看,好用。

3 微服务的组件

3.1 配置中心

3.1.1 配置的发展

有一种说法是,开发人员是被迫写配置的。因为开发者得让自己的软件去适应不同的变化。

650,450

一开始是写配置文件,但是太零散,于是将配置都写在一个大的集合里面,比如数据库或者一个文件。

但是存在几个问题:

  • 不容易拓展:就一个人文件或者数据库,一般就只能读取和修改。想要别的功能,SORRY。
  • 边界模糊:谁来管理这个大的集合体?权限怎么分配?怎么保证你的修改不影响到别人?
  • 单点故障:数据库挂了,文件丢了,读取这个集合的配置的应用也都可能受到影响。当然可以通过一些手段解决,但大都比较麻烦。

所以,后面提出了 配置中心 ,希望能够解决上述的问题。

3.1.2 需求

对于程序而言,它希望:

  • 配置与程序部署完全分离
  • 调用可别太麻烦了
  • 配置有变化请马上通知我
  • 配置中心千万别挂掉

对于配置中心而言,它希望:

  • 隐私,加密,配置得是足够安全的
  • 权限,每个人只能读取自己的配置
3.1.3 配置中心工作流程

800,600

3.1.4 问题

既然配置中心存了所有服务的配置,当服务们要加载配置就至少要和配置中心通信一次。如果说服务要硬编码配置中心的地址,假如配置中心换个地址,那么就必须手动修改所有服务的这个配置。当服务数量达到几十个的时候,这简直是个噩耗。

所以硬编码地址显然不是一个好的方案。

微服务架构里面,配置中心也是一个服务。发散的思考一下:不同服务之间的通信是怎样的?总不可能都是硬编码吧?

3.2 服务发现

3.2.1 问题

回顾上一个章节的问题,服务之间应该如何通信。简单来说就是:服务太多了,不知道怎么才能找到对方。

3.2.2 方案

其实很简单,我们找一个第三者,让第三者告诉我们就好了。就像是出去旅行一样,不认识路的话,你查百度地图就好了,让地图告诉你具体的位置。

800,550

所以我们要抽象出一个中间服务,专门用来做这个事情,告诉你其他服务的位置,这个过程就叫做服务发现。而其他服务告诉这个中间服务自己的位置的过程,就叫做服务注册(类似于把自己的位置告诉百度地图,让他记录你的位置)。

3.2.3 需求

服务注册/发现中心:

  • 我很懒,其他服务你们要自己上门注册自己的位置啊。

其他服务(客户端):

  • 方便我查其他服务的地址;
  • 我不管,反正你不能挂掉,不然我好不着北;
3.2.4 流程

不同的服务注册、发现中心实现,流程可能存在差异。我在这里只拿Eureka来看一下(目前我也只了解这个 = = )。

600,800

说明:

  • 启用多个Eureka ,多个Eureka 实例信息共享,防止一个服务中心挂掉导致全部服务不可用。
  • 启用客户端的时候,客户端自己告诉Eureka 自己是谁,地址是多少。
  • 客户端(消费者)定期从服务中心Eureka处拉取最新的服务地址,这个服务地址就像是一本地图小手册,上面记录了所有服务的地址。这个客户端只需要定时更新这个小手册,保证它是最新的就好了。
3.2.5 问题

服务注册于发现机制,解决了不同服务之间的位置问题。但是仅仅是这样是否足够呢?

来听一听客户端的疑问:

我就想去吃鸭脖,你这个地图小手册,鸭脖店的地址怎么有两个啊?那我到底要去哪一个店家呢?

3.3 负载均衡

3.3.1 问题

选择鸭脖子店的问题,严肃一点来说,就是当拥有多个服务实例的时候,选择哪个实例去调用的问题。

这个过程实际上就叫做负载均衡。

3.3.2 负载均衡

负载均衡可以分两种类型:服务端的负载均衡和客户端的负载均衡。他们之间存在什么样的差异呢?

服务端负载均衡

600,300

顾名思义,就是把负载均衡(选择哪个鸭脖子店的选择权)在服务端完成。在这种方案下,其实对于客户端来说,就不存在选择的问题了。当客户端说 “要吃鸭脖” 的时候,服务端查看自己的地址列表,发现有两个鸭脖店,那么服务端会挑一个店的地址告诉客户端。

怎么挑选呢?通常可以根据目标服务实例的网络状况,负载的请求数量,甚至是随机分配等,一般都是事先制定好一套规则。

客户端负载均衡

600,300

相对于服务端的负载均衡,客户端负载均衡就是把所谓的选择权交给客户端。也就是说,有多少个可用的服务实例的地址,服务端就返回多少个地址。客户端要调用哪一个服务实例,完全由自己去选择。

同样,客户端怎么去选择,一般也都是预先设定好规则的。

3.3.3 问题

现在,服务与服务之间的通信,服务注册与发现解决了 “在哪里” 的问题;负载均衡解决了 “如果有多个位置应该去哪里” 的问题。服务注册/发现 和 负载均衡是属于相互合作的关系,共同解决了 “去哪里” 的问题。

但是我们要如何保证客户端真的顺利到达目的地呢?

严肃点说就是:要如何保证客户端对目标服务的调用是正常的?

3.4 弹性

3.4.1 问题

接过上一小节的问题,你觉得我们应该如何保障我们服务之间的调用是正常的呢?

为了回答这个问题,我们先抛出一个概念,叫做 “弹性”。

何谓弹性呢?弹性对于不同的对象来说略有不同。

对于客户端来说:

弹性就是是否有响应。任何一个请求都应该有响应,且响应时间不应太长。

对于被调用的服务端来说:

弹性就是自我保护。为了保护正常的运作,我可能会拒绝一些请求,以保障服务没有因为过多的请求而崩溃。

我们在这里打个比方。服务之间的调用是正常 就像是 去店里买蛋糕,这就是一个 “是否能顺利抵达目标地点并买到蛋糕” 的问题。

在这里我们首先要明确,肯定不能保证每个人都能够顺利抵达商店,因为目标商店随时都可能会 “关门不干”;其次,就算抵达了店里,也不一定能够买到蛋糕,因为可能卖完了,或者机器出故障了。

所以我们要换个思路,如果店家关门了,我就提前告诉它关门了,你最好别去了;或者你已经到了店家但发现蛋糕都卖完了,于是你改成买饼干,或者是你干脆直接去别的店里买。

3.4.2 方案

维持弹性的方案有不少,其实之前也有碰到过,让我们来总结一下。

之前我们学习到的有:

  • 服务发现与注册机制:移除注册但已死掉的服务实例,这就保障了客户端不会调用到已经崩溃的服务啦。
  • 负载均衡:选择好的服务来调用。调用状态正佳的服务才会有快速的响应。

再来看看其他的方案吧:

  • 断路器 :就像是保险丝一样,如果请求过多了服务器承受不住,或者服务器长期有问题。就断开连接。不让后续的请求过来了。
  • 后备模式:如果不小心碰到了服务没反应或者网络故障,拿出一个B方案临时处理。
  • 舱壁模式 :就是隔离性。将某个服务(或者某些服务)进行隔离,就算调用它出错了,也不影响其他的服务调用。把影响控制在一定范围内。

由于服务发现与注册机制、负载均衡在上面的章节已经有介绍了,故在此就不再赘述。下面会围绕其他的方案来进行分析。

3.4.3 流程

断路器的流程

微服务中最常见的就是断路器了,下面来看看断路器的流程(不同的断路器实现可能会有所差异):

970,650

如上图所示,有四个对象,分别是用户、客户端、断路器、目标服务。

由用户向客户端发起请求,客户端调用目标服务来完成用户的期望。断路器在这其中承担了客户端与目标服务之间的 “中间人” 的角色。

通信可以划分为三个阶段:

  • 第一阶段:客户端对目标服务的调用 全放行。此时断路器认为目标服务没有问题,所以全部放行。但是当达到一些特定指标(比如一定次数以后的故障比率)以后,判定目标服务已经出现问题,则断路器切换到第二阶段。
  • 第二阶段:客户端对目标服务的调用在一段时间内 全拒绝。一般来说,第二阶段过了一段时间以后才会触发第三阶段的检测。
  • 第三阶段:客户端通过特殊机制 检测目标服务是否已经恢复正常。如果目标服务已恢复正常状态,则切换到第一阶段,否则切换到第二阶段。

后备模式的流程

其实很简单,用伪码来表示就是:

boolean success = callService();
if (success) {
    doSomething();
} else {
    excutePlanB();
}

其实后备模式是可以和断路器结合起来使用的,当在断路器流程中,判断目标服务出现问题后的第二阶段,客户端的调用可以全部走Plan B。

舱壁模式的流程

其实就是隔离,隔离的方式有很多种,比如线程的隔离,模块的隔离,地域隔离等。这里我们拿线程隔离的举个例子:

600,300

上图中,原本客户端对所有服务的调用都使用同一个线程池。

当某个目标服务A出现故障的时候,堆积在这个服务的调用越积越多,一直到线程池中的线程全被占用了,此时客户端新来一个需要对服务B的调用,却没有线程池可以用了,只能排队等待。

解决方案很简单,事先将不同的服务划分到不同的线程池中去,这样就不会出现一个服务占用资源,导致其他服务不可用的情况了。

3.4.4 问题

现在我们不仅解决了 “去哪里” 的问题,还解决了 “是否能抵达并买到东西” 的问题。

让我们好好想想,我们现在解决的都是服务与服务之间的通信问题。如果是外部服务(我们微服务体系外的客户端)要与我们的服务通信,如何解决呢?

3.5 网关

3.5.1 问题

接回上一小节的问题。

问题描述

内部服务与服务之间,是通过一本字典来查找对方的位置,但是如果是一个“外国人”(外部服务),一方面我不能把字典给他因为字典是机密,其次,就算给了他他也看不懂毕竟文字不通。

分析

所谓内部服务,一般对外都是不可见的。我们通过服务之间的合作给外部提供一个整体,所以对外而言我们应该是一个整体。基于安全性考虑我们也不能讲内部服务暴露给外部服务使用。

解决方案

选举出一个中间人,专门用来和外部服务通信。由这个中间人来判断对方是不是合法的请求,然后做相应转发工作。

这个中间人又像是一个守门人,只接纳有“令牌”的人,并“通风报信”。

550,400

3.5.2 流程

970,700

其实没有太多想要说的。我们看一下网关所处的位置,刚好就是处于对外来请求的“守门人”的位置。我们只需要明白网关的作用即可。

很明显,网关的作用有:

  • 验证和授权:你从哪里来?看一下随身令牌?
  • 路由(动态/静态):你要到哪里去?来,往这儿走!
  • 负载均衡:有两个蛋糕店,你去这个好了。
  • 流量控制:人满了,不能让你进来啦,抱歉!
  • 数据搜集、日志:等我记录一下,你从哪里来,去哪儿,买了啥。
  • 其他公共需求:略略略。
3.5.3 问题

想必到这里,你已经基本了解服务注册与发现、配置中心、负载均衡、断路器、网关之间是如何共同工作的了。

现在唯一的问题是,相对于之前的单体架构系统来说,服务的数量变多了,服务之间的通信也变多了。系统反而一下子变得复杂了。如果服务与服务之间的调用出了问题,我应该怎么排查问题呢?

3.6 日志

3.6.1 问题

接回上一小节问题。

问题描述

相对于单体架构系统而言,微服务架构牺牲了原有的简单,换取了系统的灵活性。所以微服务看起来反而变得更复杂了。一个请求,可能要经过网关、服务与服务之间来回的调用,所以做的日志也都十分零散,怎么监控系统、排查问题,变成了令人头疼的问题。

3.6.2 解决方案

来自风筝的启发

300,300

无论风筝怎么飞,只要我手里握着一条线,我就能知道风筝在哪里。我们可以借鉴这个思路,为我们的请求创造出一根看不见的 “线” 。

TraceID

700,450

如上图所示,当请求进来的时候,我只需要为这个请求添加上一个标识(traceID),不管后续这个请求被如何转发,只要带有这个traceID,那他们肯定是同一请求。

这个traceID不仅仅要能够被转发,做日志的时候,也要将traceID一并记录下来,这样不管日志分散在哪个服务,我都可以知道他们是不是同一个请求产生的。

通过traceID我们可以实现请求的链路跟踪问题。

日志搜集

我们知道,不同的服务可能是部署在不同的服务器上的,所以才会显得零散。如果我们要查日志,肯定不能够来回登录到不同服务器上去查。所以,我们需要将所有的服务的日志统一搜集起来,统一管理。这叫做日志聚合。

3.6.3 流程

600,500

通过将所有服务的日志统一搜集起来,统一存储,统一分析。再通过TraceID做链路的跟踪,我们就可以搭建出一个日志管理和分析平台。

如此,在微服务架构下,监控和查找问题,变得一目了然。

总结

现在来回顾一下,微服务的组件,都还记得有哪些吗?

来看一下他们是如何相互协作的吧!

1000,750

4 测试

4.1 测试分类

4.1.1 测试类型

(这只是从一种维度上去划分,划分的维度可以有很多)

  • 面向业务的测试。如验收测试、可用性测试等。
  • 面向技术的测试。如单元测试、集成测试、性能测试、安全测试等;
4.1.2 单元测试

是函数级别的。通常我们在这里捕获到大部分的错误。

4.1.3 服务测试

服务级别的集成测试。测试某个服务的功能。

4.1.4 端到端测试

系统测试,多个服务的测试。通常需要在页面上用鼠标点点点。

4.2 测试的手段

对于单元测试而言,有时候一个函数依赖于另外一个函数,也就是说,另外一个的正确与否,会直接影响到我这个测试的结果。所以有时候我们需要制造一定的隔离空间。

对于服务测试来说,也存在这样的问题。

如此来说,测试的隔离性我们需要重视。

为了创造这种隔离性,我们可以用Mock、或者打桩技术。

4.2.1 打桩

所谓打桩,最简单的理解就是你可以认为是“写死”,a函数,需要依赖b函数的返回值。那么我们可以将b函数的返回值“写死”。这就保证了b的返回肯定不会出错了。

看似简单,但是你不能直接修改函数b,因为很容易出错,若是你忘了改回来,就把你“写死”的代码一并提交了。

4.2.2 Mock

Mock和打桩很类似,区别在于,打桩是不关心你函数b(即你这个测试要依赖的那个函数)执行了多少次的。而Mock不仅仅可以模拟调用函数b很多次,还会很多其他的问题,比如,调用是否成功等。

我们通常借助一些测试框架来打桩和Mock,比如Java最常用的JUnit。

4.3 测试金字塔

900,650

如上图所示,不同的测试,会带来不同的效果。

  • 测试范围越大,会让我们对自己的系统更有信心。范围越大的测试,测试时间越长,导致反馈时间比较越长;
  • 底层的单元测试测试很快,但是单元测试是函数级的,单元测试的数量大。因为单元测试范围小,所以问题定位会比较精准,普通的错误我们甚至可以定位到“行”;
  • 金字塔的比例是可以变化的。我们应该针对现有的系统去改造。为这三者找到一个合适的测试比例。但每一次改造都会产生额外的成本,成本随着项目规模越来越大。

4.4 自动化

随着服务越来越大,测试越来越多,人工的测试变得越来越困难。微服务离不开自动化测试。所以我们需要借助CI/CD来让我们的测试变得自动化。

5 持续集成

持续集成对于微服务来说真的很重要。微服务组件解决的是技术上的问题,而持续集成所解决的是流程和效率上的问题。

持续集成能够加快微服务架构下的开发、测试、部署,能够帮助我们更快的发现和解决问题。

初闻持续集成,让人摸不着头脑,怎么会有如此晦涩的词语呢!

但是且慢,先来看看哪些典型的场景:

  • 我们写完代码后,提交到SVN/GIT,然后自己打包,登FTP将打包好的代码上传到测试服务器,然后对服务器 shutdown/startup。
  • 一个人修改了很多东西,但是不小心改错了某个东西,但是他提交了,这时候没人知道出错了。
  • 几个人一起开发同一个服务,因为周期较长,其中有一个人长时间不提交代码,一个月后,他要提交的时候发现,全是代码冲突都不知道怎么merge了。

这些场景都令人头疼,他们的共同特征是:要么都是重复的工作,要么是经常出现的问题,对项目工期、团队协作产生了较大的影响。

我们需要解决这些问题,用的手段就是持续集成。接下来我们就来解释什么叫持续集成。

5.1 CI 持续集成

所谓持续集成(Continuous Integration)。

先来看一下什么叫做集成。什么是集成呢?集成就是将代码提交到主干,通过一系列自动化的工具来构建(你可以简单理解为打包)和测试,验证是否有错误,是否会对其他人的代码产生影响。

所以持续集成,就是不断的进行集成,即经常提交代码以集成。持续集成的目的就是尽早集成,早点发现错误,早点解决问题。

我们谈到CI的时候,往往涉及到几个方面:

  • 自动化工具:人工手段的集成是及其低效率的,所以我们需要借助自动化工具来协助;
  • 版本管理:拥有一套良好的版本管理机制,我们才能够更好的进行持续集成。
  • 多提交:。所以开发者得有这样的意识,多提交代码,多集成。
  • 多写测试:有测试才能发现问题。

这其实也就是技术和流程问题。

一个好的持续集成环境是怎样的?

代码提交后自动(或者定期手动)触发持续集成:自动跑单元测试,自动构建代码,自动上传部署。不需要过多的人工参与。中间每一步出现问题的时候,都会马上通过一些手段进行反馈,防止出现更多的错误。

5.2 CD 持续部署

我们谈论CI的时候,很多时候也包含了持续部署(Continuous Delivery)的含义在里面。

何谓持续部署?

是CI的下一步。CI通过自动化的流程,确保软件语法和单元测试上是没有问题的。但是还不够,所以下一步需要将软件的新版本交给质量团队或者用户。让他们去继续测试、评审。若评审通过则说明代码就可以进入生产阶段了。

5.3 常见的CI/CD工具

  • Jenkins
  • Travis CI

6 容器

容器也是微服务架构不得不去思考的一个问题。它解决的是部署的问题。

6.1 部署的问题

部署有哪些问题呢?

  • 不同服务器之间环境存在差异且这些差异难于发现。环境的差异性可能会导致许多让人意想不到的问题。
  • 要新增或者删除一台服务器成本较高。包含时间成本和金钱成本。要能达到部署标准的服务器,需要装各种软件和配置不少环境变量,很难保证新配置的环境完全没有问题。而且不同服务的部署,所需要的环境也是不同的。
  • 难于管理。服务器数量几十台的时候,大家基本上处于战战兢兢提醒吊胆的状态,因为根本管不过来。

6.2 虚拟机

能不能减少服务器的数量,在一台服务器部署多个服务,并且能做到可以支持多种服务的部署?

要支持不同服务,肯定要支持不同的环境,比如windows,比如linux。

其实可以的,用虚拟机嘛。

虚拟机的大概思路是:

将整个环境先搭建好,然后打包成为虚拟机镜像,每次需要增加一个新环境的时候,直接实例化做好的镜像即可

900,550

一方面,虚拟机技术解决了环境的问题,部署比以前快了不少;

另一方面,构建镜像耗费时间长(一旦环境需要改变的时候,或者需要多个不同环境的时候),镜像文件大;并且,虚拟机的管理也需要占用额外的资源和空间;

虚拟机技术的特点是:

  • 拥有一个Hypervisor,用来管理所有的虚拟机;
  • 实例化的镜像中,是一个独立的空间,拥有独立的操作系统,内核,应用;
标准的虚拟机中存在一个Hypervisor,它会帮助我们管理运行在Hypervisor之上的其他虚拟机,包括资源的分配,外部的请求路由管理,内存的映射等;虚拟机越多,Hypervisor占用的资源越多;

6.3 Linux 容器

即Linux container,简称LXC。

LXC的原理是,创建一个隔离的进程空间,在这个空间中运行其他的进程。并且由物理机的内核来完成资源的分配工作。

如下图所示:

400,600

与传统虚拟机不一样的地方是:

  • 不需要Hypervisor
  • 隔离的空间内没有内核
  • 启动快,只需要几秒
  • 更轻量,对资源利用更充分

6.4 Docker

现在大家都知道Docker了

LXC是操作系统级别的虚拟化方案,毕竟是Linux下的容器。是否存在应用级别的容器技术呢?

那就是Docker了。

Docker的发展

2013年,Docker横空出世的时候,在底层上也借助了LXC来管理容器,然后自己在上层做其他的管理工作。但是过了几年,Docker 0.9的时候有了新欢libcontainer,LXC就变成了一个“备胎”,此时Docker可以选择不再依赖Linux部件了。再Docker 1.8的时候,LXC被认定为“前妻”。

再到后来,libcontainer上位。Docker致力于去实现容器化的标准,你只需要实现libcontainer提供的标准接口,那么你就能够运行Docker,这为Docker的全面跨平台提供了可能。

Docker的特点

分层

一个镜像可以分为任意多层,比如:

  • 操作系统层(第一层)
  • 依赖库(第二层)
  • 软件包和配置文件(第三层)
  • 运行的应用程序(第四层)

如果几个镜像拥有相同的层。比如,镜像A和镜像B用的是相同的操作系统,那么下载A时已经下载好了这个操作系统,下载B的时候,就不需要重新下载了,只需要下载第一层之上的数据即可。

增量更新/版本管理

对于一个公共镜像A来说,有许多镜像可能都是根据它来制作的,而镜像A制作好了便不可以改变。但对于其他镜像来说,有时候一些环境变量难免需要改变,这时应该怎么办呢?

Docker中约定:

  • 上层的优先级大于下级
  • 层都是只读的

这样,只需要在最上层添加一个读写层,将需要改变的配置都集中到这个层来进行改写。在写的时候同时拷贝出一个只读属性的文件备份,应用实际读取的时候读取的是这个只读属性的拷贝。

400,250

如上图所示,底层中环境变量A是1,但是在读写层,我们将A设置为3,那么应用程序读取的时候,读取到的是A=3。而读写层不仅仅可以修改配置,还可以将移除某些组件。每新加一层发布出去的时候,就相当于发布了一个更新。这样的一个好处就是,对于之前发布出去的镜像,我们拥有了 增量更新版本管理 的能力。

Docker的构成

Docker采用了C/S架构。分为Client和Server,然后还借鉴了Git的思想,可以搭建自己的中央镜像仓库。大体的架构如下图所示(图片来自于网络):

700,500

需要解释一下几个名词:

  • Image:镜像。一个可执行的包。它包括了一个程序要正常运行,所依赖的包括应用程序本身在内的软件,环境变量,配置文件,依赖包等环境;
  • Container:容器,一个镜像的运行实例;
  • Docker-Daemon:一个运行在后台的守护进程,可以看做是Server;
  • Registry:镜像注册中心,这里存在着各种镜像,用户可以上传或者下载上面的镜像。分为public和private,我们还可以搭建自己的Registry;

我们所说的Docker,通常是指Docker Engine,它包括了:

  • Docker-Daemon
  • REST API
  • CLI

其中,REST API和CLI都是用来跟server交互的。

6.5 与CI合作

我们原先所谓的部署,对于Java来说,都是将代码打包成jar或者war,也就是说,jar和war就是我们CI的构建物。

采用了容器技术以后,CI每次集成的结果是一个容器镜像,那么我们就只需要在服务器将这个镜像实例化就可以了,从而不需要关心环境的差异问题。

7 总结

微服务通过将系统拆分为不同的服务,通过服务与服务之间的相互协作,构成一个整体。

在微服务架构下,我们关心服务与服务之间如何通信。所以我们会单独构建一个服务中心,专用用于帮助服务与服务之间更好的沟通;再通过断路器的方式,为我们的服务提供保障,让服务间的调用具备弹性;此外,我们还会独立出一个服务,专门管理服务的配置;考虑到我们需要支持外部的访问,所以我们独立出一个网关,用来承担“看门人”的角色。上述的种种为系统带来了巨大的复杂性,为此,我们还单独创造创一套日志聚合和链路跟踪机制,用于监控微服务的状态。

除了这些,我们还需要通过持续集成和容器技术来帮助我们达成更高效的开发、测试、部署效率,让服务具备更好的伸缩性,要增加新的服务实例的时候,只需要再实例化一次镜像即可。

仿佛一切都变得美好了,这就是微服务。

8 参考书目

《Spring微服务实战》

《微服务设计》

(这是我的原创文章,若要转载,希望大家能够注明出处,谢谢大家~)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值