SpringCloud 开发与部署概要
- 1.概述
- 2.使用实例(基于 IntelliJ IDEA)
- 3.高可用服务注册中心
- 3.1 新建 spring-cloud-registry/application-peer1.properties
- 3.2 新建 spring-cloud-registry/application-peer2.properties
- 3.3 新建 spring-cloud-provider/application-peer1.properties
- 3.4 新建 spring-cloud-provider/application-peer2.properties
- 3.5 确认 spring-cloud-consumer/application.properties
- 3.6 启动所有注册中心和服务提供者、服务消费者
- 3.7 查看两个服务中心的控制台
Alibaba Dubbo 是国内流行的微服务框架,而 SpringCloud 是国外流行的微服务框架。SpringCloud 为微服务提供一站式完整的解决方案,具有独特的优势和发展前景。本文讲述了 SpringCloud 的历史和版本号规则、与同类产品相比的优势,并基于 SpringBoot 演示了服务注册中心、服务提供者和消费者的实现方法,并说明了高可用注册中心的部署思路。
作者:王克锋
出处:https://kefeng.wang/2017/12/20/spring-cloud/
版权:自由转载-非商用-非衍生-保持署名,转载请标明作者和出处。
1.概述
Spring Cloud 是一个基于 Spring Boot 实现的“微服务架构”开发工具,它为基于JVM的云应用开发中涉及的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。
所谓“微服务架构”,就是将一个完整的应用,从数据存储开始垂直拆分成多个不同的服务,每个服务都能独立部署、独立维护、独立扩展,服务与服务间通过诸如 RESTful API 的方式互相调用。
1.1 参考资料
Microservice Architecture
Martin Flower - 微服务
Spring Cloud 官网
Spring Cloud 中文网
Spring Cloud
《Spring Cloud微服务实战》 - 电子工业出版社 - 翟永超 著
1.2 Spring Cloud 版本
Spring Cloud 是一个拥有众多子项目的大项目,每个子项目都有自己的版本号,每个 Spring Cloud 版本会包含不同版本的子项目。为避免主项目与子项目版本号混淆,Spring Cloud 版本不采用数字方式,而是通过命名方式。
这些版本名称采用了伦敦地铁站名,按字母表顺序使用,如 Angel / Brixton / Camden / Dalston / Edgware / Finchley(尚未发布正式版本),当 Spring Cloud 项目的发布内容积累的重要程度时,就会发布一个“Service Releases”版本,简称 SRn 版本,其中n是一个递增数字。
Spring Cloud 版本列表见这里:Spring Cloud Dependencies
各大版本及其最新 SR、首SR月份、最新SR月份,如下(截止 2018-01-18,只涉及正式版本GA):
- Angel.SR6: 2016-01
- Brixton.SR7: 2016-05 ~ 2016-11
- Camden.SR7: 2016-09 ~ 2017-05
- Dalston.SR5: 2017-04 ~ 2017-12
- Edgware.SR1: 2017-11 ~ 2018-01
建议使用最新的 SR 正式版本(当前为 Edgware.SR1
)。
注意 Edgware 不支持 SpringBoot 2.x,可用 1.5.10.RELEASE(1.x 最高版本)。
否则应用类启动时会报错:java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder.<init>(...)
1.3 各类生态组件
- Spring Cloud Eureka: 服务治理组件(服务发现)
- Spring Cloud Ribbon: 客户端负载均衡组件
- Spring Cloud Hystrix: 服务容错保护组件(断路器)
- Spring Cloud Feign: 声明式服务调用组件
- Spring Cloud Zuul: API 网关治理组件(智能路由)
- Spring Cloud Config: 分布式配置中心组件
- Spring Cloud Bus: 消息总线组件
- Spring Cloud Stream: 消息驱动组件
- Spring Cloud Sleuth: 分布式服务跟踪组件
SpringCloud 流程图:
- 请求统一通过API网关(Zuul)来访问内部服务.
- 网关接收到请求后,从注册中心(Eureka)获取可用服务
- 由Ribbon进行均衡负载后,分发到后端具体实例
- 微服务之间通过Feign进行通信处理业务
- Hystrix负责处理服务超时熔断
- Turbine监控服务间的调用和熔断相关指标
1.4 SpringCloud 与 Dubbo/Dubbox 的比较
与 Dubbo 相比,SpringCloud 提供了完整的微服务各个组件(核心仍然是服务治理),提供更灵活的 REST API 调用方式(Dubbo只有RPC,当当扩展的Dubbox优化了RPC并增加了REST API)。
Dubbo 采用 Zookeeper 作为服务注册中心,因此只需要开发 Productor 和Consumer 即可;而 Spring Cloud 需要我们开发一个服务治理服务(服务注册中心),不过这对于 Spring Boot 是很便捷的。
对比点 | SpringCloud | Dubbo |
---|---|---|
出品者 | Spring社区 | 阿里巴巴 |
活跃度 | 更新频繁 | 五年未更新,刚重启更新 |
流行地 | 国外 | 国内 |
功能 | 整个微服务架构的各方面,包括服务治理,像品牌机稳定 | 只是实现了服务治理(注册/发现/监控/调度等),其他环节要自己接入,像组装机有风险 |
注册中心 | Netflix Eureka(遵循AP) | ZooKeeper(遵循CP,欠佳) |
调用方式 | RPC/REST API(供消双方没有代码级别的强依赖) | RPC(供消双方需要使用共同接口,必须对外封装出REST API),当当的dubbox是添加了REST API支持 |
编码侵入性 | 有侵入 | 通过Spring配置,无侵入 |
开发文档 | 英文、中文(SpringCloud中文网、SpringCloud中国社区) | 中文、英文 |
国际上,SpringCloud是主流;而Dubbo在国内是主流,有一定局限性,现在Dubbo在积极适配SpringCloud。长远来看,SpringCloud是未来趋势。
2.使用实例(基于 IntelliJ IDEA)
Spring Cloud 为服务治理(服务注册中心、服务注册与发现)做了一层抽象接口,Spring Cloud 应用中支持多种不同的服务治理框架,比如 Eureka([ju’riːkə]推荐使用) / Consul / Zookeeper,这些选项可以在新建项目时 Cloud Discovery 分支下看到。本例也是使用 Eureka。
Eureka分为两部分:
- Eureka Server: 服务注册中心(服务注册中心使用);
- Eureka Client: 服务注册(服务提供者使用)、服务发现(服务消费者使用)。
本例中,各个模块都运行在本机上,端口规划如下:
- 服务注册中心(集群):800x,如 8001,8002
- 服务提供者(多个):806x,如 8061,8062,8063
- 服务消费者(多个):808x,如 8081,8082
2.0 创建父项目 spring-cloud-demo
创建 Maven 工程(不依赖于任何 Maven 框架),创建后删除 src 目录。
wang.kefeng/spring-cloud-demo
2.1 创建子模块 spring-cloud-registry(服务注册中心)
依赖选择: Cloud Discovery / Eureka Server(服务端)
2.1.1 pom.xml
其中的 spring-cloud-starter-eureka-server
已废弃(Deprecated),需要手工改为 spring-cloud-starter-netflix-eureka-server
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
29
30
31
32<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.SR1</spring-cloud.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.1.2 application.properties
指定端口号为 8001,注意必须指定注册中心URL(eureka.client.serviceUrl.defaultZone,不论是否需要向它注册):
- 如果只有一个注册中心: 指定自身URL,指定“不注册自身至注册中心”(eureka.client.register-with-eureka=false);
- 如果有多个注册中心:指定其他注册中心URL,指定“要注册自身至指定的其他注册中心”(eureka.client.register-with-eureka=true);
1 | server.port=8001 |
2.1.3 SpringCloudRegistryApplication.java
增加注解 @EnableEurekaServer
启用“服务注册中心(Eureka Server)”
2.1.4 运行
运行 SpringCloudRegistryApplication.java1
2
3### spring-cloud-registry 日志
10:01:27.601 INFO [EurekaServerInitializerConfiguration.java:72] - Started Eureka Server
10:01:27.726 INFO [StartupInfoLogger.java:57] - Started SpringCloudRegistryApplication in 15.558 seconds (JVM running for 21.684)
进入 http://localhost:8001/
可见 “Instances currently registered with Eureka” 一栏,还没有注册到 Eureka 的服务提供者和消费者的实例。
2.2 创建子模块 spring-cloud-provider(服务提供方)
依赖选择: Cloud Discovery / Eureka Discovery(客户端)
2.2.1 pom.xml
其中的 spring-cloud-starter-eureka
已废弃(Deprecated),要手工改为 spring-cloud-starter-netflix-eureka-client
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
29
30
31
32<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.SR1</spring-cloud.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.2.2 application.properties
- 指定“服务提供者”的端口号为 8061;
- 服务提供者的名称为 spring-cloud-provider,将来 spring-cloud-consumer 以该名称调用该服务;
- 必须指定“服务注册中心”的地址 eureka.client.serviceUrl.defaultZone
1
2
3server.port=8061
spring.application.name=spring-cloud-provider
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
2.2.3 SpringCloudProviderController.java
实现了两个URL:
- /add: 服务提供者的 add 方法的具体实现;
- /info: 在“服务注册中心”控制台中有“服务提供者”的链接,用来查看服务提供者的信息。
1 | // 指定数据格式为 JSON(Apache Jackson) |
2.2.4 SpringCloudProviderApplication.java
增加注解 @EnableDiscoveryClient
激活 Eureka 中的 DiscoveryClient 实现(SpringCloudProviderController 要用)。
2.2.5 运行
运行 SpringCloudProviderApplication.java1
2
3
4
5
6
7
8### spring-cloud-provider 日志(向注册中心注册服务)
10:13:36.707 INFO [DiscoveryClient.java:804] - DiscoveryClient_SPRING-CLOUD-PROVIDER/192.168.1.101:spring-cloud-provider:8061: registering service...
10:13:36.878 INFO [DiscoveryClient.java:813] - DiscoveryClient_SPRING-CLOUD-PROVIDER/192.168.1.101:spring-cloud-provider:8061 - registration status: 204
10:13:36.925 INFO [StartupInfoLogger.java:57] - Started SpringCloudProviderApplication in 8.455 seconds (JVM running for 9.204)
### spring-cloud-registry 日志(服务提供者 SPRING-CLOUD-PROVIDER 的注册信息)
10:13:36.909 INFO [AbstractInstanceRegistry.java:267] - Registered instance SPRING-CLOUD-PROVIDER/192.168.1.101:spring-cloud-provider:8061 with status UP (replication=false)
10:13:37.565 INFO [AbstractInstanceRegistry.java:267] - Registered instance SPRING-CLOUD-PROVIDER/192.168.1.101:spring-cloud-provider:8061 with status UP (replication=true)
进入注册中心控制台 http://localhost:8001/
可以看到服务提供者 spring-cloud-provider
也可以点击查看其信息: http://localhost:8061/info
2.3 创建子模块 spring-cloud-consumer(服务消费方)
依赖选择: Cloud Discovery / Eureka Discovery(客户端)
2.3.1 pom.xml
其中的 spring-cloud-starter-eureka
已废弃(Deprecated),要手工改为 spring-cloud-starter-netflix-eureka-client
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
29
30
31
32<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.SR1</spring-cloud.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.3.2 application.properties
- 指定“服务消费者”的端口号为 8081(面向 SpringCloud 外部的 API 端口)
- 指定“服务消费者”的名称为 spring-cloud-provider
- 指定“服务注册中心”的地址 eureka.client.serviceUrl.defaultZone
1 | server.port=8081 |
2.3.3 SpringCloudConsumerController.java
实现了两个URL:
- /add: 面向 SpringCloud 外部调用的 API 接口,其中调用了服务提供者的 add 方法,注意 URL 格式为 http://providerName/methodName?argName1=argValue1&argName2=argValue2;
- /info: 在“服务注册中心”控制台中有“服务消费者”的链接,用来查看服务消费者的信息。
1 | // 指定数据格式为 JSON(Apache Jackson) |
2.3.4 SpringCloudConsumerApplication.java
1 | // 激活 Eureka 中的 DiscoveryClient 实现(SpringCloudProviderController 要用) |
负载均衡分为两种:
- 服务端负载均衡:服务清单和功能实现在服务端,位于各服务器节点之前,分为硬件(如F5)和软件(如Nginx)两种形式;
- 客户端负载均衡:服务清单和功能实现在客户端,比如 SpringCloud 的 Ribbon,Ribbon从注册中心获取服务提供者列表,以轮询的方式向各提供者请求,起到负载均衡效果,SpringCloud 只需以 @LoadBalanced 注解就能实现客户端负载均衡。
2.3.5 运行
运行 SpringCloudConsumerApplication.java1
2
3
4
5
6
7
8### spring-cloud-consumer 日志(向注册中心注册服务)
10:27:46.585 INFO [DiscoveryClient.java:804] - DiscoveryClient_SPRING-CLOUD-CONSUMER/192.168.1.101:spring-cloud-consumer:8081: registering service...
10:27:46.647 INFO [DiscoveryClient.java:813] - DiscoveryClient_SPRING-CLOUD-CONSUMER/192.168.1.101:spring-cloud-consumer:8081 - registration status: 204
10:27:46.771 INFO [StartupInfoLogger.java:57] - Started SpringCloudConsumerApplication in 8.371 seconds (JVM running for 10.392)
### spring-cloud-registry 日志(服务消费者 SPRING-CLOUD-CONSUMER 的注册信息)
10:27:46.631 INFO [AbstractInstanceRegistry.java:267] - Registered instance SPRING-CLOUD-CONSUMER/192.168.1.101:spring-cloud-consumer:8081 with status UP (replication=false)
10:27:47.158 INFO [AbstractInstanceRegistry.java:267] - Registered instance SPRING-CLOUD-CONSUMER/192.168.1.101:spring-cloud-consumer:8081 with status UP (replication=true)
进入注册中心控制台 http://localhost:8001/
可以看到服务消费者 spring-cloud-consumer
也可以点击查看其信息: http://localhost:8081/info
打开 SpringCloud 对外的 API 接口,可看到计算结果:http://localhost:8081/add
2.4 辅助脚本
1 | @echo off & cls |
编译中可能遇到警告:WARN [URLConfigurationSource.java:121] - No URLs will be polled as dynamic configuration sources.
无需理会,也可以增加空的配置文件来避免:resources/config.properties
3.高可用服务注册中心
前面的服务注册中心(Eureka Server)是单点的,可能导致单点故障,生产环境应该构建高可用的Eureka Server集群。下面以双节点注册中心为例来构建:
Eureka服务治理体系:
- 服务注册中心(registry, Eureka Server): 存储各服务提供者,分两层存储,第一层为服务名称,第二层为服务实例名称;
- 服务提供者(provider, Eureka Client): 发送 REST 请求,将自己注册到 Eureka Server 上;并定时向 Eureka Server 发送心跳包,以告知自己还在运行(称作“服务续约”);当服务提供者正常退出时,会通过 REST 请求告知服务中心,但异常退出时不会(此时由心跳机制弥补)。
- 服务消费者(consumer, Eureka Client):发送 REST 请求,向注册中心查询当前有效的服务提供者列表,然后直接调用 provider。
相关参数有:
- 关闭保护机制(配置在注册中心): 关闭后(建议采用此方式),严格以心跳断定服务提供者是否失效,认为失效时直接当作下线,不会采用一定算法的保护机制认为它可能还有效;
- 提供者列表的刷新间隔(配置在注册中心):默认值为 30(秒),注册中心缓存了一份服务提供者清单,消费者查询时的数据来自该缓存,该参数定义了最新实际有效的提供者列表向缓存更新的时间间隔;
- 服务失效时间(配置在注册中心): 默认值为 90(秒),注册中心每60秒检查一遍,在90s内没有收到某个服务提供者的心跳包,就把它看作下线,从提供给消费者的列表中剔除;
- 心跳发送间隔(配置在服务提供者): 默认值为 30(秒),服务提供者每隔这个时间段,就会向注册中心发送心跳包;
1
2
3
4eureka.server.enable-self-preservation=false
eureka.client.registry-fetch-interval-seconds=30
eureka.instance.lease-expiration-duration-in-seconds=90
eureka.instance.lease-renewal-interval-in-seconds=30
当有多个注册中心(集群)时,各服务中心相互注册为服务,只要其中一个注册中心收到服务提供者的注册信息,就会同步给其他的注册中心,以保证任何注册中心的注册表都是完整的。以两个服务中心、两个服务提供者为例,构建方案如下:
3.1 新建 spring-cloud-registry/application-peer1.properties
指定自身端口为 8001,注册中心地址为第二个注册中心(localhost:8002);
开启 eureka.client.register-with-eureka(把自身注册到注册中心),第一个注册中心会注册到第二个注册中心;1
2
3
4
5server.port=8001
spring.application.name=spring-cloud-registry
eureka.client.serviceUrl.defaultZone=http://localhost:8002/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=false
3.2 新建 spring-cloud-registry/application-peer2.properties
指定自身端口为 8002,注册中心地址为第一个注册中心(localhost:8001);
开启 eureka.client.register-with-eureka(把自身注册到注册中心),第二个注册中心会注册到第一个注册中心;1
2
3
4
5server.port=8002
spring.application.name=spring-cloud-registry
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=false
3.3 新建 spring-cloud-provider/application-peer1.properties
指定注册中心为第一个注册中心(也可以指定为所有注册中心URL列表,以逗号分隔)。
如果指定多个注册中心,那么会逐个尝试注册,一旦有一个注册成功,就不会在向后续注册中心注册。1
2
3
4server.port=8061
spring.application.name=spring-cloud-provider
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
## eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/,http://localhost:8002/eureka/
3.4 新建 spring-cloud-provider/application-peer2.properties
指定注册中心为第二个注册中心(也可以指定为所有注册中心URL列表,以逗号分隔)。1
2
3
4server.port=8062
spring.application.name=spring-cloud-provider
eureka.client.serviceUrl.defaultZone=http://localhost:8002/eureka/
## eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/,http://localhost:8002/eureka/
3.5 确认 spring-cloud-consumer/application.properties
指定注册中心为第一个注册中心(也可以指定为所有注册中心URL列表,以逗号分隔)。1
2
3
4server.port=8081
spring.application.name=spring-cloud-consumer
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
## eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/,http://localhost:8002/eureka/
3.6 启动所有注册中心和服务提供者、服务消费者
1 | java -jar target/spring-cloud-registry-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 |
发现消费者依次调用了两个服务提供者,注册中心同步机制有效,消费者也达到了负载均衡效果。
3.7 查看两个服务中心的控制台
http://localhost:8001/
http://localhost:8002/
发现他们都有两个服务提供者,都是完整的。