Spring Cloud与Docker微服务架构实战观后感

最近部署项目时接触了一个新东西:Docker。在后续的查找中发现了周立《Spring Cloud与Docker微服务架构实战》一书,接触后感觉很好,又是一套新东西(笑哭)。。如今,自我总结下从书中得到的东西。

首先,自我感觉这本书讲的是实货,对于一个没接触过Spring Cloud的人来说,简直太棒了。示例细节可算是面面俱到,还有对应的demo可下载,边看边感觉激情澎湃(笑)。

目前,通过阅读已知:

  • Spring Cloud 框架开发微服务;
  • Eureka 实现微服务的注册和发现;
  • Ribbon 实现了客户端的负载均衡;
  • Feign 实现了声明式的API调用;
  • Hystrix 实现微服务的容错(可监控单个实例数据,使用 Turbine 可聚合监控数据,若微服务与 Turbine 不通,使用消息中间件收集数据,如 RabbitMQ);
  • Zuul 建造微服务网关;
  • Spring Cloud Config 管理配置信息(JCE加密,Spring Cloud Bus实现配置的自动刷新);
  • Spring Cloud Sleuth 实现微服务跟踪(ELK日志分析,Zipkin收集系统的时序数据);
  • Docker 来部署项目,使用 Docker Compose 来管理Docker容器。

                                                                           以下大部分摘取至书中内容                                                                                    

单体应用架构与微服务架构

本人目前工作一年有余,平时用spring boot开发接口满足Web或App项目的使用,看书后,明白了以下概念:

写过的项目都是单体应用架构:一个归档包(例如war包)包含所有功能的应用程序。这种架构有以下缺点:

  • 复杂性高:以百万行级别的单体应用为例,整个项目可能包含的模块非常多、模块的边界模糊、依赖关系不清晰、代码质量参差不齐、胡乱地堆砌在一起。。。使得整个项目非常复杂,每次修改代码都要胆战心惊,甚至添加一个简单的功能,或修改一个 Bug 都会带来隐患。
  • 技术债务:随着时间推移、需求变更及人员迭代,会逐渐形成应用程序的技术债务,并且越积越多。“不坏不修”,这是在软件开发中常见的思想,单体应用更甚。已使用的系统设计或代码难以被修改,因为应用程序中国的其他模块可能会以意料之外的方式使用它。
  • 部署频率低:随着代码的增多,构建和部署的时间也会增加,而单体应用中,每次功能变更或缺陷的修复都会导致需要重新部署整个应用项目。全量部署的方式耗时长、影响范围大、风险高,使得单体应用项目上线部署的频率比较低。而这种情况又会导致两次发布之间会有大量的功能变更和缺陷修复,出错的概率较高。
  • 可靠性差:某个应用 Bug,例如死循环、OOM等,可能会导致整个应用崩溃。
  • 扩展能力受限:单体应用只能作为一个整体进行扩展,无法根据业务模块的需要进行伸缩。比如应用中有的模块是计算密集型的,它需要强劲的CPU;有的模块是IO密集型的,它需要更大的内存。由于这些模块部署在一起,不得不在硬件的选择上做出妥协。
  • 阻碍新技术创新:单体应用通常使用统一的技术平台或方案解决所有问题,团队中每个成员都必须使用相同的开发语音和框架,要引入新框架或者新技术平台会非常困难。例如一个使用Struts2构建的、有100万行代码的单体应用,如果想要更新Spring MVC,成本毫无疑问是非常高的。

微服务架构恰好能解决上述问题,微服务架构的特性如下:

  • 每个微服务可独立运行在自己的进程里。
  • 一系列独立运行的微服务共同构建起整个系统。
  • 每个服务为独立开发,一个微服务只关注某个特定的功能。
  • 微服务之间通过轻量的通信机制进行通信。
  • 可以使用不同的语言与数据存储技术。
  • 全自动的部署机制。

将整个应用分解为多个微服务,各个微服务独立运行在自己的进程中,并分别有自己的数据库,微服务之间使用REST或者其他协议通信。微服务的优点如下:

  • 易于开发和维护:一个微服务只会关注一个特定的业务功能,所以它业务清晰,代码量少。
  • 单个微服务启动较快:代码少,启动快。
  • 局部修改容易部署:单体应用只要有修改,就得重新部署整个应用,微服务只需重新部署这个服务即可。
  • 技术栈不受限:在微服务架构中,可结合项目业务及团队的特点,合理地选择技术栈。
  • 按需伸缩:可格局需求,实现细粒度的拓展。

当让,微服务架构也不是十全十美,选择使用它时,会面临如下挑战:

  • 运维要求高:更多的服务意味着更多的运维投入。
  • 分布式固有的复杂性:使用微服务构建的是分布式系统。对于一个分布式系统,系统容错、网络延迟、分布式事务等都会带来巨大的挑战。
  • 接口调整成本高:微服务之间通过接口进行通信。
  • 重复劳动:很多服务可能都会使用到相同的功能,而这个功能没有达到分解为一俄方微服务的程度,这个时候,可能各个服务都会开发这一功能,从而导致代码重复。尽管可以使用共享库来解决这个问题,但共享库在多语言环境下就不一定行得通了。

微服务设计原则如下:

  • 单一职责原则:一个单元只应关注整个系统功能中单独、有界限的一部分。
  • 服务自治原则:每个微服务应具备独立的业务能力、依赖与运行环境。
  • 轻量级通信机制:微服务之间应该通过轻量级的通信机制进行交互。
  • 微服务力度:微服务的力度时难点,也常常时争论的焦点。应使用合理的粒度划分微服务,而不是一味的把服务做小。代码量的多少不能作为微服务划分的依据,因为不同的微服务本身的业务复杂性不同,代码量也不同。

微服务架构的演进是一个循序渐进的过程。在演进过程中,常常会根据业务的变化,对微服务进行重构,甚至是重新划分,从而让架构更加合理。最终,当微服务的开发、部署、测试以及运维的效率很高,并且成本很低时,一个好的微服务架构就形成了。相对单体应用的交付,微服务应用的交付要复杂的多,不仅需要开发框架的支持,还需要一些自动化的部署工具,以及Iaas、PaaS或CaaS的支持。

Spring Cloud中碰到的问题

使用Spring Cloud的过程中,会遇到一些问题,如下:

  • Spring Cloud的依赖,dependencyManagement 标签确定版本感觉很重要,直接用版本号依赖的话有时会冲突报错:
<!-- 引入spring boot依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.3.RELEASE</version>
</parent>
<!-- 所有其他依赖都写在这里,下方为示例 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <!--eureka服务器的如下,客户端的不加-server-->
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
</dependencies>

<!-- spring cloud版本 -->
<!-- 通过它元素来管理jar包的版本,让子项目中引用一个依赖而不用显示的列出版本号 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Camden.SR4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  • 项目打JAR包需要如下依赖:

<!-- spring-boot的Maven依赖 -->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  • 在Spring Cloud中,大部分问题都可使用配置属性来解决,在Spirng Cloud进入Camden时代后,已经比较稳定。一般来说,问题都不是Spring Cloud本身的Bug导致。因此,排查思路可以按以下步骤展开:

  1. 排查配置问题:

    1. YAML缩进是否正确。

    2. 配置属性是否正确。

    3. 配置属性的位置是否正确。

  2. 排查环境问题:

    1. 环境变量。

    2. 依赖下载是否完整。

    3. 网络问题。

  3. 排查代码问题。

  4. 排查Spring Cloud自身的问题。

  • Eureka注册服务慢,一般情况下,服务注册到Eureka Server的过程较慢。在开发或测试时,常常希望能够加速这一过程,从而提升工作效率。这个问题的原因如下:服务的注册涉及到周期性心跳,默认30秒一次。只有当实例、服务器端和客户端的本地缓存中的元数据都相同时,服务才能被其他客户端发现(所以可能需要三次心跳)。更改配置可解决,但在生产环境中,建议坚持使用默认值。
  • 已停止的微服务节点注销慢或不注销。在开发环境下,常常希望Eureka Server能迅速有效地注销已停止的微服务实例。然而,由于Eureka Server清理无效节点周期长(默认90秒),以及自我保护模式等原因,可能会遇到微服务注销慢甚至不注销的问题。解决方案如下(这种方式仅建议在开发或测试中使用,生产环境建议坚持使用默认值):

    • Eureka Server端配置关闭自我保护,并按需配置Eureka Server清理无效节点的时间间隔。

    • Eureka Client端配置开启健康检查,并按需配置续约更新时间和到期时间。

  • 某些情况下,Feign或Ribbon整合Hystrix后,会出现首次调用失败的问题。Hystrix默认的超时时间是1秒,如果在1秒内得不到响应,就会进入fallback逻辑。由于Spring的懒加载机制,首次请求往往会比较慢,因此在某些机器(特别是配置低的机器)上,首次请求需要的时间可能大于1秒。解决方案如下:

    1. 更改配置,延长Hystrix的超时时间。

    2. 更改配置,禁用Hystrix的超时。

    3. 升级到Spring Cloud到Camden或更新版本。当然,也可单独升级Spring Cloud Netflix到1.2.0或更新版本,一般不建议单独升级,因为可能会跟Spirng Cloud 其他组件冲突。老版本turbine.combine-host-port默认值是false,新版本中已将默认值改为true。

先记录这些,详细的话可以从书中找答案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章