前言

这周一入职了新公司,主要负责Java后端开发工作。目前正在开展一个全新的业务,技术选型为SpringCloud全家桶,项目的骨架由我负责搭建。由于前几家公司的微服务框架都是使用Dubbo + SpringBoot,然后平时对SpringCloud 这一套了解不多。这两天正好有时间可以研究下SpringCloud如何使用、SpringCloud 如何与其他组件整合,后面计划弄一个SpringCloud整合系列出来。

一、SpringCloud简介

SpringCloud 是基于SpringBoot提供一整套的微服务解决方案,包括服务注册与发现、配置中心、消息总线、服务网关、负载均衡、熔断器、全链路监控等组件。除了Netfix提供的开源组件外,还有一些第三方的开源组件可以使用。

开发者可以很方便的将微服务中常用组件无缝整合进SpringCloud中,然后通过SpringBoot风格进行封装和屏蔽掉底层复杂的配置和实现原理,非常容易上手。简单来说,SpringCloud 就是分布式微服务的一站式解决方案,俗称SpringCloud全家桶。

二、SpringCloud和SpringBoot的关系

  • SpringBoot: 专注于方便快速开发单个微服务系统。
  • SpringCloud: 微服务系统协调治理框架,就是将SpringBoot开发的多个微服务管理起来。
  • SpringBoot 可以不依赖SpringCloud独立使用,而SpringCloud的使用一定离不开SpringBoot。

三、项目搭建

3.1 环境准备

  • 开发工具IDEA
  • JDK1.8
  • Apache Maven 3.5.2,使用阿里云镜像
  • SpringBoot 2.1.3.RELEASE 版本
  • SpringCloud Greenwich.SR1 版本

3.2 父项目

创建一个名称为 spring-cloud-parent 的pom项目。用来管理SpringBoot、SpringCloud,及其他各种jar 包依赖。pom文件内容如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.gallenzhang</groupId>
  6. <artifactId>spring-cloud-parent</artifactId>
  7. <version>1.0.0-SNAPSHOT</version>
  8. <packaging>pom</packaging>
  9. <name>spring-cloud-parent</name>
  10. <properties>
  11. <spring-boot.version>2.1.3.RELEASE</spring-boot.version>
  12. <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
  13. <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
  14. <mybatis-spring-boot.version>2.1.2</mybatis-spring-boot.version>
  15. <mysql.version>5.1.26</mysql.version>
  16. <lombok.version>1.18.12</lombok.version>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencyManagement>
  20. <dependencies>
  21. <!-- spring-boot 依赖-->
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-dependencies</artifactId>
  25. <version>${spring-boot.version}</version>
  26. <type>pom</type>
  27. <scope>import</scope>
  28. </dependency>
  29. <!-- spring-cloud 依赖-->
  30. <dependency>
  31. <groupId>org.springframework.cloud</groupId>
  32. <artifactId>spring-cloud-dependencies</artifactId>
  33. <version>${spring-cloud.version}</version>
  34. <type>pom</type>
  35. <scope>import</scope>
  36. </dependency>
  37. <!-- spring-cloud-alibaba 依赖-->
  38. <dependency>
  39. <groupId>com.alibaba.cloud</groupId>
  40. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  41. <version>${spring-cloud-alibaba.version}</version>
  42. <type>pom</type>
  43. <scope>import</scope>
  44. </dependency>
  45. <!-- mybatis -->
  46. <dependency>
  47. <groupId>org.mybatis.spring.boot</groupId>
  48. <artifactId>mybatis-spring-boot-starter</artifactId>
  49. <version>${mybatis-spring-boot.version}</version>
  50. </dependency>
  51. <!-- mysql -->
  52. <dependency>
  53. <groupId>mysql</groupId>
  54. <artifactId>mysql-connector-java</artifactId>
  55. <version>${mysql.version}</version>
  56. </dependency>
  57. <!-- test -->
  58. <dependency>
  59. <groupId>org.springframework.boot</groupId>
  60. <artifactId>spring-boot-starter-test</artifactId>
  61. <version>${spring-boot.version}</version>
  62. <scope>test</scope>
  63. <exclusions>
  64. <exclusion>
  65. <groupId>org.junit.vintage</groupId>
  66. <artifactId>junit-vintage-engine</artifactId>
  67. </exclusion>
  68. </exclusions>
  69. </dependency>
  70. <!-- lombok -->
  71. <dependency>
  72. <groupId>org.projectlombok</groupId>
  73. <artifactId>lombok</artifactId>
  74. <version>${lombok.version}</version>
  75. </dependency>
  76. </dependencies>
  77. </dependencyManagement>
  78. <!-- SpringBoot打包插件,可以将代码打包成一个可执行的jar包 -->
  79. <build>
  80. <finalName>${project.artifactId}</finalName>
  81. <plugins>
  82. <plugin>
  83. <groupId>org.apache.maven.plugins</groupId>
  84. <artifactId>maven-compiler-plugin</artifactId>
  85. <configuration>
  86. <source>${java.version}</source>
  87. <target>${java.version}</target>
  88. </configuration>
  89. </plugin>
  90. <plugin>
  91. <groupId>org.springframework.boot</groupId>
  92. <artifactId>spring-boot-maven-plugin</artifactId>
  93. <configuration>
  94. <fork>true</fork>
  95. <addResources>true</addResources>
  96. </configuration>
  97. </plugin>
  98. </plugins>
  99. </build>
  100. </project>

3.3 服务注册中心

创建一个名称为spring-cloud-eureka的项目,作为服务注册中心。这里使用eureka 来管理服务的注册和发现,工程的pom文件如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>com.gallenzhang</groupId>
  7. <artifactId>spring-cloud-parent</artifactId>
  8. <version>1.0.0-SNAPSHOT</version>
  9. <relativePath />
  10. </parent>
  11. <groupId>com.gallenzhang</groupId>
  12. <artifactId>spring-cloud-eureka</artifactId>
  13. <version>1.0.0-SNAPSHOT</version>
  14. <name>spring-cloud-eureka</name>
  15. <dependencies>
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-test</artifactId>
  19. <scope>test</scope>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.cloud</groupId>
  23. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  24. </dependency>
  25. </dependencies>
  26. <build>
  27. <plugins>
  28. <plugin>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-maven-plugin</artifactId>
  31. </plugin>
  32. </plugins>
  33. </build>
  34. </project>

在启动类上加 @EnableEurekaServer表明这是个eureka sever。

  1. /**
  2. * <p>
  3. * Eureka 启动类
  4. * </p>
  5. *
  6. * @author gallenzhang
  7. * @since 2020/6/20
  8. **/
  9. @SpringBootApplication
  10. @EnableEurekaServer
  11. public class SpringCloudEurekaApplication {
  12. public static void main(String[] args) {
  13. SpringApplication.run(SpringCloudEurekaApplication.class, args);
  14. }
  15. }

默认情况下eureka server 也是一个eureka client,所以eureka.client.register-with-eureka 和 eureka.client.fetch-registry属性都配置为false,表明自己是一个eureka server。配置文件application.yml 内容如下:

  1. server:
  2. port: 8000
  3. spring:
  4. application:
  5. name: spring-cloud-eureka
  6. eureka:
  7. instance:
  8. hostname: localhost
  9. client:
  10. #是否将自己注册到Eureka Server,默认为true
  11. register-with-eureka: false
  12. #是否从Eureka Server获取注册信息,默认为true
  13. fetch-registry: false
  14. serviceUrl:
  15. #服务注册的URL
  16. defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

右键直接运行SpringCloudEurekaApplication类,启动完成后浏览器访问 http://127.0.0.1:8000/
SpringCloud 快速入门 - 图1
看到这个页面表示 eureka 已经启动成功,这里的 No instances available 表示目前没有服务提供者注册上来。

3.4 服务提供者

创建一个名称为 spring-cloud-provider的项目,在pom文件中引入对应的依赖。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>com.gallenzhang</groupId>
  7. <artifactId>spring-cloud-parent</artifactId>
  8. <version>1.0.0-SNAPSHOT</version>
  9. <relativePath />
  10. </parent>
  11. <groupId>com.gallenzhang</groupId>
  12. <artifactId>spring-cloud-provider</artifactId>
  13. <version>1.0.0-SNAPSHOT</version>
  14. <name>spring-cloud-provider</name>
  15. <dependencies>
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-web</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.cloud</groupId>
  22. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  23. </dependency>
  24. </dependencies>
  25. <build>
  26. <plugins>
  27. <plugin>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-maven-plugin</artifactId>
  30. </plugin>
  31. </plugins>
  32. </build>
  33. </project>

配置文件application.yml 内容如下:

  1. server:
  2. port: 8100
  3. spring:
  4. application:
  5. name: spring-cloud-provider #服务之间的调用都是根据这个 name
  6. eureka:
  7. client:
  8. serviceUrl:
  9. defaultZone: http://localhost:8000/eureka/

为了方便测试,项目新建一个HelloController 类

  1. /**
  2. * <p>
  3. * Hello Controller
  4. * </p>
  5. *
  6. * @author gallenzhang
  7. * @since 2020/6/20
  8. **/
  9. @RestController
  10. public class HelloController {
  11. @Value("${server.port}")
  12. private String port;
  13. @RequestMapping("/hello")
  14. public String sayHello(@RequestParam(value = "name", defaultValue = "gallenzhang") String name){
  15. return "hello " + name + " , I am from port:" + port;
  16. }
  17. }

启动类添加@EnableEurekaClient 注解

  1. /**
  2. * <p>
  3. * 提供者启动类
  4. * </p>
  5. *
  6. * @author gallenzhang
  7. * @since 2020/6/20
  8. **/
  9. @SpringBootApplication
  10. @EnableEurekaClient
  11. public class SpringCloudProviderApplication {
  12. public static void main(String[] args) {
  13. SpringApplication.run(SpringCloudProviderApplication.class, args);
  14. }
  15. }

右键运行SpringCloudProviderApplication,将提供者注册到eureka上去。此时访问 http://127.0.0.1:8000/
SpringCloud 快速入门 - 图2
服务提供者已经注册到 eureka 上了,服务名称就是 spring.application.name 值的大写。

浏览器访问 http://localhost:8100/hello?name=allen ,在浏览器上可以看到响应结果。

hello allen , I am from port:8100

3.5 服务消费者

Feign 是一个声明式的伪Http客户端,使用它只要创建一个接口并添加注解@FeignClient 即可,它使得写Http客户端变得简单。Feign默认集成了Ribbon,并和 eureka 结合,实现了客户端负载均衡。

创建一个名称为 spring-cloud-consumer的项目,在pom文件中引入对应的依赖。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>com.gallenzhang</groupId>
  7. <artifactId>spring-cloud-parent</artifactId>
  8. <version>1.0.0-SNAPSHOT</version>
  9. <relativePath />
  10. </parent>
  11. <groupId>com.gallenzhang</groupId>
  12. <artifactId>spring-cloud-consumer</artifactId>
  13. <version>1.0.0-SNAPSHOT</version>
  14. <name>spring-cloud-consumer</name>
  15. <dependencies>
  16. <dependency>
  17. <groupId>org.springframework.cloud</groupId>
  18. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.cloud</groupId>
  26. <artifactId>spring-cloud-starter-openfeign</artifactId>
  27. </dependency>
  28. </dependencies>
  29. <build>
  30. <plugins>
  31. <plugin>
  32. <groupId>org.springframework.boot</groupId>
  33. <artifactId>spring-boot-maven-plugin</artifactId>
  34. </plugin>
  35. </plugins>
  36. </build>
  37. </project>

配置文件application.yml 内容如下:

  1. server:
  2. port: 8200
  3. spring:
  4. application:
  5. name: spring-cloud-consumer #服务之间的调用都是根据这个name
  6. eureka:
  7. client:
  8. serviceUrl:
  9. defaultZone: http://localhost:8000/eureka/

在程序的启动类SpringCloudConsumerApplication ,加上@EnableFeignClients注解开启 Feign 的功能。

  1. /**
  2. * <p>
  3. * 消费者启动类
  4. * </p>
  5. *
  6. * @author gallenzhang
  7. * @since 2020/6/20
  8. **/
  9. @SpringBootApplication
  10. @EnableEurekaClient
  11. @EnableDiscoveryClient
  12. @EnableFeignClients
  13. public class SpringCloudConsumerApplication {
  14. public static void main(String[] args) {
  15. SpringApplication.run(SpringCloudConsumerApplication.class, args);
  16. }
  17. }

定义一个接口,通过@FeignClient 来指定调用哪个服务。这里指定了调用 spring-cloud-provider 服务的 hello 接口。

  1. /**
  2. * <p>
  3. * Hello Client
  4. * </p>
  5. *
  6. * @author gallenzhang
  7. * @since 2020/6/20
  8. **/
  9. @FeignClient(value = "spring-cloud-provider")
  10. public interface HelloClient {
  11. /**
  12. * 客户端调用服务
  13. * @param name
  14. * @return
  15. */
  16. @RequestMapping(value = "/hello")
  17. String sayHelloFromClient(@RequestParam(value = "name") String name);
  18. }

在 Controller 层对外暴露一个 /hello 的Api接口,Controller中通过上面定义的HelloClient来消费服务。

  1. /**
  2. * <p>
  3. * Hello Controller
  4. * </p>
  5. *
  6. * @author gallenzhang
  7. * @since 2020/6/20
  8. **/
  9. @RestController
  10. public class HelloController {
  11. @Autowired
  12. private HelloClient helloClient;
  13. @RequestMapping(value = "/hello")
  14. public String sayHello(@RequestParam String name) {
  15. return helloClient.sayHelloFromClient(name);
  16. }
  17. }

启动 spring-cloud-eureka 服务。 修改 spring-cloud-provider 项目 application.yml文件中 server.port值,分别启动三次,端口号依次为 8100、8101、8102。

然后右键运行SpringCloudConsumerApplication,启动消费者服务。

浏览器访问 http://127.0.0.1:8200/hello?name=allen 浏览器会交替输出。

hello allen , I am from port:8100 hello allen , I am from port:8101 hello allen , I am from port:8102

源码下载:https://github.com/gallenzhang/spring-cloud-example

四、学习资料

SpringCloud 官网
SpringCloud 中文网
SpringCloud 开发指南-英文版

本文主要参考如下两篇文章。

Spring Cloud 快速入门
史上最简单的 SpringCloud 教程 | 终章

如果想进一步讨论SpringCloud 或者 其他技术问题,欢迎加我微信一起交流(请注明来意):1298552064