小菜鸟

java菜鸟号正在起航

Eureka 简介:Spring Cloud 生态的经典服务注册中心

Eureka 是 Netflix 开源的服务发现组件,作为 Spring Cloud 早期推荐的注册中心,凭借高可用性设计和与 Spring 生态的无缝集成,成为微服务架构中服务治理的经典方案。它通过Eureka Server(注册中心)和Eureka Client(服务端 / 客户端)的角色分工,实现服务的注册、发现和健康管理。

Eureka 的核心架构

Eureka 的设计遵循AP 原则(可用性优先,兼顾最终一致性),核心组件包括:

Eureka Server(注册中心)

  • 提供服务注册接口,存储所有可用服务的元数据(IP、端口、状态等);
  • 支持集群部署,通过节点间数据复制实现高可用;
  • 提供 Web 控制台(默认地址 http://localhost:8761),直观展示服务状态。

Eureka Client(服务端 / 客户端)

  • 服务提供者:启动时向 Eureka Server 注册自身信息,定期发送心跳证明可用性;

  • 服务消费者:从 Eureka Server 拉取服务列表并缓存到本地,通过服务名调用接口;

    这种方式可以使得微服务不需要每次请求都查询Eureka Server,从而降低了Eureka Server的压力,如果Eureka Server所有节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者完成调用

  • 内置轮询负载均衡算法,简化服务调用流程。

Eureka 的核心特性

阅读全文 »

Eureka 服务下线机制:确保注册中心实例准确性

在 Eureka 架构中,服务下线是维持注册中心注册表准确性的关键环节。默认情况下,Eureka 通过心跳检测机制自动剔除失效服务,但在某些场景(如服务异常崩溃)下可能出现延迟,导致无效实例残留。本文详细介绍 Eureka 服务下线的实现方式及手动干预手段。

Eureka 服务下线的默认机制

Eureka 通过心跳检测自动剔除实现服务下线管理:

  1. 心跳续约
    服务提供者(Eureka Client)启动后,会定期向 Eureka Server 发送心跳(默认每 30 秒一次),证明自身可用。心跳请求格式为:

    PUT /eureka/apps/{服务名}/{实例ID}?status=UP
  2. 自动剔除

    • 若 Eureka Server 在90 秒内未收到心跳(可通过lease-expiration-duration-in-seconds配置),会将该实例标记为DOWN
    • 标记后,Eureka Server 不会立即删除实例,而是等待一段时间(受自我保护机制影响)后从注册表中移除。
  3. 自我保护机制的影响
    当 Eureka Server 触发自我保护(如网络分区导致心跳骤减),会暂停自动剔除功能,此时即使服务已下线,仍可能保留在注册表中,避免误删健康服务。

服务下线不及时的问题与原因

常见问题

  • 服务已手动停止,但 Eureka Server 仍显示为UP状态,导致消费者调用失效实例报错;
  • 服务异常崩溃(如进程被 kill),未执行正常下线流程,Eureka Server 无法及时感知。

原因分析

阅读全文 »

Spring Bean 实例化完整流程解析:从配置加载到 Bean 就绪

Spring Bean 的实例化是 IOC 容器的核心能力,并非简单的 “new 对象”,而是一套包含 “配置元信息解析→BeanDefinition 注册→容器增强→实例化→属性注入→初始化” 的完整链路。本文结合 BeanDefinitionReaderBeanDefinitionRegistryBeanFactoryPostProcessor 等核心组件,从 “阶段拆解→组件作用→实战细节” 三个维度,彻底讲透 Bean 实例化的每一步。

Bean 实例化的整体链路(宏观视角)

Bean 实例化的本质是 “将配置元信息(XML / 注解)转化为可使用的 Bean 实例”,整体流程可分为 4 大阶段,每个阶段对应特定组件和职责:

graph TD
    A[阶段1:配置元信息加载] --> B[阶段2:BeanDefinition生成与注册]
    B --> C[阶段3:BeanFactoryPostProcessor增强]
    C --> D[阶段4:Bean实例化与初始化]
    D --> E["Bean就绪:可通过getBean()获取"]

每个阶段的核心目标:

  • 阶段 1:读取外部配置(XML / 注解 / JavaConfig),转化为 Spring 可识别的 “元信息”;
  • 阶段 2:将元信息封装为 BeanDefinition(Bean 的 “蓝图”),并注册到容器;
  • 阶段 3:修改 / 增强 BeanDefinition(如替换占位符、动态注册新 Bean);
  • 阶段 4:基于 BeanDefinition 创建实例、注入属性、执行初始化,最终生成可用 Bean。

阶段 1:配置元信息加载 —— 告诉 Spring “要创建哪些 Bean”

Spring 无法主动感知需要创建的 Bean,必须通过配置元信息明确告知。配置方式分为 “XML 配置”“注解配置”“JavaConfig 配置”,对应不同的 BeanDefinitionReader 来加载。

1. 核心组件:BeanDefinitionReader(元信息读取器)

BeanDefinitionReader 是加载配置元信息的顶层接口,不同配置方式对应不同实现类:

阅读全文 »

Hive Map 字段:键值对怎么存、怎么查、什么场景该用

Hive 支持 MAP 类型,用来存键值对——比如”科目:分数”、”属性:值”这种结构不确定的数据。

Map 类型适合的场景:

  • 字段的 key 不固定(不同行可能有不同的键)
  • 键值对的数量不固定(有的行 3 个键,有的行 10 个键)

Map 类型不适合的场景:

  • 键是固定的(比如所有学生都考语数英)→ 拆成普通列 math_scoreenglish_score,查询效率高得多
  • 需要频繁按 key 过滤 → Map 字段没有索引,每次都要扫全表

原则:能用普通列就用普通列,只有键不固定的时候才用 Map。

建表:指定三层分隔符

建 Map 类型表要指定三层分隔符:

CREATE TABLE student_score (
  name STRING,
  score MAP<STRING, INT>   -- 键:科目名,值:分数
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\t'    -- 列分隔符(name 和 score 之间)
  COLLECTION ITEMS TERMINATED BY ','  -- Map 中键值对之间的分隔符
  MAP KEYS TERMINATED BY ':';   -- 键和值之间的分隔符

数据文件格式示例:

张三    数学:90,英语:85,语文:95
李四    数学:88,英语:92
王五    语文:80,物理:75,数学:90

基本查询:取某个 key 的值

-- 查张三的数学成绩
SELECT name, score['数学'] AS math_score
FROM student_score
WHERE name = '张三';
-- 输出:张三  90

-- 查所有学生的英语成绩(没有英语成绩的返回 NULL)
SELECT name, score['英语'] AS english_score
FROM student_score;
-- 输出:
-- 张三  85
-- 李四  92
-- 王五  NULL

判断某个 key 是否存在

阅读全文 »

Scala 偏函数:精准处理集合的筛选与转换

偏函数(Partial Function)是 Scala 中一种特殊的函数,它只对满足特定条件的输入值进行处理,相当于 filter(筛选)和 map(转换)的结合体。偏函数特别适合处理包含多种类型元素的集合,能简洁地实现 “先筛选、后操作” 的逻辑。本文将详细解析偏函数的定义、使用及简化写法。

偏函数的基本概念

偏函数是仅对部分输入值有定义的函数,与普通函数(对所有输入值都有定义)形成对比。在 Scala 中,偏函数通过 PartialFunction[A, B] 特质实现,其中:

  • A 是输入参数类型
  • B 是返回值类型

PartialFunction 特质包含两个核心方法:

  1. isDefinedAt(x: A): Boolean:判断输入 x 是否符合处理条件(是否在偏函数的定义域内)。
  2. apply(x: A): B:对符合条件的输入 x 执行具体操作并返回结果。

偏函数的定义与使用

完整定义(实现特质方法)

通过实现 PartialFunction 特质的两个方法,定义一个偏函数:

// 定义偏函数:只处理 Int 类型,将其翻倍
val doubleInt: PartialFunction[Any, Int] = new PartialFunction[Any, Int] {
  // 筛选条件:仅 Int 类型符合要求
  override def isDefinedAt(x: Any): Boolean = x.isInstanceOf[Int]
  
  // 处理逻辑:将 Int 类型的值翻倍
  override def apply(x: Any): Int = x.asInstanceOf[Int] * 2
}

使用偏函数(通过 collect 方法)

集合的 collect 方法专门用于接收偏函数,它会先通过 isDefinedAt 筛选元素,再对符合条件的元素应用 apply 方法:

// 包含多种类型的集合
val mixedList: List[Any] = List(1, "scala", 3, 4.5, 'a', 5)

// 使用偏函数处理集合
val result: List[Int] = mixedList.collect(doubleInt)
println(result)  // 输出:List(2, 6, 10)
阅读全文 »
0%