SgLang代码细读-1.从req到batch

SgLang代码细读-1.从req到batch

代码入口 & 初始化

sglang/python/sglang/srt/entrypoints/http_server.py launch_server 主要分4个步骤:

  1. 启动下列进程 (_launch_subprocesses):
    • TokenizerManager: 把输入的query进行tokenize
    • DP=1: run_scheduler_process: 创建scheduler & 开始event_loop
    • DP>1: run_data_parallel_controller_process: 初始化带负载均衡的DataParallelController, 开始event_loop
    • Detokenizer: 重新将输出转为可阅读的文字输出, 返回http结果
    • launch_dummy_health_check_server: 心跳保活接口
  2. api鉴权 & warmup请求
  3. 进程启动完成后启动http服务.
image-20250514113112380

Tokenizer / Detokenizer

sglang/python/sglang/srt/managers/tokenizer_manager.py

1. 词表加载

get_tokenizer->AutoTokenizer.from_pretrained 从huggingface.io下载预训练好的tokenizer

词表的生成方式一般是通过统计方式取top词, 比较常见的有BPE, WordPiece, 例如BPE的训练步骤:

  1. 初始化词汇表: 基础单元为所有单字符
  2. 迭代合并高频字符对
    • 统计所有相邻字符对的频率
    • 合并频率最高的字符对,将其加入词表, 一直重复这个步骤直到达到目标词汇量
    • 另外还有一部分特殊标记: [UNK](未知词)、[PAD](填充)、[CLS](分类)、[SEP](分隔符)、[MASK](掩码)、语言标记( , `)等。
  3. 保留合并操作: 记下所有的合并操作,用于后续对新文本的分词
2. 请求构造

每个节点都有一个http_server接受请求. sglang/python/sglang/srt/entrypoints/http_server.py generate_request()->_handle_batch_request()->send_to_scheduler.send_pyobj() 把请求发给rank0的scheduler

scheduler

只看了DP>1的情况.

DataParallelController

负载均衡: node间请求是通过zmq的方式进行通信. 在node_rank=0接受原始请求(TokenizedGenerateReqInput), 也就是上面的send_to_scheduler的请求, 然后进行dispatch. 负载均衡采用round_robin/shortest_queue的方式, 其他节点接到请求后, 在handle_generate_request里把请求放到waiting_queue里面

Launch_schedulers

核心是launch_tensor_parallel_group这个函数, 这里根据一个开关 enable_dp_attention 进行逻辑的区分. 这个优化原理参考文档, 当模型结果中有MLA时, 如果我们在attention层使用了TP, 会导致KV结果被复制到多张卡上, 极大地浪费显存, 通过这个开关可以吧attn单独使用DP实现, 经过attn all_gather后在MOE层再进入TP逻辑. 这个函数中会计算各个device自己的PP/DP/TP rank并初始化到配置里.

image-20250514143814480

完成初始化后, 根据算好的rank启动run_scheduler_process进程, 这个函数做了下面几个事:

  • 初始化logger
  • 设置CPU亲和性: SGLANG_SET_CPU_AFFINITY, 当启动scheduler进程的时候, 把GPU与对应范围的CPU核做黏合,也就是说,做了亲和性的线程或进程,只会在这一个CPU核上运行,只在这一个CPU核上被调度,且不会切换到其他的核上运行。开超线程(HT)的时候, bind范围需要根据逻辑核心数进行设置.
  • 根据prefill/decode启动不同的event_loop, 注意这里有个overlap配置, 参考设计文档, 如果不开这个优化, cpu和GPU逻辑完全串行, 比如batch1在GPU forward计算时, CPU空闲, 而实际上可以在这段时间跑batch2的前置batch逻辑(比如tokenizer)image-20250514145813019

Event_loop

主要逻辑如图, 主要功能是把req处理成batch, 用于后续的计算backend开始forward

image-20250516152136418

prefill (sglang/python/sglang/srt/disaggregation/prefill.py)

event_loop_normal_disagg_prefill为基础看prefill中的逻辑. overlap省略

1. 接受处理请求(recv_requests & process_input_requests)

接受请求, 分几种并行方式来:

  • PP: rank0 节点从tokenizer里接受请求list, 非rank0节点等待从上一个pp_rank节点的点对点请求.
  • TP: 如果开启了enable_dp_attn, tp_rank0节点 把tokenizer相关的请求(TokenizedGenerateReqInput, TokenizedEmbeddingReqInput)视为work_req, 其他类型的视为control_req, 把这些请求都广播到同TP_group的其他节点

处理请求: TypeBasedDispatcher在这个Dispatcher内定义了每个请求类型和对应的处理方法, 心跳请求忽略

2. 从waiting_queue中pop请求

KVSender有这么几种状态: Bootstrapping(表示正在与远端节点建立连接和同步元数据), WaitingForInput(完成连接, 等待kvcache发送), Transferring(传输中)

核心函数: pop_bootstrapped, 步骤如下:

  • poll_and_all_reduce: 调用每个请求里对应的kv_sender, 也就是mooncake/conn.py里的poll方法, 用来获取KVSender的当前状态, 通过all_reduce获取各个机器上的所有请求状态
  • 把WaitingForInput状态的请求挑出来执行, 从内存池(ReqToMetadataIdxAllocator)中给这些请求分配首token的内存存储空间, 以及kv_sender初始化, TODO: 待弄清楚token_to_kv_pool分页的原因是否为保证访存连续性.
3.处理process_prefill_chunk:

把chunked请求从batch里过滤出来, 保留chunkCache

4.get_new_batch_prefill 从waitingQueue里根据优先级组合新batch (重要函数)
一.优先级计算(schedule_policy.py) calc_priority

policy策略主要分为两大类, 和tree_cache相关的和无关的. 涉及到cache命中的相关数据结构与逻辑在后文单独说.

无关的有:

  • FCFS: first come first serve, 也是默认策略
  • LOF: based on the longest output, 把waiting_queue按最长输出排序.
  • RANDOM: 直接shuffle queue

有关的有:

  • LPM: longest prefix, 根据最长前缀token匹配数排序
  • DFS_WEIGHT: DFS方式计算req 最后一个node在treeCache中的权重再排序
二. 处理chunked_req

如果存在chunked请求, 之前把它从batch里过滤出来, 在init_next_round_input根据cache中已经处理完的前缀, 确定下一个batch中要处理的chunk长度和token.

add_chunked_req中变更ids的偏移, 并且把chunked_req加到can_run_list里

三. 遍历waiting_queue
         for req in self.waiting_queue:
						#... 前面还有一些非核心逻辑,省略
            if len(adder.can_run_list) >= self.get_num_allocatable_reqs(running_bs):
                self.running_batch.batch_is_full = True
                break
            #对请求进行prefix_cache匹配, 确定在cache中的last_node, prefix_indices, 和input_len, 计算需要用多长的kvcache
            req.init_next_round_input(
                None if prefix_computed else self.tree_cache,
                self.enable_hierarchical_cache)
            #判断这个请求的token数和batch里已有的token总和是否打印max_tokens, 目标应该是防止显存超限
            #对tree_cache.last_node加锁后操作, 增加last_node的引用计数, 防止被释放.
            res = adder.add_one_req(
                req, self.chunked_req, self.enable_hierarchical_cache)

当batch满或是要处理的token数满后, 停止这个遍历循环, 然后把在can_run_list中的req从waiting_queue里剔除.

四.构建ScheduleBatch

ScheduleBatch.init_new: 从can_run_list中构建ScheduleBatch类

prepare_for_extend: 为batch内的数据分配显存, 如input_ids/seq_lens 等

5.prepare_dp_attn_batch

这个函数主要作用是统计其他DP的batch状态, 首先统计local的batch信息, 比如num_tokens, can_cuda_graph等, 通过all_gather汇聚所有DP的状态, 只要有其中一个DP存在非空的batch, 就把当前如果是空的local_batch填充一个idle_batch, 这个idle_batch的作用就是使得所有DP的运行状态保持同步, 比如其他DP有AllToAll的需求, 就可以在idle_batch中能够把对应的集合通信状态给同步运行

6.run_batch

run_batch: forward核心逻辑, 在下一个文章中详细解读

process_batch_result_disagg_prefill: 把完成forward的prefill结果启动send_kv_cache, 并添加到disagg_prefill_inflight_queue里面

process_disagg_prefill_inflight_queue: 使用all_reduce检查所有处于kv sending状态的请求, 对已经完成发送的req交给detokenizer进行处理.

7.开始下一轮循环
decode (sglang/python/sglang/srt/disaggregation/decode.py)

event_loop_normal_disagg_decode为基础. 重点看下和prefill有区别的地方

1.请求处理

recv_requests & process_input_requests, 和prefill逻辑一致

2.从decode_queue里拿取完成kvcache通信的请求(process_decode_queue)

disagg_decode_prealloc_queue.pop_preallocated: Decode阶段请求进来后首先进入这个队列, 在pop时, 根据req的token数判断当前已pop出去的token总和是否大于阈值, 如果大于就暂停, 如果小于则可以对这个请求对应的kvcache分配显存, 主要目的是防止显存超限.

disagg_decode_transfer_queue.extend & pop_transferred: 把prealloc_queue pop出来的请求再填入transfer_queue, 把其中已经完成kv通信的请求挑出来, 把这些请求, 最后把这些请求加到decode模块的waiting_queue里面

3.ScheduleBatch构建(get_next_disagg_decode_batch_to_run)

decode整体是通过continus-batching的方式进行batch组合, 从而提升GPU利用率

continus-batching

  • last_batch.filter_batch(): 把上个batch里已经处理完的请求剔除.
  • self.running_batch.merge_batch(last_batch): 把上个batch中没处理完的请求merge到running_batch里
  • get_new_prebuilt_batch, 从waiting_queue里取出请求, 把当前batch填满到(min(self.req_to_token_pool.size, self.max_running_requests))
  • update_running_batch((self.running_batch)): 检查batch里的请求是否有超显存的可能性, 如果有将部分请求拿出来再放回queue里(retract_decode), 分配decode tensor相关显存占用.

4.run_batch & process_batch_result (同prefill)

一些基础概念

MFU概念和计算方法:

  • 模型实际FLOPs计算公式
    总FLOPs = 参数量 × 前向计算次数 + 2 × 参数量 × 反向计算次数(反向传播通常需要2倍前向的FLOPs)

  • GPU理论峰值FLOPs计算公式
    理论峰值FLOPs = GPU核心数 × 核心频率 (GHz) × 每周期运算数 × 2(“×2”表示乘加运算计为2次浮点操作)

  • MFU计算:

    \[MFU=\frac{模型实际FLOPs}{GPU理论峰值FLOPs * 训练时间(秒)} * 100\% \]

参考:

sglang阅读笔记: https://www.zhihu.com/column/c_1710767953182674944

原创作者: sunstrikes 转载于: https://www.cnblogs.com/sunstrikes/p/18884152
E:\jdk\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:57666,suspend=y,server=n -javaagent:C:\Users\D1523\AppData\Local\JetBrains\IdeaIC2025.1\captureAgent\debugger-agent.jar=file:///C:/Users/D1523/AppData/Local/Temp/capture12544887213207362884.props -Dkotlinx.coroutines.debug.enable.creation.stack.trace=false -Ddebugger.agent.enable.coroutines=true -Dkotlinx.coroutines.debug.enable.flows.stack.trace=true -Dkotlinx.coroutines.debug.enable.mutable.state.flows.stack.trace=true -Dfile.encoding=UTF-8 -classpath "E:\xm\cjy\target\classes;E:\apache-maven-3.9.11\req\org\apache\poi\poi\5.2.3\poi-5.2.3.jar;E:\apache-maven-3.9.11\req\org\apache\commons\commons-collections4\4.4\commons-collections4-4.4.jar;E:\apache-maven-3.9.11\req\org\apache\commons\commons-math3\3.6.1\commons-math3-3.6.1.jar;E:\apache-maven-3.9.11\req\commons-io\commons-io\2.11.0\commons-io-2.11.0.jar;E:\apache-maven-3.9.11\req\com\zaxxer\SparseBitSet\1.2\SparseBitSet-1.2.jar;E:\apache-maven-3.9.11\req\org\apache\logging\log4j\log4j-api\2.24.3\log4j-api-2.24.3.jar;E:\apache-maven-3.9.11\req\com\volcengine\volcengine-java-sdk-ark-runtime\0.2.25\volcengine-java-sdk-ark-runtime-0.2.25.jar;E:\apache-maven-3.9.11\req\com\volcengine\volcengine-java-sdk-ark\0.2.25\volcengine-java-sdk-ark-0.2.25.jar;E:\apache-maven-3.9.11\req\com\volcengine\volcengine-java-sdk-core\0.2.25\volcengine-java-sdk-core-0.2.25.jar;E:\apache-maven-3.9.11\req\io\swagger\core\v3\swagger-annotations\2.0.0\swagger-annotations-2.0.0.jar;E:\apache-maven-3.9.11\req\com\squareup\okhttp\okhttp\2.7.5\okhttp-2.7.5.jar;E:\apache-maven-3.9.11\req\com\squareup\okhttp\logging-interceptor\2.7.5\logging-interceptor-2.7.5.jar;E:\apache-maven-3.9.11\req\io\gsonfire\gson-fire\1.8.3\gson-fire-1.8.3.jar;E:\apache-maven-3.9.11\req\org\threeten\threetenbp\1.3.5\threetenbp-1.3.5.jar;E:\apache-maven-3.9.11\req\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;E:\apache-maven-3.9.11\req\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;E:\apache-maven-3.9.11\req\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;E:\apache-maven-3.9.11\req\com\fasterxml\jackson\core\jackson-databind\2.16.1\jackson-databind-2.16.1.jar;E:\apache-maven-3.9.11\req\com\fasterxml\jackson\core\jackson-annotations\2.16.1\jackson-annotations-2.16.1.jar;E:\apache-maven-3.9.11\req\com\fasterxml\jackson\core\jackson-core\2.16.1\jackson-core-2.16.1.jar;E:\apache-maven-3.9.11\req\com\squareup\retrofit2\retrofit\2.9.0\retrofit-2.9.0.jar;E:\apache-maven-3.9.11\req\io\reactivex\rxjava2\rxjava\2.0.0\rxjava-2.0.0.jar;E:\apache-maven-3.9.11\req\org\reactivestreams\reactive-streams\1.0.4\reactive-streams-1.0.4.jar;E:\apache-maven-3.9.11\req\com\squareup\retrofit2\adapter-rxjava2\2.9.0\adapter-rxjava2-2.9.0.jar;E:\apache-maven-3.9.11\req\com\squareup\retrofit2\converter-jackson\2.9.0\converter-jackson-2.9.0.jar;E:\apache-maven-3.9.11\req\com\squareup\okhttp3\okhttp\4.12.0\okhttp-4.12.0.jar;E:\apache-maven-3.9.11\req\com\squareup\okio\okio\3.6.0\okio-3.6.0.jar;E:\apache-maven-3.9.11\req\com\squareup\okio\okio-jvm\3.6.0\okio-jvm-3.6.0.jar;E:\apache-maven-3.9.11\req\org\jetbrains\kotlin\kotlin-stdlib-common\1.9.25\kotlin-stdlib-common-1.9.25.jar;E:\apache-maven-3.9.11\req\org\apache\poi\poi-ooxml\5.4.0\poi-ooxml-5.4.0.jar;E:\apache-maven-3.9.11\req\org\apache\poi\poi-ooxml-lite\5.4.0\poi-ooxml-lite-5.4.0.jar;E:\apache-maven-3.9.11\req\org\apache\xmlbeans\xmlbeans\5.3.0\xmlbeans-5.3.0.jar;E:\apache-maven-3.9.11\req\org\apache\commons\commons-compress\1.27.1\commons-compress-1.27.1.jar;E:\apache-maven-3.9.11\req\org\apache\commons\commons-lang3\3.17.0\commons-lang3-3.17.0.jar;E:\apache-maven-3.9.11\req\com\github\virtuald\curvesapi\1.08\curvesapi-1.08.jar;E:\apache-maven-3.9.11\req\org\springframework\boot\spring-boot-starter-web\3.4.4\spring-boot-starter-web-3.4.4.jar;E:\apache-maven-3.9.11\req\org\springframework\boot\spring-boot-starter\3.4.4\spring-boot-starter-3.4.4.jar;E:\apache-maven-3.9.11\req\org\springframework\boot\spring-boot\3.4.4\spring-boot-3.4.4.jar;E:\apache-maven-3.9.11\req\org\springframework\boot\spring-boot-autoconfigure\3.4.4\spring-boot-autoconfigure-3.4.4.jar;E:\apache-maven-3.9.11\req\org\springframework\boot\spring-boot-starter-logging\3.4.4\spring-boot-starter-logging-3.4.4.jar;E:\apache-maven-3.9.11\req\ch\qos\logback\logback-classic\1.5.18\logback-classic-1.5.18.jar;E:\apache-maven-3.9.11\req\ch\qos\logback\logback-core\1.5.18\logback-core-1.5.18.jar;E:\apache-maven-3.9.11\req\org\apache\logging\log4j\log4j-to-slf4j\2.24.3\log4j-to-slf4j-2.24.3.jar;E:\apache-maven-3.9.11\req\org\slf4j\jul-to-slf4j\2.0.17\jul-to-slf4j-2.0.17.jar;E:\apache-maven-3.9.11\req\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;E:\apache-maven-3.9.11\req\org\yaml\snakeyaml\2.3\snakeyaml-2.3.jar;E:\apache-maven-3.9.11\req\org\springframework\boot\spring-boot-starter-json\3.4.4\spring-boot-starter-json-3.4.4.jar;E:\apache-maven-3.9.11\req\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.16.1\jackson-datatype-jdk8-2.16.1.jar;E:\apache-maven-3.9.11\req\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.16.1\jackson-datatype-jsr310-2.16.1.jar;E:\apache-maven-3.9.11\req\com\fasterxml\jackson\module\jackson-module-parameter-names\2.16.1\jackson-module-parameter-names-2.16.1.jar;E:\apache-maven-3.9.11\req\org\springframework\boot\spring-boot-starter-tomcat\3.4.4\spring-boot-starter-tomcat-3.4.4.jar;E:\apache-maven-3.9.11\req\org\apache\tomcat\embed\tomcat-embed-core\10.1.39\tomcat-embed-core-10.1.39.jar;E:\apache-maven-3.9.11\req\org\apache\tomcat\embed\tomcat-embed-el\10.1.39\tomcat-embed-el-10.1.39.jar;E:\apache-maven-3.9.11\req\org\apache\tomcat\embed\tomcat-embed-websocket\10.1.39\tomcat-embed-websocket-10.1.39.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-web\6.2.5\spring-web-6.2.5.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-beans\6.2.5\spring-beans-6.2.5.jar;E:\apache-maven-3.9.11\req\io\micrometer\micrometer-observation\1.14.5\micrometer-observation-1.14.5.jar;E:\apache-maven-3.9.11\req\io\micrometer\micrometer-commons\1.14.5\micrometer-commons-1.14.5.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-webmvc\6.2.5\spring-webmvc-6.2.5.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-aop\6.2.5\spring-aop-6.2.5.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-context\6.2.5\spring-context-6.2.5.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-expression\6.2.5\spring-expression-6.2.5.jar;E:\apache-maven-3.9.11\req\org\springframework\boot\spring-boot-starter-websocket\3.4.4\spring-boot-starter-websocket-3.4.4.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-messaging\6.2.5\spring-messaging-6.2.5.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-websocket\6.2.5\spring-websocket-6.2.5.jar;E:\apache-maven-3.9.11\req\org\slf4j\slf4j-api\2.0.17\slf4j-api-2.0.17.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-core\6.2.5\spring-core-6.2.5.jar;E:\apache-maven-3.9.11\req\org\springframework\spring-jcl\6.2.5\spring-jcl-6.2.5.jar;E:\apache-maven-3.9.11\req\com\google\code\gson\gson\2.10.1\gson-2.10.1.jar;E:\apache-maven-3.9.11\req\dev\langchain4j\langchain4j-open-ai-spring-boot-starter\0.32.0\langchain4j-open-ai-spring-boot-starter-0.32.0.jar;E:\apache-maven-3.9.11\req\dev\langchain4j\langchain4j-open-ai\0.36.2\langchain4j-open-ai-0.36.2.jar;E:\apache-maven-3.9.11\req\dev\langchain4j\langchain4j-core\0.36.2\langchain4j-core-0.36.2.jar;E:\apache-maven-3.9.11\req\dev\ai4j\openai4j\0.23.0\openai4j-0.23.0.jar;E:\apache-maven-3.9.11\req\com\squareup\okhttp3\okhttp-sse\4.12.0\okhttp-sse-4.12.0.jar;E:\apache-maven-3.9.11\req\org\jetbrains\kotlin\kotlin-stdlib-jdk8\1.9.25\kotlin-stdlib-jdk8-1.9.25.jar;E:\apache-maven-3.9.11\req\org\jetbrains\kotlin\kotlin-stdlib\1.9.25\kotlin-stdlib-1.9.25.jar;E:\apache-maven-3.9.11\req\org\jetbrains\annotations\13.0\annotations-13.0.jar;E:\apache-maven-3.9.11\req\org\jetbrains\kotlin\kotlin-stdlib-jdk7\1.9.25\kotlin-stdlib-jdk7-1.9.25.jar;E:\apache-maven-3.9.11\req\com\knuddels\jtokkit\1.1.0\jtokkit-1.1.0.jar;E:\apache-maven-3.9.11\req\org\projectlombok\lombok\1.18.36\lombok-1.18.36.jar;E:\apache-maven-3.9.11\req\io\milvus\milvus-sdk-java\2.6.0\milvus-sdk-java-2.6.0.jar;E:\apache-maven-3.9.11\req\io\grpc\grpc-netty-shaded\1.59.1\grpc-netty-shaded-1.59.1.jar;E:\apache-maven-3.9.11\req\com\google\guava\guava\32.0.1-android\guava-32.0.1-android.jar;E:\apache-maven-3.9.11\req\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;E:\apache-maven-3.9.11\req\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;E:\apache-maven-3.9.11\req\org\checkerframework\checker-qual\3.33.0\checker-qual-3.33.0.jar;E:\apache-maven-3.9.11\req\com\google\j2objc\j2objc-annotations\2.8\j2objc-annotations-2.8.jar;E:\apache-maven-3.9.11\req\com\google\errorprone\error_prone_annotations\2.20.0\error_prone_annotations-2.20.0.jar;E:\apache-maven-3.9.11\req\io\perfmark\perfmark-api\0.26.0\perfmark-api-0.26.0.jar;E:\apache-maven-3.9.11\req\io\grpc\grpc-core\1.59.1\grpc-core-1.59.1.jar;E:\apache-maven-3.9.11\req\com\google\android\annotations\4.1.1.4\annotations-4.1.1.4.jar;E:\apache-maven-3.9.11\req\org\codehaus\mojo\animal-sniffer-annotations\1.23\animal-sniffer-annotations-1.23.jar;E:\apache-maven-3.9.11\req\io\grpc\grpc-context\1.59.1\grpc-context-1.59.1.jar;E:\apache-maven-3.9.11\req\io\grpc\grpc-util\1.59.1\grpc-util-1.59.1.jar;E:\apache-maven-3.9.11\req\io\grpc\grpc-protobuf\1.59.1\grpc-protobuf-1.59.1.jar;E:\apache-maven-3.9.11\req\io\grpc\grpc-api\1.59.1\grpc-api-1.59.1.jar;E:\apache-maven-3.9.11\req\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;E:\apache-maven-3.9.11\req\com\google\api\grpc\proto-google-common-protos\2.22.0\proto-google-common-protos-2.22.0.jar;E:\apache-maven-3.9.11\req\io\grpc\grpc-protobuf-lite\1.59.1\grpc-protobuf-lite-1.59.1.jar;E:\apache-maven-3.9.11\req\io\grpc\grpc-stub\1.59.1\grpc-stub-1.59.1.jar;E:\apache-maven-3.9.11\req\com\google\protobuf\protobuf-java\3.25.5\protobuf-java-3.25.5.jar;E:\apache-maven-3.9.11\req\org\apache\commons\commons-text\1.10.0\commons-text-1.10.0.jar;E:\apache-maven-3.9.11\req\org\apache\commons\commons-pool2\2.12.1\commons-pool2-2.12.1.jar;E:\apache-maven-3.9.11\req\org\json\json\20231013\json-20231013.jar;E:\apache-maven-3.9.11\req\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;E:\apache-maven-3.9.11\req\org\apache\httpcomponents\httpcore\4.4.16\httpcore-4.4.16.jar;E:\apache-maven-3.9.11\req\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;E:\apache-maven-3.9.11\req\org\apache\httpcomponents\httpmime\4.5.13\httpmime-4.5.13.jar;E:\apache-maven-3.9.11\req\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;E:\idea\IntelliJ IDEA Community Edition 2025.1.3\lib\idea_rt.jar" com.yz.CjApplication 已连接到地址为 ''127.0.0.1:57666',传输: '套接字'' 的目标虚拟机 . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.4.4) 2025-08-05T16:47:02.429+08:00 INFO 30088 --- [ main] com.yz.CjApplication : Starting CjApplication using Java 17.0.15 with PID 30088 (E:\xm\cjy\target\classes started by D1523 in E:\xm\cjy) 2025-08-05T16:47:02.431+08:00 INFO 30088 --- [ main] com.yz.CjApplication : No active profile set, falling back to 1 default profile: "default" 2025-08-05T16:47:03.224+08:00 INFO 30088 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) 2025-08-05T16:47:03.235+08:00 INFO 30088 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2025-08-05T16:47:03.236+08:00 INFO 30088 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.39] 2025-08-05T16:47:03.283+08:00 INFO 30088 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2025-08-05T16:47:03.284+08:00 INFO 30088 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 814 ms 2025-08-05T16:47:03.325+08:00 WARN 30088 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fileUploadController': Injection of autowired dependencies failed 2025-08-05T16:47:03.329+08:00 INFO 30088 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] 2025-08-05T16:47:03.337+08:00 INFO 30088 --- [ main] .s.b.a.l.ConditionEvaluationReportLogger : Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2025-08-05T16:47:03.353+08:00 ERROR 30088 --- [ main] o.s.boot.SpringApplication : Application run failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fileUploadController': Injection of autowired dependencies failed at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:515) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1445) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:347) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1155) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1121) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1056) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) ~[spring-context-6.2.5.jar:6.2.5] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) ~[spring-context-6.2.5.jar:6.2.5] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.4.4.jar:3.4.4] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) ~[spring-boot-3.4.4.jar:3.4.4] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.4.4.jar:3.4.4] at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[spring-boot-3.4.4.jar:3.4.4] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) ~[spring-boot-3.4.4.jar:3.4.4] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) ~[spring-boot-3.4.4.jar:3.4.4] at com.yz.CjApplication.main(CjApplication.java:19) ~[classes/:na] Caused by: org.springframework.util.PlaceholderResolutionException: Could not resolve placeholder 'upload.temp.dir' in value "${upload.temp.dir}" at org.springframework.util.PlaceholderResolutionException.withValue(PlaceholderResolutionException.java:81) ~[spring-core-6.2.5.jar:6.2.5] at org.springframework.util.PlaceholderParser$ParsedValue.resolve(PlaceholderParser.java:423) ~[spring-core-6.2.5.jar:6.2.5] at org.springframework.util.PlaceholderParser.replacePlaceholders(PlaceholderParser.java:128) ~[spring-core-6.2.5.jar:6.2.5] at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:118) ~[spring-core-6.2.5.jar:6.2.5] at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:114) ~[spring-core-6.2.5.jar:6.2.5] at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:255) ~[spring-core-6.2.5.jar:6.2.5] at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:226) ~[spring-core-6.2.5.jar:6.2.5] at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:201) ~[spring-context-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:971) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1577) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1555) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:785) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:768) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:146) ~[spring-beans-6.2.5.jar:6.2.5] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:509) ~[spring-beans-6.2.5.jar:6.2.5] ... 19 common frames omitted 已与地址为 ''127.0.0.1:57666',传输: '套接字'' 的目标虚拟机断开连接 进程已结束,退出代码1
08-06
在 Spring Boot 应用中,出现 `Could not resolve placeholder 'upload.temp.dir'` 错误通常表示应用在配置文件中引用了一个未定义的占位符。Spring Boot 使用 `application.properties` 或 `application.yml` 文件来加载配置,若在配置中引用了未定义的属性,会导致启动失败。 ### 占位符解析失败的原因 该错误的核心原因是 Spring Boot 无法在当前的 `Environment` 中找到 `upload.temp.dir` 属性的值。Spring 在解析 `@upload.temp.dir@` 或 `${upload.temp.dir}` 等占位符时,会查找 `PropertySources` 中的配置值。如果找不到对应的键值对,就会抛出 `IllegalArgumentException` 并提示“Could not resolve placeholder”错误[^1]。 ### 解决方法 #### 1. 在 `application.properties` 或 `application.yml` 中定义属性 确保在配置文件中定义了 `upload.temp.dir` 属性。例如,在 `application.properties` 中添加: ```properties upload.temp.dir=C:/temp/upload ``` 或在 `application.yml` 中添加: ```yaml upload: temp: dir: C:/temp/upload ``` 该配置确保 Spring 在解析占位符时能找到对应的值。 #### 2. 检查属性是否被正确引用 在 Java 配置类或 XML 配置文件中,如果使用了 `@Value("${upload.temp.dir}")` 或 `@PropertySource` 注解,需确保属性名称与配置文件中的键完全一致。例如: ```java @Value("${upload.temp.dir}") private String uploadTempDir; ``` 若属性名拼写错误或层级不匹配,Spring 将无法解析该占位符。 #### 3. 使用 `@PropertySource` 显式加载配置文件 如果使用了自定义的属性文件(如 `config.properties`),需要通过 `@PropertySource` 注解显式加载该文件: ```java @Configuration @PropertySource("classpath:config.properties") public class AppConfig { } ``` 此方式确保 Spring 能从指定的配置文件中读取 `upload.temp.dir` 属性。 #### 4. 检查构建工具是否启用资源过滤 在 Maven 或 Gradle 构建过程中,若使用了资源过滤(resource filtering),需确保 `upload.temp.dir` 的值在构建时被正确替换。例如,在 `pom.xml` 中启用资源过滤: ```xml <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> ``` 若未启用资源过滤,Maven 不会替换 `application.properties` 中的 `${upload.temp.dir}` 占位符。 #### 5. 使用系统环境变量或 JVM 参数传递属性 如果希望不修改配置文件,也可以通过系统环境变量或 JVM 参数设置 `upload.temp.dir`。例如,在启动命令中添加: ```cmd java -Dupload.temp.dir=C:/temp/upload -jar your-app.jar ``` 此方式确保 Spring 在启动时能直接读取到该属性值。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值