Spring Cloud 组件快速入门
- 2020 年 3 月 2 日
- 筆記
组件简介
spring cloud 是基于 springboot 基础之上构建的一系列分布式微服务组件集,其组件主要包括:
- Eureka 微服务注册发现中心
- Feign 微服务远程调用
- Ribbon 微服务客户端之间负载均衡器
- Config server 微服务配置中心
- Zuul 微服务调用网关
- Hystrix 微服务调用熔断降级
spring cloud 提供的是一整套的开源分布式微服务解决方案。当然,随着业务发展的瓶颈,各家都会 “因地制宜” 自研对应的替代组件,像阿里的 Dubbo rpc 框架,以 zookeeper 为注册中心。携程的 Apollo 配置中心。腾讯的 tRPC,七彩石远程配置中心等等。相对而言,spring cloud 组件更易上手,有利于对分布式微服务解决方案有个大致的概念。
Eureka Server
Eureka 是 spring cloud 的微服务注册发现中心,所有的微服务只有服务注册中心注册后才能被其他服务远程调用,注册到 Eureka 的微服务会定时收到心跳信号,但服务异常时会被注册中心下线。下面我们首先实现一个简单服务注册发现中心。
首先搭建最基础的 spring boot 工程,引入 Eureka 组件依赖,最终的 maven 依赖如下 (pom.xml):
Eureka Server 依赖文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>www.gaiserchan.test</groupId> <artifactId>eureka-test</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.10.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.10.RELEASE</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> <version>2.1.4.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
项目工程结构如图所示

Eureka Server 配置文件
修改项目配置文件 (application.properties)
# 服务名称 spring.application.name=eureka-server # 服务端口号 server.port=20200 # 注册中心实例地址 eureka.instance.hostname=localhost # 是否注册到注册中心 eureka.client.register-with-eureka=false # 屏蔽注册中心 eureka.client.fetch-registry=false
编写 spring boot 启动代码 (Application.java)
Application.java
package www.eureka.test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * @author gaiserchen * @date 2020.02.20 * spring boot 应用启动注解 */ @SpringBootApplication @EnableEurekaServer public class Application { public static void main (String [] args){ SpringApplication.run (Application.class,args); } }
运行程序 'Run Application.main ()'

程序启动后访问 <http://localhost:20200> 可以看到如下界面,我们的第一个服务注册发现中心就启动成功了。
Eureka Server 启动结果

可以看到,此时注册中心是没有任何微服务注册到注册中心的,下面我们写一个简单的 web 服务,并注册到服务注册中心。
Eureka client
所有基于 spring boot 框架构建的 web 程序,只要注册到服务注册中心,都可以称之为提供 web 服务的 “Eureka client”。
同样,我们先搭建一个最简单的 spring boot 工程,引入 eureka client 组件,最终的 maven 依赖如下:
Eureka client 依赖文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>www.eureka.test</groupId> <artifactId>eureka-client</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.10.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.10.RELEASE</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.1.4.RELEASE</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
工程项目结构如图所示:

Eureka client 配置文件
修改对应工程的配置文件 (application.properties), 填写对应的服务注册中心地址。
# 服务名称 spring.application.name=eureka-client # 服务端口 server.port=20201 # 注册中心地址 eureka.client.serviceUrl.defaultZone=http://localhost:20200/eureka/
eureka client 的测试代码中我们主要编写 Application.java 和 Sample.java
所有 spring boot 工程的 Application.java 的代码都类似,主要的系统接口在 controller 层编写。
Application.java
package com.eureka.client; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; /** * @author gaiserchen * @date 2020.02.20 * spring boot 应用启动注解 */ @SpringBootApplication @EnableEurekaClient public class Application { public static void main (String [] args) { SpringApplication.run (Application.class, args); } }
controller/Sample.java, 这里的逻辑是,直接获取配置文件里的 spring.application.name 和 server.port 变量然后返回。
package com.eureka.client.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author gaiserchen * @date 2020.02.20 * sample controller */ @RestController public class Sample { @Value ("${server.port}") String port; @Value ("${spring.application.name}") String applicationName; @GetMapping (value = "/sample") public String sample (@RequestParam (value = "name", defaultValue = "gaiserchen") String name) { return "hello" + name + ", i am from:" + applicationName + ", and port is:" + port; } }
Eureka client 启动结果
同样,启动程序之后,我们直接访问 <http://localhost:20201>,其结果如图:

这时,我们再访问 Eureka Server 的地址 <http://localhost:20200>,可以看到此时的服务注册中心已经有注册的应用服务。

Feign 远程调用
在前面我们已经构建好一个 web 应用,并且已经注册到注册中心,下面我们可以通过 Feign 远程调用组件获取接口数据。
我们再次构建一个 springboot 应用,引入 feign 组件依赖,项目最终的 maven 如下:
Feign 依赖文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.eureka.feign</groupId> <artifactId>eurekaFeign</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.10.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.10.RELEASE</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> <version>1.4.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> <version>1.4.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
工程项目结构如图所示:

Feign 配置文件
修改配置文件,因为是远程调用其他服务,所以当其他远程服务异常时需要有一定的服务熔断处理,所以这里启用了 hystrix 服务熔断组件。
application.properties
# 服务名称 spring.application.name=service-feign # 服务端口 server.port=20202 # 服务注册中心地址 eureka.client.serviceUrl.defaultZone=http://localhost:20200/eureka/ # hystrix 服务熔断降级启用 feign.hystrix.enabled=true
这次我们需要编写 feign 工程自己的接口层逻辑,接口拿到请求值 name, 具体处理逻辑在 service 层。
controller/FeignController.java
package com.feign.test.controller; import com.feign.test.service.FeignService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author gaiserchen * @date 2020.02.02 * feign controller */ @RestController public class FeignController { @Autowired FeignService feignService; @GetMapping ("/feign-consumer") public String feignConsumer (@RequestParam (defaultValue = "gaiserchan") String name) { return feignService.feignConsumer (name); } }
下面编写我们 service 层的业务处理逻辑,service 接口引入了 @FeignClient 注解,这个注解直接启用 Feign 组件,value 填需要调用的远程服务名 spring.application.name,fallback 是当远程服务调用异常需要做哪些异常处理,在具体业务逻辑中,直接拿上一步取到的 name 值远程调用 eureka-client 的 sample 接口。
service/FeignService.java
package com.feign.test.service; import com.feign.test.service.impl.FeignHystricServiceImpl; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; /** * @author gaiserchen * @date 2020.02.20 * feign client */ @FeignClient (value = "eureka-client", fallback = FeignHystricServiceImpl.class) public interface FeignService { @GetMapping (value = "/sample") /** description: feign service @param name @return String string */ String feignConsumer (@RequestParam (value = "name") String name); }
在异常处理中,如果远程调用失败,直接返回,起到服务熔断作用。不会影响其他服务,避免出现系统 “雪崩”。
service/impl/FeignHystricServiceImpl.java
package com.feign.test.service.impl; import com.feign.test.service.FeignService; import org.springframework.stereotype.Component; /** * @author gaiserchen * @date 2020.02.02 * feign fallback */ @Component public class FeignHystricServiceImpl implements FeignService { @Override public String feignConsumer (String name) { return "sorry," + name; } }
Feign 启动结果
我们启动 Feign 程序之后,访问地址 <http://localhost:20202/feign-consumer?name=consumer>, 其返回结果如图,表示远程调用结果成功。

同时,feign 程序也需要注册到服务注册中心才能实现远程调用。我们可以查看服务注册中心的实例。

但当我们把之前启动的 eureka-client 服务停止时,再访问该地址,feign 会直接返回服务熔断的结果。

zuul 网关
zuul 是 spring cloud 的网关组件,起到路由代理转发的功能。同样构建一个 spring boot 工程,引入 zuul 组件依赖,其最终 maven 依赖如下:
zuul 依赖文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.eureka.test</groupId> <artifactId>zuulTest</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.10.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.10.RELEASE</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> <version>1.4.7.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
zuul 配置文件
zuul 网关主要起到请求代理和路由转发的作用,其转发规则就是在配置文件里配置,其配置如下,将所有 /eureka-client 下的请求转发到 eureka-client 这个服务,将所有 /service-feign 下的请求转发到 service-feign 这个服务。
# 服务名称 spring.application.name=eureka-zuul # 服务端口号 server.port=20204 # 服务注册中心地址 eureka.client.serviceUrl.defaultZone=http://localhost:20200/eureka/ # zuul 网关代理规则 zuul.routes.eureka-client.path=/eureka-client/* zuul.routes.eureka-client.serviceId=eureka-client zuul.routes.service-feign.path=/service-feign/* zuul.routes.service-feign.serviceId=service-feign
zuul 没有具体的接口和业务处理逻辑,所以只有 spring boot 启动代码
Application.java
package com.zuul.test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; /** * @author gaiserchen * @date 2020.02.02 * zuul client */ @EnableEurekaClient @EnableZuulProxy @SpringBootApplication public class Application { public static void main (String [] args) { SpringApplication.run (Application.class, args); } }
zuul 启动结果
zuul 启动之后,我们直接访问地址 <http://localhost:20204/eureka-client/sample?name=zuul>, 其结果如下,可以看到请求直接转发到了 eureka-client 服务。

我们接着访问 <http://localhost:20204/service-feign/feign-consumer?name=zuuConsumer>,可以看到,请求直接转发到了 service-feign 服务。

总结
以上就是几个 spring cloud 组件的基本入门,其中 config server 是配置中心,可以结合 gitlab 或者 git 仓库使用。而负载均衡器 Ribbon 已经集成在 Feign 远程调用组件里,当你的微服务在注册中心大于等于 2 个时,默认会开启轮询机制做负载均衡。当然,负载均衡策略也是可以配置的。