组件简介
spring cloud 是基于 springboot 基础之上构建的一系列分布式微服务组件集,其组件主要包括:
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 个时,默认会开启轮询机制做负载均衡。当然,负载均衡策略也是可以配置的。