Nacos 和 Apollo中的 长轮询 定时机制,太好用了

Nacos1.x配置中心客户端长轮询原理 nacos1.x配置中心巧妙的长轮询设计 阅读详情
AI摘要

本文深入解析了Nacos配置中心的长轮询机制,从客户端的NacosConfigService实例化开始,详细介绍了配置监听、定时任务启动、HttpAgent与ClientWorker的创建,以及服务端如何处理长轮询请求,包括检查配置变更、触发事件通知等关键步骤。通过源码分析,揭示了长轮询如何实现实时配置更新和效率优化。

今天这篇文章来介绍一下Nacos配置中心的原理之一:长轮询机制的应用

为方便理解与表达,这里把 Nacos 控制台和 Nacos 注册中心称为 Nacos 服务器(就是 web 界面那个),我们编写的业务服务称为 Nacso 客户端;

Nacos 动态监听的长轮询机制原理图,本篇将围绕这张图剖析长轮询定时机制的原理:

 

ConfigService 是 Nacos 客户端提供的用于访问实现配置中心基本操作的类型,我们将从 ConfigService 的实例化开始长轮询定时机制的源码之旅;

1. 客户端的长轮询定时机制

我们从
NacosPropertySourceLocator.locate()开始【断点步入】:

1.1 利用反射机制实例化 NacosConfigService 对象

客户端的长轮询定时任务是在
NacosFactory.createConfigService() 方法中,构建 ConfigService 对象是实例时启动的,我们接着 1.1 处的源码;

进入
NacosFactory.createConfigService():

public static ConfigService createConfigService(Properties properties) throws NacosException {
    //【断点步入】创建 ConfigService
    return ConfigFactory.createConfigService(properties);
}

进入
ConfigFactory.createConfigService(),发现其使用反射机制实例化 NacosConfigService 对象;

 

1.2 NacosConfigService 的构造方法里启动长轮询定时任务

进入
NacosConfigService.NacosConfigService() 构造方法,里面设置了一些跟远程任务相关的属性;

 

1.2.1 初始化 HttpAgent

MetricsHttpAgent 类的设计如下:

 

ServerHttpAgent 类的设计如下:

 

1.2.2 初始化 ClientWorker

进入 ClientWorker.ClientWorker() 构造方法,主要是创建了两个定时调度的线程池,并启动一个定时任务;

 

进入
ClientWorker.checkConfigInfo(),每隔 10s 检查一次配置是否发生变化;

  • cacheMap:是一个 AtomicReference<Map<String, CacheData>> 对象,用来存储监听变更的缓存集合,key 是根据 datalD/group/tenant(租户)拼接的值。Value 是对应的存储在 Nacos 服务器上的配置文件的内容;
  • 长轮询任务拆分:默认情况下,每个长轮询 LongPollingRunnable 任务处理3000个监听配置集。如果超过3000个,则需要启动多个 LongPollingRunnable 去执行;

 

1.3 检查配置变更,读取变更配置 LongPollingRunnable.run()

因为我们没有这么多配置项,debug 不进去,所以直接找到 LongPollingRunnable.run() 方法,该方法的主要逻辑是:

根据 taskld 对 cacheMap 进行数据分割;

再通过checkLocalConfig() 方法比较本地配置文件(在${user}\nacos\config\ 里)的数据是否存在变更,如果有变更则直接触发通知;

public void run() {
    List<CacheData> cacheDatas = new ArrayList();
    ArrayList inInitializingCacheList = new ArrayList();
    try {
        //遍历 CacheData,检查本地配置
        Iterator var3 = ((Map)ClientWorker.this.cacheMap.get()).values().iterator();
        while(var3.hasNext()) {
            CacheData cacheData = (CacheData)var3.next();
            if (cacheData.getTaskId() == this.taskId) {
                cacheDatas.add(cacheData);
                try {
                    //检查本地配置
                    ClientWorker.this.checkLocalConfig(cacheData);
                    if (cacheData.isUseLocalConfigInfo()) {
                        cacheData.checkListenerMd5();
                    }
                } catch (Exception var13) {
                    ClientWorker.LOGGER.error("get local config info error", var13);
                }
            }
        }
        //【断点步入 1.3.1】通过长轮询请求检查服务端对应的配置是否发生变更
        List<String> changedGroupKeys = ClientWorker.this.checkUpdateDataIds(cacheDatas, inInitializingCacheList);
        //遍历存在变更的 groupKey,重新加载最新数据
        Iterator var16 = changedGroupKeys.iterator();
        while(var16.hasNext()) {
            String groupKey = (String)var16.next();
            String[] key = GroupKey.parseKey(groupKey);
            String dataId = key[0];
            String group = key[1];
            String tenant = null;
            if (key.length == 3) {
                tenant = key[2];
            }
            try {
                //【断点步入 1.3.2】读取变更配置,这里的 dataId、group 和 tenant 是【1.3.1】里获取的
                String content = ClientWorker.this.getServerConfig(dataId, group, tenant, 3000L);
                CacheData cache = (CacheData)((Map)ClientWorker.this.cacheMap.get()).get(GroupKey.getKeyTenant(dataId, group, tenant));
                cache.setContent(content);
                ClientWorker.LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}", new Object[]{ClientWorker.this.agent.getName(), dataId, group, tenant, cache.getMd5(), ContentUtils.truncateContent(content)});
            } catch (NacosException var12) {
                String message = String.format("[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s", ClientWorker.this.agent.getName(), dataId, group, tenant);
                ClientWorker.LOGGER.error(message, var12);
            }
        }
        //触发事件通知
        var16 = cacheDatas.iterator();
        while(true) {
            CacheData cacheDatax;
            do {
                if (!var16.hasNext()) {
                    inInitializingCacheList.clear();
                    //继续定时执行当前线程
                    ClientWorker.this.executorService.execute(this);
                    return;
                }
                cacheDatax = (CacheData)var16.next();
            } while(cacheDatax.isInitializing() && !inInitializingCacheList.contains(GroupKey.getKeyTenant(cacheDatax.dataId, cacheDatax.group, cacheDatax.tenant)));
            cacheDatax.checkListenerMd5();
            cacheDatax.setInitializing(false);
        }
    } catch (Throwable var14) {
        ClientWorker.LOGGER.error("longPolling error : ", var14);
        ClientWorker.this.executorService.schedule(this, (long)ClientWorker.this.taskPenaltyTime, TimeUnit.MILLISECONDS);
    }
}

注意:这里的断点需要注意 Nacos 服务器上修改配置(间隔大于 30s),进入后才好理解;

1.3.1 检查配置变更 ClientWorker.checkUpdateDataIds()

我们点进
ClientWorker.checkUpdateDataIds()​ 方法,发现其最终调用的是 ClientWorker.checkUpdateConfigStr() 方法,其实现逻辑与源码如下:

2.2 执行长轮询请求 ConfigSer

 

vletInner.doPollingConfig()

进入
ConfigServletInner.doPollingConfig() 方法,该方法封装了长轮询的实现逻辑,同时兼容短轮询逻辑;

 

进入
LongPollingService.addLongPollingClient() 方法,里面是长轮询的核心处理逻辑,主要作用是把客户端的长轮询请求封装成 ClientPolling 交给 scheduler 执行;

 

2.3 创建线程执行定时任务 ClientLongPolling.run()

我们找到 ClientLongPolling.run() 方法,这里可以体现长轮询定时机制的核心原理,通俗来说,就是:

服务端收到请求之后,不立即返回,没有变更则在延后 (30-0.5)s 把请求结果返回给客户端;

这就使得客户端和服务端之间在 30s 之内数据没有发生变化的情况下一直处于连接状态;

 

2.4 监听配置变更事件

2.4.1 监听 LocalDataChangeEvent 事件的实现

当我们在 Nacos 服务器或通过 API 方式变更配置后,会发布一个 LocalDataChangeEvent 事件,该事件会被 LongPollingService 监听;

这里 LongPollingService 为什么具有监听功能在 1.3.1 版本后有些变化:

在 Nacos 1.3.1 版本之前,通过 LongPollingService 继承 AbstractEventListener 实现监听,覆盖 onEvent() 方法;

 

而在 1.3.2 版本之后,通过构造订阅者实现

 

效果是一样的,实现了对 LocalDataChangeEvent 事件的监听,并通过通过线程池执行 DataChangeTask 任务;

2.4.2 监听事件后的处理逻辑 DataChangeTask.run()

我们找到 DataChangeTask.run() 方法,这个线程任务实现了

 

3. 源码结构图小结

3.1 客户端的长轮询定时机制

3.2 服务端的长轮询定时机制

3.3 Nacos 服务器配置变更的事件监听

Nacos 服务器上的配置发生变更后,发布一个 LocalDataChangeEvent 事件;

Subscriber.onEvent() :监听 LocalDataChangeEvent 事件(1.3.2 版本后);

  • 通过MetricsHttpAgent.httpPost()​ 方法(上面 1.2.1 有提到)调用/v1/cs/configs/listener 接口实现长轮询请求;
  • 轮询请求在实现层面只是设置了一个比较长的超时时间,默认是 30s;
  • 如果服务端的数据发生了变更,客户端会收到一个 HttpResult ,服务端返回的是存在数据变更的 Data ID、Group、Tenant;
  • 获得这些信息之后,在LongPollingRunnable.run() 方法中调用 getServerConfig() 去 Nacos 服务器上读取具体的配置内容;
  • List<String> checkUpdateConfigStr(String probeUpdateString, boolean isInitializingCacheList) throws IOException {
        List<String> params = Arrays.asList("Listening-Configs", probeUpdateString);
        List<String> headers = new ArrayList(2);
        headers.add("Long-Pulling-Timeout");
        headers.add("" + this.timeout);
        if (isInitializingCacheList) {
            headers.add("Long-Pulling-Timeout-No-Hangup");
            headers.add("true");
        }
        if (StringUtils.isBlank(probeUpdateString)) {
            return Collections.emptyList();
        } else {
            try {
                //调用 /v1/cs/configs/listener 接口实现长轮询请求,返回的 HttpResult 里包含存在数据变更的 Data ID、Group、Tenant
                HttpResult result = this.agent.httpPost("/v1/cs/configs/listener", headers, params, this.agent.getEncode(), this.timeout);
                if (200 == result.code) {
                    this.setHealthServer(true);
                    //
                    return this.parseUpdateDataIdResponse(result.content);
                }
                this.setHealthServer(false);
                LOGGER.error("[{}] [check-update] get changed dataId error, code: {}", this.agent.getName(), result.code);
            } catch (IOException var6) {
                this.setHealthServer(false);
                LOGGER.error("[" + this.agent.getName() + "] [check-update] get changed dataId exception", var6);
                throw var6;
            }
            return Collections.emptyList();
        }
    }

    1.3.2 读取变更配置 ClientWorker.getServerConfig()

    进入
    ClientWorker.getServerConfig()​ 方法;读取服务器上的变更配置;最终调用的是 MetricsHttpAgent.httpGet()​ 方法(上面 1.2.1 有提到),调用 /v1/cs/configs​ 接口获取配置;然后通过调用 LocalConfigInfoProcessor.saveSnapshot() 将变更的配置保存到本地;

     

    2. 服务端的长轮询定时机制

    2.1 服务器接收请求 ConfigController.listener()

    Nacos客户端 通过 HTTP 协议与服务器通信,那么在服务器源码里必然有对应接口的实现;

    在 nacos-config 模块下的 controller 包,提供了个 ConfigController 类来处理请求,其中有个 /listener 接口,是客户端发起数据监听的接口,其主要逻辑和源码如下:

  • 获取客户端需要监听的可能发生变化的配置,并计算 MD5 值;
  • ConfigServletInner.doPollingConfig() 开始执行长轮询请求;
  • 1.3.1 前:LongPollingService.onEvent();
  • 1.3.1 后:Subscriber.onEvent();
  • NacosPropertySourceLocator.locate() :初始化 ConfigService 对象,定位配置;
  • NacosConfigService.NacosConfigService():NacosConfigService 的构造方法;
  • Executors.newScheduledThreadPool():创建 executor 线程池;
  • Executors.newScheduledThreadPool():创建 executorService 线程池;
  • ConfigController.listener() :服务器接收请求;
  • LongPollingService.addLongPollingClient():长轮询的核心处理逻辑,提前 500ms 返回响应;
  • ClientLongPolling.run():长轮询定时机制的实现逻辑;
  • Map.put():将 ClientLongPolling 实例本身添加到 allSubs 队列中;
  • Queue.remove():把 ClientLongPolling 实例本身从 allSubs 队列中移除;
  • MD5Util.compareMd5():比较数据的 MD5 值;
  • LongPollingService.sendResponse():将变更的结果通过 response 返回给客户端;
  • ConfigExecutor.scheduleLongPolling():启动定时任务,延时时间为 29.5s;
  • HttpServletRequest.getHeader():获取客户端设置的请求超时时间;
  • MD5Util.compareMd5():和服务端的数据进行 MD5 对比;
  • ConfigExecutor.executeLongPolling():创建 ClientLongPolling 线程执行定时任务;
  • MD5Util.getClientMd5Map():计算 MD5 值;
  • ConfigServletInner.doPollingConfig():执行长轮询请求;
  • DataChangeTask.run():根据 groupKey 返回配置;
  • ConfigExecutor.executeLongPolling():通过线程池执行 DataChangeTask 任务;
  • ClientWorker.checkConfigInfo():使用 executor 线程池检查配置是否发生变化;
  • ClientWorker.checkLocalConfig():检查本地配置;
  • ClientWorker.checkUpdateDataIds():检查服务端对应的配置是否发生变更;
  • ClientWorker.getServerConfig():读取变更配置
  • MetricsHttpAgent.httpPost():调用 /v1/cs/configs/listener 接口实现长轮询请求;
  • ClientWorker.checkUpdateConfigStr():检查服务端对应的配置是否发生变更;
  • MetricsHttpAgent.httpGet():调用 /v1/cs/configs 接口获取配置;
  • LongPollingRunnable.run():运行长轮询定时线程;
  • MetricsHttpAgent.MetricsHttpAgent():初始化 HttpAgent;
  • ClientWorker.ClientWorker():初始化 ClientWorker;
  • NacosFactory.createConfigService():创建配置服务器;
  • ConfigFactory.createConfigService():利用反射机制创建配置服务器;

 

Nacos 和 Apollo中的 长轮询 定时机制,太好用了~ 今天这篇文章来介绍一下Nacos配置中心的原理之一:长轮询机制的应用为方便理解与表达,这里把 Nacos 控制台和 Nacos 注册中心称为 Nacos 服务器(就是 web 界面那个),我们编写的业务服务称为 Nacso 客户端;Nacos 动态监听的长轮询机制原理图,本篇将围绕这张图剖析长轮询定时机制的原理:ConfigService 是 Nacos 客户端提供的用于访问实现配置中心基本操作的类,我们将从 ConfigService 的实例化开始长轮询定时机制的源码之旅;我们从NacosProperty 阅读详情

相关推荐

nacos配置中心源码分析——长轮询动态刷新

所谓动态刷新是指nacos用户交互页面发布或者更新配置,在spring cloud微服务中是如何感知的,并且如何动态刷新配置?用户操作nacos UI交互页面发布配置请求,请求url:config/v1/cs/configs。请求来到nacos服务端的ConfigController.publishConfig()方法,由于没有选择beta发布,所以betaIp为空,tag好像是跟容器部署有关。所以直接执行如下if里面两个核心方法:persistService.insertOrUpdate()和Config

weixin_36279234的博客 1736

Nacos的长轮询实践

Nacos长轮询的基本思路是通过Servlet3.0后提供的异步处理能力,把请求的任务添加至队列中,在有数据发生变更时,从队列中取出相应请求,然后响应请求,负责拉取数据的接口通过延时任务完成超时处理,如果等到设定的超时时间还没有数据变更时,就主动推送超时信息完成响应。关于心跳检测参考:https://blog.youkuaiyun.com/sunquan291/article/details/126556366。

sunquan291的专栏 2235

Nacos和Apollo 为什么看上了”长轮询“?

Nacos和Apollo 为什么看上了”长轮询“?

836

通过Nacos配置定时任务的开关和执行策略

Nacos配置控制定时任务的开关

qq_33369085的博客 1364

KDD 2025 | 人大团队提出多任务贝叶斯联邦学习算法,同时处理分类和回归

©PaperWeekly 原创 ·作者 |吕俊良单位 |中国人民大学统计学院本文介绍了中国人民大学统计学院的一篇关于多任务贝叶斯联邦学习的文章“Task Diversity in Bayesian Federated Learning: Simultaneous Processing of Classification and Regression”,本文被 KDD 2025 接收,代码已经...

Paper weekly 2746

WSL基础环境配置方案

通过以上步骤,您已经在 Windows 的 WSL 环境中成功搭建了一个完整的基础开发环境,包含 Docker、Kubernetes、Helm、kubectl、yq、Git 等工具。这套环境特别适用于在国内使用阿里云镜像服务部署和管理 Tekton,提升了开发和运维的效率和稳定性。

落魄书生的职业学习生涯 8876

Apollo 中的 长轮询 定时机制

今天这篇文章来介绍一下Nacos配置中心的原理之一长轮询机制的应用为方便理解与表达,这里把Nacos控制台和Nacos注册中心称为Nacos服务器(就是web界面那个),我们编写的业务服务称为Nacso客户端;Nacos动态监听的长轮询机制原理图,本篇将围绕这张图剖析长轮询定时机制的原理ConfigService是Nacos客户端提供的用于访问实现配置中心基本操作的类型,我们将从ConfigService的实例化开始长轮询定时机制的源码之旅;...

Java_ttcd的博客 875

扫地机器人全场景测试实战:从实验室到真实家庭的可靠性验证

本文深入探讨了扫地机器人从实验室到真实家庭的全场景测试方法,揭示了软硬件测试中的关键挑战。通过分析场景化测试的重要性,文章详细介绍了硬件耐久性、软件稳定性和环境适应性三大测试维度,并提供了真实家庭测试的实用方法论。消费者可重点关注清洁率、覆盖率等核心指标,同时了解测试工程师的私房建议,帮助选择更可靠的扫地机器人产品。

hhh00的博客 1077

Nacos 和 Apollo中的 长轮询 定时机制最好用的方法

今天来介绍一下Nacos配置中心的原理之一长轮询机制的应用为方便理解与表达,这里把Nacos控制台和Nacos注册中心称为Nacos服务器(就是web界面那个),我们编写的业务服务称为Nacso客户端;Nacos动态监听的长轮询机制原理图,本篇将围绕这张图剖析长轮询定时机制的原理ConfigService是Nacos客户端提供的用于访问实现配置中心基本操作的类型,我们将从ConfigService的实例化开始长轮询定时机制的源码之旅;...

weixin_62421895的博客 274

Eigen 直线拟合/曲线拟合/圆拟合/椭圆拟合

使用Eigen库进行直线拟合是数据分析和科学计算中的常见任务,主要通过最小二乘法实现。该代码演示了二维空间中的直线拟合,输出斜率和截距。

深之JohnChen的专栏 1602

HyperLynx实战:手把手教你搞定DDR4信号完整性仿真(附ODT与终端电阻配置避坑)

本文详细介绍了使用HyperLynx进行DDR4信号完整性仿真的实战技巧,重点解析了ODT配置与终端电阻优化的黄金法则。通过模型准备、拓扑构建、ODT扫描等关键步骤,帮助硬件工程师规避常见设计陷阱,提升高速数字电路设计的效率与可靠性。

weixin_30861797的博客 641

微服务架构 | *2.5 Nacos 长轮询定时机制的源码分析

为方便理解与表达,这里把 Nacos 控制台和 Nacos 注册中心称为 Nacos 服务器(就是 web 界面那个),我们编写的业务服务称为 Nacso 客户端; 由于篇幅有限,这里将源码分析分为上下两篇,其中上篇讲获取配置与事件订阅机制,下篇讲长轮询定时机制;在《微服务架构 | 2.2 Alibaba Nacos 的统一配置管理》中提到一张 Nacos 动态监听的长轮询机制原理图,本篇将围绕这张图剖析长轮询定时机制的原理;

多氯环己烷的博客 729

国宇测试盒GH7009 SPI读操作全解析:从接线到代码实现的避坑指南

本文详细解析了国宇GH7009测试盒进行SPI读操作的完整流程与避坑要点。重点阐述了硬件连接中IO2时序控制引脚的关键作用,提供了从接线方案、电源处理到上位机代码实现的实战指南,并针对常见的数据不稳定、特定寄存器读取失败等问题给出了具体解决方案。

dapp9builder的博客 224

什么是长轮询

二是在配置中心的使用过程中,用户可能随时新增配置监听,而在此之前,长轮询可能已经发出,新增的配置监听无法包含在旧的长轮询中,所以在配置中心的设计中,一般会在一次长轮询结束后,将新增的配置监听给捎带上,而如果长轮询没有超时时间,只要配置一直不发生变化,响应就无法返回,新增的配置也就没法设置监听了。在上面长轮询的图中我们看到,客户端侧标注了一个timeout时间90s,服务端最长的hold时间是80s,两个时间只是个示例,代表的是服务端hold的时间要小于客户端设置的超时时间。那么什么是长轮询的呢?...

u013978512的博客 7365

Xtract or Ucfyber的最条理的整理入门级教程

XTRACT可以对任意截面进行全过程截面分析。工程中常用来分析各种截面(如异形柱、组合截面等)的非线性特征点及承载力校核。 xtract主要思路也是根据各种材料本构关系,基于平截面假定,用fiber划分截面进行全过程非线性分析。......

→★佐佑思维★← 8402

FPGA工程师面试试题集锦11~20

时钟抖动是指芯片的某一个给定点上时钟周期发生暂时性变化,也就是说时钟周期在不同的周期上可能加长或缩短。它是一个平均值为0的平均变量。

FPGA/MATLAB学习教程/源码/项目合作开发 678

Spring Cloud微服务配置中心:Nacos与Apollo的深度对比与选型

选择Nacos的场景微服务治理需求同时包含注册发现与配置管理,倾向一体化方案对配置推送延迟有严格要求(毫秒级生效),如金融交易参数变更集群规模大(500+服务),需要更高的配置读取吞吐技术栈以Spring Cloud Alibaba为主,Nacos是生态首选选择Apollo的场景配置变更涉及业务关键参数,需要完整的审批流程与审计记录灰度发布是高频操作,需要按集群维度灰度与完善的回滚机制团队有多角色协作需求(开发→测试→运维分级权限)

dicky_zhang3的博客 4672

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

配置中心的架构迁移实录:从Apollo到Nacos的演进决策与数据治理

配置中心的迁移,三分在做技术切换,七分在数据治理与验证。建议的迁移路径:第一阶段,搭建Nacos集群并导入全量静态配置,验证版本正确性。第二阶段,部署适配器层进入双读模式,持续运行一周观察偏差告警。第三阶段,灰度切换客户端SDK,从10%逐步推向100%。第四阶段,确认稳定后下线Apollo集群。核心风险点在于:命名空间映射的错误会导致配置丢失;监听线程池的默认单线程模型可能在生产流量下暴露回调积压。这两点需要在迁移方案评审阶段就纳入检查清单,而非在线上故障中被动发现。

qq_35160742的博客 4164

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

配置中心Spring cloud config、Apollo、Nacos对比

目前在国内开发者社区比较热,在Github上有超过5k颗星,在国内众多互联网公司有落地案例,可以说Apollo是目前配置中心产品领域Number1的产品,其成熟度和企业级特性要远远强于Spring Cloud体系中的Spring Cloud Config产品。也许经过的迭代更久,功能上比Nacos更加完善,权限管理做的全面,配置上可能会做的更细节一些,不过操作比较繁琐,比较适合多业务 多团队的业务场景。Nacos内置监听心跳检测机制,每5秒、15秒、30秒对服务进行心跳探测,标注为健康、不健康、剔除;

FeelTouch Labs 1801

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Apollo- 与配置中心对比:Nacos / Config Server 与 Apollo 的选型实战

Value注入不生效?✅ 正确姿势:必须搭配或,且类不能是(因 Apollo 初始化早于 Spring 容器)@Value("${my.key}") // 不会刷新!本地开发连不上 Apollo?✅ 检查是否指向(Docker 内部网络要用✅ 检查中app.id是否与 Apollo 控制台创建的应用 ID完全一致(大小写敏感!配置未实时更新?查看是否打印日志。一个好的配置中心,应该像空气一样——你感受不到它,但离开它就窒息。

千淘万漉虽辛苦,吹尽狂沙始到金 1万+
上一篇: Java 8 的异步利器:CompletableFuture源码解析(建议精读)
下一篇: 面试还不懂如何回答面试JVM相关的问题,看这一篇就够了
java_lujj
博客等级 码龄4年 18粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值