我有订阅者和发布者订阅者收集邮件,一旦达到指定的邮件收集数量,它将控制权传递给发布者 我需要将消息发布到其他队列,成功发布每个消息后,我需要手动确认已订阅的队列以删除消息。 我已经使用过return回调并在Rabbit模板上确认了回调,但是在成功发布消息时未调用回调函数。
我想问题是发布者和消费者都使用相同的连接,这就是为什么不调用callback的原因,但我不确定。
@SpringBootApplication
@ComponentScan(basePackages={"com.comp.dftp.scrubber.configuration","com.comp.dftp.scrubber.container",
"com.comp.dftp.scrubber.subscriber","com.comp.dftp.scrubber.publisher"})
@EnableAutoConfiguration(exclude=RabbitAutoConfiguration.class)
@EnableRabbit
public class DftpEppScrubberApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(DftpEppScrubberApplication.class);
RabbitListenerEndpointRegistrar registrar;
AppConfig appConfig;
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DftpEppScrubberApplication.class);
ConfigurableApplicationContext appContext = app.run(args);
}
@PreDestroy
public void graceFullExit() {
LOGGER.info("----------- Stopping Scrubber Container--------------");
if(registrar!= null && registrar.getEndpointRegistry() != null ) {
registrar.getEndpointRegistry().stop();
}
LOGGER.info("Container Stopped Sucessfully!");
/*
* If Application is not ready to exit safely we will weight for 30 sec
* and re-check again, This will be continued until the flag ReadyToGraccefullyExit
* is set true be process which has not completed Yet, Once publisher completed the process
* and reader ack back to publisher.
*/
while(!appConfig.isReadyToGraccefullyExit()) {
try{
Thread.sleep(30 * 1000);
appConfig.setReadyToGraccefullyExit(true); /* this is just for testing*/
} catch (InterruptedException e) {
LOGGER.error("Some error in gracefully exiting the application!", e);
}
}
LOGGER.info("###STOP FROM THE LIFECYCLE###");
}
}
```java
@Component
@Configuration
public class EPPQ2ListenerConfigurer implements RabbitListenerConfigurer{
public EPPQ2ListenerConfigurer(ConfigurableApplicationContext ctx) {
// TODO Auto-generated constructor stub
}
@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory());
}
@Bean
MessageHandlerMethodFactory messageHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
messageHandlerMethodFactory.setMessageConverter(consumerJackson2MessageConverter());
return messageHandlerMethodFactory;
}
@Bean
public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
return new MappingJackson2MessageConverter();
}
}
```java
@Configuration
public class ListenerContainerFactory {
static final Logger logger = LoggerFactory.getLogger(ListenerContainerFactory.class);
@Autowired
RabbitMqConfig rabbitMqConfig;
@Autowired
EPPQ2Subscriber receiver;
public ListenerContainerFactory(ConfigurableApplicationContext ctx) {
printContainerStartMsg();
}
private void printContainerStartMsg() {
logger.info("----------- Scrubber Container Starts --------------");
}
@Bean
public CachingConnectionFactory subscriberConnectionFactory() {
CachingConnectionFactory subsCachingConnectionFactory = new CachingConnectionFactory(rabbitMqConfig.getSubscriberHosts(),
rabbitMqConfig.getSubscriberPort());
subsCachingConnectionFactory.setUsername(rabbitMqConfig.getSubscriberUsername());
subsCachingConnectionFactory.setPassword(rabbitMqConfig.getSubscriberPassword());
subsCachingConnectionFactory.setVirtualHost("hydra.services");
subsCachingConnectionFactory.setConnectionNameStrategy(f -> "subscriberConnection");
return subsCachingConnectionFactory;
}
@Bean
public SimpleRabbitListenerContainerFactory queueListenerContainer(
@Qualifier("subscriberConnectionFactory") CachingConnectionFactory subscriberConnectionFactory,
MessageListenerAdapter listenerAdapter) {
connectionFactory.setAddresses(rabbitMqConfig.getSubscriberHosts());
connectionFactory.setVirtualHost("hydra.services");
connectionFactory.setPort(rabbitMqConfig.getSubscriberPort());
connectionFactory.setUsername(rabbitMqConfig.getSubscriberUsername());
connectionFactory.setPassword(rabbitMqConfig.getSubscriberPassword());
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
factory.setConnectionFactory(subscriberConnectionFactory);
factory.setErrorHandler(errorHandler());
return factory;
}
@Bean
MessageListenerAdapter listenerAdapter(EPPQ2Subscriber receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
@Bean
public ErrorHandler errorHandler() {
return new ConditionalRejectingErrorHandler(fatalExceptionStrategy());
}
@Bean
public ScrubberFatalExceptionStrategy fatalExceptionStrategy() {
return new ScrubberFatalExceptionStrategy();
}
}
```java
@Component
public class EPPQ2Subscriber {
private static final Logger LOGGER = LoggerFactory.getLogger(EPPQ2Subscriber.class);
//@RabbitListener(queues = "#{queue.getName()}") @TODO I wann to use this in later point in time.. !
@Autowired
RabbitMqConfig rabbitMqConfig;
@Autowired
AppConfig appConfig;
@Autowired
EPPQ2PublisherImpl eppQ2Publisher;
List<Message> messageList = new ArrayList<Message>();
List<Long> deliveryTagList = new ArrayList<Long>();
/**
* Method is listener's receive message method , invoked when there is message ready to read
* @param message - Domain object of message encapsulated
* @param channel - rabitmq client channel
* @param messageId - @TODO Delete it later.
* @param messageProperties - amqp message properties contains message properties such as delivery tag etc..
*/
@RabbitListener(id="messageListener",queues = "#{rabbitMqConfig.getSubscriberQueueName()}",containerFactory="queueListenerContainer")
public void receiveMessage(Message message, Channel channel, @Header("id") String messageId,
MessageProperties messageProperties) {
LOGGER.info("Result:" + message.getClass() + ":" + message.toString());
if(messageList.size() < appConfig.getSubscriberChunkSize() ) {
messageList.add(message);
LOGGER.info("For Test Size:"+messageList.size()+ "chunk size : "+appConfig.getSubscriberChunkSize());
deliveryTagList.add(messageProperties.getDeliveryTag());
} else {
// call the service here to decrypt, read pan, call danger to scrub, encrypt pan and re-pack them in message again.
//after this branch messageList should have scrubbed and encrypted message objects ready to publish.
// Here is call for publish and ack messages..
eppQ2Publisher.sendMessages(messageList, channel, deliveryTagList);
}
}
}
````java
@Configuration
public class TopicConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(TopicConfiguration.class);
@Autowired
RabbitMqConfig rabbitMqConfig;
@Autowired
EPPQ2Publisher eppQ2Publisher;
/**
* Caching connection factory
* @return CachingConnectionFactory
*/
@Bean
public CachingConnectionFactory cachingConnectionFactory() {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(rabbitMqConfig.getPublisherHosts(),
rabbitMqConfig.getPublisherPort());
cachingConnectionFactory.setUsername(rabbitMqConfig.getPublisherUsername());
cachingConnectionFactory.setPassword(rabbitMqConfig.getPublisherPassword());
cachingConnectionFactory.setVirtualHost("hydra.services");
cachingConnectionFactory.createConnection();
cachingConnectionFactory.setConnectionNameStrategy(f -> "publisherConnection");
return cachingConnectionFactory;
}
/**
* Bean RabbitTemplate
* @return RabbitTemplate
*/
@Bean
public RabbitTemplate template(
@Qualifier("cachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
RetryTemplate retryTemplate = new RetryTemplate();
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(500);
backOffPolicy.setMultiplier(10.0);
backOffPolicy.setMaxInterval(10000);
retryTemplate.setBackOffPolicy(backOffPolicy);
rabbitTemplate.setRetryTemplate(retryTemplate);
rabbitTemplate.setExchange(rabbitMqConfig.getPublisherTopic());
rabbitTemplate.setUsePublisherConnection(true);
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback((correlation, ack, reason) -> {
if (correlation != null) {
LOGGER.info("Received " + (ack ? " ack " : " nack ") + "for correlation: " + correlation);
if (ack) {
// this is confirmation received..
// here is code to ack Q1. correlation.getId() and ack it !!
eppQ2Publisher.ackMessage(new Long(correlation.getId().toString()));
} else {
// no confirmation received and no need to do any thing for
// retry..
}
}
});
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
LOGGER.error("Returned: " + message + "\nreplyCode: " + replyCode + "\nreplyText: " + replyText
+ "\nexchange/rk: " + exchange + "/" + routingKey);
});
return rabbitTemplate;
}
/**
* Bean Jackson2JsonMessageConverter
* @return Jackson2JsonMessageConverter
*/
@Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
Here is some logs
----------
33morg.springframework.amqp.rabbit.listener.BlockingQueueConsumer[0;39m: Received message: (Body:'[B@1c63ef5(byte[2003])' MessageProperties [headers={}, contentLength=0, receivedDeliveryMode=NON_PERSISTENT, redelivered=true, receivedExchange=, receivedRoutingKey=hydra.Syphon.q1, deliveryTag=11, consumerTag=amq.ctag-VLCSea_a-FmED6I54TyM4w, consumerQueue=hydra.Syphon.q1])
SimpleAsyncTaskExecutor-1 org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter[0;39m: Processing [GenericMessage [payload=byte[2003], headers={amqp_receivedDeliveryMode=NON_PERSISTENT, amqp_receivedRoutingKey=hydra.Syphon.q1, amqp_deliveryTag=11, amqp_consumerQueue=hydra.Syphon.q1, amqp_redelivered=true, id=2a24ec32-2576-a208-4cbf-f36e6170ac83, amqp_consumerTag=amq.ctag-VLCSea_a-FmED6I54TyM4w, timestamp=1554262675707}]]
SimpleAsyncTaskExecutor-1 com.discover.dftp.scrubber.subscriber.EPPQ2Subscriber[0;39m: Result:class com.discover.dftp.scrubber.domain.Message:Header [header={RETRY_COUNT=0, PUBLISH_EVENT_TYPE=AUTH}, payLoad={MTI=400, MTI_REQUEST=400, PAN=6011000000000000, PROCCODE=00, PROCCODE_REQUEST=00, FROM_ACCOUNT=00, TO_ACCOUNT=00, TRANSACTION_AMOUNT=000000000100, TRANSMISSION_MMDDHHMMSS=0518202930, STAN=000001, LOCALTIME_HHMMSS=010054, LOCALDATE_YYMMDD=180522, MERCHANT_TYPE=5311, ACQUIRING_COUNTRY_CODE=840, POS_ENTRY_MODE=02, POS_PIN_ENTRY_CAPABILITIES=0, FUNCTION_CODE=400, MESSAGE_REASON_CODE=06, ACQUIRING_ID_CODE=000000, FORWARDING_ID_CODE=000000, RETRIEVAL_REFERENCE_NUMBER=1646N472D597, APPROVAL_CODE=12345R, RESPONSE_CODE=00, MERCHANT_NUMBER=601100000000596, CARD_ACCEPTOR_NAME=Discover Acq Simulator, CARD_ACCEPTOR_CITY=Riverwoods, CARD_ACCEPTOR_STATE=IL, CARD_ACCEPTOR_COUNTRY=840, CARD_ACCEPTOR_COUNTRY_3NUMERIC=840, NRID=123456789012345, TRANSACTION_CURRENCY_CODE=840, POS_TERMINAL_ATTENDANCE_INDICATOR=0, POS_PARTIAL_APPROVAL_INDICATOR=0, POS_TERMINAL_LOCATION_INDICATOR=0, POS_TRANSACTION_STATUS_INDICATOR=0, POS_ECOMMERCE_TRAN_INDICATOR=0, POS_TYPE_OF_TERMINAL_DEVICE=0, POS_CARD_PRESENCE_INDICATOR=0, POS_CARD_CAPTURE_CAPABILITIES_INDICATOR=1, POS_TRANSACTION_SECURITY_INDICATOR=0, POS_CARD_DATA_TERMINAL_INPUT_CAPABILITY_INDICATOR=2, POS_CARDHOLDER_PRESENCE_INDICATOR=0, DFS_POS_DATA=0000010000200, GEODATA_STREET_ADDRESS=2500 LAKE COOK ROAD , GEODATA_POSTAL_CODE=600150000, GEODATA_COUNTY_CODE=840, GEODATA_STORE_NUMBER=10001, GEODATA_MALL_NAME=DISCOVER FINANCIAL SR, VERSION_INDICATOR=03141, REVERSAL_FLAG=Y, ISS_REFERENCE_ID=72967956, ISS_PROCESSOR_REFERENCE_ID=123459875}]
SimpleAsyncTaskExecutor-1 org.springframework.retry.support.RetryTemplate[0;39m: Retry: count=0
SimpleAsyncTaskExecutor-1 org.springframework.amqp.rabbit.core.RabbitTemplate[0;39m: Executing callback RabbitTemplate$$Lambda$268/19900766 on RabbitMQ Channel: Cached Rabbit Channel: AMQChannel(amqp://dftp_publisher@10.15.190.18:5672/hydra.services,1), conn: Proxy@10fa93b Shared Rabbit Connection: SimpleConnection@6329d2 [delegate=amqp://dftp_publisher@10.15.190.18:5672/hydra.services, localPort= 62375]
SimpleAsyncTaskExecutor-1 org.springframework.amqp.rabbit.core.RabbitTemplate[0;39m: Publishing message (Body:'{"HEADER":{"RETRY_COUNT":0,"PUBLISH_EVENT_TYPE":"AUTH"},"PAYLOAD":{"MTI":"400","MTI_REQUEST":"400","PAN":"6011000000000000","PROCCODE":"00","PROCCODE_REQUEST":"00","FROM_ACCOUNT":"00","TO_ACCOUNT":"00","TRANSACTION_AMOUNT":"000000000100","TRANSMISSION_MMDDHHMMSS":"0518202930","STAN":"000001","LOCALTIME_HHMMSS":"010054","LOCALDATE_YYMMDD":"180522","MERCHANT_TYPE":"5311","ACQUIRING_COUNTRY_CODE":"840","POS_ENTRY_MODE":"02","POS_PIN_ENTRY_CAPABILITIES":"0","FUNCTION_CODE":"400","MESSAGE_REASON_CODE":"06","ACQUIRING_ID_CODE":"000000","FORWARDING_ID_CODE":"000000","RETRIEVAL_REFERENCE_NUMBER":"1646N472D597","APPROVAL_CODE":"12345R","RESPONSE_CODE":"00","MERCHANT_NUMBER":"601100000000596","CARD_ACCEPTOR_NAME":"Discover Acq Simulator","CARD_ACCEPTOR_CITY":"Riverwoods","CARD_ACCEPTOR_STATE":"IL","CARD_ACCEPTOR_COUNTRY":"840","CARD_ACCEPTOR_COUNTRY_3NUMERIC":"840","NRID":"123456789012345","TRANSACTION_CURRENCY_CODE":"840","POS_TERMINAL_ATTENDANCE_INDICATOR":"0","POS_PARTIAL_APPROVAL_INDICATOR":"0","POS_TERMINAL_LOCATION_INDICATOR":"0","POS_TRANSACTION_STATUS_INDICATOR":"0","POS_ECOMMERCE_TRAN_INDICATOR":"0","POS_TYPE_OF_TERMINAL_DEVICE":"0","POS_CARD_PRESENCE_INDICATOR":"0","POS_CARD_CAPTURE_CAPABILITIES_INDICATOR":"1","POS_TRANSACTION_SECURITY_INDICATOR":"0","POS_CARD_DATA_TERMINAL_INPUT_CAPABILITY_INDICATOR":"2","POS_CARDHOLDER_PRESENCE_INDICATOR":"0","DFS_POS_DATA":"0000010000200","GEODATA_STREET_ADDRESS":"2500 LAKE COOK ROAD ","GEODATA_POSTAL_CODE":"600150000","GEODATA_COUNTY_CODE":"840","GEODATA_STORE_NUMBER":"10001","GEODATA_MALL_NAME":"DISCOVER FINANCIAL SR","VERSION_INDICATOR":"03141","REVERSAL_FLAG":"Y","ISS_REFERENCE_ID":"72967956","ISS_PROCESSOR_REFERENCE_ID":"123459875"}}' MessageProperties [headers={__TypeId__=com.discover.dftp.scrubber.domain.Message}, contentType=application/json, contentEncoding=UTF-8, contentLength=1705, deliveryMode=PERSISTENT, priority=0, deliveryTag=0])on exchange [hydra.test.exc], routingKey = [800]
SimpleAsyncTaskExecutor-1 org.springframework.retry.support.RetryTemplate[0;39m: Retry: count=0
SimpleAsyncTaskExecutor-1 org.springframework.amqp.rabbit.core.RabbitTemplate[0;39m: Executing callback RabbitTemplate$$Lambda$268/19900766 on RabbitMQ Channel: Cached Rabbit Channel: AMQChannel(amqp://dftp_publisher@10.15.190.18:5672/hydra.services,1), conn: Proxy@10fa93b Shared Rabbit Connection: SimpleConnection@6329d2 [delegate=amqp://dftp_publisher@10.15.190.18:5672/hydra.services, localPort= 62375]
SimpleAsyncTaskExecutor-1 org.springframework.amqp.rabbit.core.RabbitTemplate[0;39m: Publishing message (Body:'{"HEADER":{"RETRY_COUNT":0,"PUBLISH_EVENT_TYPE":"AUTH"},"PAYLOAD":{"MTI":"400","MTI_REQUEST":"400","PAN":"6011000000000000","PROCCODE":"00","PROCCODE_REQUEST":"00","FROM_ACCOUNT":"00","TO_ACCOUNT":"00","TRANSACTION_AMOUNT":"000000000100","TRANSMISSION_MMDDHHMMSS":"0518202930","STAN":"000001","LOCALTIME_HHMMSS":"010054","LOCALDATE_YYMMDD":"180522","MERCHANT_TYPE":"5311","ACQUIRING_COUNTRY_CODE":"840","POS_ENTRY_MODE":"02","POS_PIN_ENTRY_CAPABILITIES":"0","FUNCTION_CODE":"400","MESSAGE_REASON_CODE":"06","ACQUIRING_ID_CODE":"000000","FORWARDING_ID_CODE":"000000","RETRIEVAL_REFERENCE_NUMBER":"1646N472D597","APPROVAL_CODE":"12345R","RESPONSE_CODE":"00","MERCHANT_NUMBER":"601100000000596","CARD_ACCEPTOR_NAME":"Discover Acq Simulator","CARD_ACCEPTOR_CITY":"Riverwoods","CARD_ACCEPTOR_STATE":"IL","CARD_ACCEPTOR_COUNTRY":"840","CARD_ACCEPTOR_COUNTRY_3NUMERIC":"840","NRID":"123456789012345","TRANSACTION_CURRENCY_CODE":"840","POS_TERMINAL_ATTENDANCE_INDICATOR":"0","POS_PARTIAL_APPROVAL_INDICATOR":"0","POS_TERMINAL_LOCATION_INDICATOR":"0","POS_TRANSACTION_STATUS_INDICATOR":"0","POS_ECOMMERCE_TRAN_INDICATOR":"0","POS_TYPE_OF_TERMINAL_DEVICE":"0","POS_CARD_PRESENCE_INDICATOR":"0","POS_CARD_CAPTURE_CAPABILITIES_INDICATOR":"1","POS_TRANSACTION_SECURITY_INDICATOR":"0","POS_CARD_DATA_TERMINAL_INPUT_CAPABILITY_INDICATOR":"2","POS_CARDHOLDER_PRESENCE_INDICATOR":"0","DFS_POS_DATA":"0000010000200","GEODATA_STREET_ADDRESS":"2500 LAKE COOK ROAD ","GEODATA_POSTAL_CODE":"600150000","GEODATA_COUNTY_CODE":"840","GEODATA_STORE_NUMBER":"10001","GEODATA_MALL_NAME":"DISCOVER FINANCIAL SR","VERSION_INDICATOR":"03141","REVERSAL_FLAG":"Y","ISS_REFERENCE_ID":"72967956","ISS_PROCESSOR_REFERENCE_ID":"123459875"}}' MessageProperties [headers={__TypeId__=com.discover.dftp.scrubber.domain.Message}, contentType=application/json, contentEncoding=UTF-8, contentLength=1705, deliveryMode=PERSISTENT, priority=0, deliveryTag=0])on exchange [hydra.test.exc], routingKey = [800]
答案 0 :(得分:1)
您需要在var totalItems = $('.carousel-item').length;
var currentIndex = $('.carousel-item.active').index() + 1;
var down_index;
$('.num').html(''+currentIndex+'/'+totalItems+'');
$(".next").click(function(){
currentIndex_active = $('.carousel-item.active').index() + 2;
if (totalItems >= currentIndex_active)
{
down_index= $('.carousel-item.active').index() + 2;
$('.num').html(''+currentIndex_active+'/'+totalItems+'');
}
});
配置中启用退货和确认。参见the documentation。
此功能要求CachingConnectionFactory的PublisherReturns属性设置为true(请参阅发布者确认和返回)。
...
对于发布者确认(也称为发布者确认),模板需要一个CachingConnectionFactory,且其PublisherConfirm属性设置为true。
答案 1 :(得分:0)
按照@Gary Russell 的回答,我在我的代码中添加了下面的配置,然后 ConfirmCallback 开始工作。我使用 SpringBoot 2.5.2 和 RabbitMQ 3.8.18
cachingConnectionFactory.setPublisherReturns(true);
cachingConnectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);