第三篇:SpringCloud 构建微服务系统之服务注册和发现(Eureka)
前面我们已经介绍过consul和nacos作为SpringCloud 构建微服务系统之服务注册和发现的组件 ,今天我们介绍的是Eureka的使用。
- 第一篇:SpringCloud 构建微服务系统之服务注册和发现(consul)
- 第二篇:SpringCloud 构建微服务系统之服务注册和发现(nacos)
SpringCloud Eureka是SpringCloud Netflix服务套件中的一部分,它基于Netflix Eureka做了二次封装,主要负责完成微服务架构中的服务治理功能。
1、什么是 Eureka ?
Eureka 是 Netflix 的一个子模块,也是核心模块之一。Eureka 是一个基于 REST(REpresentational State Transfer) 的服务,用于定位服务,以实现云端中间层服务器的负载均衡和故障转移。Eureka还附带了一个基于java的客户端组件——Eureka Client,它使得与服务的交互更加容易。Eureka Client 还有一个内置的负载均衡器,可以进行基本的循环负载均衡,在 Netflix,一个更加复杂的负载均衡器封装了 Eureka,可以根据流量、资源的使用情况、错误条件等因素根据自定义的权重来实现负载均衡,从而提供更好的弹性服务。
对于服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。
2、Eureka 基本架构原理
Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,则使用 Eureka Client 连接到 Eureka Server 并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他子模块(例如 Gateway)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的业务逻辑。一个 Eureka 的高可用架构图如下(后面会专门的一篇文章来描述如何实现高可用):
上面图描述了 Eureka 是如何部署在 Netflix 上的,这是 Eureka 典型的使用案例。每个区域都有一个 Eureka 集群,而这个 Eureka 集群只知道该区域中的实例。每个区域至少有一个 Eureka 服务器来处理该区域故障(故障转移)。
Spring Cloud Netflix 在设计 Eureka 时就紧遵AP原则。Eureka Server 也可以运行多个实例来构建集群,解决单点问题,但不同于 ZooKeeper 的选举 leader 的过程,Eureka Server 采用的是Peer to Peer 对等通信。这是一种去中心化的架构(参看:微服务与微服务架构思想与原则),无 master/slave 之分,每一个 Peer 都是对等的。在这种架构风格中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。每个节点都可被视为其他节点的副本。
在集群环境中如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点上,当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会在节点间进行复制(replicate To Peer)操作,将请求复制到该 Eureka Server 当前所知的其它所有节点中。
当一个新的 Eureka Server 节点启动后,会首先尝试从邻近节点获取所有注册列表信息,并完成初始化。Eureka Server 通过 getEurekaServiceUrls() 方法获取所有的节点,并且会通过心跳契约的方式定期更新。默认情况下,如果 Eureka Server 在一定时间内没有接收到某个服务实例的心跳(默认周期为30秒),Eureka Server 将会注销该实例(默认为90秒,如果某个 eureka.instance.lease-expiration-duration-in-seconds 进行自定义配置)。当 Eureka Server 节点在短时间内丢失过多的心跳时,那么这个节点就会进入自我保护模式(后面有文章会谈及关于 Eureka Server 的自我保护机制)。
3、构建Eureka Server
新建一个springboot项目:eureka-server,其pom.xml配置如下:
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lidong</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RC2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
编写启动类
package com.lidong.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/** * 开启服务发现 */
@EnableEurekaServer
@SpringBootApplication
public class SpringCloudRegister {
public static void main(String[] args) {
SpringApplication.run(SpringCloudRegister.class, args);
}
}
application.yml文件配置
server:
port: 8761#提供者的端口
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
lease-renewal-interval-in-seconds: 1
lease-expiration-duration-in-seconds: 2
client:
register-with-eureka: false #不是用eureka进行服务注册
fetch-registry: false #不在本地缓存注册信息
serviceUrl:
defaultZone: http://${ eureka.instance.hostname}:${ server.port}/eureka/
server:
wait-time-in-ms-when-sync-empty: 5 #在服务器接收请求之前等待初始时间
enable-self-preservation: false #关闭自我保存
启动服务
在浏览器输入 http://localhost:8761/,
可以看到启动成,
4、构建服务提供方
新建一个springboot项目:spring-cloud-eureka-producer,其pom.xml配置如下:
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lidong</groupId>
<artifactId>spring-cloud-eureka-producer</artifactId>
<version>1.0.0</version>
<name>spring-cloud-eureka-producer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RC2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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-starter-web</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
提供者添加配置(application.yml)
server:
port: 9011 #提供者的端口
spring:
application:
name: spring-cloud-eureka-producer
eureka:
client:
service-url:
# 指定eureka server通信地址,注意/eureka/
defaultZone: http://localhost:8761/eureka/
instance:
# 是否注册IP到eureka server,如不指定或设为false,那就会注册主机名到eureka server
prefer-ip-address: true
新建服务
新建 EurekaProducerController,提供 sayHello 接口, 返回一个hello—>字符串。
package com.lidong.provider.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
/** * 创建服务 */
@RestController
public class EurekaProducerController {
@Value("${server.port}")
private Integer port;
/** * 服务接口 * @param name * @return */
@RequestMapping("/hello")
public String sayHello(@RequestParam("name")String name) {
return "hello ---> "+name+" port -->"+port;
}
}
配置启动类
package com.lidong.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;
/** * 开启服务发现 */
@SpringBootApplication
//@EnableDiscoveryClient
//@EnableEurekaClient
//注意:早期的版本(Dalston及更早版本)还需在启动类上添加注解@EnableDiscoveryClient 或
//@EnableEurekaClient ,从Edgware开始,该注解可省略。
public class SpringCloudLidongProviderApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudLidongProviderApplication.class, args);
}
}
运行应用
我们会在Eureka的控制台上看到应用名,证明服务提供者已经成功注册在Eureka上了
5、构建服务消费方
新建一个springboot项目:spring-cloud-eureka-consumer,其pom.xml配置如下:
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lidong</groupId>
<artifactId>spring-cloud-eureka-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-eureka-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RC2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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-starter-web</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-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
消费者添加配置(application.yml)
server:
port: 9012 #提供者的端口
spring:
application:
name: spring-cloud-eureka-consumer
eureka:
client:
service-url:
# 指定eureka server通信地址,注意/eureka/
defaultZone: http://localhost:8761/eureka/
instance:
# 是否注册IP到eureka server,如不指定或设为false,那就会注册主机名到eureka server
prefer-ip-address: true
创建ConsumerController
package com.lidong.consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
/** * 创建服务的消费者 */
@RestController
public class ConsumerController {
private static final String SERVICE_NAME = "spring-cloud-eureka-producer";
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
/** * 获取所有服务 */
@RequestMapping("/services")
public Object services() {
return discoveryClient.getInstances(SERVICE_NAME);
}
/** * 调用服务 */
@RequestMapping("/callSayHello")
public String services(@RequestParam("name") String name) {
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("name",name);
String callServiceResult = restTemplate.getForObject("http://"+SERVICE_NAME+"/hello?name={name}", String.class,paramMap);
System.out.println(callServiceResult);
return callServiceResult;
}
}
配置启动类
package com.lidong.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class SpringCloudEurekaConsumerApplication {
@Autowired
private RestTemplateBuilder builder;
@Bean
@LoadBalanced // 添加负载均衡支持,很简单,只需要在RestTemplate上添加@LoadBalanced注解,那么RestTemplate即具有负载均衡的功能,如果不加@LoadBalanced注解的话,会报java.net.UnknownHostException:springboot-h2异常,此时无法通过注册到Eureka Server上的服务名来调用服务,因为RestTemplate是无法从服务名映射到ip:port的,映射的功能是由LoadBalancerClient来实现的。
public RestTemplate restTemplate() {
return builder.build();
}
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaConsumerApplication.class, args);
}
}
消费者注册成功
获取服务列表的结果
[{ "host":"192.168.10.103","port":9011,"metadata":{ "management.port":"9011","jmx.port":"56141"},"secure":false,"uri":"http://192.168.10.103:9011","instanceId":"vip-PC:spring-cloud-eureka-producer:9011","serviceId":"SPRING-CLOUD-EUREKA-PRODUCER","instanceInfo":{ "instanceId":"vip-PC:spring-cloud-eureka-producer:9011","app":"SPRING-CLOUD-EUREKA-PRODUCER","appGroupName":null,"ipAddr":"192.168.10.103","sid":"na","homePageUrl":"http://192.168.10.103:9011/","statusPageUrl":"http://192.168.10.103:9011/actuator/info","healthCheckUrl":"http://192.168.10.103:9011/actuator/health","secureHealthCheckUrl":null,"vipAddress":"spring-cloud-eureka-producer","secureVipAddress":"spring-cloud-eureka-producer","countryId":1,"dataCenterInfo":{ "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo","name":"MyOwn"},"hostName":"192.168.10.103","status":"UP","overriddenStatus":"UNKNOWN","leaseInfo":{ "renewalIntervalInSecs":30,"durationInSecs":90,"registrationTimestamp":1548660640476,"lastRenewalTimestamp":1548661751127,"evictionTimestamp":0,"serviceUpTimestamp":1548660143567},"isCoordinatingDiscoveryServer":false,"metadata":{ "management.port":"9011","jmx.port":"56141"},"lastUpdatedTimestamp":1548660640476,"lastDirtyTimestamp":1548660639779,"actionType":"ADDED","asgName":null},"scheme":null}]
测试请求的url
http://localhost:9012/callSayHello?name=9012
消费的结果
hello ---> 9012 port -->9011
到这里的话,Eureka在SpringCloud 构建微服务系统之服务注册和发现使用基本说明白,后面我将会将consul
、nacos
、eureka
三种在服务注册与发现中做一个对比,分析各种优缺点等。
还没有评论,来说两句吧...