Eureka
Eureka 简介
Eureka 是 Netflix 开发的服务发现框架,本身是一个基于 REST 的服务,主要用于定 位运行在 AWS 域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。 SpringCloud 将它集成在其子项目 spring-cloud-netflix 中,以实现 SpringCloud 的服务发现 功能。
Eureka 包含两个组件:Eureka Server 和 Eureka Client。
Eureka Server
Eureka Server 提供服务注册服务,各个节点启动后,会在 Eureka Server 中进行注册, 这样 EurekaServer 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。 Eureka Server 本身也是一个服务,默认情况下会自动注册到 Eureka 注册中心。 如果搭建单机版的 Eureka Server 注册中心,则需要配置取消 Eureka Server 的自动注 册逻辑。毕竟当前服务注册到当前服务代表的注册中心中是一个说不通的逻辑。 Eureka Server 通过 Register、Get、Renew 等接口提供服务的注册、发现和心跳检测等 服务。
Eureka Client
Eureka Client 是一个 java 客户端,用于简化与 Eureka Server 的交互,客户端同时也具 备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向 Eureka Server 发送心跳,默认周期为 30 秒,如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认 90 秒)。 Eureka Client 分 为 两 个 角 色 , 分 别 是 : Application Service(Service Provider) 和 Application Client(Service Consumer)
Application Service
服务提供方,是注册到 Eureka Server 中的服务
Application Client
服务消费方,通过 Eureka Server 发现服务,并消费。
注意:在这里,Application Service 和 Application Client 不是绝对上的定义,因为 Provider 在提供服务的同时,也可以消费其他 Provider 提供的服务;Consumer 在消费服 务的同时,也可以提供对外服务。
Eureka 的CAP定理
CAP 定理
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency(数据一致性)、 Availability(服务可用性)、Partition tolerance(分区容错性),三者不可兼得。
- 数据一致性:
系统在执行某项操作后仍然处于一致的状态。在分布式系统 中,更新操作执行成功后所有的用户都应该读到最新的值, 这样的系统被认为是具有强一致性的。等同于所有节点访问 同一份最新的数据副本。- 优点:数据一致,没有数据错误可能
- 缺点:相对效率降低
- 服务可用性:
每一个操作总是能够在一定的时间内返回结果,这里需 要注意的是"一定时间内"和"返回结果"。一定时间内指的是, 在可以容忍的范围内返回结果,结果可以是成功或者是失败 - 分区容错性
在网络分区的情况下,被分隔的节点仍能正常对外提供服务(分布式集群,数据被分布存储在不同的服务器上,无论 什么情况,服务器都能正常被访问)
定律:任何分布式系统只可同时满足二点,没法三者兼顾
Eureka满足AP
Eureka 分布集群是平等模型(无主模型), 所有的节点都是平等的,客户端访问任意节 点都可以提供实时的服务响应。如果某节点发送宕机等故障,接收到的请求会转交给其他的节点。无主模型,每个节点的数据可能不实时一致,节点需要通过网络通讯从其他节点获取数据,并实现数据的一 致。可能有网络延迟或 网络故障或通讯频率问 题。
搭建 Eureka 注册中心
Eureka Server 既是一个注册中心,同时也是一个服务。那么搭建 Eureka Server 的方式 和以往搭建 Dubbo 注册中心 ZooKeeper 的方式必然不同,那么首先搭建一个单机版的 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 https://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.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lanh</groupId>
<artifactId>eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<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</artifactId>
</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>
- application.yml
server:
# 设置 Eureka Server WEB 控制台端口、服务注册发现端口。Eureka Server 服务注册 发现端口默认为 8761。
port: 8761
spring:
application:
# 设置 spring 应用命名,默认为 null。同命名的应用会注册到同一个服务集群中。
name: cloud-eureka
eureka:
client:
# 是否将自己注册到 Eureka-Server 中,默认的为 true
register-with-eureka: false
# 是否从 Eureka-Server 中获取服务注册信息,默认为 true
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
- 启动类
package com.lanh.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
管理页面解读
在浏览器输入:http://localhost:8761/
管理页面如下:
- System Status 系统状态展示
- DS Replicas 注册中心集群列表
- Instances currently registered with Eureka 已在注册中心中注册的服务列表
- General Info 当前注册中心相关信息展示
- Instance Info 当前注册中心实例信息展示
集群原理
Eureka Server 注册中心的集群和 Dubbo 的 ZooKeeper 注册中心集群在结构上有很大的 不同。Eureka Server 注册中心集群中每个节点都是平等的,集群中的所有节点同时对外提供 服务的发现和注册等功能。同时集群中每个 Eureka Server 节点又是一个微服务,也就是说,每个节点都可以在集群中的其他节点上注册当前服务。又因为每个节点都是注册中心,所 以节点之间又可以相互注册当前节点中已注册的服务,并发现其他节点中已注册的服务。
将服务注册到Eureka
创建服务提供者
- 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 https://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.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lanh</groupId>
<artifactId>provider-user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>provider-user</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<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</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.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- application.yml
server:
# 设置 Eureka Server WEB 控制台端口、服务注册发现端口。Eureka Server 服务注册 发现端口默认为 8761。
port: 8081
spring:
application:
# 设置 spring 应用命名,默认为 null。同命名的应用会注册到同一个服务集群中。
name: provider-user
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
- 提供服务
package com.lanh.provideruser.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author Lanh
**/
@RestController
public class Controller {
@GetMapping("/show")
public String show(){
return "Hello";
}
}
- 启动类
package com.lanh.provideruser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ProviderUserApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderUserApplication.class, args);
}
}
服务消费者
- 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 https://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.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lanh</groupId>
<artifactId>consumer-movie</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consumer-movie</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<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</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.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- application.yml
server:
# 设置 Eureka Server WEB 控制台端口、服务注册发现端口。Eureka Server 服务注册 发现端口默认为 8761。
port: 8082
spring:
application:
# 设置 spring 应用命名,默认为 null。同命名的应用会注册到同一个服务集群中。
name: consumer
eureka:
client:
# 是否将自己注册到 Eureka-Server 中,默认的为 true
register-with-eureka: false
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
- 调用服务
package com.lanh.consumermovie.controller;
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;
import org.springframework.web.client.RestTemplate;
/**
* @Author Lanh
**/
@RestController
public class Controller {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/show/{name}")
public String show(@PathVariable String name){
return this.restTemplate.getForObject("http://localhost:8081/show",String.class)+" "+name;
}
}
- 启动类
package com.lanh.consumermovie;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
public class ConsumerMovieApplication {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieApplication.class, args);
}
}
测试: