一文带你看懂Tomcat

Tomcat技术详解

目录

Tomcat简介

什么是Tomcat

Apache Tomcat是一个开源的Java Servlet容器,实现了Java Servlet、JavaServer Pages (JSP)、Java Expression Language (EL)和WebSocket技术规范。它是Apache软件基金会的一个项目,是目前最流行的Java Web应用服务器之一。

核心特性

  • Servlet容器:完全实现Java Servlet 4.0规范
  • JSP支持:支持JavaServer Pages 2.3规范
  • 轻量级:相比完整的应用服务器,Tomcat更加轻量
  • 可扩展性:支持集群和负载均衡
  • 跨平台:支持Windows、Linux、macOS等操作系统
  • 开源免费:Apache许可证,完全免费使用

版本历史

版本发布时间Java版本Servlet规范JSP规范重要特性
10.x2021Java 8+5.03.0Jakarta EE
9.x2017Java 8+4.02.3HTTP/2支持
8.x2014Java 7+3.12.3WebSocket
7.x2011Java 6+3.02.2注解支持

Tomcat架构

整体架构

层次结构图
Tomcat Server
├── Service 1
│   ├── Connector (HTTP:8080)
│   ├── Connector (AJP:8009)
│   └── Engine
│       ├── Host (localhost)
│       │   ├── Context (/app1)
│       │   ├── Context (/app2)
│       │   └── Context (/manager)
│       └── Host (example.com)
│           └── Context (/)
└── Service 2 (可选)
    ├── Connector (HTTPS:8443)
    └── Engine
        └── Host (secure.example.com)
请求处理流程图
Web应用
Tomcat Server
Servlet
Connector
Engine
Host
Context
Wrapper
客户端请求
响应

核心组件

组件作用说明包含关系
ServerTomcat的顶级组件代表整个Tomcat实例,负责启动和关闭整个服务器包含一个或多个Service
Service服务组件将Connector和Engine关联起来,提供完整的服务包含多个Connector和一个Engine
Engine请求处理引擎接收来自Connector的请求,进行路由和处理包含一个或多个Host
Host虚拟主机代表一个域名,处理特定域名的请求包含一个或多个Context
ContextWeb应用上下文对应一个Web应用程序,管理应用的生命周期包含多个Wrapper
Connector连接器处理客户端连接,支持多种协议独立组件,与Engine配合工作
Wrapper包装器包装单个Servlet,管理Servlet的生命周期被Context包含

组件详细说明

Server(服务器)
  • 职责:Tomcat的顶级容器,管理整个服务器的生命周期
  • 配置:在 server.xml的根元素中定义
  • 端口:默认监听8005端口用于关闭命令
Service(服务)
  • 职责:将Connector和Engine组合成完整的服务
  • 特点:一个Server可以包含多个Service
  • 配置:在 server.xml中通过 <Service>元素定义
Engine(引擎)
  • 职责:请求处理的核心,负责请求路由和响应生成
  • 特点:每个Service只能有一个Engine
  • 配置:通过 defaultHost属性指定默认主机
Host(虚拟主机)
  • 职责:处理特定域名的请求
  • 特点:支持多个虚拟主机,通过域名区分
  • 配置:通过 appBase属性指定应用部署目录
Context(应用上下文)
  • 职责:管理单个Web应用的生命周期
  • 特点:每个Context对应一个Web应用
  • 配置:可以独立配置,支持热部署
Connector(连接器)
  • 职责:处理客户端连接,支持多种协议
  • 协议:HTTP/1.1、HTTP/2、AJP、HTTPS
  • 配置:可以配置多个Connector监听不同端口

Tomcat安装与配置

安装方式

1. 二进制安装
# 下载Tomcat
wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.xx/bin/apache-tomcat-9.0.xx.tar.gz

# 解压
tar -xzf apache-tomcat-9.0.xx.tar.gz

# 移动到目标目录
sudo mv apache-tomcat-9.0.xx /opt/tomcat

# 设置权限
sudo chown -R tomcat:tomcat /opt/tomcat
sudo chmod +x /opt/tomcat/bin/*.sh
2. 包管理器安装
# Ubuntu/Debian
sudo apt-get install tomcat9

# CentOS/RHEL
sudo yum install tomcat

# 使用systemd管理
sudo systemctl start tomcat
sudo systemctl enable tomcat
3. Docker安装
# Dockerfile
FROM tomcat:9.0-jdk11-openjdk
COPY myapp.war /usr/local/tomcat/webapps/
EXPOSE 8080
# 运行容器
docker run -d -p 8080:8080 --name my-tomcat tomcat:9.0

目录结构

tomcat/
├── bin/                    # 可执行文件
│   ├── startup.sh         # 启动脚本
│   ├── shutdown.sh        # 关闭脚本
│   ├── catalina.sh        # 核心脚本
│   └── setenv.sh          # 环境变量设置
├── conf/                  # 配置文件
│   ├── server.xml         # 主配置文件
│   ├── web.xml            # 全局Web配置
│   ├── context.xml        # 上下文配置
│   └── logging.properties # 日志配置
├── lib/                   # 共享库文件
├── logs/                  # 日志文件
│   ├── catalina.out       # 控制台输出
│   ├── localhost.log      # 应用日志
│   └── manager.log        # 管理日志
├── temp/                  # 临时文件
├── webapps/               # Web应用目录
│   ├── ROOT/              # 根应用
│   ├── manager/           # 管理应用
│   └── host-manager/      # 主机管理
└── work/                  # 工作目录

Tomcat使用方式

基本操作

1. 启动Tomcat
# 方式1:使用startup脚本
./bin/startup.sh

# 方式2:使用catalina脚本
./bin/catalina.sh start

# 方式3:后台启动
nohup ./bin/startup.sh &

# 方式4:使用systemd(如果已安装为服务)
sudo systemctl start tomcat
2. 停止Tomcat
# 方式1:使用shutdown脚本
./bin/shutdown.sh

# 方式2:使用catalina脚本
./bin/catalina.sh stop

# 方式3:强制停止
./bin/catalina.sh stop -force

# 方式4:使用systemd
sudo systemctl stop tomcat
3. 重启Tomcat
# 方式1:先停止再启动
./bin/shutdown.sh && ./bin/startup.sh

# 方式2:使用catalina脚本
./bin/catalina.sh restart

# 方式3:使用systemd
sudo systemctl restart tomcat

部署Web应用

1. 静态部署
# 将WAR文件复制到webapps目录
cp myapp.war /opt/tomcat/webapps/

# 或者解压到目录
mkdir /opt/tomcat/webapps/myapp
unzip myapp.war -d /opt/tomcat/webapps/myapp/
2. 动态部署
# 使用Tomcat Manager
curl -X PUT "http://localhost:8080/manager/text/deploy?path=/myapp" \
     -F "deploy=@myapp.war"

# 使用Tomcat Manager Web界面
# 访问 http://localhost:8080/manager/html
3. 热部署
<!-- 在server.xml中配置 -->
<Context path="/myapp" docBase="/path/to/myapp" reloadable="true" />

配置文件管理

server.xml配置示例
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
    <Service name="Catalina">
        <!-- HTTP连接器 -->
        <Connector port="8080" 
                   protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443"
                   maxThreads="200"
                   minSpareThreads="10"
                   maxSpareThreads="50"
                   acceptCount="100" />
      
        <!-- AJP连接器 -->
        <Connector port="8009" 
                   protocol="AJP/1.3"
                   redirectPort="8443" />
      
        <!-- 引擎 -->
        <Engine name="Catalina" defaultHost="localhost">
            <Host name="localhost" 
                  appBase="webapps"
                  unpackWARs="true"
                  autoDeploy="true">
                <Context path="/myapp" 
                         docBase="/path/to/myapp" 
                         reloadable="true" />
            </Host>
        </Engine>
    </Service>
</Server>

Tomcat参数调优

JVM参数调优

1. 内存设置
# 在setenv.sh中设置
export CATALINA_OPTS="$CATALINA_OPTS -Xms2g -Xmx4g"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=256m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=512m"
2. 垃圾回收优化
# G1垃圾回收器
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=200"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1HeapRegionSize=16m"

# 或者使用Parallel GC
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseParallelGC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=4"
3. 性能监控
# 启用JMX监控
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote"
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=9999"
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false"

连接器参数调优

1. HTTP连接器优化
<Connector port="8080" 
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           maxThreads="200"           <!-- 最大工作线程数 -->
           minSpareThreads="10"       <!-- 最小空闲线程数 -->
           maxSpareThreads="50"       <!-- 最大空闲线程数 -->
           acceptCount="100"          <!-- 等待队列长度 -->
           maxConnections="8192"      <!-- 最大连接数 -->
           compression="on"           <!-- 启用压缩 -->
           compressionMinSize="2048"  <!-- 压缩最小大小 -->
           compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json"
           enableLookups="false"      <!-- 禁用DNS查询 -->
           URIEncoding="UTF-8" />     <!-- URI编码 -->
2. NIO连接器配置
<Connector port="8080" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           connectionTimeout="20000"
           maxThreads="200"
           minSpareThreads="10"
           maxSpareThreads="50"
           acceptCount="100"
           maxConnections="8192"
           acceptorThreadCount="2"    <!-- 接收线程数 -->
           pollerThreadCount="2"      <!-- 轮询线程数 -->
           selectorTimeout="1000" />  <!-- 选择器超时 -->

应用级优化

1. 会话管理优化
<!-- 在context.xml中配置 -->
<Context>
    <Manager className="org.apache.catalina.session.StandardManager"
             maxActiveSessions="1000"
             maxInactiveInterval="1800"
             sessionIdLength="32" />
</Context>
2. 静态资源优化
<!-- 在web.xml中配置 -->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>

与其他容器对比

主流Java应用服务器对比

特性TomcatJettyUndertowWildFlyWebLogic
类型Servlet容器Servlet容器Servlet容器完整应用服务器商业应用服务器
大小轻量级极轻量级轻量级重量级重量级
启动速度很快很快很慢
内存占用中等很多
配置复杂度简单简单简单复杂复杂
集群支持需要额外配置需要额外配置需要额外配置内置内置
管理界面基础基础丰富丰富
许可证Apache 2.0Apache 2.0Apache 2.0LGPL商业
学习曲线平缓平缓平缓陡峭陡峭

详细对比分析

1. Tomcat vs Jetty

Tomcat优势:

  • 更成熟稳定
  • 社区支持更好
  • 文档更完善
  • 与Spring生态集成更好

Jetty优势:

  • 启动更快
  • 内存占用更少
  • 嵌入式部署更简单
  • 异步处理性能更好
// Jetty嵌入式示例
public class JettyServer {
    public static void main(String[] args) throws Exception {
        Server server = new Server(8080);
        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/");
        context.addServlet(HelloServlet.class, "/hello");
        server.setHandler(context);
        server.start();
    }
}
2. Tomcat vs Undertow

Tomcat优势:

  • 更广泛使用
  • 配置更灵活
  • 管理功能更丰富

Undertow优势:

  • 性能更高
  • 内存占用更少
  • 支持HTTP/2
  • 异步处理能力强
// Undertow配置示例
public class UndertowServer {
    public static void main(String[] args) {
        Undertow server = Undertow.builder()
            .addHttpListener(8080, "localhost")
            .setHandler(exchange -> {
                exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                exchange.getResponseSender().send("Hello World");
            }).build();
        server.start();
    }
}
3. 选择建议

选择Tomcat的情况:

  • 传统Web应用
  • 需要稳定可靠的服务
  • 团队对Tomcat更熟悉
  • 需要丰富的管理功能

选择Jetty的情况:

  • 嵌入式应用
  • 微服务架构
  • 对启动速度要求高
  • 资源受限环境

选择Undertow的情况:

  • 高并发应用
  • 对性能要求极高
  • 需要HTTP/2支持
  • 异步处理场景

Spring Boot集成Tomcat

默认集成

Spring Boot默认内嵌Tomcat,无需额外配置即可使用。

<!-- Maven依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
// Spring Boot应用
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

Tomcat配置

1. 基本配置
# application.yml
server:
  port: 8080
  servlet:
    context-path: /myapp
  tomcat:
    threads:
      max: 200
      min-spare: 10
    max-connections: 8192
    accept-count: 100
    connection-timeout: 20000
    compression:
      enabled: true
      min-response-size: 2048
      mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
2. 高级配置
server:
  tomcat:
    uri-encoding: UTF-8
    max-http-form-post-size: 2MB
    max-swallow-size: 2MB
    processor-cache: 200
    additional-tld-skip-patterns: "*.jar"
    remote-ip-header: x-forwarded-for
    protocol-header: x-forwarded-proto
    port-header: x-forwarded-port
3. 自定义Tomcat配置
@Configuration
public class TomcatConfig {
  
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        return new TomcatServletWebServerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                // 自定义上下文配置
                context.setSessionTimeout(30);
                context.setSessionCookieName("JSESSIONID");
            }
        };
    }
  
    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
        return factory -> {
            factory.addConnectorCustomizers(connector -> {
                Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
                protocol.setMaxThreads(200);
                protocol.setMinSpareThreads(10);
                protocol.setMaxSpareThreads(50);
                protocol.setAcceptCount(100);
                protocol.setMaxConnections(8192);
            });
        };
    }
}

替换为其他容器

1. 替换为Jetty
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
2. 替换为Undertow
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

性能优化配置

1. JVM参数优化
# 启动参数
java -Xms2g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
     -jar myapp.jar
2. 应用配置优化
spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 20000
      idle-timeout: 300000
      max-lifetime: 1200000
  jpa:
    hibernate:
      ddl-auto: none
    show-sql: false
    properties:
      hibernate:
        jdbc:
          batch_size: 20
        order_inserts: true
        order_updates: true

性能监控与故障排查

监控工具

1. JMX监控
@Configuration
public class JmxConfig {
  
    @Bean
    public MBeanServer mBeanServer() {
        return ManagementFactory.getPlatformMBeanServer();
    }
  
    @Bean
    public TomcatMetrics tomcatMetrics() {
        return new TomcatMetrics();
    }
}
2. Actuator监控
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always
  metrics:
    export:
      prometheus:
        enabled: true
3. 自定义监控
@Component
public class TomcatMonitor {
  
    private final MeterRegistry meterRegistry;
  
    public TomcatMonitor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
  
    @EventListener
    public void handleRequest(RequestEvent event) {
        Timer.Sample sample = Timer.start(meterRegistry);
        // 处理请求
        sample.stop(Timer.builder("tomcat.request.duration")
            .tag("method", event.getRequest().getMethod())
            .register(meterRegistry));
    }
}

常见问题排查

1. 内存泄漏
# 生成堆转储
jmap -dump:format=b,file=heap.hprof <pid>

# 分析堆转储
jhat heap.hprof
2. 线程问题
# 查看线程状态
jstack <pid>

# 查看线程CPU使用
top -H -p <pid>
3. 连接问题
# 查看网络连接
netstat -an | grep :8080

# 查看连接数
ss -tuln | grep :8080

日志配置

1. 访问日志
<!-- 在server.xml中配置 -->
<Valve className="org.apache.catalina.valves.AccessLogValve"
       directory="logs"
       prefix="localhost_access_log"
       suffix=".txt"
       pattern="%h %l %u %t "%r" %s %b %D" />
2. 应用日志
logging:
  level:
    org.apache.catalina: INFO
    org.apache.coyote: INFO
    org.apache.tomcat: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
  file:
    name: logs/application.log
    max-size: 100MB
    max-history: 30

最佳实践

1. 生产环境配置

安全配置
<!-- 禁用不必要的方法 -->
<security-constraint>
    <web-resource-collection>
        <web-resource-name>Disable TRACE</web-resource-name>
        <url-pattern>/*</url-pattern>
        <http-method>TRACE</http-method>
    </web-resource-collection>
    <auth-constraint/>
</security-constraint>
性能配置
server:
  tomcat:
    threads:
      max: 200
      min-spare: 10
    max-connections: 8192
    accept-count: 100
    connection-timeout: 20000
    compression:
      enabled: true
      min-response-size: 2048

2. 集群配置

负载均衡配置
<!-- 在server.xml中配置 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
    <Manager className="org.apache.catalina.ha.session.DeltaManager"
             expireSessionsOnShutdown="false"
             notifyListenersOnReplication="true"/>
    <Channel className="org.apache.catalina.tribes.group.GroupChannel">
        <Membership className="org.apache.catalina.tribes.membership.McastService"
                    address="228.0.0.4"
                    port="45564"
                    frequency="500"
                    dropTime="3000"/>
        <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                  address="auto"
                  port="4000"
                  autoBind="100"
                  selectorTimeout="5000"
                  maxThreads="6"/>
        <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
            <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
        </Sender>
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
    </Channel>
    <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
           filter=""/>
    <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
    <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
              tempDir="/tmp/war-temp/"
              deployDir="/tmp/war-deploy/"
              watchDir="/tmp/war-listen/"
              watchEnabled="false"/>
    <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
    <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

3. 监控告警

健康检查
@Component
public class TomcatHealthIndicator implements HealthIndicator {
  
    @Override
    public Health health() {
        try {
            // 检查Tomcat状态
            MBeanServer server = ManagementFactory.getPlatformMBeanServer();
            ObjectName objectName = new ObjectName("Catalina:type=Server");
            String state = (String) server.getAttribute(objectName, "stateName");
          
            if ("STARTED".equals(state)) {
                return Health.up()
                    .withDetail("tomcat", "running")
                    .withDetail("state", state)
                    .build();
            } else {
                return Health.down()
                    .withDetail("tomcat", "not running")
                    .withDetail("state", state)
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("tomcat", "error")
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

4. 部署策略

蓝绿部署
#!/bin/bash
# 蓝绿部署脚本

# 停止旧版本
./bin/shutdown.sh

# 备份当前版本
cp -r webapps/myapp webapps/myapp.backup.$(date +%Y%m%d_%H%M%S)

# 部署新版本
cp new-version.war webapps/myapp.war

# 启动新版本
./bin/startup.sh

# 健康检查
sleep 30
if curl -f http://localhost:8080/myapp/health; then
    echo "Deployment successful"
else
    echo "Deployment failed, rolling back"
    ./bin/shutdown.sh
    rm -rf webapps/myapp
    mv webapps/myapp.backup.$(date +%Y%m%d_%H%M%S) webapps/myapp
    ./bin/startup.sh
fi

总结

Tomcat作为最流行的Java Servlet容器,具有以下优势:

主要优势

  1. 成熟稳定:经过多年发展,非常稳定可靠
  2. 轻量级:相比完整应用服务器,资源占用较少
  3. 易于配置:配置简单,学习成本低
  4. 社区支持:活跃的社区和丰富的文档
  5. Spring集成:与Spring Boot完美集成

适用场景

  • 传统Web应用
  • 微服务架构
  • 开发和测试环境
  • 中小型企业应用
  • Spring Boot应用

选择建议

  • 新项目:推荐使用Spring Boot + 内嵌Tomcat
  • 传统项目:可以继续使用独立Tomcat
  • 高并发场景:考虑Undertow或Jetty
  • 企业级应用:考虑WildFly或WebLogic

学习建议

  1. 从基础配置开始,逐步深入
  2. 关注性能调优和监控
  3. 了解集群和负载均衡
  4. 掌握故障排查技能
  5. 关注新版本特性和最佳实践
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值