Spring Cloud

微服务架构的系统是一个分布式系统,按业务领域划分为独立的服务单元,有自动化运维容错、快速演进的特点,它能够解决传统单体架构系统的痛点,同时也能满足复杂的业务需求。
Spring Cloud是java语言的微服务框架,依赖于Spring boot。

1. Spring Cloud 核心组件

Spring Cloud 的本质是在 Spring Boot 的基础上,增加了一堆微服务相关的规范,并对应用上下文(Application Context)进行了功能增强。目前 Spring Cloud 规范已有 Spring 官方,Spring Cloud Netflix,Spring Cloud Alibaba等实现。通过组件化的方式,Spring Cloud 将这些实现整合到一起构成全家桶式的微服务技术栈。
Spring Cloud Netflix组件

| 组件名称 | 作用 | | —- | —- |

| Eureka | 服务注册中心,服务的注册和发现组件 |

| Ribbon | 客户端负载均衡组件 |

| Feign | 声明式服务调用 |

| Hystrix | 客户端容错保护,熔断组件 |

| Zuul | API服务网关 |

Spring Cloud Alibaba组件

| 组件名称 | 作用 | | —- | —- |

| Nacos | 服务注册中心 |

| Sentinel | 客户端容错保护 |

Spring Cloud原生及其他组件

| 组件名称 | 作用 | | —- | —- |

| Consul | 服务注册中心 |

| Config | 分布式配置中心,配置文件统一管理 |

| Gateway | API服务网关 |

| Sleuth/Zipkin | 分布式链路追踪组件 |

| Security | 服务单元用户验证和权限认证 |

| Stream | Spring Cloud数据流操作包 |

2. Spring Cloud 体系结构图

01-Spring-Cloud-Overview - 图1
Spring Cloud各个组件相互配合,合作支持了一套完整的微服务架构。

  • 注册中心负责服务的注册与发现,很好将各服务连接起来
  • 断路器负责监控服务之间的调用情况,连续多次失败进行熔断保护。
  • API网关负责转发所有对外的请求和服务
  • 配置中心提供了统一的配置信息管理服务,可以实时的通知各个服务获取最新的配置信息
  • 链路追踪技术可以将所有的请求数据记录下来,方便进行后续分析
  • 各个组件又提供了功能完善的dashboard监控平台,可以方便的监控各组件的运行状况

    3. Spring Cloud 版本说明

    3.1. 版本命名说明

  • 多数Spring项目都是以【主版本号.次版本号.增量版本号.里程碑版本号】的形式命名版本号的。如:Spring Framework稳定版本4.3.5.RELEASE、里程碑版本5.0.0.M4等

  • 其中,主版本号表示项目的重大重构;次版本号表示新特性的添加和变化;增量版本号一般表示bug修复;里程碑版本号表示某版本号的里程碑
  • 而Spring Cloud采用了伦敦地铁站的名字,根据字母表的顺序来对应版本时间顺序。以此方式的命名,为了避免与子项目版本号的重名误解

    3.2. 选择Spring Cloud版本与Spring Boot版本例子

  • 在选择了Spring Cloud版本后,需要在官网上选择对应的子项目的版本。pom.xml的依赖配置案例如下:

  • Spring Cloud版本Finchley.RELEASE对应Spring boot版本为2.0.1.RELEASE
  • Spring Cloud版本Dalston.SR5对应Spring boot版本为1.5.1.RELEASE

版本1
版本2

3.3. Spring Cloud 对应的子项目版本

官网截图(2018.6.24):https://projects.spring.io/spring-cloud/#quick-start
版本
对应子项目的说明,红色部门是重点掌握

| Component | 备注 | | —- | —- |

| spring-cloud-aws | 用于简化整合Amazon Web Service的组件 |

| spring-cloud-bus | 事件、消息总线,用于传播集群中的状态变化或事件。 |

| spring-cloud-cli | 用于在Groovy平台创建Spring Cloud应用。 |

| spring-cloud-commons | 服务发现、负载均衡、熔断机制这种模式为Spring Cloud客户端提供了一个通用的抽象层。 |

| spring-cloud-contract | |

| spring-cloud-config | 配置管理工具,支持使用git、svn等存储配置文件。并在支持客户端配置信息的刷新,加密解密配置内容等。 |

| spring-cloud-netflix | 核心组件,对多个Netflix OSS开源套件进行整合。 |

| spring-cloud-security | 安全工具包。 |

| spring-cloud-cloudfoundry | 整合Pivotal Cloudfoundry(Vmware推出的业界第一个开源PaaS云平台)支持。 |

| spring-cloud-consul | 服务发现与配置管理工具 |

| spring-cloud-sleuth | Spring Cloud应用的分布式跟踪实现。 |

| spring-cloud-stream | 通过Redis、RabbitMQ、Kafka实现的消息微服务。 |

| spring-cloud-zookeeper | 基于ZooKeeper的服务发现与配置管理组件。 |

| spring-boot | |

| spring-cloud-task | 用于快速构建数据处理的应用。 |

| spring-cloud-vault | |

| spring-cloud-gateway | Spring Cloud网关相关的整合实现。 |

4. Spring Cloud 框架的特点

  • 约定优于配置
  • 适用于各种环境。开发、部署在PC Server或各种云环境(如阿里云、AWS等)
  • 隐藏了组件的复杂性,并提供声明式、无xml的配置方式
  • 开箱即用,快速启动
  • 轻量级的组件。Spring Cloud整合的组件大多比较轻量。如:Eureka、Zuul等等,都是各自领域轻量级的实现
  • 组件丰富,功能齐全。Spring Cloud为微服务架构提供了非常完整的支持,如:配置管理、服务发现、熔断器、微服务网关等
  • 选型中立、丰富。支持使用Eureka、Zookeeper或Consul实现服务发现
  • 灵活。Spring Cloud的组成部分是解耦的

    5. Spring Cloud 和 Spring Boot 的关系

  • Spring boot 是 Spring 的一套快速配置脚手架,可以基于spring boot 快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;

  • Spring boot专注于快速、方便集成的单个个体,Spring Cloud是关注全局的服务治理框架;
  • Spring boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring boot来实现,可以不基于Spring boot吗?不可以。
  • Spring boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring boot,属于依赖的关系。
  • spring -> spring booot -> spring cloud 这样的关系。

    6. 微服务案例(不使用SpringCloud)

此案例代码地址:spring-cloud-note\spring-cloud-greenwich-sample\01-microservice-no-springcloud\

微服务架构的分布式系统,微服务之间通过网络通信。通过服务提供者与服务消费者来描述微服务间的调用关系

  • 服务提供者:服务的被调用方,提供调用接口的一方
  • 服务消费者:服务的调用方,依赖于其他服务的一方

以电商系统中常见的用户下单为例,用户向订单微服务发起一个购买的请求。在进行保存订单之前需要调用商品微服务查询当前商品库存,单价等信息。在这种场景下,订单微服务就是一个服务消费者,商品微服务就是一个服务提供者
01-Spring-Cloud-Overview - 图5

6.1. 准备数据库表

创建springcloud_sample_db数据库,案例中所涉及的表完整建表语句在项目资料spring-cloud-note\spring-cloud-greenwich-sample\document\sql
用户表:

  1. CREATE TABLE `tb_user` (
  2. `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
  3. `username` VARCHAR ( 40 ) DEFAULT NULL COMMENT '用户名',
  4. `password` VARCHAR ( 40 ) DEFAULT NULL COMMENT '密码',
  5. `age` INT ( 3 ) DEFAULT NULL COMMENT '年龄',
  6. `balance` DECIMAL ( 10, 2 ) DEFAULT NULL COMMENT '余额',
  7. `address` VARCHAR ( 80 ) DEFAULT NULL COMMENT '地址',
  8. PRIMARY KEY ( `id` )
  9. ) ENGINE = INNODB DEFAULT CHARSET = utf8;

商品表:

  1. CREATE TABLE `tb_product` (
  2. `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
  3. `product_name` VARCHAR ( 40 ) DEFAULT NULL COMMENT '名称',
  4. `status` INT ( 2 ) DEFAULT NULL COMMENT '状态',
  5. `price` DECIMAL ( 10, 2 ) DEFAULT NULL COMMENT '单价',
  6. `product_desc` VARCHAR ( 255 ) DEFAULT NULL COMMENT '描述',
  7. `caption` VARCHAR ( 255 ) DEFAULT NULL COMMENT '标题',
  8. `inventory` INT ( 11 ) DEFAULT NULL COMMENT '库存',
  9. PRIMARY KEY ( `id` )
  10. ) ENGINE = INNODB AUTO_INCREMENT = 2 DEFAULT CHARSET = utf8;

订单表:

  1. CREATE TABLE `tb_order` (
  2. `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
  3. `user_id` INT ( 11 ) DEFAULT NULL COMMENT '用户id',
  4. `product_id` INT ( 11 ) DEFAULT NULL COMMENT '商品id',
  5. `number` INT ( 11 ) DEFAULT NULL COMMENT '数量',
  6. `price` DECIMAL ( 10, 2 ) DEFAULT NULL COMMENT '单价',
  7. `amount` DECIMAL ( 10, 2 ) DEFAULT NULL COMMENT '总额',
  8. `product_name` VARCHAR ( 40 ) DEFAULT NULL COMMENT '商品名',
  9. `username` VARCHAR ( 40 ) DEFAULT NULL COMMENT '用户名',
  10. PRIMARY KEY ( `id` )
  11. ) ENGINE = INNODB DEFAULT CHARSET = utf8;

6.2. 搭建环境 - 聚合父工程

创建聚合父工程01-microservice-no-springcloud,修改pom.xml文件引入相关依赖

  1. <modelVersion>4.0.0</modelVersion>
  2. <groupId>com.moon</groupId>
  3. <artifactId>01-microservice-no-springcloud</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. <name>${project.artifactId}</name>
  6. <packaging>pom</packaging>
  7. <description>不使用Spring Cloud的情况下实现微服务架构,分析此方法存在的问题</description>
  8. <!-- 引入 spring boot 父工程 -->
  9. <parent>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-parent</artifactId>
  12. <version>2.1.6.RELEASE</version>
  13. </parent>
  14. <!-- 子模块 -->
  15. <modules>
  16. <module>shop-service-common</module>
  17. <module>shop-service-product</module>
  18. <module>shop-service-order</module>
  19. </modules>
  20. <properties>
  21. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  22. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  23. <java.version>1.8</java.version>
  24. <mysql-connector-java.version>5.1.32</mysql-connector-java.version>
  25. <lombok.version>1.18.4</lombok.version>
  26. </properties>
  27. <!-- 版本控制 -->
  28. <dependencyManagement>
  29. <dependencies>
  30. <dependency>
  31. <groupId>mysql</groupId>
  32. <artifactId>mysql-connector-java</artifactId>
  33. <version>${mysql-connector-java.version}</version>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.projectlombok</groupId>
  37. <artifactId>lombok</artifactId>
  38. <version>${lombok.version}</version>
  39. </dependency>
  40. <!-- 子模块版本号控制 -->
  41. <dependency>
  42. <groupId>com.moon</groupId>
  43. <artifactId>shop-service-common</artifactId>
  44. <version>${project.version}</version>
  45. </dependency>
  46. </dependencies>
  47. </dependencyManagement>
  48. <!-- 公共依赖 -->
  49. <dependencies>
  50. <dependency>
  51. <groupId>org.springframework.boot</groupId>
  52. <artifactId>spring-boot-starter-logging</artifactId>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.springframework.boot</groupId>
  56. <artifactId>spring-boot-starter-test</artifactId>
  57. <scope>test</scope>
  58. </dependency>
  59. </dependencies>
  60. <!-- 项目构建部分 -->
  61. <build>
  62. <plugins>
  63. <plugin>
  64. <groupId>org.springframework.boot</groupId>
  65. <artifactId>spring-boot-maven-plugin</artifactId>
  66. </plugin>
  67. </plugins>
  68. </build>

6.3. 搭建公共模块

6.3.1. 引入依赖

创建shop-service-common工程模块,用于存放公共的实体类和工具类。注:后面的order、product、user模块都会依赖common模块

  1. <dependencies>
  2. <!-- lombok 实体工具类库依赖 -->
  3. <dependency>
  4. <groupId>org.projectlombok</groupId>
  5. <artifactId>lombok</artifactId>
  6. <scope>provided</scope>
  7. </dependency>
  8. <!-- springboot jpa 持久层依赖 -->
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-data-jpa</artifactId>
  12. </dependency>
  13. </dependencies>

6.3.2. 创建实体类

shop-service-common工程中创建 Product 实体类

  1. @Data
  2. @Entity // 标识为jpa实体类
  3. @Table(name = "tb_product") // 建立实体类和表的映射关系
  4. public class Product {
  5. @Id // 声明当前私有属性为数据库表的主键
  6. @GeneratedValue(strategy = GenerationType.IDENTITY) // 配置主键的生成策略
  7. private Long id;
  8. private String productName;
  9. private Integer status;
  10. private BigDecimal price;
  11. private String productDesc;
  12. private String caption;
  13. private Integer inventory;
  14. }

6.4. 搭建商品微服务模块

6.4.1. 引入依赖

创建商品微服务模块shop-service-product,配置相关依赖

  1. <dependencies>
  2. <!-- 公共模块依赖 -->
  3. <dependency>
  4. <groupId>com.moon</groupId>
  5. <artifactId>shop-service-common</artifactId>
  6. </dependency>
  7. <!-- springboot web 项目依赖 -->
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter-web</artifactId>
  11. </dependency>
  12. <!-- 数据库连接 -->
  13. <dependency>
  14. <groupId>mysql</groupId>
  15. <artifactId>mysql-connector-java</artifactId>
  16. </dependency>
  17. </dependencies>

6.4.2. 编写dao接口

shop-service-product中创建 ProductDao 接口

  1. // 继承JPA相关接口,其中JpaSpecificationExecutor是用于复杂动态查询
  2. public interface ProductDao extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> { }

6.4.3. 编写service层

shop-service-product中创建 ProductService 业务接口与其实现

  1. public interface ProductService {
  2. /**
  3. * 根据id查询
  4. */
  5. Product findById(Long id);
  6. /**
  7. * 查询全部
  8. */
  9. List<Product> findAll();
  10. /**
  11. * 保存
  12. */
  13. void save(Product product);
  14. /**
  15. * 更新
  16. */
  17. void update(Product product);
  18. /**
  19. * 删除
  20. */
  21. void delete(Long id);
  22. }
  1. @Service
  2. public class ProductServiceImpl implements ProductService {
  3. @Autowired
  4. private ProductDao productDao;
  5. @Override
  6. public Product findById(Long id) {
  7. return productDao.findById(id).get();
  8. }
  9. @Override
  10. public List<Product> findAll() {
  11. return productDao.findAll();
  12. }
  13. @Override
  14. public void save(Product product) {
  15. productDao.save(product);
  16. }
  17. @Override
  18. public void update(Product product) {
  19. productDao.save(product);
  20. }
  21. @Override
  22. public void delete(Long id) {
  23. productDao.deleteById(id);
  24. }
  25. }

6.4.4. 编写controller层

shop-service-product中创建 ProductController 控制类

  1. @RestController
  2. @RequestMapping("product")
  3. public class ProductController {
  4. @Autowired
  5. private ProductService productService;
  6. @GetMapping("/{id}")
  7. public Product findById(@PathVariable Long id) {
  8. return productService.findById(id);
  9. }
  10. @GetMapping
  11. public List<Product> findAll() {
  12. return productService.findAll();
  13. }
  14. @PostMapping
  15. public String save(@RequestBody Product product) {
  16. productService.save(product);
  17. return "保存成功";
  18. }
  19. @PutMapping
  20. public String update(@RequestBody Product product) {
  21. productService.update(product);
  22. return "修改成功";
  23. }
  24. @DeleteMapping("/{id}")
  25. public String delete(@PathVariable Long id) {
  26. productService.delete(id);
  27. return "删除成功";
  28. }
  29. }

6.4.5. 编写项目配置文件

resources包下创建SpringBoot项目的核心配置文件application.yml

  1. server:
  2. port: 9001 # 项目端口
  3. spring:
  4. application:
  5. name: shop-service-product # 服务名称
  6. datasource: # 数据库配置
  7. driver-class-name: com.mysql.jdbc.Driver
  8. url: jdbc:mysql://localhost:3306/springcloud_sample_db?useUnicode=true&characterEncoding=utf8
  9. username: root
  10. password: 123456
  11. jpa: # jpa配置
  12. database: MySQL
  13. show-sql: true
  14. open-in-view: true

6.4.6. 创建项目启动类

shop-service-product中创建 ProductApplication 控制类

  1. @SpringBootApplication(scanBasePackages = "com.moon.product")
  2. @EntityScan("com.moon.entity") // 指定扫描实体类的包路径
  3. public class ProductApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(ProductApplication.class, args);
  6. }
  7. }

6.5. 搭建其他微服务

创建订单微服务模块 shop-service-order 与用户微服务模块 shop-service-user,搭建的步骤与商品微服务模块一致

6.6. 服务的调用

多个基础的微服务中,在用户下单时需要调用商品微服务获取商品数据,此时需要调用商品微服务提供的HTTP接口。所以需要使用http请求的相关工具类完成,如常见的HttpClientOkHttp、Spring提供的RestTemplate

6.6.1. RestTemplate 简介

Spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接,只需要传入url及返回值类型即可
RestTemplate类的设计原则与许多其他Spring 模板类(例如JdbcTemplateJmsTemplate)相同,简化复杂的操作。RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。
RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连。后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()getForObject()postForObject()put()delete()等方法。

6.6.2. RestTemplate 方法介绍

01-Spring-Cloud-Overview - 图6

6.6.3. 通过RestTemplate调用微服务

shop-service-order工程,创建HttpConfig配置类,创建RestTemplate实例并注册到spring容器中

  1. @Configuration
  2. public class HttpConfig {
  3. @Bean("restTemplate")
  4. public RestTemplate createRestTemplate() {
  5. return new RestTemplate();
  6. }
  7. }

shop-service-order工程OrderController控制类中,增加创建订单的方法

  1. @RestController
  2. @RequestMapping("order")
  3. public class OrderController {
  4. /* 日志对象 */
  5. private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);
  6. // 注入HTTP请求工具类RestTemplate
  7. @Autowired
  8. private RestTemplate restTemplate;
  9. /**
  10. * 根据商品id创建订单
  11. */
  12. @PostMapping("/{id}")
  13. public String createOrder(@PathVariable Long id) {
  14. // 通过http请求,获取商品数据
  15. Product product = restTemplate.getForObject("http://127.0.0.1:9001/product/" + id, Product.class);
  16. LOGGER.info("当前下单的商品是: ${}", product);
  17. return "创建订单成功";
  18. }
  19. }

启动商品与订单微服务,测试调用接口

6.6.4. 硬编码存在的问题

上面示例已经可以通过RestTemplate调用商品微服务的RESTFul API接口,但把提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:

  • 应用场景有局限
  • 无法动态调整

解决方法:通过注册中心动态的对服务注册和服务发现

7. SpringCloud不同组件的注册中心与服务调用对比总结

注:详细用法详见本系列中不同的章节笔记

7.1. 注册中心对比

7.1.1. Eureka

搭建注册中心

  • 引入 spring-cloud-starter-netflix-eureka-server 依赖
  • 配置 Eureka Server
  • 通过 @EnableEurekaServer 激活Eureka Server端配置

服务注册

  • 服务提供者引入 spring-cloud-starter-netflix-eureka-client 依赖
  • 通过 eureka.client.serviceUrl.defaultZone 配置注册中心地址

    7.1.2. Consul

    搭建注册中心

  • 下载安装consul

  • 启动consul consul agent -dev

服务注册

  • 服务提供者引入 spring-cloud-starter-consul-discovery 依赖
  • 通过 spring.cloud.consul.hostspring.cloud.consul.port 指定Consul Server的请求地址

    7.2. 服务调用对比

    7.2.1. Ribbon

  • 通过Ribbon结合RestTemplate方式进行服务调用只需要在声明RestTemplate的方法上添加注解@LoadBalanced即可

  • 可以通过 服务名称.ribbon.NFLoadBalancerRuleClassName 配置负载均衡策略

    7.2.2. Feign

  • 服务消费者引入 spring-cloud-starter-openfeign 依赖

  • 通过 @FeignClient 声明一个调用远程微服务接口
  • 启动类上通过 @EnableFeignClients 激活Feign