spring boot整合IBM WebSphere MQ,并配置多个队列管理器

IBM WebSphere MQ概述

网上关于IBM WebSphere MQ的资料挺少的,毕竟是一项老技术,整理一下从零开始对于IBM WebSphere MQ的理解

IBM WebSphere MQ是一种消息中间件技术,可用于多个系统间通信,对于消息中间件的作用,这里就不多做展开。首先要了解IBM WebSphere MQ中的几个名词定义

  • 队列管理器:构件独立的MQ运行环境,类似于RabbitMQ中的vhost,主要作用是维护和管理消息队列
  • 队列:存放消息的容器,可分为本地队列,远程队列等
  • 消息:MQ中的最小对象,默认情况下,消息缺省可以达到 4MB。消息可以分成持久消息和非持久消息。所 谓“持久”的 意思,就是在MQ 队列管理器重启动后,消息是否仍然能保持。持久的消息写入或读出队列的同时会在 Log 中记录,所以性能上比非持久消息差不少。
  • 通道:通道是两个队列管理器之间的一种单向的点对点的通信连接。队列管理器之间的通信是通过配置通道来实现 的,通道两侧的队列管理器对这个通道的相关参数应该能对应起来。在通道上可以配置不同的通信协议,这样就使得编程接口与通信协议无关。通道两端的配置必须匹配, 且名字相同,否则无法连通。消息在通道中只能单向的流动。如果需要双向的流动,可以创建一对通道,一来一去。

其基本服务架构如图在这里插入图片描述
图中显示了 IBM WebSphere MQ 编程的原理。第一步是让应用程序与队列管理器连接。它通过 MQConnect 调用来进行此连接。下一步使用 MQOpen 调用为输出打开一个队列。然后应用程序使用 MQPut 调用将其数据放到队列上。要接收数据,应用程序调用 MQOpen 调用打开输入队列。应用程序使用 MQGet 调用从队列上接收数据。
还展示了消息通道代理(MCA)、通道出口和对象权限管理器(OAM)。MCA 是 IBM WebSphere MQ 程序,它使用现有传输服务诸如 TCP/IP 与 SNA 将消息从本地传输队列移到目标队列管理器。这些传输服务即通道。通道出口是用户写入库,可以在通道运作期间,从已定义位置号之一进入这些库。OAM 是命令和对象管理的缺省授权服务(针对操作系统)。这三个组件对 IBM WebSphere MQ 的现有安全性解决方案非常重要。

IBM WebSphere MQ服务的安装

服务端的安装这里就不做描述,请查阅其他博文。
服务安装后为了使客户端能连接使用,需做如下准备:

  1. 创建队列管理器
  2. 新建本地队列
  3. 新建服务连接通道,并将其属性-MCA用户标识值设置为“MUSR_MQADMIN”
    在这里插入图片描述

注意:此处博主并未设置客户端的登录账号与密码,因为未找到在哪里设置,请知晓的朋友告知一下。

springboot配置多个队列管理器连接

1.引入依赖

<!--ibm mq-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.jms</groupId>
            <artifactId>javax.jms-api</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.ibm.mq</groupId>
            <artifactId>com.ibm.mq.allclient</artifactId>
            <version>9.1.1.0</version>
        </dependency>
  1. yml配置文件信息
spring:
  ibmmq:
    zsam:
      host: 10.1.16.156
      port: 1415 #此处配置的是队列管理器监听端口
      queue-manager: qm_ntfm_zsam
      channel: ch_service
      ccsid: 1381 #CCSID要与连接到的队列管理器一致,Windows下默认为1381,Linux下默认为1208。1208表示UTF-8字符集,建议把队列管理器的CCSID改为1208
      username: MUSR_MQADMIN
      password:
      receive-timeout: 2000
      pub-queue: ZSAM.TO.NTFM #推送给NTFM的对列名
      FCTI-queue: FCTI.TO.ZSAM
    zsfz:
      host: 10.1.16.103
      port: 1416
      queue-manager: qm_ntfm_zsfz
      channel: ch_service
      ccsid: 1381
      username: 1
      password: 1
      receive-timeout: 2000
      pub-queue: ZSFZ.TO.NTFM
      FCTI-queue: FCTI.TO.ZSFZ
  1. IBM MQ配置类
package com.pantech.ntfmadapter.config;

import com.ibm.mq.jms.MQQueueConnectionFactory;
import com.ibm.msg.client.wmq.common.CommonConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.core.JmsTemplate;

/**
 * @author lkh
 * @date 2021/6/28 15:48
 * @description IBM MQ配置
 */
@Configuration
@Slf4j
public class IbmMqConfig {
    /**
     * 配置连接工厂
     */
    @Bean(name = "zsamMqQueueConnectionFactory")
    public MQQueueConnectionFactory zsamMqQueueConnectionFactory(
            @Value("${spring.ibmmq.zsam.host}") String host,
            @Value("${spring.ibmmq.zsam.ccsid}") Integer ccsid,
            @Value("${spring.ibmmq.zsam.channel}") String channel,
            @Value("${spring.ibmmq.zsam.port}") Integer port,
            @Value("${spring.ibmmq.zsam.queue-manager}") String queueManager
    ) {
        MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
        mqQueueConnectionFactory.setHostName(host);
        try {
            mqQueueConnectionFactory.setTransportType(CommonConstants.WMQ_CM_CLIENT);
            mqQueueConnectionFactory.setCCSID(ccsid);
            mqQueueConnectionFactory.setChannel(channel);
            mqQueueConnectionFactory.setPort(port);
            mqQueueConnectionFactory.setQueueManager(queueManager);
        } catch (Exception e) {
            log.error("创建ZSAM机场IBM MQ连接工厂异常,原因:", e);
        }
        return mqQueueConnectionFactory;
    }

    /**
     * 配置连接认证
     * 如不需要账户密码链接可以跳过此步,直接将MqQueueConnectionFactory注入下一步的缓存连接工厂
     */
    @Bean(name = "zsamUserCredentialsConnectionFactoryAdapter")
    UserCredentialsConnectionFactoryAdapter zsamUserCredentialsConnectionFactoryAdapter(
            @Value("${spring.ibmmq.zsam.username}") String username,
            @Value("${spring.ibmmq.zsam.password}") String password,
            @Qualifier("zsamMqQueueConnectionFactory") MQQueueConnectionFactory mqQueueConnectionFactory
    ) {
        UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter = new UserCredentialsConnectionFactoryAdapter();
        userCredentialsConnectionFactoryAdapter.setUsername(username);
        userCredentialsConnectionFactoryAdapter.setPassword(password);
        userCredentialsConnectionFactoryAdapter.setTargetConnectionFactory(mqQueueConnectionFactory);
        return userCredentialsConnectionFactoryAdapter;
    }

    /**
     * 配置缓存连接工厂:
     * 不配置该类则每次与MQ交互都需要重新创建连接,大幅降低速度。
     */
    @Bean(name = "zsamCachingConnectionFactory")
    @Primary
    public CachingConnectionFactory zsamCachingConnectionFactory(
            @Qualifier("zsamUserCredentialsConnectionFactoryAdapter") UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter) {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setTargetConnectionFactory(userCredentialsConnectionFactoryAdapter);
        cachingConnectionFactory.setSessionCacheSize(500);
        cachingConnectionFactory.setReconnectOnException(true);
        return cachingConnectionFactory;
    }

    /**
     * 配置DefaultJmsListenerContainerFactory, 用@JmsListener注解来监听队列消息时候,尤其存在多个监听的时候,通过实例化配置DefaultJmsListenerContainerFactory来控制消息分发
     * 如果只有一个连接工厂,即可跳过此步骤,因为默认会创建一个监听容器工厂
     */
    @Bean(name = "zsamJmsQueueListenerContainerFactory")
    @Primary
    public DefaultJmsListenerContainerFactory zsamJmsQueueListenerContainerFactory(
            @Qualifier("zsamCachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory
    ) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(cachingConnectionFactory);
        //设置连接数
        factory.setConcurrency("3-10");
        //重连间隔时间
        factory.setRecoveryInterval(1000L);
        return factory;
    }

    /**
     * 配置JMS模板:可以在方法中通过@autowired的方式注入模板
     * JmsOperations为JmsTemplate的实现接口。
     * 重要:不设置setReceiveTimeout时,当队列为空,从队列中取出消息的方法将会一直挂起直到队列内有消息
     * 如果只是接收消息,可以不配置此步
     */
    @Bean(name = "zsamJmsOperations")
    public JmsOperations zsamJmsOperations(
            @Value("${spring.ibmmq.zsam.receive-timeout}") Integer receiveTimeout,
            @Qualifier("zsamCachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory) {
        JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
        jmsTemplate.setReceiveTimeout(receiveTimeout);
        return jmsTemplate;
    }
    
    //************************************************以下为福州机场IBM MQ配置*******************************************************

    /**
     * 配置连接工厂
     */
    @Bean(name = "zsfzMqQueueConnectionFactory")
    public MQQueueConnectionFactory zsfzMqQueueConnectionFactory(
            @Value("${spring.ibmmq.zsfz.host}") String host,
            @Value("${spring.ibmmq.zsfz.ccsid}") Integer ccsid,
            @Value("${spring.ibmmq.zsfz.channel}") String channel,
            @Value("${spring.ibmmq.zsfz.port}") Integer port,
            @Value("${spring.ibmmq.zsfz.queue-manager}") String queueManager
    ) {
        MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
        mqQueueConnectionFactory.setHostName(host);
        try {
            mqQueueConnectionFactory.setTransportType(CommonConstants.WMQ_CM_CLIENT);
            mqQueueConnectionFactory.setCCSID(ccsid);
            mqQueueConnectionFactory.setChannel(channel);
            mqQueueConnectionFactory.setPort(port);
            mqQueueConnectionFactory.setQueueManager(queueManager);
        } catch (Exception e) {
            log.error("创建ZSFZ机场IBM MQ连接工厂异常,原因:", e);
        }
        return mqQueueConnectionFactory;
    }

    /**
     * 配置连接认证
     * 如不需要账户密码链接可以跳过此步,直接将MqQueueConnectionFactory注入下一步的缓存连接工厂
     */
    @Bean(name = "zsfzUserCredentialsConnectionFactoryAdapter")
    UserCredentialsConnectionFactoryAdapter zsfzUserCredentialsConnectionFactoryAdapter(
            @Value("${spring.ibmmq.zsfz.username}") String username,
            @Value("${spring.ibmmq.zsfz.password}") String password,
            @Qualifier("zsfzMqQueueConnectionFactory") MQQueueConnectionFactory mqQueueConnectionFactory
    ) {
        UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter = new UserCredentialsConnectionFactoryAdapter();
        userCredentialsConnectionFactoryAdapter.setUsername(username);
        userCredentialsConnectionFactoryAdapter.setPassword(password);
        userCredentialsConnectionFactoryAdapter.setTargetConnectionFactory(mqQueueConnectionFactory);
        return userCredentialsConnectionFactoryAdapter;
    }

    /**
     * 配置缓存连接工厂:
     * 不配置该类则每次与MQ交互都需要重新创建连接,大幅降低速度。
     */
    @Bean(name = "zsfzCachingConnectionFactory")
    public CachingConnectionFactory zsfzCachingConnectionFactory(
            @Qualifier("zsfzUserCredentialsConnectionFactoryAdapter") UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter) {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setTargetConnectionFactory(userCredentialsConnectionFactoryAdapter);
        cachingConnectionFactory.setSessionCacheSize(500);
        cachingConnectionFactory.setReconnectOnException(true);
        return cachingConnectionFactory;
    }

    /**
     * 配置DefaultJmsListenerContainerFactory, 用@JmsListener注解来监听队列消息时候,尤其存在多个监听的时候,通过实例化配置DefaultJmsListenerContainerFactory来控制消息分发
     * 如果只有一个连接工厂,即可跳过此步骤,因为默认会创建一个监听容器工厂
     */
    @Bean(name = "zsfzJmsQueueListenerContainerFactory")
    public DefaultJmsListenerContainerFactory zsfzJmsQueueListenerContainerFactory(
            @Qualifier("zsfzCachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory
    ) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(cachingConnectionFactory);
        //设置连接数
        factory.setConcurrency("3-10");
        //重连间隔时间
        factory.setRecoveryInterval(1000L);
        return factory;
    }

    /**
     * 配置JMS模板:可以在方法中通过autowired的方式注入模板
     * JmsOperations为JmsTemplate的实现接口。
     * 重要:不设置setReceiveTimeout时,当队列为空,从队列中取出消息的方法将会一直挂起直到队列内有消息
     * 如果只是接收消息,可以不配置此步
     */
    @Bean(name = "zsfzJmsOperations")
    public JmsOperations zsfzJmsOperations(
            @Value("${spring.ibmmq.zsfz.receive-timeout}") Integer receiveTimeout,
            @Qualifier("zsfzCachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory) {
        JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
        jmsTemplate.setReceiveTimeout(receiveTimeout);
        return jmsTemplate;
    }
}

  1. 监听IBM MQ上的队列消息
package com.pantech.ntfmadapter.consumer;

import com.pantech.ntfmadapter.service.HandlerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;

import javax.jms.Message;
import javax.jms.TextMessage;

/**
 * @author lkh
 * @date 2021/6/28 15:47
 * @description 厦门机场IMB消费者
 */
@Component
@Slf4j
public class ZsamIbmMqConsumer extends MessageListenerAdapter {
    private final HandlerService handlerService;

    @Autowired
    public ZsamIbmMqConsumer(HandlerService handlerService) {
        this.handlerService = handlerService;
    }

    @Override
    // 可以配置多个@JmsListener以监听多个队列
    @JmsListener(destination = "${spring.ibmmq.zsam.FCTI-queue}")
    @JmsListener(destination = "${spring.ibmmq.zsfz.FCTI-queue}", containerFactory = "zsfzJmsQueueListenerContainerFactory")
    public void onMessage(Message message) {
        try {
            //必须转换如果不转换直接message.tostring消息的传输有限制。
            TextMessage textMessage = (TextMessage) message;
            String messagebody = textMessage.getText();
            log.info("监听到NTFM系统厦门机场消息:{}", messagebody);
            handlerService.ntfmHandler(messagebody);
        } catch (Exception e) {
            log.error("{}消息获取发生异常,原因:", message, e);
        }
    }
}

  1. 发送消息到IBM MQ
package com.pantech.ntfmadapter.provider;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.core.JmsOperations;
import org.springframework.stereotype.Component;

/**
 * @author lkh
 * @date 2021/6/29 15:45
 * @description RabbitMQ生产者
 */
@Component
@Slf4j
public class Provider {

    private final RabbitTemplate rabbitTemplate;
    private final JmsOperations zsamJmsOperations;
    private final JmsOperations zsfzJmsOperations;
    @Value("${spring.ibmmq.zsam.pub-queue}")
    private String pubQueueName;
    // 根据类名称注入到spring容器,所以这里的zsamJmsOperations等必须得与配置类中的bean命名一样
    @Autowired
    public Provider(RabbitTemplate rabbitTemplate, JmsOperations zsamJmsOperations, JmsOperations zsfzJmsOperations) {
        this.rabbitTemplate = rabbitTemplate;
        this.zsamJmsOperations = zsamJmsOperations;
        this.zsfzJmsOperations = zsfzJmsOperations;
    }
    public void sendToImbMq(String messageBody){
        zsamJmsOperations.convertAndSend(pubQueueName,messageBody);
        log.info("推送至IBM MQ成功,原消息为:{}",messageBody);
    }

    public void sendToRabbitMq(String messageBody){
        Message message = new Message(messageBody.getBytes());
        rabbitTemplate.convertAndSend("",message);
        log.info("转发至RabbitMQ成功,原消息为:{}",messageBody);
    }
}

springboot配置监听单个队列管理器

大体与上面配置多个队列管理器类似,这里贴一下配置类代码

@Configuration
public class IbmMqConfig {
    @Value("${spring.ibmmq.host}")
    private String host;
    @Value("${spring.ibmmq.port}")
    private Integer port;
    @Value("${spring.ibmmq.queue-manager}")
    private String queueManager;
    @Value("${spring.ibmmq.channel}")
    private String channel;
    @Value("${spring.ibmmq.username}")
    private String username;
    @Value("${spring.ibmmq.password}")
    private String password;
    @Value("${spring.ibmmq.receive-timeout}")
    private long receiveTimeout;

    /**
     * 配置连接工厂:
     * CCSID要与连接到的队列管理器一致,Windows下默认为1381,
     * Linux下默认为1208。1208表示UTF-8字符集,建议把队列管理器的CCSID改为1208
     */
    @Bean
    public MQQueueConnectionFactory mqQueueConnectionFactory() {
        MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
        mqQueueConnectionFactory.setHostName(host);
        try {
            mqQueueConnectionFactory.setTransportType(CommonConstants.WMQ_CM_CLIENT);
            mqQueueConnectionFactory.setCCSID(1381);
            mqQueueConnectionFactory.setChannel(channel);
            mqQueueConnectionFactory.setPort(port);
            mqQueueConnectionFactory.setQueueManager(queueManager);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mqQueueConnectionFactory;
    }

    /**
     * 配置连接认证:
     * 如不需要账户密码链接可以跳过此步,直接将mqQueueConnectionFactory注入下一步的缓存连接工厂。
     */
//    @Bean
//    UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter(MQQueueConnectionFactory mqQueueConnectionFactory) {
//        UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter = new UserCredentialsConnectionFactoryAdapter();
//        userCredentialsConnectionFactoryAdapter.setUsername(username);
//        userCredentialsConnectionFactoryAdapter.setPassword(password);
//        userCredentialsConnectionFactoryAdapter.setTargetConnectionFactory(mqQueueConnectionFactory);
//        return userCredentialsConnectionFactoryAdapter;
//    }

    /**
     * 配置缓存连接工厂:
     * 不配置该类则每次与MQ交互都需要重新创建连接,大幅降低速度。
     */
//    @Bean
//    @Primary
//    public CachingConnectionFactory cachingConnectionFactory(UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter) {
//        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
//        cachingConnectionFactory.setTargetConnectionFactory(userCredentialsConnectionFactoryAdapter);
//        cachingConnectionFactory.setSessionCacheSize(500);
//        cachingConnectionFactory.setReconnectOnException(true);
//        return cachingConnectionFactory;
//    }

    @Bean
    @Primary
    public CachingConnectionFactory cachingConnectionFactory(MQQueueConnectionFactory mqQueueConnectionFactory) {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setTargetConnectionFactory(mqQueueConnectionFactory);
        cachingConnectionFactory.setSessionCacheSize(500);
        cachingConnectionFactory.setReconnectOnException(true);
        return cachingConnectionFactory;
    }
    /**
     * 配置事务管理器:
     * 不使用事务可以跳过该步骤。
     * 如需使用事务,可添加注解@EnableTransactionManagement到程序入口类中,事务的具体用法可参考Spring Trasaction。
     */
    @Bean
    public PlatformTransactionManager jmsTransactionManager(CachingConnectionFactory cachingConnectionFactory) {
        JmsTransactionManager jmsTransactionManager = new JmsTransactionManager();
        jmsTransactionManager.setConnectionFactory(cachingConnectionFactory);
        return jmsTransactionManager;
    }

    /**
     * 配置JMS模板:
     * JmsOperations为JmsTemplate的实现接口。
     * 重要:不设置setReceiveTimeout时,当队列为空,从队列中取出消息的方法将会一直挂起直到队列内有消息
     */
    @Bean
    public JmsOperations jmsOperations(CachingConnectionFactory cachingConnectionFactory) {
        JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
        jmsTemplate.setReceiveTimeout(receiveTimeout);
        return jmsTemplate;
    }

我待你好,不要骂我。

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
IBM WebSphere MQIBM公司的一种消息中间件产品,它提供可靠、安全的消息传递服务,使分布式应用程序可以在不同的计算机之间进行交互。 WebSphere MQ的基础教程主要包括以下几个方面: 首先,介绍WebSphere MQ的概念和架构。WebSphere MQ是一个分布式消息队列系统,由消息队列管理器MQ Manager)和多个连接到该管理器的应用程序组成。它采用消息队列的方式来传递消息,确保消息的可靠传递和处理。 其次,说明WebSphere MQ的安装和配置。在教程中,会详细介绍如何安装WebSphere MQ软件,并配置消息队列管理器、通道、队列和应用程序等组件。 然后,介绍如何使用WebSphere MQ发送和接收消息。教程会演示如何编写发送消息和接收消息的代码,并解释如何通过队列管理器队列来实现消息传递。 另外,还会介绍如何保证消息的可靠性和安全性。WebSphere MQ提供了事务和确认机制,确保消息的可靠传递和处理。同时,它还支持SSL/TLS加密,保护消息的安全性。 最后,讲解如何监控和管理WebSphere MQ。教程中会介绍如何使用WebSphere MQ控制台和命令行工具来监控消息队列管理器和消息传递的情况,以及如何进行配置和故障排除。 通过学习WebSphere MQ的基础教程,可以了解到WebSphere MQ的基本概念、架构和功能,以及如何使用和管理它。这将帮助开发人员和系统管理员更好地利用WebSphere MQ来构建可靠的分布式应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值