Hexo

点滴积累 豁达处之

0%

01 基础知识和微服务构建

微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务

1,基础知识

什么是微服务架构

​ 微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间通过基于HTTP的RESTful API 进行通信协作,

微服务的问题

  • 运维的新挑战:在微服务架构中,运维人员需要维护的进程数量会大大增加。
  • 接口的一致性:虽然我们拆分了服务,但是业务逻辑上的依赖并不会消除,只是单体应用中的代码依赖变成了服务间的通行依赖。
  • 分布式的复杂性:由于拆分后的各个微服务都是独立部署并运行在各自的进程中,他们只能通过通信来进行协作。所以分布式环境的问题都将是微服务架构系统设计师需要考虑的重要因素,比如网络延迟,分布式事物,异步消息等。

spring cloud子项目

  • Spring Cloud Config:配置管理工具,支持使用git存储配置内容,可以使用它实现应用配置的外部化存储,并支持客户端配置信息刷新,加密/解密配置内容等
  • Spring Cloud Netflix:核心组件,对多个NetflixOSS开元套件进行整合。
    • Eureka: 服务治理组件,包含服务注册中心,服务注册与发现机制的实现。
    • Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和故障提供强大的容错能力。
    • Ribbon: 客户端负载均衡的服务调用组件。
    • Feign: 基于Ribbon 和 Hystrix的声明是服务调用组件。
    • Zuul: 网关组件,提供智能路由,访问过滤等功能。
    • Archaius:外部化配置组件
  • Spring Cloud Bus:事件,消息总线,用于传播集群中状态变化或事件,以触发后续的处理。比如用来动态刷新配置等。
  • Spring Cloud Cluster:针对ZooKeeper,Redis,Hazelcast,Consul的选举算法和通用状态模式的实现。
  • Spring Cloud Cloudfoundry: 与Pivotal Cloudfoundry 的整合支持。
  • Spring Cloud Consul: 服务发现与配置管理工具。
  • Spring Cloud Stream:通过Redis,Rabbit或者Kafka实现的消费微服务,可以通过简单的声明式模型来发送和接收消息。
  • ……

2,微服务构建

2.1 配置文件

2.1.1 Yml配置信息

  • spring.profiles: 属性来定义多个不同环境配置
  • server.port: 指定端口

2.1.2 自定义参数

配置文件中可以配置自定义属性。比如:

1
2
book.name=SpringCloudInAction
book.name=ZhaiYongchao

然后,在应用中可以通过@Value注解来加载这些自定义参数。比如:

1
2
3
4
5
6
7
8
9
@Component
public class Book {
@Value("${book.name}")
private String name;

@Value("${book.author}")
private String author;

}

@Value 注解加载属性值得时候可以支持两种表达式来进行配置,如下所示。

  • 一种是上面介绍的PlaceHolder方式, 格式为${…}
  • 另一种是使用SpEL表达式(Spring Expressing Language),格式为#{…}

2.1.3 参数引用

在application.properties中的各个参数之间可以直接通过使用PlaceHolder的方式进行引用,就像下面的设置:

1
2
3
book.name=SpringCloud
book.author=zhaiyongchao
book.desc=${book.author} is writing《${book.name}》

2.1.4 使用随机数

1
2
3
4
5
6
7
8
9
10
11
# ${random} 的配置方式主要有一下几种
# 随机字符串
com.didispace.blog.value=${random.value}
# 随机int
com.didispace.blog.number=${random.int}
# 随机long
com.didispace.blog.bignumber=${random.long}
# 10以内随机数
com.didispace.blog.test1=${random.int(10)}
# 10-20 随机数
com.didispace.blog.test2=${random.int[10,20]}

2.1.5 多环境配置

多环境配置的文件名需要满足application-{profile}.properties的格式,其中{profile}对应你的环境标识,

  • application-dev.properties: 开发环境
  • application-test.properties: 测试环境
  • application-prod.properties: 生产环境

至于具体哪个配置文件会被加载,需要在application.properties文件中通过spring.profiles.active 属性来设置,其值对应配置文件中的{profile}值。如spring.profiles.active=test就会加载application-test.properties配置文件内容。

2.2 加载顺序

Spring Boot 使用了下面这种较为特别的属性加载顺序:

  1. 在命令行中传入参数。
  2. SPRING_APPLICATION_JSON中的属性。SPRING_APPLICATION_JSON是以JSON格式配置在系统环境变量中的内容。
  3. java:comp/env中的JNDI属性
  4. java的系统属性,可以通过System.getProperties()获取内容
  5. 操作系统的环境变量
  6. 通过random.*配置的随机属性
  7. 位于当前应用jar包之外,针对不同{profile}环境的配置文件内容,例如application-{profile}.properties 或是YAML定义的配置文件。
  8. 位于当前应用jar包之内,针对不同{profile}环境的配置文件内容,例如application-{profile}.properties 或是YAML定义的配置文件。
  9. 位于当前应用jar包之外的application.properties 和 YAML配置内容。
  10. 位于当前应用jar包之内的application.properties 和 YAML配置内容。
  11. 在@Configuration注解修改的类中,通过@PropertySource注解定义的属性。
  12. 应用默认属性,使用SpringApplication.setDefaultProperties定义的内容。

优先级按上面的顺序由高到低,数字越小优先级越高

2.3 初识actuator

  1. 新增spring-boot-starter-actuator依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  2. 关闭验证:management.security.enabled=false

  3. 启动应用,可以在控制台中看到一批端点定义

    actuator

2.3.1原生端点

原生端点分为三类:

  • 应用配置类:获取应用程序中加载的应用配置,环境变量,自动化配置报告等与Spring Boot应用密切相关的配置类信息
  • 度量指标类:获取应用程序运行过程中用于监控的度量指标,比如内存信息,线程池信息,http请求统计等。
  • 操作控制类:提供了对应用的关闭等操作类功能
2.3.2 应用配置类

​ 由于Spring Boot为了改善传统Spring应用繁杂的配置内容,采用了包扫描和自动化配置的机制来加载原本集中于XML 文件中的各项内容,虽然这样的做法让我们的代码变得非常简洁,但是整个应用实例创建和依赖关系等信息都被分散到各个配置类的注解上,这使得我们分析整个应用中资源和实例的各种关系变得非常困难。而这类端点可以帮助我们轻松获取一系列关于Spring 应用配置内容的详细报告,比如自动化配置的报告,Bean创建的报告,环境属性的报告等。

  • /autoconfig: 该端点用来获取应用的自动化配置报告,其中包括所有自动化配置的候选项。同时还列出了每个候选项是否满足自动化配置的各个先决条件。所以该端点可以帮助我们方便地找到一些自动化配置为什么没有生效的具体原因。该报告内容将自动化配置内容分为以下两部分。

    • positiveMatches 中返回的是条件匹配成功的自动化配置

    • negativeMatches 中返回的是条件匹配不成功的自动化配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      {
      "positiveMatches": { //条件匹配成功的
      "DruidDataSourceAutoConfigure": [
      {
      "condition": "OnClassCondition",
      "message": "@ConditionalOnClass found required class 'com.alibaba.druid.pool.DruidDataSource'; @ConditionalOnMissingClass did not find unwanted class"
      }
      ]
      "negativeMatches": { //条件匹配不成功的
      "DruidSpringAopConfiguration": {
      "notMatched": [
      {
      "condition": "OnPropertyCondition",
      "message": "@ConditionalOnProperty (spring.datasource.druid.aop-patterns) did not find property 'spring.datasource.druid.aop-patterns'"
      }
      ],
      "matched": []
      }
      }

    从如上示例中我们可以看到,每个自动化配置候选项中都有一系列的条件,所以当我们发现有一些期望的配置没有生效时,就可以通过该端点来查看没有生效的具体原因。

  • /beans: 该端点用来获取应用上下文中创建的所有Bean。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "context": "application:8081",
    "parent": null,
    "beans": [
    {
    "bean": "transactionApplication",
    "aliases": [],
    "scope": "singleton",
    "type": "com.spring.transaction.TransactionApplication$$EnhancerBySpringCGLIB$$124e63db",
    "resource": "null",
    "dependencies": []
    },

    在如上示例中,我们可以看到在每个Bean中都包含了下面这些信息。

    • bean:Bean的名称
    • scope:Bean的作用域
    • type:Bean的java类型
    • resource:class文件的具体路径
    • dependencies:以来的Bean的名称
  • /configprops:该端点用来获取应用中配置的属性信息报告。prefix 属性代表了属性的配置前缀,properties代表了各个属性的名称和值。所以,我们可以通过该报告来看到各个属性的配置路径,比如我们要关闭该端点,就可以通过使用endpoints.enabled=false 来完成设置

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "endpoints-org.springframework.boot.actuate.endpoint.EndpointProperties": {
    "prefix": "endpoints",
    "properties": {
    "enabled": true,
    "sensitive": null
    }
    },
  • /env: 该端点与Iconfigprops不同, 它用来获取应用所有可用的环境属性报告。 包括环境变量、只币4属性、应用的配置属性、命令行中的参数。从下面该端点返回的示例片段中, 可以看到它不仅返回了应用的配置属性, 还返回了系统属性、环境变量等丰富的配置信息, 其中还包括了应用还没有使用的配置, 所以它可以帮助我们方便地看到当前应用可以加载的配置信息, 并配合@ConfigurationProperties 注解将它们引入到我们的应用程序中来进行使用。 另外, 为了配置属性的安全, 对一
    于 些类似密码等敏感信息, 该端点都会进行隐私保护, 但是我们需要让属性名中包含password、secret、key这些关键词, 这样该端点在返回它们的时候会使用女来替代实际的属性值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    {
    "profiles": [],
    "server.ports": {
    "local.server.port": 8081
    },
    "servletContextInitParams": {},
    "systemProperties": {
    "java.runtime.name": "Java(TM) SE Runtime Environment",
    "user.language": "zh",
    "java.version": "1.8.0_144",
    "sun.desktop": "windows",
    },
    "systemEnvironment": {
    "APPDATA": "C:\\Users\\chenqingsong\\AppData\\Roaming",
    "USERNAME": "chenqingsong",
    "USERDNSDOMAIN": "ALAMAP.COM",
    "ProgramFiles(x86)": "C:\\Program Files (x86)",
    },
    "applicationConfig: [classpath:/application.properties]": {
    },
    "applicationConfig: [classpath:/application.yml]": {
    "server.port": 8081,
    "management.security.enabled": false,
    "spring.datasource.name": "test",
    "spring.datasource.url": "jdbc:mysql://localhost:3306/springlearning",
    "spring.datasource.username": "root",
    }
    }
  • /mappings: 该端点用来返回所有SpringMVC的控制器映射关系报告。从下面的示例片段中,我们可以看到该报告的信息与我们在启用SpringMVC的Web应用时输出的日志信息类似,其中bean属性标识了该映射关系的请求处理器,method属性标识了该映射关系的具体处理类和处理函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    "/webjars/**": {
    "bean": "resourceHandlerMapping"
    },
    "/**": {
    "bean": "resourceHandlerMapping"
    },
    "/**/favicon.ico": {
    "bean": "faviconHandlerMapping"
    },
    }
  • /info: 该端点用来返回一些应用自定义的信息。默认情况下,该端点只会返回个 空的JSON内容。我们可以在applicatio口.properties配置文件中通过info前缀来设置一些属性,比如下面这样

    1
    info.app.version=v1.0.0

    再访问/info端点,我们可以得到下面的返回报告,其中就包含了上面我们在应用

2.3.3 度量指标类

​ 上面我们所介绍的应用配置类端点所提供的信息报告在应用启动的时候就己经基本确定了其返回内容,可以说是一个静态报告。而度量指标类端点提供的报告内容则是动态变化的,这些端点提供了应用程序在运行过程中的一些快照信息,比如内存使用情况、HTTP请求统计、外部资源指标等。这些端点对于我们构建微服务架构中的监控系统非常有帮助,由于SpringBoot应用自身实现了这些端点,所以我们可以很方便地利用它们来收集我们想要的信息,以制定出各种自动化策略。下面,我们就来分别看看这些强大的端点功能。

  • /metrics: 该端点用来返回当前应用的各类重要度量指标,比如内存信息、线程信息、垃圾回收信息等。
1
2
3
4
5
6
7
8
9
10
11
{
"mem": 380778,
"mem.free": 267127,
"processors": 4,
"instance.uptime": 973964,
"uptime": 980820,
"systemload.average": -1,
"heap.committed": 327680,
"heap.init": 131072,
"heap.used": 60552,
}

从上面的示例中,我们看到有如下这些重要的度量值。

  1. 系统信息:包括处理器数量 processors、运行时间uptime 和instance.uptime、系统平均负载systemload.averageo
  2. mem:内存概要信息,包括分配给应用的总内存数量以及当前空闲的内存数量。这些信息来自java.la口g.Runtime。
  3. heap :堆内存使用情况。 这些信息来自 java.lang.ma口agemeηt. MemoryMXBea口 接口中 getHeapMemoryUsage 方法获取的java.lang. managemeηt.MemoryUsageo
  4. nonheap: 非堆内存使用情况。 这些信息来自 java.la口g.management. MemoryMXBean接口中getNo口HeapMemoryUsage方法获取的java .. lang. management.MemoryUsage。
  5. threads :线程使用情况,包括线程数、守护线程数(daemon)、线程峰值 (peak)等, 这些数据均来自java.lang.manageme口t.ThreadMXBeano
  6. classes :应用加载和卸载的类统计。这些数据均来自java.la叼.mana弓ement. ClassLoadingMXBean。
  7. gc.: 垃圾收集器的详细信息,包括垃圾回收次数gc.ps scave口ge.cou口t、垃圾回收消耗时间 gc.ps scave口ge.time、标记-清除算法的次数 gc.ps marksweep.count、标记-清除算法的消耗时间gc.ps marksweep.time。这些数据均来自java.lang.management.GarbageCollectorMXBeano
  8. httpsessio口s.: Tomcat容器的会话 使用情况。 包括最大会话数 httpsessions.max和活跃会话数httpsessions.active。 该度量指标信 息仅在引入嵌入式Tomcat作为应用容器的时候才会提供。
  9. gauge: HTTP请求的性能指标之,它主要用来反映一个绝对数值。比如上面示例中的gauge.response.hello;它表示上一次hello请求的延迟时间为5秒
  10. counter: HTTP请求的性能指标之一,它主要作为计数器来使用,记录了增加量和减少量。上述示例中的counter.status. 200. hello: 11,它代表了hello请求返回200状态的次数为1

对于gauge.*和counter.女的统计,这里有一个特殊的内容请求star-star它代表了对静态资源的访问。这两类度量指标非常有用,我们不仅可以使用它默认的统计指标,还可以在程序中轻松地增加自定义统计值。只需要通过注入 org.springframework.boot.actuate.metrics.Cou口terService和org. springframework.boot.actuate.metrics.GaugeService来实现自定义的统计指标信息。比如我们可以像下面这样自定义实现对hello接口的访问次数统计

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class HelloController {
@Autowired
private CounterService counterService;

@RequestMapping("/hello")
public String greet(){
counterService.increment("didispace.hello.count");
return "";
}
}
  • /metrics: 端点可以提供应用运行状态的完整度量指标报告,这项功能非常实用,但是对于监控系统中的各项监控功能,它们的监控内容、数据收集频率都有所不同,如果每次都通过全量获取报告的方式来收集,略显粗暴。所以,我们还可以通过 /metrics/{口ame}接口来更细粒度地获取度量信息,比如可以通过访问 /metrics/mem.free来获取当前可用内存数量。
  • /health: 该端点在一开始的示例中我们已经使用过了,它用来获取应用的各类健康指标信息。在spri口g-boot-starter-actuator模块中自带实现了些常用资源的健康指标检测器。这些检测器都通过Healthindicator接口实现,并且会根据依赖关系的引入实现自动化装配,比如下面列出的这些。
检测器 功能
DiskSpaceHealthlndicator 低磁盘空间检测
DataSourceHealthlndicator 检测 DataSource 的连接是否可用
MongoHealthlndicator 检测 Mongo 数据库是否可用
RabbitHealthlndicator 检测 Rabbit 服务器是否可用
RedisHealthlndicator 检测 Redis 服务器是否可用
SolrHealthlndicator 检测 Solr 服务器是否可用

有时候,我们可能还会用到 些 Spring Boot 的 Starter POMs 中还没有封装的产品来进行开发, 比如, 当使用 RocketMQ 作为消息代理时, 由于没有自动化配置的检测器,所以需要自己来实现 个用来采集健康信息的检测器。 我们可以在 Spring Boot 的应用中,为org.springframework.boot.actuate.health.Healthindicator 接口实现 个对 RocketMQ 的检测器类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class RocketMQHealthInficator implements HealthIndicator {
@Override
public Health health() {
int errorCode = check();
if(errorCode != 0){
return Health.down().withDetail("Error Code", errorCode).build();

}
return null;
}

private int check() {
//对监控数据的检测
return 0;
}
}

通过重写health ()函数可实现健康检查,在返回的Heath对象中, 共有两项内容,一个是状态信息,除了该示例中的UP与与DOWN之外,还有UNKNOWN和UT OF SERVICE, 可以根据需要来实现返回;还有一个详细信息, 采用 Map 的方式存储,在这里通过withDetail函数,注入一个Error Code 信息,我们也可以填入其他信息,比如,检测对象的E地址、 端口等。
重新启动应用,并访问/health接口,我们在返回的JSON 字符串中,将会包含如 下信息:

1
2
3
"rocketMq": {
"status": "UP"
}
  • /dump: 该端点用来暴露程序运行中的线程信息。它使用j ava.lang.manageme口t. ThreadMXBean的dumpAllThreads方法来返回所有含有同步信息的活动线程详情。

  • /trace : 该端点用来返回基本的HTTP 跟踪信息。 默认情况下, 跟踪信息的存储采用 org.springframework.boot.actuate.trace.I口MemoryTraceRepository实现的内存方式, 始终保留最近的100 条请求记录。

2.3.4 操作控制类

仔细的读者可能会发现, 我们在 “ 初识 actuator '' 小节中的示例的控制台中输出的所有监控端点, 己经在介绍应用配置类端点和度量指标类端点时都讲解完了。 那么还有哪些是 操作控制类端点呢?实际上, 由于之前介绍的所有端点都是用来反映应用自身的属性或是运行中的状态,相对于操作控制类端点没有那么敏感,所以它们都是默认启用的。而操作 控制类端点拥有更强大的控制能力,如果要使用它们的话,需要通过属性来配置开启操作。

​ 在原生端点中,只提供了一个用来关闭应用的端点:/shutDown(在后续我们引用了Eureka之后,会引入更多控制端点)可以通过如下配置开启它:

1
endpoints.shutdown.enabled=true

在配置了上述属性之后,只需要访问该应用的/shutdown端点就能实现关闭该应用的远程操作。由于开放关闭应用的操作本身是一件非常危险的事,所以真正在线上使用的时候,需要对其加入一定的保护机制,比如定制actuator的端点路径、整合SpringSecurity进行安全校验等。

小结

​ 本章我们通过构建一个最基本的SpringBoot工程,让大家对其有一个最直观的的感受。同时,也为后续构建各类SpringCloud组件和微服务应用做了些基础准备工作。另外,我们对SpringBoot中的配置原理以及监控管理做了深入的介绍,因为这些内容将在后续的介绍中有所涉及,并且它们有助于理解SpringCloud组件的运行原理。更多关于使用Spring Boot开发微服务应用的内容,我们不在本书中详细介绍,读者可参阅官方文档或其他书籍资料来学习。