百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

SpringCloud升级之路2020.0.x版-20. 启动一个 Eureka Server 集群

bigegpt 2024-10-20 04:28 3 浏览

本系列代码地址: https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

我们的业务集群结构是这样的:

  • 不同 Region,使用不同的 Eureka 集群管理,不同 Region 之间不互相访问。
  • 同一 Region 内,可能有不同的业务集群,不同业务集群之间也不互相访问,共用同一套业务集群。
  • 同一业务集群内可以随意访问,同时同一业务集群会做跨可用区的容灾。
  • 在我们这里的抽象中, zone 代表不同集群,而不是实际的不同可用区

在这里,我们提供一个 Eureka Server 的集群模板,供大家参考。

首先,项目依赖是:

<dependencies>
    <dependency>
        <groupId>com.github.hashjang</groupId>
        <artifactId>spring-cloud-iiford-spring-cloud-webmvc</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>

其实就是包含之前我们定义的所有同步微服务的依赖,以及 eureka-server 的相关依赖。

编写启动类,其实核心就是添加注解 @EnableEurekaServer

package com.github.hashjang.spring.cloud.iiford.eureka.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

我们准备启动一个两个 Eureka Server 实例的集群,首先编写两个实例公共的配置,放入 application.yml

spring:
  application:
    name: eureka-server

eureka:
  server:
    #主动检查服务实例是否失效的任务执行间隔,默认是 60s
    eviction-interval-timer-in-ms: 1000
    #这个配置在两个地方被使用:
    #如果启用用了自我保护,则会 renewal-threshold-update-interval-ms 指定的时间内,收到的心跳请求个数是否小于实例个数乘以这个 renewal-percent-threshold
    #定时任务检查过期实例,每次最多过期 1 - renewal-percent-threshold 这么多比例的实例
    renewal-percent-threshold: 0.85
    #注意,最好所有的客户端实例配置的心跳时间相关的配置,是相同的。这样使用自我保护的特性最准确。
    #关闭自我保护
    #我们这里不使用自我保护,因为:
    #自我保护主要针对集群中网络出现问题,导致有很多实例无法发送心跳导致很多实例状态异常,但是实际实例还在正常工作的情况,不要让这些实例不参与负载均衡
    #启用自我保护的情况下,就会停止对于实例的过期
    #但是,如果出现这种情况,其实也代表很多实例无法读取注册中心了。
    #并且还有一种情况就是,Eureka 重启。虽然不常见,但是对于镜像中其他的组件更新我们还是很频繁的
    #我倾向于从客户端对于实例缓存机制来解决这个问题,如果返回实例列表为空,则使用上次的实例列表进行负载均衡,这样既能解决 Eureka 重启的情况,又能处理一些 Eureka 网络隔离的情况
    #自我保护模式基于每分钟需要收到 renew (实例心跳)请求个数,如果启用了自我保护模式,只有上一分钟接收到的 renew 个数,大于这个值,实例过期才会被注销
    enable-self-preservation: false
    # 增量实例队列实例过期时间,默认 3 分钟
    retention-time-in-m-s-in-delta-queue: 180000
    # 增量实例队列过期任务间隔,默认 30s
    delta-retention-timer-interval-in-ms: 30000
    # 响应缓存中有两个主要元素,一个是 readOnlyCacheMap,另一个是 readWriteCacheMap
    # 是否使用 readOnlyCacheMap,默认为 true
    # 如果为是,则从 readOnlyCacheMap 读取,否则直接读取 readWriteCacheMap
    use-readonly-response-cahce: true
    # 初始 readWriteCacheMap 大小,默认 1000
    initial-capacity-of-response-cache: 1000
    # LoadingCache 缓存过期时间,默认 180s
    response-cache-auto-expiration-in-seconds: 9
    # 定时从 LoadingCache 同步到只读缓存的间隔时间,默认为 30s
    response-cache-update-interval-ms: 3000
  client:
    service-url:
      # 默认eureka集群,这里必须是defaultZone,不能用-替换大写,与其他的配置不一样,因为实在EurekaClientConfigBean里面写死的
      defaultZone: 'http://127.0.0.1:8211/eureka/,http://127.0.0.1:8212/eureka/'
    # 是否从 eureka 上面拉取实例, eureka server 不调用其他微服务,所以没必要拉取
    fetch-registry: false
    # 是否将自己注册到 eureka 上面,eureka server 不参与负载均衡,所以没必要注册
    register-with-eureka: false

server:
  undertow:
    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作
    # 如果每次需要 ByteBuffer 的时候都去申请,对于堆内存的 ByteBuffer 需要走 JVM 内存分配流程(TLAB -> 堆),对于直接内存则需要走系统调用,这样效率是很低下的。
    # 所以,一般都会引入内存池。在这里就是 `BufferPool`。
    # 目前,UnderTow 中只有一种 `DefaultByteBufferPool`,其他的实现目前没有用。
    # 这个 DefaultByteBufferPool 相对于 netty 的 ByteBufArena 来说,非常简单,类似于 JVM TLAB 的机制
    # 对于 bufferSize,最好和你系统的 TCP Socket Buffer 配置一样
    # `/proc/sys/net/ipv4/tcp_rmem` (对于读取)
    # `/proc/sys/net/ipv4/tcp_wmem` (对于写入)
    # 在内存大于 128 MB 时,bufferSize 为 16 KB 减去 20 字节,这 20 字节用于协议头
    buffer-size: 16364
    # 是否分配的直接内存(NIO直接分配的堆外内存),这里开启,所以java启动参数需要配置下直接内存大小,减少不必要的GC
    # 在内存大于 128 MB 时,默认就是使用直接内存的
    directBuffers: true
    threads:
      # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个读线程和一个写线程
      io: 4
      # 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程
      # 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
      worker: 128
    # http post body 大小,默认为 -1B ,即不限制
    max-http-post-size: -1B
    # 是否在启动时创建 filter,默认为 true,不用修改
    eager-filter-init: true
    # 限制路径参数数量,默认为 1000
    max-parameters: 1000
    # 限制 http header 数量,默认为 200
    max-headers: 200
    # 限制 http header 中 cookies 的键值对数量,默认为 200
    max-cookies: 200
    # 是否允许 / 与 %2F 转义。/ 是 URL 保留字,除非你的应用明确需要,否则不要开启这个转义,默认为 false
    allow-encoded-slash: false
    # 是否允许 URL 解码,默认为 true,除了 %2F 其他的都会处理
    decode-url: true
    # url 字符编码集,默认是 utf-8
    url-charset: utf-8
    # 响应的 http header 是否会加上 'Connection: keep-alive',默认为 true
    always-set-keep-alive: true
    # 请求超时,默认是不超时,我们的微服务因为可能有长时间的定时任务,所以不做服务端超时,都用客户端超时,所以我们保持这个默认配置
    no-request-timeout: -1
    # 是否在跳转的时候保持 path,默认是关闭的,一般不用配置
    preserve-path-on-forward: false
    options:
      # spring boot 没有抽象的 xnio 相关配置在这里配置,对应 org.xnio.Options 类
      socket:
        SSL_ENABLED: false
      # spring boot 没有抽象的 undertow 相关配置在这里配置,对应 io.undertow.UndertowOptions 类
      server:
        ALLOW_UNKNOWN_PROTOCOLS: false
    # access log相关配置
    accesslog:
      # 存放目录,默认为 logs
      dir: ./logs/${server.port}
      # 是否开启
      enabled: true
      # 格式,各种占位符后面会详细说明
      pattern: '{
                  "transportProtocol":"%{TRANSPORT_PROTOCOL}",
                  "scheme":"%{SCHEME}",
                  "protocol":"%{PROTOCOL}",
                  "method":"%{METHOD}",
                  "reqHeaderUserAgent":"%{i,User-Agent}",
                  "reqHeaderUserId":"%{i,uid}",
                  "traceId":"%{i,X-B3-TraceId}",
                  "spanId":"%{i,X-B3-SpanId}",
                  "queryString": "%q",
                  "uri": "%U",
                  "thread": "%I",
                  "hostPort": "%{HOST_AND_PORT}",
                  "localIp": "%A",
                  "localPort": "%p",
                  "localServerName": "%v",
                  "remoteIp": "%a",
                  "bytesSent": "%b",
                  "time":"%{time,yyyy-MM-dd HH:mm:ss.S}",
                  "status":"%s",
                  "reason":"%{RESPONSE_REASON_PHRASE}",
                  "timeUsed":"%Dms"
                }'
      # 文件前缀,默认为 access_log
      prefix: access.
      # 文件后缀,默认为 log
      suffix: log
      # 是否另起日志文件写 access log,默认为 true
      # 目前只能按照日期进行 rotate,一天一个日志文件
      rotate: true

management:
  endpoint:
    health:
      show-details: always
  endpoints:
    jmx:
      exposure:
        exclude: '*'
    web:
      exposure:
        include: '*'

除了同步微服务 undertow 的配置以及 actuator 的配置,Eureka 配置中, 由于 Eureka Server 感知其他实例,仅仅通过 eureka.client.service-url 这个配置读取,所以不需要 eureka server 注册到 eureka server 或者读取 eureka server 上面的实例 ,因此这里我们配置不注册也不读取。然后,我们这里按照之前分析的,关闭了自我保护,开启了定时过期任务,并且将相关的定时任务时间间隔都调低了不少,因为我们的集群不是万个实例级别的,而是一千左右,所以可以调高这些任务频率。

之后,我们编写两个实例特定 profile 的配置,其实就是提供服务的端口不一样,即:

application-eureka1.yml

server:
  port: 8211

application-eureka2.yml

server:
  port: 8212

之后,我们通过 IDEA 的环境变量配置,第一个 Eureka Server 的环境变量指定 spring.profiles.active=eureka1 ,第二个 Eureka Server 的环境变量指定 spring.profiles.active=eureka2 ,分别启动。即可成为一个 Eureka 集群。大家可以尝试往其中一个实例注册一个服务实例,看另一个实例上是否被同步了这个服务实例。

如果您觉得文章对您有用,请转发、评论谢谢~

原文链接:https://www.tuicool.com/articles/aIfAvuV

相关推荐

Java 泛型大揭秘:类型参数、通配符与最佳实践

引言在编程世界中,代码的可重用性和可维护性是至关重要的。为了实现这些目标,Java5引入了一种名为泛型(Generics)的强大功能。本文将详细介绍Java泛型的概念、优势和局限性,以及如何在...

K8s 的标签与选择器:流畅运维的秘诀

在Kubernetes的世界里,**标签(Label)和选择器(Selector)**并不是最炫酷的技术,但却是贯穿整个集群管理与运维流程的核心机制。正是它们让复杂的资源调度、查询、自动化运维变得...

哈希Hash算法:原理、应用(哈希算法 知乎)

原作者:Linux教程,原文地址:「链接」什么是哈希算法?哈希算法(HashAlgorithm),又称为散列算法或杂凑算法,是一种将任意长度的数据输入转换为固定长度输出值的数学函数。其输出结果通常被...

C#学习:基于LLM的简历评估程序(c# 简历)

前言在pocketflow的例子中看到了一个基于LLM的简历评估程序的例子,感觉还挺好玩的,为了练习一下C#,我最近使用C#重写了一个。准备不同的简历:image-20250528183949844查...

55顺位,砍41+14+3!季后赛也成得分王,难道他也是一名球星?

雷霆队最不可思议的新星:一个55号秀的疯狂逆袭!你是不是也觉得NBA最底层的55号秀,就只能当饮水机管理员?今年的55号秀阿龙·威金斯恐怕要打破你的认知了!常规赛阶段,这位二轮秀就像开了窍的天才,直接...

5分钟读懂C#字典对象(c# 字典获取值)

什么是字典对象在C#中,使用Dictionary类来管理由键值对组成的集合,这类集合被称为字典。字典最大的特点就是能够根据键来快速查找集合中的值,其键的定义不能重复,具有唯一性,相当于数组索引值,字典...

c#窗体传值(c# 跨窗体传递数据)

在WinForm编程中我们经常需要进行俩个窗体间的传值。下面我给出了两种方法,来实现传值一、在输入数据的界面中定义一个属性,供接受数据的窗体使用1、子窗体usingSystem;usingSyst...

C#入门篇章—委托(c#委托的理解)

C#委托1.委托的定义和使用委托的作用:如果要把方法作为函数来进行传递的话,就要用到委托。委托是一个类型,这个类型可以赋值一个方法的引用。C#的委托通过delegate关键字来声明。声明委托的...

C#.NET in、out、ref详解(c#.net framework)

简介在C#中,in、ref和out是用于修改方法参数传递方式的关键字,它们决定了参数是按值传递还是按引用传递,以及参数是否必须在传递前初始化。基本语义对比修饰符传递方式可读写性必须初始化调用...

C#广义表(广义表headtail)

在C#中,广义表(GeneralizedList)是一种特殊的数据结构,它是线性表的推广。广义表可以包含单个元素(称为原子),也可以包含另一个广义表(称为子表)。以下是一个简单的C#广义表示例代...

「C#.NET 拾遗补漏」04:你必须知道的反射

阅读本文大概需要3分钟。通常,反射用于动态获取对象的类型、属性和方法等信息。今天带你玩转反射,来汇总一下反射的各种常见操作,捡漏看看有没有你不知道的。获取类型的成员Type类的GetMembe...

C#启动外部程序的问题(c#怎么启动)

IT&OT的深度融合是智能制造的基石。本公众号将聚焦于PLC编程与上位机开发。除理论知识外,也会结合我们团队在开发过程中遇到的具体问题介绍一些项目经验。在使用C#开发上位机时,有时会需要启动外部的一些...

全网最狠C#面试拷问:这20道题没答出来,别说你懂.NET!

在竞争激烈的C#开发岗位求职过程中,面试是必经的一道关卡。而一场高质量的面试,不仅能筛选出真正掌握C#和.NET技术精髓的人才,也能让求职者对自身技术水平有更清晰的认知。今天,就为大家精心准备了20道...

C#匿名方法(c#匿名方法与匿名类)

C#中的匿名方法是一种没有名称只有主体的方法,它提供了一种传递代码块作为委托参数的技术。以下是关于C#匿名方法的一些重要特点和用法:特点省略参数列表:使用匿名方法可省略参数列表,这意味着匿名方法...

C# Windows窗体(.Net Framework)知识总结

Windows窗体可大致分为Form窗体和MDI窗体,Form窗体没什么好细说的,知识点总结都在思维导图里面了,下文将围绕MDI窗体来讲述。MDI(MultipleDocumentInterfac...