JMS笔记

JMS


JMS、JMS消息模型
Java Message Service(JMS)是一个JAVA API,用来访问面向消息中间件(MOM)服务。
JMS为Java程序生成、发送、接收和读取异步消息提供通用的方法。


JMS模型:点到点和发布与订阅
point-to-point——消息通过队列进行发送。
消息由消息生产者(客户端)放入队列中。消息消费者有责任从队列中取出消息。
当给定的消息只能被给定的消费者接收时,使用P2P。这种情况下,该给定信息只有一个消费者。

Publish-and-subscribe(简称pub/sub)——消息通过标题传递。
由生产者将消息发布为标题。消息可以被任何订阅该标题的消费者收到。通过这种方法,一个消息可以被多个消费者接收或处理。


面向服务体系结构
事件驱动体系结构
异构平台集成
企业应用集成
企业到企业
地理分散
信息广播
构建动态系统

1、消息传送机制的优点:
1、异构集成;
2、缓解系统瓶颈:异步请求;
3、提高可伸缩性:通过引入能够并发处理不同消息的多个消息接收者,消息传送系统的可伸缩性得以实现;
      另一种方法是通过系统异步达到组件去耦(ESB经典应用);
4、提高最终用户什产率:异步请求;
5、体系结构灵活性和敏捷性;


2、JMS应用场景:1、面向服务的体系结构;2、事件驱动体系结构;3、异构平台集成;4、企业应用集成;5、企业到企业;6、地理分散;
             7、信息广播;8、构建动态系统;


3、公共API
ConnectionFactory、Destination、Connection、Session、Message、MessageProducer、MessageConsumer


点对点API
QueueConnectionFactory、Queue、QueueConnection、QueueSession、Message、QueueSender、QueueReceiver


发布/订阅API
TopicConnectionFactory、Topic、TopicConnection、TopicSession、Message、TopicPublisher、TopicSubscriber


4、一个Message对象有3个部分:消息头、消息属性、消息数据自身,消息数据自身也被称为有效负载或消息体;


5、消息头提供了和消息有关的元数据,它描述了消息由谁创建、何时创建、数据的有效长度等信息;消息头还包含了描述消息目的地(主题和队列)
    的路由信息、消息如何被确认等另外一些信息;


6、每条JMS消息都有一组消息头,每个消息头都由一组取值函数和赋值函数方法所标识,这些方法名称紧跟在术语setJMS<HEADER>()、getJMS<HEADER>之后:
自动分配的消息头
message.getXXX()/message.setXXX();


//目的地
public Destination getJMSDestination()throws JMSException;
public void setJMSDestination(Destination destination)throws JMSException;


//传送模式:持久/非持久
public int getJMSDeliveryMode()throws JMSException;
public void setJMSDeliverMode(int deliveryMode)throws JMSException;

//消息标识id
public String getJMSMessageID()throws JMSException;
public void setJMSMessageID(String id)throws JMSException;

//JMS发送消息、消费者接收消息的时间间隔
public long getJMSTimestamp()throws JMSException;
public void setJMSTimestamp(long timestamp)throws JMSException;

//消息有效期
public long getJMSExpiration()throws JMSException;
public void setJMSExpiration(long expiration)throws JMSException;

//消息被重新传送给消费者的标识true/false
public boolean getJMSRedelivered()throws JMSException;
public void setJMSRedelivered(boolean redelivered)throws JMSException; 

//优先级
public int getJMSPriority()throws JMSException;
public void setJMSPriority(int priority)throws JMSException;

开发者分配的消息头
//回应
public Destination getJMSReplyTo()throws JMSException;
public void setJMSReplyTo(Destination replyTo)throws JMSException;

//与回应消息联系起来
public String getJMSCorrelationID()throws JMSException;
public void setJMSCorrelationID(String correlationID)throws JMSException;

public byte[] getJMSCorrelationIDAsBytes()throws JMSException;
public void setJMScorrelationIDAsBytes(byte[] correlationID)throws JMSException;

//标识消息的结构和有效负载类型
public String getJMSType()throws JMSException;
public void setJMSType(String type)throws JMSException;


7、大多数JMS消息头是自动分配的,在传送消息时,消息头的值由JMS提供者来设置,因此开发者使用setJMS<HEADER>()方法分配的值
就被忽略了。换句话说大多数自动分配的消息头来说,使用赋值函数方法显然是徒劳的。不过,这并非意味着开发者无法控制这些
消息头的值。一些自动分配的消息头可以在创建session和MessageProducer(也就是TopicPublisher)时,由开发者通过编程方式来
设置。


JMSDestination:使用一个Topic或Queue对象来标识目的地,二者都是Destination类型;
Topic destination = (topic)message.getJMSDestination();


JMSDeliveryMode:传送模式:持久性模式和非持久性模式。
一条持久性消息应该被传送“一次而且仅仅一次”,这就意味着如果JMS提供者出现故障,该消息并不会丢失;
它会在服务器恢复正常之后再次传送。一条非持久性消息最多只会发送一次,这意味着如果JMS提供者出现故障,
该消息可能会永久消失。在持久性和非持久性这两种传送模式中,消息服务器都不会将一条消息向同一消费者
发送一次以上,不过,这在技术是有可能实现的 —— JMSRedelivered;
int deliverymode = message.getJMSDeliveryMode();
if(deliverymode == javax.jms.DeliverMode.PERSISTENT);


传送模式可以使用生产者(TopicPublisher或QueueSender)上的setJMSDeliveryMode()方法来设定;
TopicPublisher topicPubliser = topicSession.createPublisher(topic);
topicPublisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);


JMSMessageID:是一个String类型值,唯一地标识了一条消息;
message.getJMSMessageID();


JMSTimestamp:由MessageProducer在调用send()操作时自动设置的。它包含的是JMS提供者接收消息的时间,
而不是该消息实际传送时间。这条消息头用于确定发送消息和它被消费者实际接收的时间间隔。
    
JMSExpiration:一个message对象的有效期用来防止把过期的消息传送给消费者。使用setTimeToLive()在
生产者上设置:
TopicPublisher topicPubsher = topicSession.createPublisher(topic);
topicPublisher.setTimeToLive(3600000);
接下来,提供者将timeToLive值添加到系统时间戳中,并对JMSExpiration进行设置。


JMSRedelivered:表示该消息将被重新传送给消费者。如果该消息被重新传送,JMSRedelivered消息头就为true,
否则为false;如果一个消费者未能确认先前传送的消息,或者JMS提供者并不确定消费者是否已经接收到该消息时,
就可以将这条消息标记为重新发送:
boolean isRedelivered = message.getJMSRedelivered();


JMSPriority:在发送一条消息时,消息生产者能够为改消息分配一个优先级,0-4:普通优先级;
5-9:加急优先级;
TopicPublisher topicPublisher = TopicSession.createPublisher(someTopic);
topicPublisher.setPriority(9);


JMSReplyTo:有些情况下,一个JMS消息生产者可能会要求消费者对一条消息做出应答。JMSReplyTo消息头包含了一个
javax.jms.Destination,标明了JMS消费者应该应答的地址。在使用请求/应答场景时,通过这条消息头属性可以进一步
实现消息生产者和消费者之间的去耦。注意:如果某条消息头属性设置如下,JMS消费者就无须应答:
message.setJMSReplyTo(topic);
...
Topic topic = (Topic)message.getJMSReplyTo();


JMSCorrelationID:用于将当前的消息和先前的某些消息或应用程序特定的ID关联起来。在大多数情况下,
JMSCorrelationID用于将一条消息标记为对JMSMessageID标识的上一条消息的应答,不过,JMSCorrelationID
可以是任何值,而不仅仅是JMSMessageID;


JMSType:是由JMS客户端设置的一个可选消息头。它的主要作用是标识消息结构和有效负载的类型;
请注意,这个消息头并未指明正被发送的消息类型(MapMessage、TextMessage等),而是JMS提供者使用的
内部消息仓库中一个条目。一些MOM系统(比如MQ)将消息体现为连续字节流。这些系统通常为应用程序提供
了一种消息类型,作为标记消息体的一种简单方式。有些非JMS的客户端需要使用某种类型的消息来处理有效
负载,因此,在和这类非JMS客户端进行消息交换时,消息类型会非常有用;


8、消息属性:由JMS客户端定义和设置的属性;
消息的属性就像可以分配给一条消息的附加消息头一样,它们允许开发者添加有关消息的不透明附加信息。
它们还用于暴露消息选择器在消息过滤时使用的数据。message接口为读取和写入属性提供了若干个取值
函数和赋值函数方法。消息的属性可以是String、boolean、byte、double、int、long或float型;

9、消息属性有3种基本类型:应用程序特定的属性、JMS定义的属性和提供者特定的属性。
应用程序属性由应用程序开发者定义并应用到Message对象上;JMS扩展和提供者特定的属性大多由JMS
提供者自动添加的附加消息头;

应用程序特定的属性
由应用程序开发者定义的所有属性都可以作为一个应用程序特定的属性。应用程序属性在消息传送之前进行
设置。并不存在预先定义的应用程序属性,开发者可以自由定义能够满足它们需要的任何属性:
TextMessage message = pubSession.createTextMessage();
message.setText(text);
message.setStringProperty("userName", userName);
publisher.publish(message);
作为一个应用程序的特定属性,userName一旦离开chat应用程序就变得毫无意义;它专门用于应用程序根据发布者
身份对消息进行过滤;
属性值可以是boolean、byte、short、int、long、float、double、String类型;
Message方法:getStringProperty(String name)/setStringProperty(String name, String value);
getXXXProperty(String key)/setXXXProperty(String key, XXX xxx)等等;

一但一条消息发布或发送以后,它就变成了只读属性,消费者或生产者都无法修改它的属性。
不过,通过clearPropertyies()删除一条消息的所有属性,就可以改变属性了;

JMS定义的属性
JMS定义的属性具有和应用程序属性相同的特性,除了前者大多数在消息发送时由JMS提供者来设置之外。
JMS定义的属性可以作为可选的JMS消息头;对于某些另有声明的例外,各厂商可以分别选择不支持、
部分支持或全部支持。下面是JMS定义的9个属性清单:
JMSXUserID、JMSXAppID、JMSXProducerTXID、JMSXConsumerTXID、JMSXRcvTimestamp、JMSXDeliveryCount
JMSXState、JMSXGroupID、JMSXGroupSeq

提供者特定的属性
每个JMS提供者都可以定义一组私有属性,这些属性可以由客户端或提供者自动设置。提供者特定的属性必需以
前缀JMS开头,后面紧跟着属性名称,提供者特定的属性,其作用就是支持厂商的私有特性;


13、消息类型:Message、TextMessage、StreamMessage、MapMessage、ObjectMessage、ByteMessage;
在传送消息时,会将消息体设置为只读模式;
通过clearBody()清除消息体可以重新设置消息体;


客户端确认消息
在消费者已经选择CLIENT_ACKNOWLEDGE作为它的确认模式时,会用到在Message接口中定义的acknowledge()方法;
在创建JMS消费者的会话时,有3种确认模式可供JMS消费者设置,它们是:AUTO_ACKNOWLEDGE、DUPS_OK_ACKNOWLEDGE
和CLIENT_ACKNOWLEDGE
TopicSession topic = topicConnection.createTopicSession(false, Session.CLIENT_ACKNOWLEDGE);

public void onMessage(Message message){
message.acknowledge();
}


14、消息选择器
JMS消费者可以使用一种称为消息选择器的特殊过滤机制,基于特定的消息头和属性值来选择接收消息;
消息选择器使用消息属性和消息头作为条件表达式的准则。
消息选择器无法参考消息体内的数据;
在创建QueueReceiver、QueueBrower或TopicSubscriber时,可以将消息选择器应用于消息消费者;

15、消息选择器由三个元素组成:标识符、常量和比较运算符;
遵守sql-92规范:symbol = 'ABC' AND side = 'BUY' AND shares <= 1000.0 AND JMSPriority > 4;

可以用作标识符的JMS消息头包括:JMSDeliveryMode、JMSPriority、JMSMessageID、JMSTimestamp、JMSCorrelationID、JMSType;
JMSRedelivered在传送期间可以修改,因此不允许在选择器中使用;

运算符:算术比较运算符、like、between、in、not、is null运算符;

QueueBrowser queueBrowser = queueSession.createBrowser(Queue queue, String messageSelector);
QueueReceiver queueReceiver = queueSession.createReceiver(Queue queue, String messageSelector);
TopicSubscriber topicSubscriber = topicSession.createSubscriber(Topic topic, String messageSelector, boolean noLocal);
TopicSubscriber topicSubscriber = topicSession.createDurableSubscriber(Queue queue, String name, String messageSelector, boolean noLocal);//持久化消息
noLocal:消息生产者发布的消息是否应该传送给这个生产者本身;

queueReceiver、queueBrowser、topicSubscriber的getMessageSelector()方法获取选择器;
ex:
String filter = "(currentRate - newRate) >= 1.0";
TopicSubscriber subscriber = session.createSubscriber(topic, filter, true);


15、保证消息传送和事务
客户端运行时环境和消息服务器共同称为JMS提供者;


保证消息传送有3个主要部分:消息自主性、存储转发和底层消息确认语义;
消息是自包含的自主性实体,当设计分布式消息应用程序时,要将这个事实作为头条法则。

topicSession = topicConnection.createTopicSession(false, Session.CLIENT_ACKNOWLEDGE);
queueSession = queueSession.createQueueSession(false, Session.DUPS_OK_ACKNOWLEDGE);

AUTO_ACKNOWLEDGE:须返回消息确认;发送一次且仅仅一次;
DUPS_OK_ACKNOWLEDGE:JMS提供者可以将一条消息向同一目的地发送两次以上。
CLIENT_ACKNOWLEDGE:在AUTO_ACKNOWLEDGE模式中,确认总是隐式地在onMessage()处理器返回之后发生。
通过在消费会话上指定CLIENT_ACKNOWLEDGE模式,接收消息的客户端能够实现对"保证消息传送"更细粒度的控制;
应用程序使用CLIENT_ACKNOWLEDGE,可以控制何时发送确认。通过在onMessage中调用message.acknowledge()方法
实现的,可以处理其异常,而这个方法在AUTO_ACKNOWLEDGE或DUPS_OK_ACKNOWLEDGE模式中是无效被忽略的!

CLIENT_ACKNOWLEDGE模式还允许将多个消息收据成批组合在一起,并以“要么全有要么全无”的方式消费它们;

消息组
空有效消息起始-结束:
msg.setStringProperty("SequenceMarker", "START_SEQUENCE");
...
msg.setStringProperty("SequenceMarker", "END_SEQUENCE");
一旦完成处理以后,消息消费者就会使用message.acknowledge()方法来确认所有消息;


问题:一个监听器接收到一组消息的前几个消息,另一个监听器接收到后几个消息;
解决办法:msg.setStringProperty("JMSGroupID", "GROUP1");

16、事务性消息
1、生产者在会话上发布commit()之前,JMS提供者不会开始向它的消费者传送消息,即使它已经从发送者处接收到所有消息;
JMS的commit()和JTA的commit()类似,但并不同;

2、消费者正在接收一组事务性消息,要么全部接收,要么一条也不接收;

3、从事务性接收者的角度来看,这些消息会尽快地传送给接收者,但是它们一直由JMS提供者保存,直到接收者在会话对象
上发布commit()为止;如果发生了故障,或者发布rollback(),提供者会试图重新传送这些消息,在这种情况下,这些消息
就会设置重新传送标记;

17、创建事务性会话
TopicSession session = connec.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
QueueSession session = connect.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
session.commit();
session.rollback();

18、JMS提供了ExceptionListener接口,用于诱捕丢失的连接,并向客户端通知这个情况;
public interface ExceptionListener{
void onException(JMSException exception);
}

connection.setExceptionListener(this);

19、消息驱动bean
public interface MessageListener{
public void onMessage(Message message);
}

public interface MessageDrivenBean extends EnterpriseBean{
public void ejbCreate();
public void ejbRemove()throws EJBException;
public void setMessageDrivenContext(MessageDrivenContext ctx)throws EJBException;
}

20、helloWrold
点对点:
public class QBorrower{
private QueueConnection qConnect = null;
private QueueSession qSession = null
private Queue responseQ = null;
private Queue requestQ = null;

public QBorrower(String queuecf, String requestQueue, String responseQueue){
Context ctx = new InitialContext();
QueueConnectionFactory qFactory = (QueueConnectionFactory)ctx.lookup(queuecf);
qConnect = qFactory.createQueueConnection();
qSession = qConnect.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

requestQ = (Queue)ctx.lookup(requestQueue);
responseQ = (Queue)ctx.lookup(responseQueue);

qConnect.start();
}

private void sendLoanRequest(double salary, double loanAmt){
MapMessage msg = qSession.createMapMessage();
msg.setDouble("Salary", salary);
msg.setDouble("LoanAmount", loanAmt);
msg.setJMSReplyTo(responseQ);

QueueSender qSender = qSession.createSender(requestQ);
qSender.send(msg);

String filter = "JMSCorrelationID = " + "'" + msg.getJSMMessageID() + "'";
QueueReceiver qReceiver = qSession.createReceiver(responseQ, filter);
TextMessage tmsg = (textMessage)qReceiver.receive(30000);
if(tmsg == null){
System.out.println("QLender not responding");
}else{
System.out.println("loan request was " + tmsg.getText());
}
}

private void exit(){
qConnect.close();
}

public static void main(String[] args){
String queuecf = args[0];
String requestq = args[1];
String responseq = args[2];

QBorrower borrower = new QBorrower(queuecf, requestq, responseq);

double salary = 10;
double loanAmt = 10;
borrower.sendLoanRequest(salary, loanAmt);
}
}

public class QLender implements MessageListener{
private QueueConnection qConnect = null;
private QueueSession qSession = null;
private Queue requestQ = null;

public QLender(String queuecf, String requestQueue){
Context ctx = new InitialContext();
QueueConnectionFactory qFactory = (QueueConnectionFactory)ctx.lookup(queuecf);
qConnect = qFactory.createQueueConnection();
qSession = qConnect.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
requestQ = (Queue)ctx.lookup(requestQueue);

qConnect.start();

QueueReceiver qReceiver = qSession.createReceiver(requestQ);
qReceiver.setMessageListener(this);
}

public void onMessage(Message message){
boolean accepted = false;
MapMessage msg  = (MapMessage)message;
double salary = msg.getDouble("Salary");
double loanAmt = msg.getDouble("LoanAmount");
if(loanAmt < 2000){
accepted = (salary/loanAmt)>0.25;
}

TextMessage tmsg = qSession.createTextMessage();
tmsg.setText(accepted?"accepted":"declined");
tmsg.setJMSCorrelationID(message.getJMSMessageID());

QueueSender qSender = qSession.createSender((Queue)message.getJMSReplyTo());
qSender.send(tmsg);
}

private void exit(){
qConnect.close();
}

public static void main(String[] args){
String queuecf = args[0];
String requestq = args[1];

QLender lender = new QLender(queuecf, requestq);
lender.exit();
}
}

发布订阅:通过监听器监听返回的消息;
InitialContext ctx = new InitialContext();
TopicConnectionFactory conFactory = (TopicConnectionFactory)ctx.lookup(topicFactory);
TopicConnection connection = conFactory.createTopicConnection();

TopicSession pubSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
TopicSession subSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);

Topci chatTopic = (Topic)ctx.lookup(topicName);

TopicPublisher publisher = pubSession.createPublisher(chatTopic);
TopicSubscriber subscriber = subSession.createSubscriber(chatTopic, null, true);

public class chatListener implements javax.jms.MessageListener{
public void onMessage(Message message){
TextMessage textMessage = (TextMessage)message;
System.out.println(textMessage.getText());
}
}

public class chat{
public chat(TopicSession pubSession, TopicPublisher publisher, TopicSubscriber subscriber TopicConnection connection, MessageListener messageListener){
subscriber.setMessageListener(messageListener);
}

public void writeMessage(String text){
TextMessage message = pubSession.createTextMessage();
publisher.publish(message);
}

public void start(){
connection.start();
}

public void close(){
connection.close();
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Java通讯开发jms源代码 (jms通讯开发源码) java,net,socket,通讯开发,jms /* * @(#)Message.java 1.60 02/04/09 * * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. * * SUN PROPRIETARY/CONFIDENTIAL. * This software is the proprietary information of Sun Microsystems, Inc. * Use is subject to license terms. * */ import java.util.Enumeration; public interface Message { String getJMSMessageID() throws JMSException; void setJMSMessageID(String id) throws JMSException; long getJMSTimestamp() throws JMSException; void setJMSTimestamp(long timestamp) throws JMSException; byte [] getJMSCorrelationIDAsBytes() throws JMSException; void setJMSCorrelationIDAsBytes(byte[] correlationID) throws JMSException; String getJMSCorrelationID() throws JMSException; Destination getJMSReplyTo() throws JMSException; void setJMSReplyTo(Destination replyTo) throws JMSException; Destination getJMSDestination() throws JMSException; void setJMSDestination(Destination destination) throws JMSException; int getJMSDeliveryMode() throws JMSException; void setJMSDeliveryMode(int deliveryMode) throws JMSException; boolean getJMSRedelivered() throws JMSException; void setJMSRedelivered(boolean redelivered) throws JMSException; String getJMSType() throws JMSException; void setJMSType(String type) throws JMSException; long getJMSExpiration() throws JMSException; void setJMSExpiration(long expiration) throws JMSException; int getJMSPriority() throws JMSException; void setJMSPriority(int priority) throws JMSException; void clearProperties() throws JMSException; boolean propertyExists(String name) throws JMSException; boolean getBooleanProperty(String name) throws JMSException; byte getByteProperty(String name) throws JMSException; short getShortProperty(String name) throws JMSException; int getIntProperty(String name) throws JMSException; long getLongProperty(String name) throws JMSException; float getFloatProperty(String name) throws JMSException; double getDoubleProperty(String name) throws JMSException; String getStringProperty(String name) throws JMSException; Object getObjectProperty(String name) throws JMSException; Enumeration getPropertyNames() throws JMSException; void setBooleanProperty(String name, boolean value) throws JMSException; void setByteProperty(String name, byte value) throws JMSException; void setShortProperty(String name, short value) throws JMSException; void setIntProperty(String name, int value) throws JMSException; void setLongProperty(String name, long value) throws JMSException; void setFloatProperty(String name, float value) throws JMSException; void setDoubleProperty(String name, double value) throws JMSException; void setStringProperty(String name, String value) throws JMSException; void setObjectProperty(String name, Object value) throws JMSException; void acknowledge() throws JMSException; void clearBody() throws JMSException; } 通讯开发必备源码资料!
tonglinkq jms是一款基于电子商务平台的产品。它提供了一个集成的解决方案,旨在帮助企业快速搭建和运营自己的电子商务平台。 首先,tonglinkq jms具有强大的功能和灵活的配置选项。它包括了商品管理、订单管理、支付管理、物流管理等一系列完整的电子商务功能模块。用户可以根据自己的需求,灵活地配置和扩展这些功能,以适应不同的业务场景。 其次,tonglinkq jms提供了友好的用户界面和操作流程。用户可以通过直观的界面进行商品上架、订单处理、支付设置等操作,无需编写复杂的代码。同时,它还提供了丰富的数据统计和报表功能,帮助用户及时了解业务状况,并做出相应的决策。 另外,tonglinkq jms具有高性能和稳定的特点。它采用了现代化的技术架构和优化的数据库设计,保证了系统的高并发处理能力和稳定性。无论是在访问量较小时,还是在双十一等大促期间,tonglinkq jms都能够稳定运行,确保用户的正常使用体验。 最后,tonglinkq jms也提供了丰富的技术支持和售后服务。用户可以通过在线文档、视频教程和社区论坛等渠道,获取到解决问题的帮助和支持。而且,官方团队也会定期更新和升级系统,为用户提供更加稳定和强大的功能。 综上所述,tonglinkq jms是一款功能强大、操作简便、性能稳定的电子商务平台产品。无论是新兴企业还是传统实体店,都可以通过它来打造自己的线上销售渠道,提升经营效益。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值