Apollo Local File System

本文介绍了Adobe Apollo提供的文件系统API,使Web应用能够与用户的本地文件系统进行交互,包括读写文件、创建目录等操作。

From Adobe Labs

By Darron Schall (http://www.darronschall.com)

A key feature Apollo brings is the ability for web applications to interact with the local file system on the user's computer. Examples of the file system interaction exposed through Apollo include activities such as listing the contents of a directory, reading and writing text and/or binary files, copying and moving files, etc. This article will guide you through a high level overview of the Apollo File API, and cover in depth some of the common tasks you'll likely want to perform.

Table of contents [hide]
[ edit]

Requirements

In order to make the most of this article, you need the following software and files:

Prerequisites: General knowledge of ActionScript 3.0.

[ edit]

General overview

With the new File API, Apollo applications can take advantage of an unprecedented level of access to the user's local file system. Apollo applications run in their own security sandbox and aren't confined by the limitations web developers have grown accustomed to inside of the browser. In traditional web applications, the only file system action available is selecting a file to be uploaded to a server. Apollo expands on this ability by enabling developers to perform operations such as:

  • Create Files and Directories
  • Open and Read Files
  • Write Files
  • List the contents of a Directory
  • Find the user's home or documents directory
  • Inspect File and Directory Properties

The Apollo File API centers on the new flash.filesystem package. The File class is the workhorse of the File API. Most of the operations you perform will be done through him. His supporting cast includes the characters FileStream and FileMode. Their roles are broken down in the following manner:

Table 1. File API classes (flash.filesystem.*)

ClassDescription
FileThe representation of a File or a Directory
FileModeContains constant strings that specify different ways to open a file, used in the open() and openAsync() method of File
FileStreamAn object used when reading or writing files

One thing that might be confusing at first is the apparent lack of support for Directories. There exists a File class, but no Directory counterpart. It turns out that files and directories share the same basic operations (copying, moving, deleting, etc.) and are very similar on a conceptual level. Because of this, the directory functionality has been rolled up in the File class. The isDirectory property allows you to easily determine if the object being pointed to is either a file or a directory.

[ edit]

Referencing a file or directory

Creating a reference to a file is usually done in one of two ways. The first way involves the use of one of the directory shortcuts available as static constants of the File class, coupled with the resolve() method to convert a custom string path to the actual file or subdirectory itself, like this:

/*
  Create a reference to the "example.txt" file in the "apollo" subdirectory
  of my documents directory. This resolves to C:/Documents and Settings/Darron/My Documents/apollo/example.txt on Windows.
*/
var file:File = File.documentsDirectory.resolve( "apollo/example.txt" );

// Get a reference to the desktop directory.  For me, this points to
// C:/Documents and Settings/Darron/Desktop
var dir:File = File. desktopDirectory;

The second way involves setting the nativePath property of a File instance to the specific file or directory:

// Create a new file instance first
var file:File = new File();
// Use the string path to the file to get a reference to it via nativePath.
file.nativePath = "C:/Documents and Settings/Darron/Desktop/example.txt";

Table 2. The static string constants of the File class that point to common directories

Static constantDescription
appStorageDirectoryA place to store files needed by the application, such a settings or log files.
appResourceDirectoryThe application's installation directory.
currentDirectoryThe current directory of the application when it was launched. This is useful when dealing with command-line parameters.
desktopDirectoryThe "Desktop" directory underneath the user directory.
documentsDirectoryStaring from user directory, this is the "My Documents" subdirectory on Windows, and the "Documents" subdirectory on Mac OS X.
userDirectoryThe user's home directory. This is typically C:/Documents and Settings/<username>/Desktop on Windows and /Users/<username/Desktop on Mac OS X.

Once you have a reference to a file or directory, you can start interacting with the file system. The File API provides two different approaches for working with file system data, as you'll see in the next section.

[ edit]

Synchronous vs. asynchronous

When you start exploring the File API, one of the first things you'll notice is that there are two similarly named methods to perform a single operation. For example, to copy a file or directory you use either the copyTo() method, or the copyToAsync() method. While the methods are named very similarly and they do in fact perform the same operation, they are conceptually worlds apart.

In synchronous operations, the code will wait for the method to return before moving on to the next line:

 // Copy a file to the desktop
 var file:File = File.userDirectory.resolve( "apollo/example.txt" );
 file.copyTo( File.desktopDirectory.resolve( "Copy of example.txt" ) );
 trace( "Copy complete" );  // Displays after "copyTo" finishes up and
                            // the file has been copied

In asynchronous operations, the operation is started in the background and the code execution continues on without waiting for the original operation to complete. An event is generated by the background process when it's done working, letting listeners know that the operation has completed:

 // Copy a file to the desktop
 var file:File = File.userDirectory.resolve( "apollo/example.txt" );
 file.copyToAsync( File.desktopDirectory.resolve( "Copy of example.txt" ) );
 trace( "Started copy" );  // Displays right away, before the copy can finish
 
 // Listen for the "complete" event to know when the background copy process
 // has completed
 file.addEventListener( Event.COMPLETE, completeHandler );
 
 public function completeHandler( event:Event ):void
 {
    trace( "Copy complete" ); // Displays when the copy finishes
 }

Both synchronous and asynchronous operations have their place. As you can clearly see, the synchronous method results in less overall code and is more readable and easier to write. However, the downside is that if you are performing a long running process, the application will appear "stuck" until the synchronous process finishes. While the code is waiting for the operation to complete, no other code execution can be done. This means that the display list will remain frozen in time and all animations and user interactions will appear paused.

On the other hand, asynchronous operations require more care to write. Because they kick off the operation in the background, long running processes will not impact the normal use and interaction of the application. You should use the asynchronous methods when performing time-consuming actions.

The following is a list of synchronous methods and their asynchronous counterparts:

Table 3. Synchronous and Asynchronous File API methods

Synchronous methodsAsynchronous methods
File.copyTo()File.copyToAsync()
File.deleteDirectory()File.deleteDirectoryAsync()
File.deleteFile()File.deleteFileAsync()
File.listDirectory()File.listDirectoryAsync()
File.moveTo()File.moveToAsync()
File.moveToTrash()File.moveToTrashAsync()
FileStream.open()FileStream.openAsync()
[ edit]

Reading and writing files

The FileStream class provides the necessary functionality for reading and writing files. Any time you want to interact with a file, the following steps should be performed:

 # Create a reference to the file and open it with a FileStream
 # Perform the read / write operations as necessary
 # Close the file and free valuable system resources

The first step gets translated into code similar to this:

 // Create a reference to the file on the file system
 var file:File = File.desktopDirectory.resolve( "example.txt" );
  
 // Create a stream and open the file for reading
 var stream:FileStream = new FileStream();
 stream.open( file, FileMode.READ );

Once the file is opened for reading, you can use any of the read methods on the stream from the flash.utils.IDataInput interface. In this case, the file is not writeable because it was opened in read-only mode (see: Ways to Open FileStreams). If it were writeable, the write methods could be used from the flash.utils.IDataOuput interface.

Because the example.txt file is a plain text file for sample purposes, the code below reads the entire contents of the file as a string and then display that output in the console window:

 // Read the entire contents of the file as a string
 var contents:String = stream.readUTFBytes( stream.bytesAvailable )
 
 // Displays "Hello Apollo World"
 trace( contents ); 

After the file contents have been examined the only thing left to do now is clean up and close the stream:

 // Clean up - free resources that are in use
 stream.close();

Note that in the above code samples, the synchronous open() method was used for the sake of simplicity. The openAsync() method could have been substituted in its place. The major difference would be that reading and closing the file would occur in an Event.COMPLETE event handler, as demonstrated by the following:

 // Create a stream and open the file for asynchronous reading
 var stream:FileStream = new FileStream();
 stream.openAsync( file, FileMode.READ );
 
 // Add the complete event handler to know when the file has been opened
 stream.addEventListener( Event.COMPLETE, handleComplete );
 
 private function handleComplete( event:Event ):void
 {
    // Get the stream reference back from the event object
    var stream:FileStream = event.target as FileStream;
    
    // Read the entire contents of the file as a string
    var contents:String = stream.readUTFBytes( stream.bytesAvailable )
 
    // Displays "Hello Apollo World"
    trace( contents ); 
    
    // Clean up - free resources that are in use
    stream.close();
 }
[ edit]

Ways to open FileStreams

There's more to reading files than simply reading all of the data at once as a string. The FileStream class supports 4 different modes for interacting with files. The mode of interaction is specified as the second parameter in open() or openAsync() by using one of the static string constants from the FileMode class.

Table 4. Different ways to open a FileStream

Open mode constantDescription
FileMode.READThe file is opened for read-only. The file must already exist first.
FileMode.WRITEThe file is opened for write-only. The file will be created if it does not already exist. If the file does already exist, it is overwritten.
FileMode.APPENDThe file is opened for write-only. The file will be created if it does not exist. Written data will be placed the end of the file.
FileMode.UPDATEThe file is opened for read-write. If the file doesn't exist, it will be created. Data can be written to or read from any location in the file.
[ edit]

Creating, writing, and reading a binary file

The process of writing a file is essentially the same as reading a file. You need to choose the appropriate FileMode that allows you to create a file if it doesn't exist. The following example creates a new file, writes some binary data into the file, and then closes it, and then reads the data back in:

 // Create a reference to the file on the filesystem
 var file:File = File.desktopDirectory.resolve( "apollo test.dat" );
                
 // Create a stream and open the file for asynchronous reading
 var stream:FileStream = new FileStream();
 stream.open( file, FileMode.WRITE );
        
 // Write some raw data to the file
 stream.writeBoolean( false );
 stream.writeInt( 1000 );
 stream.writeByte( 0xC );
                
 // Clean up
 stream.close();
                
 // For demo purposes, open the file in read-only mode and read the data back
 stream.open( file, FileMode.READ );
                
 trace( stream.readBoolean() ); // false
 trace( stream.readInt() ); // 1000
 trace( stream.readByte().toString( 16 ) ); // c
            
 // Clean up
 stream.close();
[ edit]

Working with directories

As mentioned previously, working with directories is essentially the same as working with files. The basic idea is to create a new File instance that references the directory path. Once we have a reference to the directory, there are a few directory specific operations to perform:

Table 5. Common directory operations

Method nameDescription
File.createDirectory()Creates the directory (and all parent directories) if it does not exist.
File.deleteDirectory()Removes the directory. Can be done asynchronously.
File.listDirectory()Lists the contents of a directory. Can be done asynchronously.
File.listRootDirectories()Lists all of the root directories of the file system.
[ edit]

Creating a new directory

The following code example will create a new directory and a corresponding subdirectory on the user's desktop.

 // Create a reference to the target directory
 var dir:File = File.desktopDirectory.resolve( "Apollo/Dir Test" );
                
 // Check to see if the directory exists.. if not, then create it
 if ( !dir.exists )
 {
    dir.createDirectory();
    trace( "Directory created." );
 }
 else
 {
    trace( "Directory already exists." );
 }
[ edit]

Creating a temporary directory

Apollo allows for the ability to create a temporary directory, rather than creating a more permanent directory on the file system. Temporary directories are useful in a number of situations, one of which is a centralized place to store files that are going to get archived as a .zip file.

// Create a temporary directory via a static File method
var tempDir:File = File.createTempDirectory();
				
// Displays C:/Documents and Settings/Darron/Local Settings/Temp/fla4.tmp
trace( tempDir.nativePath );

// ...

// Delete the temporary directory once we're done with it
tempDir.deleteDirectory();
				
// Clear the variable to prevent accidentally using it at this point
tempDir = null;

Once the temporary directory is created, use the reference to the directory as a place to store anything you might need (by creating new files or directory there, or moving/coping items there). When finished, delete the directory and set the variable to null so that you don't accidentally try to reference a directory that no longer exists.

[ edit]

Listing the contents of a directory

Through the listDirectory() and listDirectoryAsync() methods, Apollo makes it easy to iterate through the contents of a directory. In general, listing directory contents should be performed asynchronously. A directory could have any number of children, from 0 upwards into the thousands. Because this could be a potentially long operation, performing it asynchronously will ensure that your application doesn't appear to be frozen while directory contents are retrieved.

The following example shows how to get the contents within a directory, and displays various properties of each item:

// List the contents of the user's home directory
var dir:File = File.userDirectory;
dir.listDirectoryAsync();
				
// Listen for the appropriate event to handle when the listing is complete
dir.addEventListener( FileListEvent.DIRECTORY_LISTING, 
                      handleDirectoryListing );

			
private function handleDirectoryListing( event:FileListEvent ):void
{
    // Display a header
    trace( "Name/tSize/tDir?/tCreated On" );
				
    // Loop over all of the files and subdirectories, and display 
    // some relevant information about each item in the console window
    for each ( var item:File in event.files )
    {
        trace( item.name + "/t" 
             + item.size + "/t" 
             + item.isDirectory + "/t" 
             + item.creationDate );
    }
}
[ edit]

Where to go from here

This article has demonstrated basic concepts of the new Apollo File API. The File API centers on the File class in the flash.filesystem package and allows developers access to the user's local file system, exposing a new world of functionality.

The File API allows for synchronous and asynchronous operations to be performed. Synchronous operations are easier to read, but have the disadvantage of locking up the application until the process completes. For long-running processes, it's best to use the asynchronous methods to kick the process off in the background, allowing the application to run as it normally would. When the process completes, an event is generated that can be handled and acted upon accordingly.

Working with files and directories is straightforward through Apollo's File API. Pay attention to the FileMode specified when opening a file, as that dictates what actions can be take on the FileStream. Working with files is the same as working with any class that supports the flash.utils.IDataInput and IDataOutput interfaces. If you're familiar with flash.net.Socket or flash.utils.ByteArray, you'll feel right at home.

  • For specific information about the classes in the File API, see the flash.filesystem package in the Apollo ASDoc documentation.
  • For general information about working with the File API, see "Using the Apollo file APIs" in the Apollo documentation.
  • For help about specific problems or for more ideas about what to do with the File API, see the Apollo for Adobe Flex developers pocket guide, specifically Chapter 4 ("Using the File System API") and Chapter 5 ("Working with the File System").
[ edit]

About the author

Darron Schall has a BS in Computer Science from Lehigh University. Darron is an independent consultant specializing in Rich Internet Applications and Flash Platform development. He maintains a Flash Platform related blog at http://www.darronschall.com and is an active voice in the Flash and Flex communities.

INFO] 开始编译项目... Build thread num: 2 Build Params: build --jobs=8 --define sd_coarse_match_enable=true --config=release_build modules/perception/env/... [INFO] Configure .apollo.bazelrc in non-interactive mode You have bazel 3.7.1 installed. Found possible Python library paths: /apollo /apollo/cyber_release/python/ /apollo/cyber_release/python/cyber/python/internal/ /usr/lib/python3/dist-packages /usr/local/lib/python3.8/dist-packages Found CUDA 11.4 in: /usr/local/cuda-11.4/targets/x86_64-linux/lib /usr/local/cuda-11.4/targets/x86_64-linux/include Found cuDNN 8 in: /usr/lib/x86_64-linux-gnu /usr/include Found TensorRT 8 in: /usr/lib/x86_64-linux-gnu /usr/include/x86_64-linux-gnu [ OK ] Successfully configured .apollo.bazelrc in non-interactive mode. 构建目标名称为: build Configuration 'release_mode' is not set. is_release_mode: False is_release_mode: False cfg_file: /apollo/scripts/deploy_v3/05_adas_noa/idc/debug/service_cfg.json service_file_root_path: /apollo/system/services/ statemgr_cfg_file_tem: /apollo/system/adas_app_template.json statemgr_cfg_file: /apollo/system/adas_app.json build target: build not support auto build, use default cfg Starting local Bazel server and connecting to it... (10:09:32) INFO: Invocation ID: 6a29d342-b23d-42ef-b6ae-d89591b35144 (10:09:32) INFO: Current date is 2025-07-31 (10:09:50) Loading: 0 packages loaded Fetching @bazel_skylib; fetching 1800s 等待过长,可能是什么原因,需要怎么检查,以及怎么解决
08-01
内容概要:本文详细介绍了“秒杀商城”微服务架构的设计与实战全过程,涵盖系统从需求分析、服务拆分、技术选型到核心功能开发、分布式事务处理、容器化部署及监控链路追踪的完整流程。重点解决了高并发场景下的超卖问题,采用Redis预减库存、消息队列削峰、数据库乐观锁等手段保障数据一致性,并通过Nacos实现服务注册发现与配置管理,利用Seata处理跨服务分布式事务,结合RabbitMQ实现异步下单,提升系统吞吐能力。同时,项目支持Docker Compose快速部署和Kubernetes生产级编排,集成Sleuth+Zipkin链路追踪与Prometheus+Grafana监控体系,构建可观测性强的微服务系统。; 适合人群:具备Java基础和Spring Boot开发经验,熟悉微服务基本概念的中高级研发人员,尤其是希望深入理解高并发系统设计、分布式事务、服务治理等核心技术的开发者;适合工作2-5年、有志于转型微服务或提升架构能力的工程师; 使用场景及目标:①学习如何基于Spring Cloud Alibaba构建完整的微服务项目;②掌握秒杀场景下高并发、超卖控制、异步化、削峰填谷等关键技术方案;③实践分布式事务(Seata)、服务熔断降级、链路追踪、统一配置中心等企业级中间件的应用;④完成从本地开发到容器化部署的全流程落地; 阅读建议:建议按照文档提供的七个阶段循序渐进地动手实践,重点关注秒杀流程设计、服务间通信机制、分布式事务实现和系统性能优化部分,结合代码调试与监控工具深入理解各组件协作原理,真正掌握高并发微服务系统的构建能力。
<think>我们之前已经讨论过调整配置顺序的方法,但用户反馈在Apollo 1.4.0版本中按照方式1(调整配置源顺序)后仍然读取Apollo配置。结合用户提供的引用信息,特别是引用[1]提到Apollo会在本地缓存配置,以及引用[4]中关于环境变量配置的示例,我们需要重新梳理解决方案。 ### 关键问题分析 在Apollo 1.4.0版本中,即使调整了配置源顺序,仍可能因为以下原因导致配置覆盖: 1. **Apollo本地缓存机制**:客户端在第一次获取配置后会在本地缓存,即使后续调整配置顺序,客户端可能仍然优先读取缓存(引用[1])。 2. **环境变量覆盖**:在docker-compose部署中(如引用[4]),环境变量的设置可能强制启用了Apollo配置源。 3. **配置顺序调整未生效**:在Spring Boot中,配置源顺序的调整可能被其他配置覆盖。 ### 解决方案(针对Apollo 1.4.0) #### 步骤1:完全禁用Apollo配置初始化 在`application.yml`(或`bootstrap.yml`)中设置: ```yaml apollo: bootstrap: enabled: false # 彻底关闭Apollo自动配置 ``` #### 步骤2:清除Apollo本地缓存 - 定位缓存目录(默认在`/opt/data/{appId}/config-cache`),删除对应目录。 - 或启动时指定空缓存目录: ```bash java -jar your-app.jar -Dapollo.cacheDir=/tmp/empty_dir ``` #### 步骤3:验证配置源顺序 启动应用时添加`--debug`参数,检查日志中配置源顺序: ```log 2023-10-01 12:00:00 [main] DEBUG o.s.b.c.c.ConfigFileApplicationListener - Loaded config file 'file:./config/application.yml' (order: 1) 2023-10-01 12:00:00 [main] DEBUG o.s.b.c.c.ConfigFileApplicationListener - Skipped config file 'classpath:/apollo-client.properties' (order: 10) # 确保Apollo配置源被跳过 ``` #### 步骤4:检查环境变量(适用于Docker部署) 参考引用[4]的docker-compose配置,确保未设置强制启用Apollo的环境变量: ```yaml environment: # 确保以下变量不存在或为false APOLLO_BOOTSTRAP_ENABLED: "false" # 检查其他Apollo相关环境变量 ``` #### 步骤5:代码层面排除Apollo自动配置 在Spring Boot启动类添加排除项: ```java @SpringBootApplication(exclude = { ApolloAutoConfiguration.class, ApolloApplicationContextInitializer.class }) ``` ### 特殊场景:仅禁用部分环境配置 若需在特定环境(如dev)禁用Apollo,而其他环境启用: ```yaml # application-dev.yml apollo: bootstrap: enabled: false ``` ### 配置覆盖原理说明 Apollo客户端的配置加载顺序为: 1. 启动参数(最高优先级) 2. 环境变量 3. Apollo远程配置 4. 本地配置文件 5. Apollo本地缓存 当调整配置源顺序无效时,通常是因为Apollo通过**高优先级渠道**(如环境变量或启动参数)注入了配置。 ### 验证方法 ```java @RestController public class ConfigController { @Value("${your.config.key}") private String configValue; @GetMapping("/config") public String getConfig() { return "Actual value: " + configValue; // 通过接口确认配置来源 } } ``` 访问该接口并观察返回值,若为本地配置值则调整成功。 ### 相关问题 1. 如何在不同环境中动态切换Apollo配置? 2. Apollo本地缓存机制如何工作? 3. 在Kubernetes中部署Apollo时如何避免配置冲突? 4. Spring Cloud Config与Apollo的兼容性如何? > 通过上述步骤,可确保在Apollo 1.4.0版本中彻底禁用远程配置读取。若问题仍存在,需检查是否有其他组件(如Spring Cloud Bus)重新激活了配置中心连接[^1][^4]。 [^1]: Apollo 客户端默认启用本地缓存,需手动清除 [^4]: Docker环境变量可能覆盖配置文件设置</think>### 问题排查与解决方案(针对 Apollo 1.4.0) 根据您描述的 Apollo 1.4.0 版本配置覆盖问题,结合引用[1]中提到的本地缓存机制,以下是完整的排查和解决方案: #### 1️⃣ **强制清除 Apollo 本地缓存**(最关键步骤) 引用[1]明确指出:**当客户端不能连接到 Apollo 配置中心时,默认使用本地缓存文件中的配置**。这是配置覆盖最常见的原因。 ```bash # 进入 Apollo 缓存目录(默认位置) cd /opt/data/{appId}/config-cache # 删除所有缓存文件(请替换 {appId} 为实际应用ID) rm -f * ``` *验证方式*:重启应用后检查是否生成新的缓存文件,确认修改时间 #### 2️⃣ **彻底禁用 Apollo 配置源** 在 `application.yml` 中添加: ```yaml apollo: bootstrap: enabled: false # 完全禁用 Apollo 初始化 eagerLoad: enabled: false # 禁用急切加载 # 禁用本地缓存(1.4.0 新增特性) cache: enabled: false ``` #### 3️⃣ **配置加载顺序验证** 启动时添加参数检查加载顺序: ```bash java -jar your-app.jar --debug | grep -i "PropertySources" ``` *期望输出*: ``` ConfigurationPropertySourcesPropertySource [name='applicationConfig...'] ApolloPropertySource [name='ApolloBootstrapPropertySources'] NOT FOUND # 应消失 ``` #### 4️⃣ **环境变量覆盖检查** 检查容器环境变量(引用[4]的 Docker 部署方式): ```bash # 在容器内执行 env | grep -i "APOLLO" ``` *需确保不存在以下变量*: ``` APOLLO_BOOTSTRAP_ENABLED=true APOLLO_META=http://... # 配置中心地址 ``` #### 5️⃣ **Apollo 客户端配置覆盖** 在 `bootstrap.yml` 中显式指定本地配置优先: ```yaml spring: cloud: config: override-none: true # 禁止远程配置覆盖本地 allow-override: false override-system-properties: false ``` #### 6️⃣ **版本特定解决方案(1.4.0)** 该版本存在缓存加载优先级 BUG,需在代码中强制重置: ```java @PostConstruct public void resetApolloCache() { // 清除 ConfigService 缓存 ConfigService.getConfig("application").getPropertyNames() .forEach(key -> ConfigService.getConfig("application").delete(key, null)); // 重置配置源 SpringApplicationBootstrapPrivileged.apply(null); } ``` ### 验证流程 1. 访问配置检查接口:`curl http://localhost:8080/actuator/env` 2. 查看日志关键词: ```log [Apollo] Skip init - bootstrap.enabled=false [LocalPropertySource] Loaded 15 properties from file:/config/local.yaml ``` ### 配置加载优先级示意图 ```mermaid graph TD A[启动参数] -->|最高级| B[环境变量] B --> C[本地配置文件] C -->|默认被覆盖| D[Apollo远程配置] D -->|缓存机制| E[Apollo本地缓存] F[解决方案] -->|禁用Apollo| C F -->|清除| E ``` ### 相关问题 1. Apollo 1.4.0 的本地缓存机制与早期版本有何不同? 2. 如何在 Docker 部署中永久禁用 Apollo 缓存(引用[4]部署方式)? 3. 配置中心不可用时,如何确保本地配置正确回退? 4. 多环境配置下如何避免 Apollo 的自动注入? > 通过上述步骤,可解决 Apollo 1.4.0 版本的配置覆盖问题。若仍存在异常,建议检查 Apollo 服务端是否通过灰度发布强制推配置(引用[2]的 Portal 配置界面)[^1][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值