Java框架 SpringCloud 快速入门: Nacos 服务多级存储模型

概述

服务注册进 Nacos 之后,一个服务往往有多个实例,这些实例如果分散在全国不同的机房,调用方该怎么选?Nacos 在「服务 → 实例」之间又插了一层「集群」,就是为了让调用尽量落在同机房。

纲要

  • 三级结构:服务(service)/ 集群(cluster)/ 实例(instance)
  • 为什么需要集群:跨机房调用延迟高,同机房优先才能压住响应时间
  • 给实例配置集群:spring.cloud.nacos.discovery.cluster-name
  • 验证:Nacos 控制台里的集群分组与实例分布
  • 集群划分的实践含义:集群名怎么取、什么时候分、什么时候不用配
  • 与 namespace 的区别(环境维度 vs 机房维度)
  • 本篇只解决「集群是前提」,同机房优先要靠 NacosRule ------ 留给下一篇
  • 常见坑:消费者没配集群、大小写不一致、指望跨机房零延迟、改完不重启

三级结构:服务 → 集群 → 实例

先回忆一下已经用过的两层结构。userservice 提供用户查询,orderservice 提供订单查询,它们都是服务 ;userservice 部署了 8081、8082、8083 三个进程,三个都是 userservice 的实例。所以一开始是两层:一个服务包含多个实例。

单机房部署的问题在于风险集中。所有实例都放在一个机房里,机房一旦出问题(断电、断网、甚至更严重的灾害),整个服务就全挂了。解决办法是把同一个服务的多个实例分散到不同机房 ------ 阿里、京东这类业务就是这么干的,上海、杭州、北京各放一批。这就是容灾。

但分散之后带来一个新问题:杭州的 orderservice 要调 userservice,它可以调杭州本地的实例,也可以调北京的实例,该怎么选?

Nacos 的答案是在「服务」和「实例」之间加一层「机房/地域」的划分,把同一机房内的多个实例称为一个集群。于是分级模型变成三级:
#mermaid-svg-6H7th7dxui05Qu2k{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6H7th7dxui05Qu2k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6H7th7dxui05Qu2k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6H7th7dxui05Qu2k .error-icon{fill:#552222;}#mermaid-svg-6H7th7dxui05Qu2k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6H7th7dxui05Qu2k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6H7th7dxui05Qu2k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6H7th7dxui05Qu2k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6H7th7dxui05Qu2k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6H7th7dxui05Qu2k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6H7th7dxui05Qu2k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6H7th7dxui05Qu2k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6H7th7dxui05Qu2k .marker.cross{stroke:#333333;}#mermaid-svg-6H7th7dxui05Qu2k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6H7th7dxui05Qu2k p{margin:0;}#mermaid-svg-6H7th7dxui05Qu2k .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6H7th7dxui05Qu2k .cluster-label text{fill:#333;}#mermaid-svg-6H7th7dxui05Qu2k .cluster-label span{color:#333;}#mermaid-svg-6H7th7dxui05Qu2k .cluster-label span p{background-color:transparent;}#mermaid-svg-6H7th7dxui05Qu2k .label text,#mermaid-svg-6H7th7dxui05Qu2k span{fill:#333;color:#333;}#mermaid-svg-6H7th7dxui05Qu2k .node rect,#mermaid-svg-6H7th7dxui05Qu2k .node circle,#mermaid-svg-6H7th7dxui05Qu2k .node ellipse,#mermaid-svg-6H7th7dxui05Qu2k .node polygon,#mermaid-svg-6H7th7dxui05Qu2k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6H7th7dxui05Qu2k .rough-node .label text,#mermaid-svg-6H7th7dxui05Qu2k .node .label text,#mermaid-svg-6H7th7dxui05Qu2k .image-shape .label,#mermaid-svg-6H7th7dxui05Qu2k .icon-shape .label{text-anchor:middle;}#mermaid-svg-6H7th7dxui05Qu2k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6H7th7dxui05Qu2k .rough-node .label,#mermaid-svg-6H7th7dxui05Qu2k .node .label,#mermaid-svg-6H7th7dxui05Qu2k .image-shape .label,#mermaid-svg-6H7th7dxui05Qu2k .icon-shape .label{text-align:center;}#mermaid-svg-6H7th7dxui05Qu2k .node.clickable{cursor:pointer;}#mermaid-svg-6H7th7dxui05Qu2k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6H7th7dxui05Qu2k .arrowheadPath{fill:#333333;}#mermaid-svg-6H7th7dxui05Qu2k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6H7th7dxui05Qu2k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6H7th7dxui05Qu2k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6H7th7dxui05Qu2k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6H7th7dxui05Qu2k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6H7th7dxui05Qu2k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6H7th7dxui05Qu2k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6H7th7dxui05Qu2k .cluster text{fill:#333;}#mermaid-svg-6H7th7dxui05Qu2k .cluster span{color:#333;}#mermaid-svg-6H7th7dxui05Qu2k div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6H7th7dxui05Qu2k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6H7th7dxui05Qu2k rect.text{fill:none;stroke-width:0;}#mermaid-svg-6H7th7dxui05Qu2k .icon-shape,#mermaid-svg-6H7th7dxui05Qu2k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6H7th7dxui05Qu2k .icon-shape p,#mermaid-svg-6H7th7dxui05Qu2k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6H7th7dxui05Qu2k .icon-shape .label rect,#mermaid-svg-6H7th7dxui05Qu2k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6H7th7dxui05Qu2k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6H7th7dxui05Qu2k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6H7th7dxui05Qu2k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 服务 userservice
集群 HZ(杭州机房)
集群 SH(上海机房)
实例 127.0.0.1:8081
实例 127.0.0.1:8082
实例 127.0.0.1:8083

三级各自代表什么,别混:

层级 对应实体 说明
服务 service 同一个应用 userservice,由 spring.application.name 决定
集群 cluster 地理 / 机房维度的划分 HZ、SH,同一机房内的实例归为一组
实例 instance 一个具体进程 IP:port,真正的网络终点

为什么要引入集群

这不是为了概念好看。回到那个问题:杭州机房的 orderservice 要访问 userservice,本地机房有实例,外地机房也有实例,选哪个?

答案是本地优先。原因很直接:同机房的访问走的是局域网,距离短、链路少,延迟通常在毫秒级以内;一旦跨集群,比如从杭州调到广州或北京,物理距离几百上千公里,往返延迟会高出一两个数量级。所以服务调用应该尽可能落在本地集群,只有本地集群不可用时才退到其他集群。

Nacos 引入集群概念,本质就是为了减少跨集群调用的比例,而不是消灭跨集群调用。
#mermaid-svg-GtVxSHGTMUUb2R59{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GtVxSHGTMUUb2R59 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GtVxSHGTMUUb2R59 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GtVxSHGTMUUb2R59 .error-icon{fill:#552222;}#mermaid-svg-GtVxSHGTMUUb2R59 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GtVxSHGTMUUb2R59 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GtVxSHGTMUUb2R59 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GtVxSHGTMUUb2R59 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GtVxSHGTMUUb2R59 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GtVxSHGTMUUb2R59 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GtVxSHGTMUUb2R59 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GtVxSHGTMUUb2R59 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GtVxSHGTMUUb2R59 .marker.cross{stroke:#333333;}#mermaid-svg-GtVxSHGTMUUb2R59 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GtVxSHGTMUUb2R59 p{margin:0;}#mermaid-svg-GtVxSHGTMUUb2R59 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GtVxSHGTMUUb2R59 .cluster-label text{fill:#333;}#mermaid-svg-GtVxSHGTMUUb2R59 .cluster-label span{color:#333;}#mermaid-svg-GtVxSHGTMUUb2R59 .cluster-label span p{background-color:transparent;}#mermaid-svg-GtVxSHGTMUUb2R59 .label text,#mermaid-svg-GtVxSHGTMUUb2R59 span{fill:#333;color:#333;}#mermaid-svg-GtVxSHGTMUUb2R59 .node rect,#mermaid-svg-GtVxSHGTMUUb2R59 .node circle,#mermaid-svg-GtVxSHGTMUUb2R59 .node ellipse,#mermaid-svg-GtVxSHGTMUUb2R59 .node polygon,#mermaid-svg-GtVxSHGTMUUb2R59 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GtVxSHGTMUUb2R59 .rough-node .label text,#mermaid-svg-GtVxSHGTMUUb2R59 .node .label text,#mermaid-svg-GtVxSHGTMUUb2R59 .image-shape .label,#mermaid-svg-GtVxSHGTMUUb2R59 .icon-shape .label{text-anchor:middle;}#mermaid-svg-GtVxSHGTMUUb2R59 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GtVxSHGTMUUb2R59 .rough-node .label,#mermaid-svg-GtVxSHGTMUUb2R59 .node .label,#mermaid-svg-GtVxSHGTMUUb2R59 .image-shape .label,#mermaid-svg-GtVxSHGTMUUb2R59 .icon-shape .label{text-align:center;}#mermaid-svg-GtVxSHGTMUUb2R59 .node.clickable{cursor:pointer;}#mermaid-svg-GtVxSHGTMUUb2R59 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GtVxSHGTMUUb2R59 .arrowheadPath{fill:#333333;}#mermaid-svg-GtVxSHGTMUUb2R59 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GtVxSHGTMUUb2R59 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GtVxSHGTMUUb2R59 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GtVxSHGTMUUb2R59 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GtVxSHGTMUUb2R59 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GtVxSHGTMUUb2R59 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GtVxSHGTMUUb2R59 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GtVxSHGTMUUb2R59 .cluster text{fill:#333;}#mermaid-svg-GtVxSHGTMUUb2R59 .cluster span{color:#333;}#mermaid-svg-GtVxSHGTMUUb2R59 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GtVxSHGTMUUb2R59 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GtVxSHGTMUUb2R59 rect.text{fill:none;stroke-width:0;}#mermaid-svg-GtVxSHGTMUUb2R59 .icon-shape,#mermaid-svg-GtVxSHGTMUUb2R59 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GtVxSHGTMUUb2R59 .icon-shape p,#mermaid-svg-GtVxSHGTMUUb2R59 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GtVxSHGTMUUb2R59 .icon-shape .label rect,#mermaid-svg-GtVxSHGTMUUb2R59 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GtVxSHGTMUUb2R59 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GtVxSHGTMUUb2R59 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GtVxSHGTMUUb2R59 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 优先
本地不可用时才走
orderservice(杭州集群)
userservice HZ 实例
userservice SH 实例

还要注意一点:现在工程里的实例,集群名默认是 default。在 Nacos 控制台点进服务详情就能看到 ------ 集群名称 DEFAULT,也就是没有划分集群。下面来给它配上。

给实例配置集群

修改 user-service 的 application.yml,在 spring.cloud.nacos 下加 discovery.cluster-name:

yaml 复制代码
server:
  port: 8081
spring:
  application:
    name: userservice
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: HZ # 集群名称,HZ 表示杭州

cluster-name 的值是自定义 的,HZ 只是「杭州」的缩写,你写 hangzhou、hz-1、room-a 都一样,Nacos 不解释它的语义,只按字符串分组。

启动两个 user-service 实例,它们就都归到了 HZ 集群。如果要再起一个属于上海集群的实例,有两种写法:

  • 改一份 application.yml 把 cluster-name 写成 SH 再启动;
  • 不动配置文件,用启动参数覆盖:
bash 复制代码
java -jar user-service.jar -Dserver.port=8083 -Dspring.cloud.nacos.discovery.cluster-name=SH

用 IDEA 调试时,是在 Run Configuration 的 VM options 里填:

text 复制代码
-Dserver.port=8083 -Dspring.cloud.nacos.discovery.cluster-name=SH

这里有个坑:用 IDEA 启动多个实例时,别在同一个实例上反复重启改集群名 。比如你已经在 8081 上跑了一个实例,把它改回 SH 再重启,那这个端口上的实例就从 HZ 跑到 SH 去了,原来的 HZ 实例没了。要多机房模拟,就多复制几份启动配置,各自固定集群名。

对应到 order-service,写法一样,只是值不同:

yaml 复制代码
server:
  port: 8088
spring:
  application:
    name: orderservice
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: SH # 订单服务部署在上海机房

上文的配置位置以讲义为准。本工程终态代码里,user-service 的 Nacos 连接信息放在 bootstrap.yml(spring.application.name、spring.profiles.active、spring.cloud.nacos.config),集群属性加在 application.yml 的 spring.cloud.nacos.discovery 下即可,两者最终都会合并进环境。终态仓库里这段 cluster-name 目前是注释状态:

yaml 复制代码
#      discovery:
#        cluster-name: HZ

它被留在 user-service/src/main/resources/application.yml 里,只是因为仓库快照没有开放这段演示配置,实际演示时按上面取消注释即可。

验证:在 Nacos 控制台看集群分组

实例启动后,打开 Nacos 控制台(默认 http://localhost:8848/nacos),走一遍:

  1. 左侧菜单进入 服务管理 → 服务列表;
  2. 找到 userservice,列表里会显示「集群数目」这一列;
  3. 点服务名进详情,看到按集群分组的实例分布。

预期看到的内容大致是这样:

text 复制代码
服务名: userservice
集群数目: 2

集群: HZ
  - 127.0.0.1:8081   (临时实例)
  - 127.0.0.1:8082   (临时实例)

集群: SH
  - 127.0.0.1:8083   (临时实例)

到这一步,实例的集群属性就配好了。服务列表里的「集群数目」从 1 变成 2,就是配置生效的直接证据。

集群划分的实践含义

cluster-name 一旦配下去,工程里就多了一个维度,几个实际问题得想清楚。

集群名怎么取? 没有硬性规范,机房、可用区(AZ)、区域名都行,业务自定义。常见做法是用城市/机房缩写(HZ、SH)或物理机房代号(room-a)。关键是同一个机房的实例必须写完全相同的字符串。

什么时候需要划分? 只有实例跨机房、跨可用区、跨区域 部署时才需要。单体机房部署时完全可以不配,Nacos 会把实例都算到 DEFAULT 集群里,功能不受影响。

划分带来什么? 它是后续「同机房优先」的前提条件 。不配集群,所有实例都在一个默认集群里,也就无所谓同机房优先了。至于「怎么让消费者优先调同机房」,那不是本节的配置能完成的 ------ 默认的 ZoneAvoidanceRule 并不会按同集群优先挑实例,需要把负载均衡规则换成 NacosRule。所以这里只要记住:集群划分是先决条件,策略配置在下一篇。

和 namespace(环境隔离)是一回事吗? 不是。这两个概念层级完全不同,初学者最容易把它们混在一起。

维度 cluster(集群) namespace(命名空间)
解决的问题 同一环境内,机房/可用区维度的就近调用 多环境之间的资源隔离(dev / test / prod)
划分依据 物理位置(机房、区域) 环境(开发、测试、生产)
配置项 spring.cloud.nacos.discovery.cluster-name spring.cloud.nacos.discovery.namespace
是否隔离资源 否,同一命名空间内互相可见 是,不同命名空间的服务默认互相看不见
典型值 HZ、SH、room-a namespace 的 ID(UUID)

一句话:cluster 是同一环境内的机房维度,namespace 是环境维度 。生产环境里的 userservice 和测试环境里的 userservice 应该用 namespace 隔开;而生产环境内部的杭州、上海机房之间,才用 cluster 区分。层级不同,别混用。

常见坑

  • 只给提供者配了 cluster-name 。userservice 配了 HZ/SH,order-service 没配(还在 DEFAULT),那么「同机房优先」根本无从谈起 ------ 消费端自己都不在任何集群里,拿什么跟别人比。要玩同机房优先,消费者和提供者两边都得配。
  • 集群名大小写不一致 。HZ 和 hz 在 Nacos 看来是两个不同的集群,会被拆成两组,永远命中不了对方。约定好命名后全工程统一。
  • 跨机房却期望零延迟。集群分组只降低「跨机房调用的比例」,不改变跨机房调用本身的网络开销。真出现本地集群不可用、流量全打到外地机房的情况,延迟该高还是高。
  • 实例分错集群,改完不重启 。cluster-name 是启动时注册到 Nacos 的,改完必须重启服务才会以新集群重新注册。热改配置文件不生效。

API / 配置速览

项 内容
配置项 spring.cloud.nacos.discovery.cluster-name
默认值 DEFAULT(不配时所有实例同属一个默认集群)
取值 自定义字符串,同机房实例必须一致
覆盖方式 配置文件写死,或 VM 参数 -Dspring.cloud.nacos.discovery.cluster-name=SH
生效时机 服务启动注册时,改后需重启
验证入口 Nacos 控制台 → 服务管理 → 服务列表 → 详情(按集群分组)
下游依赖 同机房优先需切换到 com.alibaba.cloud.nacos.ribbon.NacosRule

官方文档

总结

多级存储模型把原来的「服务 → 实例」两层扩成「服务 → 集群 → 实例」三层,多的这一层代表机房/地域。它要解决的是微服务跨机房部署后,调用方在本地机房和外地机房之间怎么选的问题 ------ 本地优先,因为局域网延迟远低于跨机房。

配置本身只有一行:spring.cloud.nacos.discovery.cluster-name。真正需要记住的是三件事:集群名是纯字符串分组,大小写必须统一;单体机房可以不配,跨机房才需要;集群划分只是前提,把「同一集群优先」真正落到调用链上,还得靠 NacosRule。另外别把 cluster 和 namespace 搞混,前者是环境内的机房维度,后者是环境之间的隔离维度。

相关推荐
小羊没烦恼!1 小时前
系统内部模块(子系统)之间的耦合以及模块(子系统)划分
java·开发语言·前端·数据库·算法·c#
Chase_______1 小时前
【杂项知识点】一文搞懂 JVM、JRE 与 JDK:从概念混淆到生产部署
java·开发语言·jvm
Nebula_g1 小时前
JavaSE拓展:Arrays和Collections工具类
java·开发语言·算法·排序算法·工具类·javase·技术栈
一个有温度的技术博主1 小时前
凌晨三点的消息堆积:当 MQ 变成“停车场“
java·后端·场景
成旭先生2 小时前
AI 文本审核 API:一段中文文本判风险等级、命中标签与处置建议
java·前端·人工智能·api接口·内容风控·文本审核·ugc审核
~~~4552 小时前
Java 数组详细笔记
java
Jinkxs2 小时前
Zookeeper - Java API 实现节点的创建与删除开发
java·zookeeper·java-zookeeper
liangshanbo12152 小时前
前端面试题:AI 对话中超长消息导致内存溢出,怎么解决?
java·开发语言·前端
极客先躯2 小时前
高级java每日一道面试题-2026年01月20日-实战篇[Docker]-如何实现镜像的跨区域复制?
java·运维·docker·容器·架构图