前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Cloud 系列之注册中心 Eureka

Spring Cloud 系列之注册中心 Eureka

作者头像
Demo_Null
发布2020-11-04 14:48:20
3560
发布2020-11-04 14:48:20
举报
文章被收录于专栏:Java 学习

1.1 简介

1.1.1 概述

  Netflix Eureka 是由 Netflix 开源的一款基于 REST 的服务发现组件,包括 Eureka Server 及 Eureka Client。2012 年 9 月在 GitHub 上发布 1.1.2 版本,目前 Netflix 以宣布闭源,所以市面上还是以 1.x 版本为主。Eureka 提供基于 REST 的服务,在集群中主要用于服务管理。Eureka 提供了基于 Java 语言的客户端组件,客户端组件实现了负载均衡的功能,为业务组件的集群部署创造了条件。使用该框架,可以将业务组件注册到 Eureka 容器中,这些组件可进行集群部署,Eureka 主要维护这些服务的列表并自动检查它们的状态。Spring Cloud Netflix Eureka 是 Pivotal 公司为了将 Netflix Eureka 整合于 Spring Cloud 生态系统提供的版本。   Eureka 包含两个组件:Eureka Server 和 Eureka Client, Eureka Server 提供服务注册服务。各个微服务节点通过配置启动后,会在 EurekaServer 中进行注册,这样 EurekaServer 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。EurekaClient 通过注册中心进行访问。它是一个 Java 客户端,用于简化 Eureka Server 的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向 Eureka Server 发送心跳(默认周期为30秒)。如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳,EurekaServer 将会从服务注册表中把这个服务节点移除(默认90秒)

1.1.2 原理图

  一个简单的 Eureka 集群,需要一个 Eureka 服务器、若干个服务提供者。我们可以将业务组件注册到 Eureka 服务器中,其他客户端组件可以向服务器获取服务并且进行远程调用。Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址;提供者:启动后向 Eureka 注册自己信息(地址,提供什么服务);消费者:向 Eureka 订阅服务,Eureka 会将对应服务的所有提供者地址列表发送给消费者,并且定期更新;心跳(续约):提供者定期通过 http 方式向 Eureka 刷新自己的状态。

1.1.3 相关依赖

代码语言:javascript
复制
<!-- 需要确定 Spring Cloud 版本 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

1.2 搭建 EurekaServer

1.2.1 相关依赖

  现在都是子父工程,我们将子模块中都需要用的依赖放到父工程的 pom 文件中

代码语言:javascript
复制
<?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>
    <packaging>pom</packaging>
    <modules>
        <module>eureka</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.software</groupId>
    <artifactId>spring-cloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- 演示就不使用数据库了 -->
		<!--<dependency>-->
		<!--<groupId>org.springframework.boot</groupId>-->
		<!--<artifactId>spring-boot-starter-data-jpa</artifactId>-->
		<!--</dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- H 版 Spring Cloud 将 server 与 client 分开了,需要导入两个坐标 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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>

</project>

1.2.2 声明为 Eureka Server

  在 eureka 服务的启动类上使用 @EnableEurekaServer,声明当前应用为 Eureka 服务。

代码语言:javascript
复制
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description Eureka 启动类
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

1.2.3 配置文件

代码语言:javascript
复制
server:
  port: 8081

spring:
  application:
    name: eurekaServer  # 应用名称,在 Eureka 中作为 id 标识

eureka:
  client:
    register-with-eureka: false  # 不注册自己
    fetch-registry: false  # 不拉取自己
    service-url:
      defaultZone: http://127.0.0.1:8081/eureka/  # EurekaServer 的地址,如果是集群,需要加上其它 Server 的地址

1.2.4 启动服务

  启动服务访问对应的端口就可以看到以下界面,现在是一个服务都没有注册上来。可以把 register-with-eurekafetch-registry 两个配置取消就可以看到 eureka 自己了。

1.3 提供者

1.3.1 声明为 Eureka Client

  在服务提供者启动类中使用 @EnableDiscoveryClient,让 Eureka 能够发现,扫描到该服务。@EnableEurekaClient 注解也能实现但是该注解只支持 Eureka 作为注册中心,@EnableDiscoveryClient 可以是其他注册中心,建议使用 @EnableDiscoveryClient

代码语言:javascript
复制
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description 服务提供者启动类
 */
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}

1.3.2 配置文件

代码语言:javascript
复制
server:
  port: 8082

spring:
  application:
    name: ProviderServer  # 应用名称,在 Eureka 中作为 id 标识

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8081/eurake/

1.3.3 提供服务

代码语言:javascript
复制
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description
 */

@RestController
@RequestMapping("/provider")
public class ProviderController {

    @GetMapping("/get")
    public Object get() {
        return "你已经消费了";
    }
}

1.3.4 启动服务

  启动服务之后,会自动将自己注册到 Eureka 中

1.4 消费者

1.3.1 声明为 Eureka Client

代码语言:javascript
复制
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description 消费者启动类
 */
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
	
	// 将 RestTemplate 交由容器管理
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

1.3.2 配置文件

代码语言:javascript
复制
server:
  port: 8083

spring:
  application:
    name: ConsumerServer  # 应用名称,在 Eureka 中作为 id 标识

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8081/eureka

1.3.3 消费服务

  我们之前使用 RestTemplate 需要自己写 URI,这样很不利于维护,而且容易出错,现在只需要确定应用名称,利用应用名称从 Eureka 中就可以获取到详细信息。

代码语言:javascript
复制
/**
 * Created with IntelliJ IDEA.
 *
 * @author gaohu9712@163.com
 * @date 2020/10/29
 * @description
 */
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private DiscoveryClient discoveryClient;


    @GetMapping("/go")
    public void go() {
        List<ServiceInstance> providerServer = discoveryClient.getInstances("ProviderServer");

        if (0 == providerServer.size()) {
            return;
        }

        ServiceInstance serviceInstance = providerServer.get(0);
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        URI uri = serviceInstance.getUri();
        System.out.println("主机:" + host);
        System.out.println("端口:" + port);
        System.out.println("uri:" + uri);


        RestTemplate restTemplate = new RestTemplate();
        String str = restTemplate.getForObject(uri + "/provider/get", String.class);
        System.out.println(str);
    }
}

1.3.4 启动服务

1.3.5 请求服务

1.3.6 执行流程

 ♞ 先启动 eureka 注册中心  ♞ 启动服务提供者 provider  ♞ 服务提供者启动后会把自身信息(比如服务地址以别名方式注册进 eureka)  ♞ 消费者 consumer 服务在需要调用接口时,使用服务别名去注册中心获取实际的 RPC 远程调用地址  ♞ 消费者获得调用地址后,底层实际是利用 HttpClient 技术实现远程调用  ♞ 消费者获得服务地址后会缓存在本地 jvm 内存中,默认每间隔 30 秒更新一次服务调用地址

1.5 补充配置

1.5.1 actuator 信息完善

  我们现在的服务注册到 Eureka 上面是没有 ip 地址的,以后等服务搭建集群是很不方便的,所以我们需要让他显示自己的 ip 地址;第二个就是服务名称为主机 + 服务名 + 端口,这样就暴露了主机名,我们可以指定显示的名称。

在配置文件中添加如下配置,通过健康检查(http://ip:port/actuator/health)查看是否修改成功。

代码语言:javascript
复制
eureka:
  instance:
    # 实例名称
    instance-id: consumer-01
    # 地址中显示 ip 
    prefer-ip-address: true

1.5.2 自我保护机制

☞ 概述

  我们可以看到 Eureka 上有一行红色的英文 EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.它代表了 Eureka 保护模式的开启。一旦进入保护模式,Eureka Server 将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。   默认情况下,如果 Eureka Server 在一定时间内没有接收到某个微服务实例的心跳,Eureka Server 将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与 Eureka Server 之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。为了防止 Eureka Client 可以正常运行,但是与 Eureka Server 网络不通情况下,Eureka Server 不会立刻将 Eureka Client 服务剔除。

☞ 关闭自我保护机制

  我们之前的 Eureka 截图中可以看到 DESKTOP-GL7GS52:ConsumerServer:8083consumer-01 两个同时存在,这明明是一个服务,修改完配置之后前面的没有剔除,这就是因为自我保护机制打开了。

代码语言:javascript
复制
# Eureka Server 配置
eureka:
  server:
    # 关闭自我保护模式, 默认为打开
    enable-self-preservation: false    
    # 续期时间,即扫描失效服务的间隔时间   
    eviction-interval-timer-in-ms: 5000   


# Eureka Client 配置
eureka:
  instance:
    # Eureka Client 给 Eureka Server 发送心跳的时间间隔,默认 30 单位是 s
    lease-renewal-interval-in-seconds: 1
    # Eureka Server 最后一次收到心跳的等待上限,超时剔除服务,默认 90 单位是 s
    lease-expiration-duration-in-seconds: 2

1.6 Eureka 高可用

1.6.1 Eureka 集群搭建

  在之前的单体中我们的端口是随意的,但是搭建集群我们需要对端口进行规划,例如将 808X 端口作为 Eureka 集群的端口。先来看下配置有什么区别,起初我们是将自己注册到自己上,现在我们需要将自己注册到其他 Eureka 上,有多个则用 , 隔开。

代码语言:javascript
复制
server:
  port: 8081

spring:
  application:
    name: eurekaServer

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://127.0.0.1:8082/eureka
  server:
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 5000
代码语言:javascript
复制
server:
  port: 8082

spring:
  application:
    name: eurekaServer_back

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://127.0.0.1:8081/eureka
  server:
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 5000

1.6.2 Privoder 集群

  服务提供者的集群配置了多个 Eureka 地址,会将自己同时注册到多个 Eureka 上,除了配置文件以外其他的服务代码完全一致,也可以加以区分是哪个提供的服务。需要注意的是 Eureka 集群的应用名称可以不一致甚至不写,但是服务提供者的应用名称必须保持一致,否则会被认为不是一个服务。

代码语言:javascript
复制
server:
  port: 8091

spring:
  application:
    name: ProviderServer  # 应用名称,在 Eureka 中作为 id 标识

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8081/eureka, http://127.0.0.1:8082/eureka
  instance:
    instance-id: provider-prim
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 1
    lease-expiration-duration-in-seconds: 2
代码语言:javascript
复制
server:
  port: 8092

spring:
  application:
    name: ProviderServer

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8081/eureka, http://127.0.0.1:8082/eureka
  instance:
    instance-id: provider-back
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 1
    lease-expiration-duration-in-seconds: 2

1.6.3 远程调用

代码语言:javascript
复制
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description
 */
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private DiscoveryClient discoveryClient;


    @GetMapping("/go")
    public void go() {
        List<ServiceInstance> providerServer = discoveryClient.getInstances("ProviderServer");

        if (0 == providerServer.size()) {
            return;
        }

        RestTemplate restTemplate = new RestTemplate();

        for (ServiceInstance instance : providerServer) {
            System.out.print(instance.getUri() + "---");
            String url = instance.getUri() + "/provider/get";
            System.out.println(restTemplate.getForObject(url, String.class));
        }
    }
}

  咱们可以使用服务发现 DiscoveryClient 来获取服务信息,但是无法自动选择使用那个服务,这里就涉及到 Ribbon 负载均衡了。我们可以将 RestTemplate 交由 Ioc 管理,在注入时使用 @LoadBalanced 注解进行负载均衡。


☞ 源码

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/11/02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1 简介
    • 1.1.1 概述
      • 1.1.2 原理图
        • 1.1.3 相关依赖
        • 1.2 搭建 EurekaServer
          • 1.2.1 相关依赖
            • 1.2.2 声明为 Eureka Server
              • 1.2.3 配置文件
                • 1.2.4 启动服务
                • 1.3 提供者
                  • 1.3.1 声明为 Eureka Client
                    • 1.3.2 配置文件
                      • 1.3.3 提供服务
                        • 1.3.4 启动服务
                        • 1.4 消费者
                          • 1.3.1 声明为 Eureka Client
                            • 1.3.2 配置文件
                              • 1.3.3 消费服务
                                • 1.3.4 启动服务
                                  • 1.3.5 请求服务
                                    • 1.3.6 执行流程
                                    • 1.5 补充配置
                                      • 1.5.1 actuator 信息完善
                                        • 1.5.2 自我保护机制
                                          • ☞ 概述
                                          • ☞ 关闭自我保护机制
                                      • 1.6 Eureka 高可用
                                        • 1.6.1 Eureka 集群搭建
                                          • 1.6.2 Privoder 集群
                                            • 1.6.3 远程调用
                                            相关产品与服务
                                            负载均衡
                                            负载均衡(Cloud Load Balancer,CLB)提供安全快捷的四七层流量分发服务,访问流量经由 CLB 可以自动分配到多台后端服务器上,扩展系统的服务能力并消除单点故障。轻松应对大流量访问场景。 网关负载均衡(Gateway Load Balancer,GWLB)是运行在网络层的负载均衡。通过 GWLB 可以帮助客户部署、扩展和管理第三方虚拟设备,操作简单,安全性强。
                                            领券
                                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档