注册中心
理解注册中心:
服务管理,核心是有个服务注册表,心跳机制动态维护。
Eureka Server: 服务注册中心,负责服务列表的注册、维护和查询等功能;
Service Provider: 服务提供方,同时也是一个Eureka Client,启动的时候向注册中心上报自己的网络信息,负责将所提供的服务向Eureka Server进行注册、续约和注销等操作。注册时所提供的主要数据包括服务名、机器ip、端口号、域名等,从而能够使服务消费方能够找到;
Service Consumer:服务消费方,同时也是一个Eureka Client。 启动的时候向注册中心上报自己的网络信息,拉取provider的相关网络信息。
Service Provider(服务提供方)和Service Consumer(服务消费方)并不是一个严格的概念,往往服务消费方也是一个服务提供方,同时服务提供方也可能会调用其它服务方所提供的服务。当然在我们进行微服务构建时还是需要遵守业务层级之间的划分,尽量避免服务之间的循环依赖。
为什么要用注册中心【服务治理】:
- 微服务数量众多,要进行远程调用就需要知道服务端的ip地址和端口,注册中心帮助我们管理这些服务的ip和端口。
- 微服务会实时上报自己的状态,注册中心统一管理这些微服务的状态,将存在问题的服务踢出服务列表,客户端获取到可用的服务进行调用。
主流的注册中心:
zookeeper、Eureka、consul、etcd 等
Eureka和Zookeeper的比较
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
- 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
- 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
- 分区容忍性(P):
一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中,这就叫分区。
当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是无法容忍的。
提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。容忍性就提高了。然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。
但我们可以设想不提供P,也就是不允许出现分区的情况,是有可能同时提供AC两个特性的。 实际上这在分布式系统中,并不受控。所以一般认为P是必须提供的,所以AC需要权衡。
Zookeeper保证CP
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。
在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
Eureka保证AP
Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。
- Eureka不持久化,缓存,Zookeeper持久化,对于注册中心没必要持久化,我们只关心当前瞬时的服务状态
- Eureka通过增量更新注册信息,Zookeeper通过Watch事件监控变化,对于服务注册变化的过程,我们不关心,只关心瞬时状态
- Eureka提供客户端缓存,Zookeeper无客户端缓存,在网络隔离注册中心访问不了的情况下,宁可返回某服务5分钟之前在哪几个服务器上可用的信息,也不能因为暂时的网络故障而找不到可用的服务器
总结
Eureka作为单纯的服务注册中心来说要比zookeeper更加“专业”,因为注册服务更重要的是可用性,我们可以接受短期内达不到一致性的状况。
Zookeeper:CP设计,保证了一致性,集群搭建的时候,某个节点失效,则会进行选举行的leader,或者半数以上节点不可用,则无法提供服务,因此可用性没法满足
Eureka:AP原则,无主从节点,一个节点挂了,自动切换其他节点可以使用,去中心化
所以Eureka适合作为服务注册发现中心,Zookeeper适合更广泛的分布式协调服务
如果要求可用性,则Eureka,如电商系统;如果要求一致性,则选择zookeeper,如金融行业
Eureka
Eureka介绍
Spring Cloud Eureka 是对Netflix公司的Eureka的二次封装,它实现了服务治理的功能,Spring Cloud Eureka提供服务端与客户端,服务端即是Eureka服务注册中心,客户端完成微服务向Eureka服务的注册与发现。下图显示了Eureka Server与Eureka Client的关系:
Eurka 工作流程
1、Eureka Server 启动成功,等待服务端注册。在启动过程中如果配置了集群,集群之间定时通过 Replicate 同步注册表,每个 Eureka Server 都存在独立完整的服务注册表信息
2、Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务
3、Eureka Client 会每 30s 向 Eureka Server 发送一次心跳请求,证明客户端服务正常
4、当 Eureka Server 90s 内没有收到 Eureka Client 的心跳,注册中心则认为该节点失效,会注销该实例
5、单位时间内 Eureka Server 统计到有大量的 Eureka Client 没有上送心跳,则认为可能为网络异常,进入自我保护机制,不再剔除没有上送心跳的客户端
6、当 Eureka Client 心跳请求恢复正常之后,Eureka Server 自动退出自我保护模式
7、Eureka Client 定时全量或者增量从注册中心获取服务注册表,并且将获取到的信息缓存到本地
8、服务调用时,Eureka Client 会先从本地缓存找寻调取的服务。如果获取不到,先从注册中心刷新注册表,再同步到本地缓存
9、Eureka Client 获取到目标服务器信息,发起服务调用
10、Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中删除
在Eureka中,其核心的概念主要有如下几个:
服务注册(Register)
当Eureka客户端向Eureka服务注册器注册时,它提供自身的元数据,比如IP地址、端口等信息。
服务续约(Renew)
在服务续约中,Eureka客户端会每隔30秒发送一次心跳来进行服务续约。通过续约来告知Eureka服务器该客户端仍然存在,希望服务器不要剔除自己。
服务下线(Cancel)
Eureka客户端在程序关闭时向Eureka服务器发送取消请求。发送请求后,该客户端实例信息将从服务器的实例注册列表中删除。
服务剔除(Eviction)
在默认的情况下,当Eureka客户端连续90秒没有像Eureka服务器发送服务续约的心跳(Heartbeat),Eureka服务器就会将该服务实例从服务注册列表中删除,即剔除该服务。
获取服务注册列表信息(Fetch Registries)
Eureka客户端从Eureka服务器获取服务注册列表信息,并将其缓存到本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每隔30秒)更新一次。每次返回的注册列表信息可能与Eureka客户端的缓存信息有所不同,Eureka客户端会自动处理两者之间的差异。
Eureka搭建
工程结构如下:
1、搭建微服务Parent工程
先构建一个parent
工程,该工程仅用来定义一个pom
文件,后续工程的pom文件的皆继承该pom。在该pom中我们将定义各工程所共同使用的第三方依赖
及相应版本定义
,比如我们接下来的各工程中对Spring Cloud的依赖等。这样我们就可以统一对第三方依赖及基础信息定义进行管理,后续当我们需要升级第三方依赖时,只需要修改一个地方
就可以了。
注意:Spring Cloud
的版本与Spring Boot
的版本一定要对应上,不然会编译不通过。
parent 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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<groupId>twostepsfromjava.cloud</groupId>
<artifactId>twostepsfromjava-cloud-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、搭建服务治理服务器——Eureka服务器
- 编写的
pom.xml
文件要继承上面的Parent
项目,增加spring-cloud-starter-netflix-eureka-server
依赖包,并把artifactId定义为:service-discovery。
注意:
- 这里我们直接继承parent项目中的pom,所以只需要声明我们需要的新增的spring-cloud-starter-eureka-server依赖即可。
- 很多老的博客或者书中,用的是spring-cloud-starter-eureka-server依赖包,是废弃的。特别对于Spring Boot2.0以上的版本是无法使用的。
<?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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>twostepsfromjava.cloud</groupId>
<artifactId>twostepsfromjava-cloud-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../parent</relativePath>
</parent>
<artifactId>service-discovery</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
关于
Spring Cloud
的命名:由于Spring Cloud
是诸多子项目集合的综合项目,原则上由其子项目维护自己的发布版本号,也就是我们常用的版本号,如:1.2.3.RELEASE
、1.1.4.RELEASE
等。因此Spring Cloud
为了避免版本号与其子项目的版本号混淆,所以没有采用版本号的方式,而是采用命名的方式。这些版本名称采用了伦敦地铁站的名字,根据字母表的顺序来对应版本时间顺序。比如,最早的Release版本名称为Angel
,第二个Release版本的名称为Brixton
,以此类推……。而我们在本系列文章所使用的版本名称为:Dalston.SR1
,也就是最新版本。后续版本名称根据项目中公布的分别为:Edgware
和Finchley
。
另,Dalston.SR1
中的SR
是service releases
的简写,而1
则是该版本名称中的第1个版本。
具体关于Spring Cloud
版本的命名说明可以参考这里.
- 修改Spring Boot的应用引导类,在应用引导类中增加服务治理服务器的注解。
通过@EnableEurekaServer注解,Spring Boot在启动应用的时候就会自动构建一个默认的服务治理服务器。
@EnableEurekaServer//标识这是一个Eureka服务
@SpringBootApplication
public class GovernCenterApplication {
public static void main(String[] args) {
SpringApplication.run(GovernCenterApplication.class, args);
}
}
- 增加Eureka相应的配置:
server.port=8260
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
-
registerWithEureka:
用来控制当Spring Boot启动服务完成后是否将该服务注册到服务治理服务器上。因为该服务本身就是服务治理服务器,而且未构建服务治理集群,所以设置为false,表示不注册。
被其它服务调用时设置为true,表示需向Eureka注册。 -
fetchRegistry
应用启动后不需要从服务治理服务器上同步已注册的服务注册列表数据到本地。需要从Eureka中查找要调用的目标服务时需要设置为true。 -
serviceUrl.defaultZone
配置上报Eureka服务地址高可用状态配置对方的地址,单机状态配置自己 -
enable-self-preservation
自保护设置。
Eureka Server有一种自我保护模式,当微服务不再向Eureka Server上报状态,Eureka Server会从服务列表将此服务删除,如果出现网络异常情况(微服务正常),此时Eureka server进入自保护模式,不再将微服务从服务列表删除。
在开发阶段建议关闭自保护模式 -
eviction-interval-timer-in-ms:
清理失效结点的间隔,在这个时间段内如果没有收到该结点的上报则将结点从服务列表中剔除。
- 启动Eureka Server
接下来你可以在你的IDE中启动该服务。当然你也可以将该服务器打包成一个Fat Jar,然后通过java -jar的命令启动,如:
java -jar service-discovery-1.0.0-SNAPSHOT.jar
说明: 如果需要打包成一个Fat Jar你需要修改pom.xml中的配置,增加如下内容:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
增加一个Spring Boot打包插件。这样编译出来的Jar包就可以通过上述命令直接运行。
启动成功后访问http://localhost:50101/可以查看Eureka的控制台。
上图红色提示信息:
THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OFNETWORK/OTHER PROBLEMS.
自我保护模式被关闭。在网络或其他问题的情况下可能不会保护实例失效。
搭建服务提供者——注册服务
Eureka服务器我们已经编写好了,接下来我们就可以编写一个Eureka的客户端了。这个客户端可能是一个服务提供者,也可能是一个服务消费者,甚至两者都是。
我们先编写一个简单的Eureka Client
,该客户端提供一个简单的服务,就是调用/hello
服务端点(EndPoint)时返回一个字符串Hello, Spring Cloud!
。
1. 编写pom.xml文件
同样,我们继承自parent
项目的pom.xml
,这里将artifactId
定义为:service-hello
,也就是提供Hello
服务。
pom
文件增加spring-cloud-starter-netflix-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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>twostepsfromjava.cloud</groupId>
<artifactId>twostepsfromjava-cloud-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../parent</relativePath>
</parent>
<artifactId>service-hello</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. 在application.yml配置
server.port=2100
spring.application.name=SERVICE-HELLO
eureka.client.service-url.defaultZone=http://localhost:8260/eureka
这里spring.application.name
必须要设置,服务消费者将通过该名称调用所提供的服务。 eureka.client.service-url
也必须设置,表示我们要向那些Eureka
服务器进行服务注册,这里可以声明多个Eureka
服务器,具体我们将在后面关于Eureka高可用相关章节中进行详细说明。
3. 在启动类上添加注解
引导类中增加@EnableDiscoveryClient
,表示这是一个Eureka客户端,通过该注解,在Spring Boot启动完毕之后,就会根据配置中的信息尝试与服务治理服务器进行连接,连接成功之后进行服务注册或服务注册信息的同步。
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
说明: 这里与service-discovery的唯一区别就是启动类上注解变成了@EnableDiscoveryClient,声明这是一个Eureka Client。
4. 编写一个简单的API服务
@RestController
public class HelloEndpoint {
protected Logger logger = LoggerFactory.getLogger(HelloEndpoint.class);
@Autowired
private EurekaInstanceConfig eurekaInstanceConfig;
@Value("${server.port}")
private int serverPort = 0;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
this.logger.info("/hello, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
return "Hello, Spring Cloud! My port is " + String.valueOf(serverPort);
}
}
该服务仅提供一个/hello
服务端点,调用该服务后将返回一个字符串Hello, Spring Cloud!
。
5. 启动服务器
同样启动该服务器。启动成功后,我们将在控制台上看到这么一句日志:
[DiscoveryClient-InstanceInfoReplicator-0] INFO c.netflix.discovery.DiscoveryClient - DiscoveryClient_SERVICE-HELLO/192.168.0.105:SERVICE-HELLO:2100 - registration status: 204
这时候我们回到浏览器,刷新http://localhost:8260,将会看到如下界面:
注意:一个服务实例注册到Eureka服务器时需要30s才能够在控制台中查看该服务。这是因为,Eureka要求服务提供者必须发送3次心跳才认为该服务实例是ok的。
构建服务消费者
上面一个简单的Eureka服务器和客户端就已经构建完毕,接下来构建一个服务消费者,该服务消费者将调用SERVICE-HELLO
所提供的服务。
1. 编写pom.xml文件
同样,我们继承自parent
项目的pom.xml
,这里将artifactId
定义为:consumer-hello
,也就是Hello
服务消费者。
<?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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>twostepsfromjava.cloud</groupId>
<artifactId>twostepsfromjava-cloud-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../parent</relativePath>
</parent>
<artifactId>consumer-hello</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
</project>
这里需要注意的是我们除了依赖
spring-cloud-starter-eureka
,还依赖了Spring Cloud中的另外一个子项目spring-cloud-starter-ribbon
,该子项目提供客户端负载均衡功能,可以自动从Eureka服务器中获取服务提供者的地址列表,从而能够发起相应的调用。这个后面我们将详细进行说明,这里先引入进来就可以了。
2. 编写启动类
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
//@Bean
//@LoadBalanced
//RestTemplate restTemplate() {
// return new RestTemplate();
//}
@Bean
@LoadBalanced
RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build ();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 同
Service-Hello
一样在启动类上注解了@EnableDiscoveryClient
,说明这也是一个Eureka Client
。 -
SpringBoot
版本大于1.4
Spring Boot
不在自动定义RestTemplate
,而是定义了一个RestTemplateBuilder
。自动扫描注入RestTempate Bean
。 -
@LoadBalanced
是必须要加的
3. 编写服务调用
@RestController
public class HelloController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
}
}
该服务调用时一个标准的controller
,hello()
方法将通过restTemplate
调用SERVICE-HELLO/hello
服务并返回。
4. 编写配置文件
server.port=8080
spring.application.name=consumer-hello
eureka.client.service-url.defaultZone=http://localhost:8260/eureka
5. 启动服务器
启动成功后,同样我们将在控制台上看到这么一句日志:
[DiscoveryClient-InstanceInfoReplicator-0] INFO c.netflix.discovery.DiscoveryClient - DiscoveryClient_CONSUMER-HELLO/192.168.0.105:consumer-hello:8080 - registration status: 204
然后我们回到浏览器,刷新http://localhost:8260,将会看到如下界面:
说明我们的两个服务都已经在Eureka服务器上注册成功。
验证服务调用
在浏览器中,我们输入http://localhost:8080/hello,也就是该服务所定义的端口server.port=8080
,将会看到如下界面:
同时在Service-Hello的控制台中会打印下面一句日志:
[http-nio-2100-exec-1] INFO i.t.c.s.hello.api.HelloEndpoint - /hello, instanceId:cd826dembp.lan:SERVICE-HELLO:2100, host:192.168.0.105
获取Eureka中的注册信息
通过URL的方式获取服务的注册信息
这种方式比较简单,比如说我们现在有一个本地的Eureka服务器,其端口号为8761,当我们想获取其中所注册的服务信息时,我们只需要访问下面的地址:
http://localhost:8761/eureka/apps
其展示的结果为:
当我们想访问其中的product-service服务时,此时我们应该访问下面的地址:
http://localhost:8761/eureka/apps/product-service
其展示的结果为:
使用DiscoveryClient获取服务的注册信息
尽管说通过Eureka提供的管理界面和HTTP端点可以获取服务的详细信息,但是这还远远不够。在现实应用中,很多时候我们希望通过代码在运行时能够实时获取注册中心的服务列表。
在Eureka的客户端,我们通过Eureka提供的DiscoveryClient工具类就可以获取Eureka中注册的服务信息。
EurekaController
@RestController
@RequestMapping("/eurekaCentre")
public class EurekaController {
@Autowired
private DiscoveryClient discoveryClient;
/**
* 获取注册在Eureka中的服务名称
* @return
*/
@GetMapping("/getEurekaServices")
public List<String> getEurekaServices(){
List<String> services = new ArrayList<>();
List<String> serviceNames = discoveryClient.getServices();
for(String serviceName : serviceNames){
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
for(ServiceInstance serviceInstance : serviceInstances){
services.add(String.format("%s:%s",serviceName,serviceInstance.getUri()));
}
}
return services;
}
}
application.yml
# 服务名称
spring:
application:
name: product-service
# 服务端口号
server:
port: 8081
eureka:
instance:
# 指明使用IP而不是服务名称来注册自身服务。因为Eureka默认是使用域名进行服务注册和解析
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
运行
在浏览器中输入下列地址:
http://127.0.0.1:8081/eurekaCentre/getEurekaServices
其返回的服务列表如下:
[
"eureka-server:http://peer1:8761",
"eureka-server:http://peer2:8762",
"product-service:http://192.168.23.1:8081",
"product-service:http://192.168.23.1:8082",
"account-service:http://192.168.23.1:8084",
"order-service:http://192.168.23.1:8083"
]
搭建服务消费者——获取服务01
必须将eureka.client.fetch-registry的值设置为true,表示应用启动成功之后需要从服务治理服务器中获取已注册的服务列表到本地。
SpringBoot版本大于1.4 Spring Boot不在自动定义RestTemplate,而是定义了一个RestTemplateBuilder。所以创建RestTempateConfig自动扫描注入RestTempate Bean。
@LoadBalanced是必须要加的,不然会提示下面的错误
Controller代码示例
搭建服务消费者——获取服务02
<?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.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-consumer</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.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.7</java.version>
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.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>
</plugin>
</plugins>
</build>
</project>
然后更新Maven依赖,然后在入口文件添加@EnableDiscoveryClient注解和@EnableFeignClients注解,声明这个一个服务消费者。
package com.example.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
//启用服务注册与发现
@EnableDiscoveryClient
//启用feign进行远程调用
@EnableFeignClients
public class SpringCloudConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConsumerApplication.class, args);
}
}
配置application.properties文件
#指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问
spring.application.name=spring-cloud-consumer
#配置端口号
server.port=9002
#服务注册中心的配置内容,指定服务注册中心的Url
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
编写feign调用实现接口
package com.example.consumer.remote;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name= "spring-cloud-producer")
public interface HelloRemote {
//restful api 调用
@GetMapping("/hello/{name}")
public String hello(@PathVariable("name") String name);
//传统api调用
//@GetMapping(value = "/hello")
//public String hello(@RequestParam(value = "name") String name);
}
web层调用远程服务
package com.example.consumer.controller;
import com.example.consumer.remote.HelloRemote;
import com.example.consumer.remote.HiRemote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConsumerController {
@Autowired
HelloRemote helloRemote;
@GetMapping("/hello/{name}")
public String index(@PathVariable("name") String name) {
return helloRemote.hello(name);;
}
}
高可用环境搭建
Eureka Server 高可用环境需要部署两个Eureka server,它们互相向对方注册。如果在本机启动两个Eureka需要注意两个Eureka Server的端口要设置不一样,这里我们部署一个Eureka Server工程,将端口可配置,制作两个Eureka Server启动脚本,启动不同的端口,如下图:
- 在实际使用时Eureka Server至少部署两台服务器,实现高可用。
- 两台Eureka Server互相注册。
- 微服务需要连接两台Eureka Server注册,当其中一台Eureka死掉也不会影响服务的注册与发现。
- 微服务会定时向Eureka server发送心跳,报告自己的状态。
- 微服务从注册中心获取服务地址以RESTful方式发起远程调用
配置如下:
1、端口可配置
server:
port: ${PORT:50101} #服务端口
2、Eureka服务端的交互地址可配置
eureka:
client:
registerWithEureka: true #服务注册,是否将自己注册到Eureka服务中
fetchRegistry: true #服务发现,是否从Eureka中获取注册信息
serviceUrl: #Eureka客户端与Eureka服务端的交互地址,高可用状态配置对方的地址,单机状态配置自己(如果不配置则默认本机8761端口)
defaultZone: ${EUREKA_SERVER:http://eureka02:50102/eureka/}
3、配置hostname
Eureka 组成高可用,两个Eureka互相向对方注册,这里需要通过域名或主机名访问,这里我们设置两个Eureka服务的主机名分别为 eureka01、eureka02。
完整的eureka配置如下:
eureka:
client:
registerWithEureka: true #服务注册,是否将自己注册到Eureka服务中
fetchRegistry: true #服务发现,是否从Eureka中获取注册信息
serviceUrl: #Eureka客户端与Eureka服务端的交互地址,高可用状态配置对方的地址,单机状态配置自己(如果不配置则默认本机8761端口)
defaultZone: ${EUREKA_SERVER:http://eureka02:50102/eureka/}
server:
enable‐self‐preservation: false #是否开启自我保护模式
eviction‐interval‐timer‐in‐ms: 60000 #服务注册表清理间隔(单位毫秒,默认是60*1000)
instance:
hostname: ${EUREKA_DOMAIN:eureka01}
4、在IDEA中制作启动脚本
启动1:
启动2:
在host文件配置
127.0.0.1 eureka01
127.0.0.1 eureka02
运行两个启动脚本,分别浏览:
http://localhost:50101/
http://localhost:50102/
Eureka主画面如下