热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

rabbitmq可靠性投递_Rabbitmq的使用五_消息的可靠性投递

Rabbitmq的使用五_消息的可靠性投递官网地址:https:www.rabbitmq.comtutorialstutorial-seven-java.html通过

Rabbitmq的使用五_消息的可靠性投递

官网地址:https://www.rabbitmq.com/tutorials/tutorial-seven-java.html

通过前面的学习,可以知道消息的可靠性投递,可以做持久化操作

过程一:从生产者发送消息到RabbitMQ服务器的过程。

过程二:确保消息从交换机路由到队列

过程三:确保消息在队列中正确的存储

过程四:确保消息从队列正确地投递到消费者

消息本身也要做持久化操作。

2.Rabbitmq的工作模型

执行流程:生产者发送消息通过channel和rabbitmq建立连接,发送一条携带路由关键字的消息到交换机上面的时候。交换机根据消息携带关键字,去查哪一些队列绑定了这些关键字,然后把消息路由到这些队列上面,然后消费者从队列中取消息就可以了

一个rabbitmq里面,可以建立多个不同的交换机和绑定队列,这时就用到我们的virtual host虚拟机。我们可以把每一个虚拟机当做一个rabbitmq的服务器。这样做解决了硬件资源的问题。因此,我们可以创建多个虚拟机,每一个虚拟机都可以看做是一个rabbitmq的服务器,可以根据此来创建很多的交换机和队列,以及定义之间的绑定关系,然后我们去创建一些用户,比如我们的资金系统,有资金系统的虚拟机,我们把资金系统的用户分配资金系统的权限就ok了。那么对应的其他系统,每一个系统都有自己的用户,自己的权限,那么不同的虚拟机之间是完全透明的。他们之间可以建立同名的交换机,通过这样的方式,我们可以实现硬件资源的高效利用和硬件资源的隔离。

生产者或者消费者和rabbitmq之间的连接是一个长连接。如果我们直接连接,频繁的创建连接,就会造成性能问题,我们引入一个channel的通道。这是一些虚拟的连接。我们需要连接rabbitmq的话,直接从这些虚拟的连接中拿一个连接就可以使用了。

exchange:本质是地址的清单,本身不存储消息。相当于一个路由功能

引入交换机的作用:是为了达到消息的一个灵活的投递

能够创建多少队列:这个主要取决于队列是保存在哪里的,如果队列是保存在内存里的,那么创建多少队列,取决于内存空间的大小,如果队列是保存在硬盘上的,取决于硬盘空间的大小

如何实现消息的灵活投递:交换机和队列之间的关系是多对多的关系

2.1如果过程一发生了异常怎么办?

从生产者发送消息到rabbitmq服务器的过程中,失败了。RabbitMQ给我们提供了两种方式:1.AMQP事务  2.Confirm模式

1.事务模式:每一个事务都要等待消息的一个应答,所以事务模式极其消耗性能的一个东西。因为极其消耗性能,所以在生产中一般也不会使用,因为,如果每一条消息,都开启事务,太消耗性能了。

事务模式的三个方法:channel.txSelect(); channel.txCommit(); channel.txRollback();

代码案例:

public classRabbitMqTransActionSender {//事务队列

private static final String TRANSTATION_QUEUE = "transaction_queue";public static void main(String[] args) throwsIOException, TimeoutException {//1.获取连接

Connection connection =RabbitMQConnectionFactory.getRabbitMQConnections();//2.获取通道

Channel channel =connection.createChannel();//3.声明队列

channel.queueDeclare(TRANSTATION_QUEUE, false, false, false, null);//4.发送消息

String msg = "小河流水哗啦啦,我和姐姐去采花1";//将channel设置为事务模式

try{

channel.txSelect();

channel.basicPublish("", TRANSTATION_QUEUE, null, msg.getBytes());int a = 10 / 0;

channel.txCommit();

System.out.println("消息已经提交");

}catch(Exception e) {

channel.txRollback();

System.out.println("消息已经回滚");

}

channel.close();

connection.close();

}

}

View Code

演示正常情况,发送者发送消息,消费者正常接收消息

消息发送过程中,出现异常。消息回滚掉,消费者不会接收到任何消息

图4生产者                                              图5消费者

2.确认模式

当我们发送消息成功之后,会有一个ack应答,只要我们的channel.waitForConfirms返回一个true,表示我们的消息就是发送成功的。

rabbitmq的消息确认,默认不启动了,需要开启

Channel channel =connection.createChannel();

channel.confirmSelect();

单条发送消息

当发送者发送消息报错时,消费者就不会受到消息

public classRabbitMqConfirmSender {private static final String CONFIRM_QUEUE = "confirm_queue";public static void main(String[] args) throwsIOException, TimeoutException, InterruptedException {//1.获取连接

Connection connection =RabbitMQConnectionFactory.getRabbitMQConnections();//2.获取通道

Channel channel =connection.createChannel();//3.声明队列

channel.queueDeclare(CONFIRM_QUEUE, false, false, false, null);//4.发送消息

String msg = "小河流水哗啦啦,我和姐姐去采花1";//将channel设置为confirm模式

channel.confirmSelect();

channel.basicPublish("", CONFIRM_QUEUE, null, msg.getBytes());if(channel.waitForConfirms()) {

System.out.println("消息发送成功");

}

channel.close();

connection.close();

}

}

View Code

我们在开始时提到,代理异步确认已发布的消息,代码会同步等待,直到消息确认为止。客户端实际异步接收确认,并解除调用阻塞,可以看作是一个在内部依赖于异步通知的同步助手。

单条消息的发送确认模式,效率也是很低的,每一条消息,先开启消息确认机制,然后发送,然后处理消息应答,效率太低。

批量发送消息

public classRabbitMqConfirmBatchSender {private static final String BATCH_CONFIRM_QUEUE = "batch_confirm_queue";public static void main(String[] args) throwsIOException, TimeoutException, InterruptedException {//1.获取连接

Connection connection =RabbitMQConnectionFactory.getRabbitMQConnections();//2.获取通道

Channel channel =connection.createChannel();//3.声明队列

channel.queueDeclare(BATCH_CONFIRM_QUEUE, false, false, false, null);//4.发送消息//将channel设置为confirm模式

channel.confirmSelect();

String msg&#61; "小河流水哗啦啦,我和姐姐去采花";for (int i &#61; 1; i <&#61; 100; i&#43;&#43;) {//int a &#61; 19/0;

channel.basicPublish("", BATCH_CONFIRM_QUEUE, null, (msg &#43;i).getBytes());

}if(channel.waitForConfirms()) {

System.out.println("消息发送成功");

}

channel.close();

connection.close();

}

}

View Code

当批量发送异常的时候&#xff0c;消费者不会收到任何消息,如下图所示&#xff1a;

这种批量发送消息的方式&#xff0c;只要有一条消息未被broker确认&#xff0c;就会发生异常,也就是说当我们channel.waitFroConfirms;只要不抛出异常&#xff0c;就可以认为我们的消息发送成功了

但是:这种方式也有缺点&#xff1a;

1.我们是积累多少条消息进行消息的发送

2.我们假设是1000条发送消息一次&#xff0c;如果前999条发送失败&#xff0c;刚好第1000条发送失败了&#xff0c;怎么办

Rabbitmq官网还给我们提供了一种方式&#xff0c;是采用异步的方式&#xff0c;进行消息的收发&#xff0c;使用异步的方式&#xff0c;是可以进行一边发送&#xff0c;一边确认的方式&#xff0c;进行消息的收发的&#xff0c;异步的情况&#xff0c;消息不会自动重发的

Broker异步确认&#xff1a;只需要在发送者客户端注册一个异步回调&#xff0c;就可以接收到确认消息

代码如下:

public classRabbitMqAsyncConfirmSender {private static final String ASYNC_CONFIRM_QUEUE &#61; "async_confirm_queue";public static void main(String[] args) throwsIOException, TimeoutException, InterruptedException {//1.获取连接

Connection connection &#61;RabbitMQConnectionFactory.getRabbitMQConnections();//2.获取通道

Channel channel &#61;connection.createChannel();//3.声明队列

channel.queueDeclare(ASYNC_CONFIRM_QUEUE, false, false, false, null);//4.发送消息

String msg &#61; "小河流水哗啦啦,我和姐姐去采花";//将channel设置为confirm模式

channel.confirmSelect();//发送消息

for (int i &#61; 1; i <&#61; 20; i&#43;&#43;) {//if (i&#61;&#61;17){//int a &#61; 10/0;//}

channel.basicPublish("", ASYNC_CONFIRM_QUEUE, null, (msg &#43; "&#61;&#61;&#61;&#61;&#61;&#61;>" &#43;i).getBytes());

}

channel.addConfirmListener(newConfirmListener() {

&#64;Overridepublic void handleAck(long deliveryTag, boolean multiple) throwsIOException {

System.out.print("已确认的消息,标识&#xff1a;" &#43;deliveryTag);

System.out.println("多个消息&#xff1a; " &#43;multiple);

}

&#64;Overridepublic void handleNack(long deliveryTag, boolean multiple) throwsIOException {

System.out.println("Broker 未确认消息,标识: " &#43;deliveryTag);

}

});

System.out.println("程序执行完成");

}

}

View Code

执行结果如下&#xff1a;

在某些应用程序中&#xff0c;确保已发布的消息到达broker可能非常重要。发布者确认是RabbitMQ的一个特性&#xff0c;有助于满足这一需求。发布者确认在本质上是异步的&#xff0c;但是也可以同步地处理它们。

1.单条发布消息&#xff0c;同步等待确认:简单&#xff0c;但吞吐量非常有限。

2.批量发布消息&#xff0c;同步等待批处理的确认:简单、合理的吞吐量&#xff0c;但是很难判断什么时候出现了错误

3.异步处理:最佳的性能和资源的使用&#xff0c;在错误的情况下良好的控制。

2.2 过程二发送失败了怎么办?

就是交换机路由消息到队列的过程中发送失败了。

解决办法&#xff1a;可以给当前交换机设置备份交换机

交换机本身也是支持持久化操作的。在声明交换机的时候&#xff0c;参数三表示是否持久化

/*** Actively declare a non-autodelete exchange with no extra arguments

*&#64;seecom.rabbitmq.client.AMQP.Exchange.Declare

*&#64;seecom.rabbitmq.client.AMQP.Exchange.DeclareOk

*&#64;paramexchange the name of the exchange

*&#64;paramtype the exchange type

*&#64;paramdurable true if we are declaring a durable exchange (the exchange will survive a server restart)

*&#64;throwsjava.io.IOException if an error is encountered

*&#64;returna declaration-confirm method to indicate the exchange was successfully declared*/Exchange.DeclareOk exchangeDeclare(String exchange, String type,boolean durable) throws IOException;

2.3 过程三失败了怎么办&#xff1f;

当我们没有对消息进行配置的时候&#xff0c;默认是保存在内存中的&#xff0c;消息保存在内存中&#xff0c;不可避免的会出现rabbitmq服务器的重启&#xff0c;宕机等问题&#xff0c;所以&#xff0c;可以对消息做一个消息的持久化的处理。

发送消息的时候&#xff0c;可以给消息设置一个properties的属性&#xff0c;通过配置properties可以配置消息的一个持久化操作。

队列可以做持久化操作、消息也可以做持久化操作。

2.4过程四失败了怎么办&#xff1f;

确保消息从队列正确地投递到消费者ack应答

消费者接收到消息的时候&#xff0c;就会给broker一个应答&#xff0c;broker拿到应答之后&#xff0c;就会从队列中删除这条消息。而不是在方法执行完之后&#xff0c;再给服务器的应答。我们可以手动的执行一个ack应答机制。

2.5 如果消费者处理消息的时候&#xff0c;抛出异常了&#xff0c;生产者怎么知道&#xff1f;

1.消费者回调

1) 发送回执(表示每发送一条消息&#xff0c;都给生产者一条消息回执)

2)生产者提供api(生产者发送消息的时候&#xff0c;保存一条消息入库)&#xff0c;消费者调用暴露的api&#xff0c;去修改这条消息的状态。如果我们消息的状态没有发生变更的话&#xff0c;那么我就可以判断有可能消费者在处理消息的时候&#xff0c;发生了问题

2.补偿机制

如果没有发送回执&#xff1a;可能是因为网络问题

如果没有调用api:可能消费者调用生产者的过程中出现了问题

在以上的情况都没有使用的情况下面&#xff0c;我们就需要使用一个补偿机制&#xff0c;比如

1)消息的重发&#xff1a;

重发的前提:我们发送前需要把数据保存到数据库中&#xff0c;然后重发的时候&#xff0c;直接从数据库中进行消息的获取&#xff0c;然后重新进行一个消息的发送。但是如果发送5次&#xff0c;10次&#xff0c;如果消费者一直没有应答。。。就会一直重发。所以这里一定要做一个次数的控制&#xff0c;等达到一定的次数之后&#xff0c;我们就不进行重发操作了&#xff0c;我们会在夜间进行一个对账的操作。

可能发送消息的时候&#xff0c;就是消费成功了&#xff0c;但是由于网络原因&#xff0c;回执执行慢了&#xff0c;

3.消息的幂等性

1)处理1次消息&#xff0c;跟处理10 次消息的结果都是一样的

2) 消息必须有一个唯一性的标志&#xff1a;在金融系统中&#xff0c;任何一笔交易&#xff0c;都会有一个全局流水号的标志, message发送消息的时候&#xff0c;有一个消息的id&#xff0c;消息id&#43;业务id唯一判断 (重帐控制)消费会不会重复消费

3.如果保证消息的顺序消费

多个生产者和多个消费者的情况&#xff0c;基本是没有办法实现一个消息的顺序消费的&#xff0c;比如发送了3条消息&#xff0c;消费者的消费速率是不一样&#xff0c;所以我们无法保证哪一个消息是先消费完成的。所以我们完成消费的顺序性&#xff0c;如果我们只有一个生产者&#xff0c;和一个消费者&#xff0c;根据队列先进先出的思想&#xff0c;我们是可以保证消息的一个顺序消费的。对于每一组消息&#xff0c;我们都有一个parentID(批次号),也有一个seqNo&#xff0c;如果上一个批次的消息没有消费完&#xff0c;就不能消费下一个消息的

int sequenceNumber &#61;channel.getNextPublishSeqNo());

ch.basicPublish(exchange, queue, properties, body);

当然如果要保证消息的一个顺序性的消费&#xff0c;我们可以使用rocketmq的顺序消费&#xff0c;他本身就是支持的

4.springboot整合rabbitmq实现事务模式

在SpringBoot项目中&#xff0c;使用RabbitMQ事务&#xff0c;只需要声明一个事务管理的Bean&#xff0c;并将RabbitTemplate的事务设置为true即可

消费者方

1.消费者添加配置文件

server:

port: 8010

spring:

rabbitmq:

host: ip地址

username: yingxiaocao

password: yingxiaocao

virtual-host: /yingxiaocao

listener:

type: simple

simple: # 开启手动应答

acknowledge-mode: manual

配置mq地址

2.声明队列、交换、绑定关系、事务bean(消费者和生产者都要配置。否则当生产者启动&#xff0c;消费者未启动时&#xff0c;发送消息&#xff0c;就会报错)

&#64;PropertySource("classpath:transaction_mq.properties")

&#64;Configurationpublic classConsumerConfig {

&#64;Value("${TRANSACTION_EXCHANGE_NAME}")privateString exchangeName;

&#64;Value("${FIRST_QUEUE}")privateString firstQueue;//1.声明一个交换机

&#64;Bean("fanout_exchange")publicFanoutExchange getFanoutExchange() {

FanoutExchange fanoutExchange&#61; newFanoutExchange(exchangeName);returnfanoutExchange;

}//2.声明2个队列

&#64;Bean("first_queue")publicQueue getFirstQueue() {

Queue queue&#61; newQueue(firstQueue);returnqueue;

}//3.绑定关系

&#64;Beanpublic Binding bindingExchange(&#64;Qualifier("fanout_exchange") FanoutExchange fanoutExchange, &#64;Qualifier("first_queue") Queue queue) {returnBindingBuilder.bind(queue).to(fanoutExchange);

}

&#64;BeanpublicRabbitTransactionManager rabbitTransactionManager(CachingConnectionFactory cachingConnectionFactory) {return newRabbitTransactionManager(cachingConnectionFactory);

}

声明队列、交换机

3.创建一个消费者

&#64;Component

&#64;PropertySource("classpath:transaction_mq.properties")public classRabbitmqConsumer {

&#64;RabbitListener(queues&#61; {"${FIRST_QUEUE}"})public void receive(Message message, Channel channel) throwsIOException {

String msg&#61; newString(message.getBody());

System.out.println("接收到的消息: " &#43;msg);

channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

}

}

消费者

4.创建一个启动类

&#64;SpringBootApplicationpublic classTransactionConsumerStartApp {public static voidmain(String[] args) {

SpringApplication.run(TransactionConsumerStartApp.class, args);

}

}

消费者启动类

生产者方

1.创建一个controller

&#64;RequestMapping("/producer")

&#64;RestControllerpublic classProducerController {

&#64;AutowiredprivateProducerService producerService;

&#64;RequestMapping("/send")public voidsend(String msg) {

producerService.send(msg);

}

}

controller

2.创建一个service,用来发送消息

&#64;PropertySource("classpath:transaction_mq.properties")

&#64;Servicepublic classProducerService {

&#64;Value("${TRANSACTION_EXCHANGE_NAME}")privateString exchangeName;

&#64;AutowiredprivateRabbitTemplate rabbitTemplate;

&#64;PostConstructpublic voidinit() {//创建对象的同时&#xff0c;开启channel事务模式

rabbitTemplate.setChannelTransacted(true);

}/*** 发送消息

*&#64;parammsg*/&#64;Transactionalpublic voidsend(String msg) {

rabbitTemplate.convertAndSend(exchangeName,"",msg);

System.out.println("消息已发送: "&#43;msg);if (msg.equals("xxx")) {throw new RuntimeException("抛出异常了");

}

}/*** 配置启用rabbitmq事务

*&#64;paramconnectionFactory

*&#64;return

*/&#64;BeanpublicRabbitTransactionManager rabbitTransactionManager(CachingConnectionFactory connectionFactory) {return newRabbitTransactionManager(connectionFactory);

}

}

service发送消息

3.创建一个启动类

&#64;SpringBootApplicationpublic classTransactionProducerStartApp {public static voidmain(String[] args) {

SpringApplication.run(TransactionProducerStartApp.class, args);

}

}

View Code

4.声明配置

&#64;PropertySource("classpath:transaction_mq.properties")

&#64;Componentpublic classRabbitConfig {

&#64;Value("${TRANSACTION_EXCHANGE_NAME}")privateString exchangeName;

&#64;Value("${FIRST_QUEUE}")privateString firstQueue;//1.声明一个交换机

&#64;Bean("fanout_exchange")publicFanoutExchange getFanoutExchange() {

FanoutExchange fanoutExchange&#61; newFanoutExchange(exchangeName);returnfanoutExchange;

}//2.声明1个队列

&#64;Bean("first_queue")publicQueue getFirstQueue() {

Queue queue&#61; newQueue(firstQueue);returnqueue;

}//3.绑定关系

&#64;Beanpublic Binding bindingExchange(&#64;Qualifier("fanout_exchange") FanoutExchange fanoutExchange, &#64;Qualifier("first_queue") Queue queue) {returnBindingBuilder.bind(queue).to(fanoutExchange);

}

&#64;BeanpublicRabbitTransactionManager rabbitTransactionManager(CachingConnectionFactory cachingConnectionFactory) {return newRabbitTransactionManager(cachingConnectionFactory);

}

}

声明队列、交换机、绑定关系

执行结果&#xff1a;

正常情况&#xff0c;生产者发送消息&#xff0c;消费者收到消费

异常情况&#xff1a;生产者发送消息后&#xff0c;抛出异常了&#xff0c;消费者并没有收到消息&#xff0c;可见消息回滚了

但是事务模式效率太差。

5.springboot整合rabbitmq实现消息确认模式

通过生产者确认机制&#xff0c;生产者可以在消息被服务器成功接收时得到反馈&#xff0c;并有机会处理未被成功接收的消息。

在Springboot中开启RabbitMQ的生产者确认模式也很简单&#xff0c;只多了一行配置&#xff1a;publisher-confirms: true即表示开启生产者确认模式。

server:

port: 9010

spring:

rabbitmq:

username: yingxiaocao

password: yingxiaocao

host: 地址

virtual-host: /yingxiaocao

listener:

type: simple

publisher-confirms: true # 开启消息确认模式

View Code

改变生产者代码

&#64;PropertySource("classpath:confirm_mq.properties")

&#64;Service

&#64;Slf4jpublic class ProducerService implementsRabbitTemplate.ConfirmCallback {

&#64;Value("${CONFRIM_EXCHANGE_NAME}")privateString exchangeName;

&#64;AutowiredprivateRabbitTemplate rabbitTemplate;

&#64;PostConstructpublic voidinit() {//创建对象的同时&#xff0c;开启channel事务模式//rabbitTemplate.setChannelTransacted(true);//开启确认模式

rabbitTemplate.setConfirmCallback(this);

}/*** 发送消息

*

*&#64;parammsg*/

public voidsend(String msg) {//创建一个消息编号

CorrelationData correlationData &#61; newCorrelationData(UUID.randomUUID().toString());

rabbitTemplate.convertAndSend(exchangeName,"aaa", msg, correlationData);

log.info("消息id{},路由key{},发送消息内容{}", correlationData.getId(), "aaa", msg);//创建一个无法投递成功的消息

CorrelationData correlationData1 &#61; newCorrelationData(UUID.randomUUID().toString());

rabbitTemplate.convertAndSend(exchangeName,"bbb", msg, correlationData1);

log.info("消息id{},路由key{},发送消息内容{}", correlationData1.getId(), "bbb", msg);

}/*** 消息回调

*

*&#64;paramcorrelationData

*&#64;paramack

*&#64;paramcause*/&#64;Overridepublic void confirm(CorrelationData correlationData, booleanack, String cause) {

String id&#61; correlationData !&#61; null ? correlationData.getId() : "";if(ack) {

log.info("消息投递成功,消息id{}", id);

}else{

log.info("消息投递失败,消息id{},原因:{}", id, cause);

}

}

}

View Code

执行代码结果如下&#xff1a;

生产者截图

消费者截图。

由运行结果可知。消息只要发送到了交换机&#xff0c;不管消息有没有成功投递到队列里面&#xff0c;都会给生产者一个ack应答。

如何让消息被路由到队列后再返回ACK呢&#xff1f;

1.设置mandatory参数

设置 mandatory 参数可以在当消息传递过程中不可达目的地时将消息返回给生产者。

当把 mandotory 参数设置为 true 时&#xff0c;如果交换机无法将消息进行路由时&#xff0c;会将该消息返回给生产者&#xff0c;而如果该参数设置为false&#xff0c;如果发现消息无法进行路由&#xff0c;则直接丢弃。

rabbitTemplate.setMandatory(true);

如果要让消息返回给生产者&#xff0c;需要添加一个回调

为了进行回调&#xff0c;我们需要实现一个接口 RabbitTemplate.ReturnCallback

使用mandatory这种方式&#xff0c;如果消息发送失败&#xff0c;返回给生产者&#xff0c;通过看日志的方式&#xff0c;就比较麻烦了。可以使用备份交换机的方式。如果消息从交换机路由到队列失败&#xff0c;转发给备份交换机&#xff0c;由备份交换机绑定的队列&#xff0c;进行处理。就比较好了

2.设置备份交换机

创建交换机的时候&#xff0c;为交换机添加备份交换机代码&#xff0c;可为备份交换机添加不同的队列&#xff0c;实现不同的功能

.withArgument("alternate-exchange",BUSINESS_BACKUP_EXCHANGE_NAME);



推荐阅读
  • 本文提供了 RabbitMQ 3.7 的快速上手指南,详细介绍了环境搭建、生产者和消费者的配置与使用。通过官方教程的指引,读者可以轻松完成初步测试和实践,快速掌握 RabbitMQ 的核心功能和基本操作。 ... [详细]
  • JVM参数设置与命令行工具详解
    JVM参数配置与命令行工具的深入解析旨在优化系统性能,通过合理设置JVM参数,确保在高吞吐量的前提下,有效减少垃圾回收(GC)的频率,进而降低系统停顿时间,提升服务的稳定性和响应速度。此外,本文还将详细介绍常用的JVM命令行工具,帮助开发者更好地监控和调优JVM运行状态。 ... [详细]
  • 使用 MyEclipse 和 TestNG 测试框架在 Java 中高效进行单元测试
    通过MyEclipse集成TestNG测试框架,可以在Java开发中高效地进行单元测试。本文介绍了在JDK 1.8.0_121和MyEclipse 10.0离线环境下配置和使用TestNG的具体步骤,帮助开发者提高测试效率和代码质量。 ... [详细]
  • Java 中优先级队列的轮询方法详解与应用 ... [详细]
  • 本题库精选了Java核心知识点的练习题,旨在帮助学习者巩固和检验对Java理论基础的掌握。其中,选择题部分涵盖了访问控制权限等关键概念,例如,Java语言中仅允许子类或同一包内的类访问的访问权限为protected。此外,题库还包括其他重要知识点,如异常处理、多线程、集合框架等,全面覆盖Java编程的核心内容。 ... [详细]
  • 如何在Java中高效构建WebService
    本文介绍了如何利用XFire框架在Java中高效构建WebService。XFire是一个轻量级、高性能的Java SOAP框架,能够简化WebService的开发流程。通过结合MyEclipse集成开发环境,开发者可以更便捷地进行项目配置和代码编写,从而提高开发效率。此外,文章还详细探讨了XFire的关键特性和最佳实践,为读者提供了实用的参考。 ... [详细]
  • Java 8 引入了 Stream API,这一新特性极大地增强了集合数据的处理能力。通过 Stream API,开发者可以更加高效、简洁地进行集合数据的遍历、过滤和转换操作。本文将详细解析 Stream API 的核心概念和常见用法,帮助读者更好地理解和应用这一强大的工具。 ... [详细]
  • 计算 n 叉树中各节点子树的叶节点数量分析 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • 本文深入探讨了 MXOTDLL.dll 在 C# 环境中的应用与优化策略。针对近期公司从某生物技术供应商采购的指纹识别设备,该设备提供的 DLL 文件是用 C 语言编写的。为了更好地集成到现有的 C# 系统中,我们对原生的 C 语言 DLL 进行了封装,并利用 C# 的互操作性功能实现了高效调用。此外,文章还详细分析了在实际应用中可能遇到的性能瓶颈,并提出了一系列优化措施,以确保系统的稳定性和高效运行。 ... [详细]
  • 本文介绍了一种基于最大匹配算法的简易分词程序的设计与实现。该程序通过引入哈希集合存储词典,利用前向最大匹配方法对输入文本进行高效分词处理,具有较高的准确率和较快的处理速度,适用于中文文本的快速分词需求。 ... [详细]
  • 如何在 Java LinkedHashMap 中高效地提取首个或末尾的键值对? ... [详细]
  • 在探讨 AS3 中的数据深度复制技术时,本文详细介绍了实现数据深度克隆的有效方法。通过对比多种方案,最终确定了一种高效且可靠的实现方式,所有代码均来源于公开资源,确保了方法的实用性和可操作性。 ... [详细]
  • 在使用群报数小程序进行高效接龙与统计时,可以通过创建 `LinkedList` 对象并利用 `for` 循环生成指定数量的 `Person` 对象,为每个人员分配唯一的编号,并将其添加到 `LinkedList` 集合中。这一过程确保了数据的有序性和高效管理,便于后续的接龙和统计操作。此外,该小程序还支持实时更新和查看参与人员的状态,进一步提升了活动组织的便利性和准确性。 ... [详细]
  • 在Java编程中,初学者常会遇到“无法从静态上下文中引用非静态变量this”的编译错误。此问题根源在于对静态(static)与非静态成员特性的理解不足。静态成员属于类本身,而非特定对象实例,因此在静态方法或静态初始化块中直接访问非静态成员会导致编译失败。解决这一问题的关键是将相关代码移至非静态方法中,或通过创建类的实例来间接访问非静态成员。 ... [详细]
author-avatar
C1_VISION
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有