seata 1.7 windows服务端和客户端基于nacos环境搭建

1、首先安装nacos

https://github.com/alibaba/naco ,下载后解压,本地新建数据库nacos-config,执行在naocs/conf下面的mysql-schema.sql,启动前修改bin目录下的startup.cmd中 set MODE = ”cluster“为 set MODE = ”standalone"然后双击启动

2、下载seata服务端

在这里插入图片描述
我这里下载的seata 1.7,本地新建数据库seata,然后在解压的seata文件找到script->server->db->mysql.sql,执行这个sql脚本。

  1. 在nacos上添加新的命名空间
    在这里插入图片描述
  2. 修改配置文件config.txt并加载到nacos上
    config.txt文件在seata/script/config-center目录下,需要修改的地方如下:
    store.mode=db
    store.lock.mode=db
    store.session.mode=db
    store.db 数据库的配置修改成自己的
    我的完整配置如下:
在这#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none

#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_group=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false

#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h

#Log rule configuration, for client and server
log.exceptionRate=100

#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
store.mode=db
store.lock.mode=db
store.session.mode=db
#Used for password encryption
store.publicKey=

#If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100

#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true&serverTimezone=GMT%2B8
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

#These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.
store.redis.mode=single
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.sentinel.masterName=
store.redis.sentinel.sentinelHosts=
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
store.redis.queryLimit=100

#Transaction rule configuration, only for the server
#二阶段提交未完成状态全局事务重试提交线程间隔时间 默认1000,单位毫秒,我改为60000
server.recovery.committingRetryPeriod=60000
#二阶段异步提交状态重试提交线程间隔时间 默认1000,单位毫秒
server.recovery.asynCommittingRetryPeriod=60000
#二阶段回滚状态重试回滚线程间隔时间 默认1000,单位毫秒
server.recovery.rollbackingRetryPeriod=60000
#超时状态检测重试线程间隔时间 默认1000,单位毫秒,检测出超时将全局事务置入回滚会话管理器
server.recovery.timeoutRetryPeriod=60000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.distributedLockExpireTime=10000
server.xaerNotaRetryTimeout=60000
server.session.branchAsyncQueueSize=5000
server.session.enableBranchAsyncRemove=false
server.enableParallelRequestHandle=false

#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
里插入代码片

进入seata目录,找到nacos-config.sh,路径为:script->config-center->nacos->nacos-config.sh
进入cmd窗口,执行以下命令:

sh nacos-config.sh -h 本机ip -p 8848 -g SEATA_GROUP -t 7dfa67c0-8296-4fc1-b6a2-a9a9020d7cea -u nacos -w nacos

-h nacos服务IP
-p nacos服务端口
-u nacos登录名
-w nacos登录密码
-g nacos 配置的分组名称,默认设置SEATA_GROUP
-t 上一步配置的命名空间ID

  1. 修改seata 配置文件
    进入seata/conf目录下,有两个配置文件,把application.yml 随意修改一个名字,然后把 application.example.yml修改成application.yml 作为主要配置文件。
    我的配置文件如下:
#  Copyright 1999-2019 Seata.io Group.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

server:
  port: 7091

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash
console:
  user:
    username: seata
    password: seata

seata:
  config:
    # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: 7dfa67c0-8296-4fc1-b6a2-a9a9020d7cea
      group: SEATA_GROUP
      username: nacos
      password: nacos
      context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:
      data-id: seataServer.properties
    #consul:
    #  server-addr: 127.0.0.1:8500
    #  acl-token:
    #  key: seata.properties
    #apollo:
    #  appId: seata-server
    #  apollo-meta: http://192.168.1.204:8801
    #  apollo-config-service: http://192.168.1.204:8080
    #  namespace: application
    #  apollo-access-key-secret:
    #  cluster: seata
    #zk:
    #  server-addr: 127.0.0.1:2181
    #  session-timeout: 6000
    #  connect-timeout: 2000
    #  username:
    #  password:
    #  node-path: /seata/seata.properties
    #etcd3:
    #  server-addr: http://localhost:2379
    #  key: seata.properties
  registry:
    # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
    type: nacos
    preferred-networks: 30.240.*
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: 7dfa67c0-8296-4fc1-b6a2-a9a9020d7cea
      cluster: default
      username: nacos
      password: nacos
      #context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:
    #eureka:
    #  service-url: http://localhost:8761/eureka
    #  application: default
    #  weight: 1
    #redis:
    #  server-addr: localhost:6379
    #  db: 0
    #  password:
    #  cluster: default
    #  timeout: 0
    #zk:
    #  cluster: default
    #  server-addr: 127.0.0.1:2181
    #  session-timeout: 6000
    #  connect-timeout: 2000
    #  username:
    #  password:
    #consul:
    #  cluster: default
    #  server-addr: 127.0.0.1:8500
    #  acl-token:
    #etcd3:
    #  cluster: default
    #  server-addr: http://localhost:2379
    #sofa:
    #  server-addr: 127.0.0.1:9603
    #  application: default
    #  region: DEFAULT_ZONE
    #  datacenter: DefaultDataCenter
    #  cluster: default
    #  group: SEATA_GROUP
    #  address-wait-time: 3000

  server:
    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
    max-commit-retry-timeout: -1
    max-rollback-retry-timeout: -1
    rollback-retry-timeout-unlock-enable: false
    enable-check-auth: true
    enable-parallel-request-handle: true
    retry-dead-threshold: 130000
    xaer-nota-retry-timeout: 60000
    enableParallelRequestHandle: true
    recovery:
      committing-retry-period: 1000
      async-committing-retry-period: 1000
      rollbacking-retry-period: 1000
      timeout-retry-period: 1000
    undo:
      log-save-days: 7
      log-delete-period: 86400000
    session:
      branch-async-queue-size: 5000 #branch async remove queue size
      enable-branch-async-remove: false #enable to asynchronous remove branchSession
  store:
    # support: file 、 db 、 redis
    mode: db
    session:
      mode: db
    lock:
      mode: db
    #file:
    #  dir: sessionStore
    #  max-branch-session-size: 16384
    #  max-global-session-size: 512
    #  file-write-buffer-cache-size: 16384
    #  session-reload-read-size: 100
    #  flush-disk-mode: async
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true&useSSL=false
      user: root
      password: 123456
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 1000
      max-wait: 5000
    #redis:
    #  mode: single
    #  database: 0
    #  min-conn: 10
    #  max-conn: 100
    #  password:
    #  max-total: 100
    #  query-limit: 1000
    #  single:
    #    host: 127.0.0.1
    #    port: 6379
    #  sentinel:
    #    master-name:
    #    sentinel-hosts:
  metrics:
    enabled: false
    registry-type: compact
    exporter-list: prometheus
    exporter-prometheus-port: 9898
  transport:
    rpc-tc-request-timeout: 15000
    enable-tc-server-batch-send-response: false
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      boss-thread-size: 1
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login

4.启动
找到seata-server.bat,点击启动,路径为:seata->bin->seata-server.bat,成功后可以在nacos控制台服务列表中看到多了一个服务。
在这里插入图片描述
5.客户端集成seata
这里只给出order的代码,并给出可能的坑
在这里插入图片描述

  • 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private Long id;
    private Long userId;
    private Long productId;
    private Integer count;
    private BigDecimal money;
    private Integer status; //订单状态:0:创建中;1:已完结
}
  • 返回类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;
    public CommonResult(Integer code, String message) {
        this(code, message, null);
    }
}
  • 配置类
@Configuration
public class DataSourceProxyConfig {

    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }

}
public class MultipartSupportConfig implements RequestInterceptor {
 
    /**
     * 解决服务直接调用请求头不传递的问题
     *
     * @param template
     */
    @Override
    public void apply(RequestTemplate template) {
        // 解决seata的xid未传递
        String xid = RootContext.getXID();
        template.header(RootContext.KEY_XID, xid);
    }
}
@Configuration
@MapperScan({"com.oetsky.seata.mapper"})
public class MybatisConfig {
}
  • 控制层
@RestController
public class OrderController {
    @Resource
    private OrderService orderService;
    @GetMapping("/order/create")
    public CommonResult create(Order order) {
        orderService.create(order);
        return new CommonResult(200, "订单创建成功");
    }
}
  • 服务层
public interface OrderService {
    void create(Order order);
}
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private AccountService accountService;
    @Resource
    private StorageService storageService;

    @Override
   // @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class,timeoutMills = 100000)
    public void create(Order order) {
        System.out.println(RootContext.getXID());
        log.info("----->开始新建订单");
        //1 新建订单
        orderMapper.create(order);
        //2 扣减库存
        log.info("----->订单微服务开始调用库存,做扣减Count");
        storageService.decrease(order.getProductId(), order.getCount());
        log.info("----->订单微服务开始调用库存,做扣减end");

        //3 扣减账户
        log.info("----->订单微服务开始调用账户,做扣减Money");
        accountService.decrease(order.getUserId(), order.getMoney());
        log.info("----->订单微服务开始调用账户,做扣减end");

        //4 修改订单状态,从零到1,1代表已经完成
        log.info("----->修改订单状态开始");
        orderMapper.update(order.getUserId(), 0);
        log.info("----->修改订单状态结束");

        log.info("----->下订单结束了,O(∩_∩)O哈哈~");
    }
}
@FeignClient(value = "account",configuration = MultipartSupportConfig.class)
public interface AccountService {
    @PostMapping(value = "/account/decrease")
    CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
@FeignClient(value ="storage",configuration = MultipartSupportConfig.class)
public interface StorageService {
    @PostMapping(value = "/storage/decrease")
    CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}

  • 启动类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients
@EnableDiscoveryClient
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class,args);
    }
}
  • mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.oetsky.seata.mapper.OrderMapper">
    <resultMap id="order" type="com.oetsky.seata.bean.Order">
        <result property="id" column="id" jdbcType="BIGINT"></result>
        <result property="userId" column="user_id" jdbcType="BIGINT"></result>
        <result property="productId" column="product_id" jdbcType="BIGINT"></result>
        <result property="count" column="count" jdbcType="INTEGER"></result>
        <result property="money" column="money" jdbcType="BIGINT"></result>
        <result property="status" column="status" jdbcType="INTEGER"></result>
    </resultMap>

    <insert id="create">
        insert into t_order(user_id, product_id, count, money, status)
        value (#{userId},#{productId},#{count},#{money},0)
    </insert>

    <update id="update">
        update t_order
        set status = 1
        where user_id = #{userId}
        and status = #{status}
    </update>
</mapper>
  • application.yml
server:
  port: 2001

spring:
  application:
    name: order
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

feign:
  hystrix:
    enabled: false

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper/*.xml
seata:
  enabled: true
  application-id: order
  # 是否启用数据源bean的自动代理
  enable-auto-data-source-proxy: false
  service:
    vgroup-mapping:
      order-seata-service-group: default # 必须和服务器配置一样
    disable-global-transaction: false
  tx-service-group: order-seata-service-group  # 必须和服务器配置一样
  registry:
    type: nacos
    nacos:
      # Nacos 服务地址   本机localhost
      server-addr: 10.15.35.2:8848
      group: SEATA_GROUP
      namespace: 7dfa67c0-8296-4fc1-b6a2-a9a9020d7cea
      application: seata-server # 必须和服务器配置一样
      username: nacos
      password: nacos
      cluster: default
  config:
    type: nacos
    nacos:
      server-addr: 10.15.35.2:8848
      group: SEATA_GROUP
      namespace: 7dfa67c0-8296-4fc1-b6a2-a9a9020d7cea
      username: nacos
      password: nacos
  client:
    rm:
      # 是否上报成功状态
      report-success-enable: true
      # 重试次数
      report-retry-count: 5

每个service方法上都加上@GlobalTransactional,访问地址:http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
同时记得在每个参与分布式事务的数据库下新建undo_log表,建表语句为

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

踩坑

  1. 如果用的远程调用是openFeign的话,或者事物不生效的情况下检查你其他服务的xid是不是为空或者不一致(其他远程调用框架也一样这样配置请求头加xid)。你的事物生效就是因为你远程调用的时候远程调用框架没有把xid在请求头传到对应的服务去,所以才会导致事物失效,因为想要事物生效一整个服务调用链必须保证xid是一样的。
    这里openFeign远程调用的解决方法是这样的
import feign.RequestInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
 
/**
 * openFeign配置类
 *
 * @author 刘子固
 * @version 1.0
 * @date 2023/10/9 17:25
 */
 
public class MultipartSupportConfig implements RequestInterceptor {
 
    /**
     * 解决服务直接调用请求头不传递的问题
     *
     * @param template
     */
    @Override
    public void apply(RequestTemplate template) {
        // 解决seata的xid未传递
        String xid = RootContext.getXID();
        template.header(RootContext.KEY_XID, xid);
    }
}
  1. nacos 控制台记得添加 vgroupMapping配置 内容都为default
    在这里插入图片描述
    3.全局事务注册超时
    在这里插入图片描述
    修改如下配置为60000,seata默认是1000毫秒
    在这里插入图片描述
    同时可以设置@GlobalTransactional注解的超时时间,时间设长一点
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Seata 1.7的安装和部署,您可以按照以下步骤进行操作: 1. 下载Seata:您可以从Seata的官方GitHub仓库中下载最新版本的Seata。选择与您的项目兼容的版本,并下载安装包。 2. 解压安装包:将下载的安装包解压到您希望安装Seata的目录。 3. 配置文件:进入解压后的Seata目录,找到conf目录下的seata.conf文件。根据您的需求,修改该文件中的配置项,例如存储模式、事务日志存储类型等。 4. 数据库初始化:根据您在seata.conf文件中配置的数据库信息,执行对应数据库脚本进行初始化。Seata提供了多种数据库脚本,您可以根据您使用的数据库类型选择相应的脚本并执行。 5. 启动Seata Server:在Seata目录下的bin目录中,找到seata-server.bat(Windows)或seata-server.sh(Linux/Mac)文件。运行该文件来启动Seata Server。 6. 集成Seata到您的应用程序:在您的应用程序中,根据您使用的框架选择相应的Seata适配器,并按照适配器的文档说明将Seata集成到您的项目中。例如,如果您使用Spring Cloud框架,可以使用seata-spring-boot-starter适配器。 7. 配置应用程序:在您的应用程序的配置文件中,添加与Seata相关的配置项,例如Seata的事务组名称、Seata Server的地址等。 8. 测试事务:在您的应用程序中,编写测试代码来验证Seata的事务管理功能。可以尝试进行简单的事务操作,例如插入一条数据并进行回滚。 以上是Seata 1.7的基本安装和部署步骤。根据您的具体环境和需求,可能还需要进行额外的配置和调整。请参考Seata的官方文档和示例代码,以获取更详细的指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值