服务治理:Spring Cloud Eureka
在微服务架构中,服务治理是最为基本、最为核心的模块,它主要用来实现各个微服务实例的自动注册与发现。在互联网时代初期,微服务还未盛行之时,构建的服务完全可以使用静态配置文件来维护服务实例清单,根据实例清单来完成服务的调用,随着业务逐渐繁杂,手动维护服务实例清单已经无法满足需求,那么就需要构建一套自动维护服务实例清单的系统,该系统支持动态维护服务实例清单,当服务实例发生变化时,能及时通知客户端来应对服务实例的变化,它围绕服务实例注册与发现,实现自动化管理。
Spring Cloud Eureka
是Spring Cloud Netflix
微服务套件中的一部分,它基于Netflix Eureka
进行了二次封装,主要负责完成微服务架构中的服务治理功能。Netflix Eureka
包含了服务端组件和客户端组件,对于Eureka
服务端,我们常常称之为服务注册中心,它与其他注册中心一样,支持高可用配置,各个分片依赖强一致性原则提供了良好的服务实例可用性,当某个分片发生故障,那么Eureka
会进入自我保护状态,当分片恢复功能的时候,也会实时将其他分片的服务实例同步到恢复的分片中;对于Eureka
客户端,它主要处理的是服务的注册与发现,客户端通过注解和配置,可以将服务注册和发现功能集成到应用程序代码中,当运行起应用程序,Eureka
客户端会自动将自身作为服务注册到服务注册中心并以周期性发送心跳来维护它与服务注册中心的租约护着自动从服务注册中心获取服务实例清单,从而使用某种轮询算法来调用服务实例清单中的接口。对于服务的注册和发现,有必要对其两者进行简单介绍。
在服务治理体系中,会有一个服务注册中心,这个服务注册中心扮演的角色就是给各个独立的微服务提供注册场所,这个场所就好比一个房产中介,卖房者只需要在房产中介这里进行登记,并不时向房产中介表达自己的卖房意愿,房产中介会将房产信息挂在门户网站上,供买房者选择购买。类似,服务构建完毕之后,会向服务注册中心进行登记,并以心跳的方式向注册中心反馈自己是否继续提供服务,而注册中心,在保证服务可用的情况下,会将服务实例清单暴露给服务的调用方,供调用方选择调用。
由于在服务注册中心的管理下,服务的调用方不再关心提供服务的那方的具体地址,而是通过服务名来发起调用,这样的好处就是只要服务提供方不改变服务名的情况下,地址的变化对服务调用方无任何影响,在这种机制下,服务调用方只需要向服务注册中心发起调用请求,服务注册中心向服务调用方提供服务实例清单,剩下的交给服务调用方采用某种轮询的方式对服务实例清单中的服务进行调用。
为了方便后面学习,这里首先搭建一个Spring Boot
聚合工程,命名为learning-springcloud
,然后在聚合工程下搭建子工程,这样方便管理各类jar
的版本。
首先列出搭建Spring Boot
聚合工程所需要的基本的依赖,下表的前两个是学习Spring Cloud
必备的依赖,第三个是一款优秀的国产工具包,读者可以选择性集成。
名称 | 版本 |
---|---|
Spring Boot | 2.0.6.RELEASE |
Spring Cloud | Finchley.SR2 |
hutool工具包 | 4.1.19 |
这里给出项目的基本结构,如下图所示:
对于聚合工程的pom
文件,给出代码清单如下:
<?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>cn.itlemon.springcloud</groupId>
<artifactId>learning-springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>learning-springcloud</name>
<description>Learning Spring Cloud Project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<springboot.version>2.0.6.RELEASE</springboot.version>
<springcloud.version>Finchley.SR2</springcloud.version>
<hutool.version>4.1.19</hutool.version>
</properties>
<modules>
<module>spring-cloud-eureka-server</module>
<module>spring-cloud-eureka-producer-client</module>
<module>spring-cloud-eureka-consumer-client</module>
</modules>
<dependencies>
<!-- hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring cloud聚合工程 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${springcloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${springboot.version}</version>
</plugin>
</plugins>
</build>
</project>
接下来搭建单节点服务注册中心,我们创建工程spring-cloud-eureka-server
,并将其作为learning-springcloud
的子项目,该工程的pom文件代码清单如下所示:
<?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>
<artifactId>spring-cloud-eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-eureka-server</name>
<description>Spring cloud eureka server project for Spring Boot</description>
<parent>
<groupId>cn.itlemon.springcloud</groupId>
<artifactId>learning-springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</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</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
pom
文件的依赖添加好了以后,写一个Spring Boot
应用的入口主方法,在入口类中上添加注解@EnableEurekaServer
即可,当Spring Boot
应用启动的时候,就会启动一个注册中心提供给其他应用进行注册和发现,对于Spring Boot
应用入口,代码如下:
package cn.itlemon.springcloud.eureka.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author jiangpingping
* @date 2018/11/06 21:28
*/
@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaServerApplication.class, args);
}
}
配置到如此,已经完成了对于注册中心的搭建,启动项目即可进入Eureka
服务注册中心,@EnableEurekaServer
注解就是开启了服务注册中心功能。前面提到,Spring Cloud Eureka
包含Eureka
服务端和客户端,在项目启动时,该服务注册中心会将自己作为一项服务进行注册,这在实际的开发中,往往是不提倡的,作为服务注册中心,它的核心任务就是当作服务中心,而不进行客户端注册行为,所以,我们往往还会对其服务注册中心项目进行简单配置,在application.properties
增加如下配置:
# 配置端口
server.port=1111
# 配置服务注册中心地址
eureka.instance.hostname=localhost
# 作为服务注册中心,禁止本应用向自己注册服务
eureka.client.register-with-eureka=false
# 作为服务注册中心,禁止本应用向自己检索服务
eureka.client.fetch-registry=false
# 设置服务注册中心服务注册地址,提供给其他应用进行注册或者发现
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
对于上面的配置,在每一条配置中都进行了注释,这里仅仅对最后一条配置进行简单解释:
eureka.client.service-url.defaultZone
配置的是服务注册地址和服务发现地址,service-url
在配置类EurekaClientConfigBean
中对应的属性是serviceUrl
,它的值是一个HashMap<String, String>
,eureka.client.service-url.defaultZone=xxx
的值就相当于使用defaultZone
作为Map
的键,xxx
作为值来进行存储的,由于配置类EurekaClientConfigBean
使用了@ConfigurationProperties
注解,那么在项目启动过程中,Spring
实例化Bean
的时候自动会去配置文件中寻找对应的配置值来进行填充。至于为什么使用defaultZone
作为键,这是因为在EurekaClientConfigBean
源码中规定了DEFAULT_ZONE
,而这个DEFAULT_ZONE
的值就是defaultZone
。如果我们没有配置eureka.client.service-url.defaultZone
,那么Spring Cloud
会默认采用EurekaClientConfigBean
的默认配置地址,也就是http://localhost:8761/eureka/
,现在我们自定义配置后,服务注册和发现的地址就变成了http://localhost:1111/eureak/
了,在实际的开发中,我们也不会去使用默认的注册地址,一般都会根据项目来配置它。
我们启动Spring Boot
应用,访问http://localhost:1111
,就能进入到Eureka
服务注册中心,如下如所示:
在上面的配置页面中,我们可以看出,注册中心搭建完毕之后,并没有服务实例注册到上面,所以Instances currently registered with Eureka
列表为空,在没有构建服务实例的之前,为了演示服务实例清单列表,我们可以将application.properties
中eureka.client.register-with-eureka
的属性设置为true
(默认值),重启Spring Boot
,就可以发现注册中心将自己作为一个独立服务注册到了自身中,并且在列表中会出现自身的服务实例。
搭建单节点服务注册中心还是很方便简单的,有了服务注册中心,那么我们可以继续搭建服务提供者。我们创建一个Spring Boot
应用,这个应用的名称设置为spring-cloud-eureka-producer-client
,它作为之前建立的Spring Boot
聚合工程的子工程,这里给出其pom
文件的代码清单:
<?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>
<artifactId>spring-cloud-eureka-producer-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-eureka-producer-client</name>
<description>Producer Client project for Spring Boot</description>
<parent>
<groupId>cn.itlemon.springcloud</groupId>
<artifactId>learning-springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</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>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在项目spring-cloud-eureka-producer-client
的入口主类中加入注解@EnableDiscoveryClient
,加上注解后Spring Boot
会自动激活Eureka
中的DiscoveryClient
接口的实现类对象,这得益于Spring Boot
的自动化配置,这样就可以将服务注册到服务注册中心,当然,我们还得配置服务注册地址才会正确地进行注册行为。主方法代码为:
package cn.itlemon.springcloud.producer.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author jiangpingping
* @date 2018/11/07 00:04
*/
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudEurekaProducerClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaProducerClientApplication.class, args);
}
}
我们继续写一个RESTful
风格的服务,代码如下所示:
package cn.itlemon.springcloud.producer.client.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author jiangpingping
* @date 2018/11/7 00:05
*/
@RestController
@Slf4j
public class ProducerController {
private final DiscoveryClient discoveryClient;
@Autowired
public ProducerController(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
@GetMapping("/hello/producer")
public String helloProducer() {
List<String> services = discoveryClient.getServices();
log.info("服务个数:{}, 第一个服务名称为:{}", services.size(), services.size() > 0 ? services.get(0) : "null");
return "Hello Spring Cloud!";
}
}
我们在这里提供了一个RESTful
风格的服务,并在此Controller
中注入了DiscoveryClient
的实现类对象,该接口的源代码如下:
package org.springframework.cloud.client.discovery;
import java.util.List;
import org.springframework.cloud.client.ServiceInstance;
/**
* DiscoveryClient represents read operations commonly available to Discovery service such as
* Netflix Eureka or consul.io
* @author Spencer Gibb
*/
public interface DiscoveryClient {
/**
* A human readable description of the implementation, used in HealthIndicator
* @return the description
*/
String description();
/**
* Get all ServiceInstances associated with a particular serviceId
* @param serviceId the serviceId to query
* @return a List of ServiceInstance
*/
List<ServiceInstance> getInstances(String serviceId);
/**
* @return all known service ids
*/
List<String> getServices();
}
根据源代码可知,可以通过它的实现类对象获取相关描述、通过服务ID
(服务名称)来获取服务实例的集合、获取所有服务名称。
我们继续配置application.properties
文件,代码清单如下:
# 配置服务名称
spring.application.name=producer-service
# 配置服务注册地址
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
其中eureka.client.service-url.defaultZone
的值就是我们之前在spring-cloud-eureka-server
工程中配置文件定义的服务注册和发现的地址。当注册中心启动之后,再启动这个服务提供者,就可以实现服务的自动注册。服务注册中心的服务实例列表如下所示:
在浏览器中访问http://localhost:8080/hello/producer
,在控制台中我们可以看到日志的输出:
2018-11-08 22:30:46.865 INFO 20388 --- [nio-8080-exec-2] c.i.s.p.c.controller.ProducerController : 服务个数:1, 第一个服务名称为:producer-service
这种访问方式一般只是在测试的时候使用,当搭建服务消费者后,我们可以使用RestTemplate
对象来完成服务的调用。
我们现在搭建的是单节点服务注册中心,在微服务架构这样的分布式环境中,我们需要充分考虑到发生故障的时候,如果搭建的仅仅是单节点的服务注册中心,那么当它发生故障的时候,就会导致整个服务不可用,所以在生产环境中,我们必须部署高可用服务注册中心。在Eureka
设计之初,就充分考虑到了这个问题,也就是说,Eureka
不仅支持单节点部署,同样支持多节点部署,形成高可用集群。本小节将介绍服务注册中心高可用集群的搭建,这里仅仅是演示,所以在一台机器上进行部署多个服务注册中心,采用的是端口来进行区别,在实际的生产环境中,一般要求多个服务注册中心部署在不同的机器中,防止某台机器的断电或者其他异常导致整个集群不可用。
在本文的开始,我们搭建的是单节点服务注册中心,当时在配置文件中加入了如下的配置:
# 配置端口
server.port=1111
# 配置服务注册中心地址
eureka.instance.hostname=localhost
# 作为服务注册中心,禁止本应用向自己注册服务
eureka.client.register-with-eureka=false
# 作为服务注册中心,禁止本应用向自己检索服务
eureka.client.fetch-registry=false
# 设置服务注册中心服务注册地址
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
其中第三条和第四条配置是禁止服务注册中心注册自己,也禁止服务注册中心检索其他服务,这两条配置的默认值都是true
,搭建高可用服务注册中心的时候,必须设置第三条为true
,就表示当前服务注册中心可以向其他服务注册中心注册自己,说到这,你也许明白了,Eureka
服务注册中心就是一群互相注册的单节点服务注册中心组成的。我们这里新建三个节点的配置文件,分别命名为application-peer1.properties
、application-peer2.properties
、application-peer2.propertie
,或者使用YML
配置文件,可以在同一个文件中配置多个环境,三个配置文件的代码清单如下所示:
application-peer1.properties
# 配置端口
server.port=1111
# 配置应用名
spring.application.name=eureka-server
# 配置服务注册中心地址
eureka.instance.hostname=peer1
# 设置服务注册中心服务注册地址
eureka.client.service-url.defaultZone=http://peer2:1112/eureka/,http://peer3:1113/eureka/
application-peer2.properties
# 配置端口
server.port=1112
# 配置应用名
spring.application.name=eureka-server
# 配置服务注册中心地址
eureka.instance.hostname=peer2
# 设置服务注册中心服务注册地址
eureka.client.service-url.defaultZone=http://peer1:1111/eureka/,http://peer3:1113/eureka/
application-peer3.properties
# 配置端口
server.port=1113
# 配置应用名
spring.application.name=eureka-server
# 配置服务注册中心地址
eureka.instance.hostname=peer3
# 设置服务注册中心服务注册地址
eureka.client.service-url.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/
在配置文件中,我们设置了三个hostname
分别为peer1
、peer2
、peer3
,并将自己分别注册给了其他两个注册中心(也可以设置注册到集群中的任何一个服务注册中心即可,启动后会被服务注册中心同步)。我们配置一下hosts
,将peer1
、peer2
、peer3
都映射到127.0.0.1
,如下所示:
127.0.0.1 peer1
127.0.0.1 peer2
127.0.0.1 peer3
我们将项目spring-cloud-eureka-server
安装到本地仓库,然后到本地仓库去启动三个节点:
java -jar spring-cloud-eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1
java -jar spring-cloud-eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2
java -jar spring-cloud-eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer3
这时候去访问三个Eureka
服务注册中心,发现DS Replicas
多出了其他节点组成的列表:
到这里,也就完成了Eureka
服务注册中心高可用集群的搭建。
一般对于集成了微服务的系统来说,各个服务提供者不再是一个运行在单节点上的应用,更多的都是构成服务集群。借助服务注册中心,我们可以将服务提供者打包成jar
,然后直接使用不同端口来运行服务即可。当然,对于服务提供者,我们需要将配置文件application.properties
设置如下:
# 配置服务名称
spring.application.name=producer-service
# 配置服务注册地址(向集群中注册一个即可,或者全部注册也可以)
eureka.client.service-url.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/,http://peer3:1113/eureka/
我们将这个服务分别注册到三个服务注册中心,其实,由于注册中心搭建了集群,那么可以向其中任一一个注册中心注册即可,其他的两个都会拿到副本。
java -jar spring-cloud-eureka-producer-client-0.0.1-SNAPSHOT.jar --server.port=8081
java -jar spring-cloud-eureka-producer-client-0.0.1-SNAPSHOT.jar --server.port=8082
java -jar spring-cloud-eureka-producer-client-0.0.1-SNAPSHOT.jar --server.port=8083
这里在三个端口分别启动了一个服务,那么这三个服务都注册到了服务注册中心,如下图所示:
高可用服务注册中心和服务提供者搭建完毕之后,就需要来搭建消费者,作为消费者,它需要完成两项任务,一个是服务发现,另一个是服务消费。服务发现由Eureka客户端来完成,而服务消费则由Ribbon
来完成,Ribbon
作为一款基于HTTP/TCP
的客户端负载均衡器,它通过在客户端中配置RibbonServerList
服务端列表,采用某种轮询的方式去访问服务达到负载均衡的作用,而当Ribbon
和Eureka
联合使用的时候,RibbonServerList
则会被DiscoveryEnabledNIWSServerList
覆盖,从而可以从服务注册中心获取服务列表,至于服务注册中心是否已经启动,则委托给NIWSDiscoveryPing
来判断。
我们创建一个spring-cloud-eureka-consumer-client
项目,该项目的pom
文件代码清单如下所示:
<?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>
<artifactId>spring-cloud-eureka-consumer-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-eureka-consumer-client</name>
<description>Eureka Consumer Client project for Spring Boot</description>
<parent>
<groupId>cn.itlemon.springcloud</groupId>
<artifactId>learning-springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</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>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
相较于之前的服务提供者的pom
,我们增加了依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
我们在Spring Boot
启动类,需要实例化RestTemplate
对象,并使用注解@LoadBalanced
开启客户端负载均衡,代码如下:
package cn.itlemon.springcloud.consumer.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author lemon
*/
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudEurekaConsumerClientApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaConsumerClientApplication.class, args);
}
}
我们在创建一个Controller
,通过RestTemplate
对象来发起HTTP
请求,代码如下:
package cn.itlemon.springcloud.consumer.client.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @author jiangpingping
* @date 2018/11/7 19:06
*/
@RestController
@Slf4j
public class ConsumerController {
private final RestTemplate restTemplate;
@Autowired
public ConsumerController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/hello/consumer")
public String helloConsumer() {
String result = restTemplate.getForEntity("http://producer-service/hello/producer", String.class).getBody();
log.info("远程调用返回结果是:{}", result);
return result;
}
}
上面的代码中,有一个很重要的特性,那就是restTemplate
并没有向指定的IP
和端口发起请求,而是直接向服务名producer-service
发起接口调用,这一点表明,服务消费段无需关心服务提供者的真实地址和端口,也可以对服务提供者发起请求,这正是符合服务治理的思想。
当然,我们还需要在配置文件中进行配置如下:
# 配置应用名称
spring.application.name=ribbon-consumer
# 配置服务注册和发现地址
eureka.client.service-url.defaultZone=http://peer1:1111/eureka/
启动Spring Boot
应用,向http://localhost:8080/hello/consumer
发起请求,那么浏览器成功返回Hello Spring Cloud!
,说明此时服务调用成功。我们再次观察Eureka
信息面板,发现ribbon-consumer
也作为服务注册到了服务注册中心供其他服务调用。由于我们启动了多个服务提供者,我们可以多发送几个请求,在多个服务提供者的控制台可以看到,每一次请求,控制台的消息打印并不是规律的(其实Ribbon利用了亲和特性来优先访问同一个zone的服务),也就是说,这就实现了ribbon-consumer
对producer-service
的调用是负载均衡的。
这里就对搭建高可用Eureka Server
、服务注册与发现的基本内容就介绍结束了,后期将将继续介绍Spring Cloud
其他内容,敬请关注。