spring-rabbit version:1.7.4.RELEASE
this is my code:
#Configuration
public class RabbitmqConfiguration {
public RabbitmqConfiguration(RabbitTemplate rabbitTemplate,ConfirmCallback confirmCallback) throws Exception {
rabbitTemplate.setConfirmCallback(confirmCallback);
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter(mapper));
}
}
#Component
#Slf4j
public class OrderStatusChangeComponentImpl implements OrderStatusChangeComponent,ConfirmCallback{
#Autowired
private RabbitTemplate rabbitTemplate;
#Autowired
private OrderMessageLogComponent orderMessageLogComponent;
#Autowired
private Gson gson;
/*
* (non-Javadoc)
*
* #see org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback#
* confirm(org.springframework.amqp.rabbit.support.CorrelationData, boolean,
* java.lang.String)
*/
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
long nowTime = System.nanoTime();
String uuid = correlationData.getId();
if (ack) {
orderMessageLogComponent.deleteOrderMessageLogByUUID(uuid);
} else {
log.error(cause, nowTime);
}
}
i test rabbitmq send msg by jmeter about 512 thread and 1000 loops;
i see log have so much error.
Channel shutdown: clean channel shutdown; protocol method: #method<channel.close>(reply-code=406, reply-text=TIMEOUT WAITING FOR ACK, class-id=0, method-id=0)
finally my application cannot connection rabbitmq.
btw my rabbitmq server is healty.
Try doing your send() within a RabbitTemplate.invoke() and invoke a template.waitForConfirmsOrDie() with a longer timeout.
If you are using invoke() and don't do that, the framework will only wait 5000ms for confirms.
If you are not using invoke(), it's not clear how you can get that close error.
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
long nowTime = System.nanoTime();
String uuid = correlationData.getId();
if (ack) {
orderMessageLogComponent.deleteOrderMessageLogByUUID(uuid);
} else {
log.error("消息发送失败,消息唯一标识为{},具体原因为{},当前时间为{}", uuid, cause, nowTime);
}
}
#Override
#Async(MsgSendAsyncConfig.MSGSEND_SYNC_POOL)
public void deleteOrderMessageLogByUUID(String uuid) {
orderMessageLogService.deleteOrderMessageLogByUUID(uuid);
}
Related
In SpringBoot I use RabbitTemplate and #RabbitListener to produce and receive Messages.
I have an abstract class to send and receive RabbitMQ message of a specific type and send those messages to a STOMP web socket.
I use one RabbitMQ topic exchange which is bind to one RabbitMQ queue. In the topic-exchange I send messages (Java objects) of 2 different types and I am using 2 #RabbitListener to consume these messages.
#Configuration
public class WsRabbitMqConfiguration {
public static final String WS_EXCHANGE_NAME = "websocket.replication";
#Getter
#Value(value = "${spring.application.name}")
private String routingKey;
#Bean
TopicExchange wsReplicationExchange() {
return new TopicExchange(
WS_EXCHANGE_NAME,
true,
false,
null);
}
#Bean
public Queue wsReplicationQueue() {
return new AnonymousQueue();
}
#Bean
public Binding bindingWsAttributeValueQueue(TopicExchange wsReplicationExchange,
Queue wsReplicationQueue) {
return BindingBuilder
.bind(wsReplicationQueue)
.to(wsReplicationExchange)
.with(routingKey);
}
}
#RequiredArgsConstructor
public abstract class AbstractWebsocketService<T> implements WebSocketService {
public static final String HEADER_WS_DESTINATION = "x-Websocket-Destination";
private final RabbitTemplate rabbitTemplate;
private final WsRabbitMqConfiguration wsRabbitMqConfiguration;
#Override
public final <T> void send(String destination, T payload) {
rabbitTemplate.convertAndSend(WsRabbitMqConfiguration.WS_EXCHANGE_NAME, wsRabbitMqConfiguration.getRoutingKey(), payload,
message -> putDestination(message, destination));
}
protected abstract void handleWebsocketSending(T payload, String destination);
#Service
public class FooWebSocketService extends AbstractWebsocketService<Foo> {
public FooWebSocketService(RabbitTemplate rabbitTemplate,
WsRabbitMqConfiguration rabbitMqConfiguration) {
super(rabbitTemplate, rabbitMqConfiguration);
}
#RabbitListener(queues = "#{wsReplicationQueue.name}", ackMode = "NONE")
protected void handleWebsocketSending(#Payload Foo payload, #Header(HEADER_WS_DESTINATION) String destination) {
// logic here
}
}
#Service
public class BarWebSocketService extends AbstractWebsocketService<Bar> {
public BarWebSocketService(RabbitTemplate rabbitTemplate,
WsRabbitMqConfiguration rabbitMqConfiguration) {
super(rabbitTemplate, rabbitMqConfiguration);
}
#RabbitListener(queues = "#{wsReplicationQueue.name}", ackMode = "NONE")
protected void handleWebsocketSending(#Payload Bar payload, #Header(HEADER_WS_DESTINATION) String destination) {
// logic here
}
}
From another service class I want to send RMQ messages, but randomly wrong #RabbitListener is activated.
Eg.
#Service
#RequiredArgsConstructor
public class TestService {
private final BarWebSocketService barWebSocketService;
public void sendMessage(Bar bar) {
// 1st message
barWebSocketService.send("bar-destination", bar);
// 2nd message
barWebSocketService.send("bar-destination2", bar);
}
}
For the 1st message #RabbitListener in FooWebSocketService is activated (which is wrong) and for the 2nd message #RabbitListener in BarWebSocketService (which is right).
Any suggestions what I am doing wrong? Thank you!
First calls usually successful, but then I have exception with message like those:
io.grpc.StatusRuntimeException: DEADLINE_EXCEEDED: ClientCall started after deadline exceeded: -175.597476157s from now
Why is the number of seconds negative? How do I fix it?
My grpc config:
public class MyAppLibGrpcSenderConfig {
#Value("${grpc.client.host:localhost}")
private String host;
#Value("${grpc.client.port:9090}")
private int port;
#Value("${grpc.client.negotiationType:PLAINTEXT}")
private String negotiationType;
#Value("${grpc.client.deadline:300000}")
private long deadline;
#Autowired
private Tracer tracer;
#Bean
public ManagedChannel managedChannel() {
ManagedChannelBuilder<?> builder = ManagedChannelBuilder.forAddress(host, port);
if ("PLAINTEXT".equals(negotiationType)) {
builder.usePlaintext();
}
return builder.build();
}
#Bean
public TracingClientInterceptor tracingClientInterceptor(Tracer tracer) {
return TracingClientInterceptor
.newBuilder()
.withTracer(this.tracer)
.build();
}
#Bean
public MyAppSenderServiceGrpc.MyAppSenderServiceBlockingStub myAppSenderServiceBlockingStub(
TracingClientInterceptor tracingClientInterceptor,
ManagedChannel managedChannel) {
return MyAppSenderServiceGrpc
.newBlockingStub(tracingClientInterceptor.intercept(managedChannel))
.withDeadlineAfter(deadline, TimeUnit.MILLISECONDS);
}
#Bean
public MyAppCodeLoaderServiceGrpc.MyAppCodeLoaderServiceBlockingStub myAppCodeLoaderServiceBlockingStub(
TracingClientInterceptor tracingClientInterceptor,
ManagedChannel managedChannel) {
return MyAppCodeLoaderServiceGrpc
.newBlockingStub(tracingClientInterceptor.intercept(managedChannel))
.withDeadlineAfter(deadline, TimeUnit.MILLISECONDS);
}
}
Client code:
#net.devh.boot.grpc.server.service.GrpcService
public class MyAppEventKafkaSender extends MyAppSenderServiceGrpc.MyAppSenderServiceImplBase {
...
#SneakyThrows
#Override
public void sendMessage(ContextMyAppEventGrpc contextMyAppEventGrpc,
StreamObserver<Empty> responseObserver) {
try {
sendEvent(contextMyAppEventGrpc);
Empty reply = Empty.newBuilder().build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
} catch (Exception e) {
Status status = Status.INTERNAL.withDescription(e.getMessage());
responseObserver.onError(status.asRuntimeException());
}
}
}
Deadline is an absolute point in time and is set immediately when you create your stub (and not necessarily when you execute it) - this is in contrast to timeouts which are relative to the start of the call.
So negative deadline means that it expired before your stub was executed.
To fix the issue, you should be setting deadline immediately before making a call.
var response = blockingStub.withDeadlineAfter(300000, TimeUnit.MILLISECONDS)
.yourRpcName();
Read more about Deadline here
I am using spring-boot-websocket (spring-boot version 1.5.10) in my project. I have configured it as below,
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
implements WebSocketMessageBrokerConfigurer {
#Value( "${rabbitmq.host}" )
private String rabbitmqHost;
#Value( "${rabbitmq.stomp.port}" )
private int rabbitmqStompPort;
#Value( "${rabbitmq.username}" )
private String rabbitmqUserName;
#Value( "${rabbitmq.password}" )
private String rabbitmqPassword;
#Override
public void configureMessageBroker( MessageBrokerRegistry registry )
{
registry.enableStompBrokerRelay("/topic", "/queue").setRelayHost(rabbitmqHost).setRelayPort(rabbitmqStompPort)
.setSystemLogin(rabbitmqUserName).setSystemPasscode(rabbitmqPassword);
registry.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints( StompEndpointRegistry stompEndpointRegistry )
{
stompEndpointRegistry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
}
#Bean
#Override
public WebSocketHandler subProtocolWebSocketHandler()
{
return new CustomSubProtocolWebSocketHandler(clientInboundChannel(), clientOutboundChannel());
}
#Override
public void configureWebSocketTransport( WebSocketTransportRegistration registry )
{
super.configureWebSocketTransport(registry);
}
#Override
public boolean configureMessageConverters( List<MessageConverter> messageConverters )
{
return super.configureMessageConverters(messageConverters);
}
#Override
public void configureClientInboundChannel( ChannelRegistration registration )
{
super.configureClientInboundChannel(registration);
}
#Override
public void configureClientOutboundChannel( ChannelRegistration registration )
{
super.configureClientOutboundChannel(registration);
}
#Override
public void addArgumentResolvers( List<HandlerMethodArgumentResolver> argumentResolvers )
{
super.addArgumentResolvers(argumentResolvers);
}
#Override
public void addReturnValueHandlers( List<HandlerMethodReturnValueHandler> returnValueHandlers )
{
super.addReturnValueHandlers(returnValueHandlers);
}
}
public class CustomSubProtocolWebSocketHandler extends SubProtocolWebSocketHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomSubProtocolWebSocketHandler.class);
#Autowired
private UserCommons userCommons;
CustomSubProtocolWebSocketHandler(MessageChannel clientInboundChannel,
SubscribableChannel clientOutboundChannel) {
super(clientInboundChannel, clientOutboundChannel);
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
LOGGER.info("************************************************************************************************************************New webSocket connection was established: {}", session);
String token = session.getUri().getQuery().replace("token=", "");
try
{
String user = Jwts.parser().setSigningKey(TokenConstant.SECRET)
.parseClaimsJws(token.replace(TokenConstant.TOKEN_PREFIX, "")).getBody().getSubject();
Optional<UserModel> userModelOptional = userCommons.getUserByEmail(user);
if( !userModelOptional.isPresent() )
{
LOGGER.error(
"************************************************************************************************************************Invalid token is passed with web socket request");
throw new DataException(GeneralConstants.EXCEPTION, "Invalid user", HttpStatus.BAD_REQUEST);
}
}
catch( Exception e )
{
LOGGER.error(GeneralConstants.ERROR, e);
}
super.afterConnectionEstablished(session);
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
LOGGER.error("************************************************************************************************************************webSocket connection was closed");
LOGGER.error("Reason for closure {} Session: {} ", closeStatus.getReason(),session.getId() );
super.afterConnectionClosed(session, closeStatus);
}
#Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
LOGGER.error("************************************************************************************************************************Connection closed unexpectedly");
LOGGER.error(GeneralConstants.ERROR, exception);
super.handleTransportError(session, exception);
}
}
From the client-side, I am creating a SockJS object to establish the connection,
let url = `/ws?token=${localStorage.getItem("access_token")}`;
// Web Socket connection
/* eslint-disable */
let sockJS = new SockJS(url);
let stompClient = Stomp.over(sockJS);
debugger
this.setState({
stompObject : stompClient,
});
But the connection is not getting established consistently, most of the times it is stuck at Opening the connection, in the backend log, I can see the connection getting established and a session is created. But, in the browser console, I can see client-side sending message to the server but the server is not acknowledging the message.
Sometimes, when I refresh the browser for 10-15 times, the connection is getting established successfully. Is there any mistake in my configuration?
Thank You.
Given that you can "hit refresh 10 or 15 times and then get a connection," I'm curious if you dealing with a cookie issue? I know Chrome is famous for that sort of thing. Anyway close out all browser windows and stop the browser, then start the browser, and tell it to clear browsing history and then attempt the connection. Also, be SURE you read the version of the spring-boot docs for the version of spring-boot you are that you are actually using, and also specify the SB version in your questions and when looking for answers.
I am using spring cloud stream version 1.1.2 to create /integrate consumer with microservice. I am setting auto-commit-offset consumer property to False so that i can receive acknowledgment header in Message and manually acknowledge messages once it is consumed successfully.
My concern is, if something fails during message consumption i will not send acknowledgment back to broker but when i can expect the same message re-delivered to consumer. Currently, i can verify re-delivery only if i restart server, how would it work when server is already up and running.
Consumer properties are set as
kafka:
bindings:
input:
consumer:
auto-commit-offset: false
reset-offsets: true
start-ofofset: earliest
You need to seek the offset before the message in you client.
The offset is kept persistent at the kafka service for your group and at your client in memory. The latter will be lost when the service is being restarted which is why you then again consume your message.
This can be solved by:
public class KafkaConsumer implements ConsumerSeekAware {
and
this.seekCallBack.get().seek(consumerRecord.topic(), consumerRecord.partition(), consumerRecord.offset());
I hope this helps you!
Complete consumer code:
public class KafkaConsumer implements ConsumerSeekAware {
private static final String USER_TOPIC = "user-topic";
private static final String USER_LISTENER_ID = "userListener";
private static final String STRING_LISTENER = "string-listener";
private final ThreadLocal<ConsumerSeekCallback> seekCallBack = new ThreadLocal<>();
private final KafkaListenerEndpointRegistry registry;
private final TaskScheduler scheduler;
private final LocalValidatorFactoryBean validatorFactory;
public KafkaConsumer(final KafkaListenerEndpointRegistry registry, final TaskScheduler scheduler, final LocalValidatorFactoryBean validatorFactory) {
this.registry = registry;
this.scheduler = scheduler;
this.validatorFactory = validatorFactory;
}
public void registerSeekCallback(ConsumerSeekAware.ConsumerSeekCallback callback) {
this.seekCallBack.set(callback);
}
#Override
public void onPartitionsAssigned(final Map<TopicPartition, Long> assignments, final ConsumerSeekCallback callback) {
}
#Override
public void onIdleContainer(final Map<TopicPartition, Long> assignments, final ConsumerSeekCallback callback) {
}
#KafkaListener(id = USER_LISTENER_ID, topics = USER_TOPIC, containerFactory = "userContainerFactory")
public void consumeJson(ConsumerRecord<String, User> consumerRecord, User user, final Acknowledgment acknowledgment) {
if (user.getName().equals("reject")) {
throw new IllegalStateException("Illegal user:" + user.getName());
}
if (!user.getName().equals("retry")) {
acknowledgment.acknowledge();
log.info("Consumed JSON Message: " + user);
} else {
log.info("Rejected: " + user);
this.seekCallBack.get().seek(consumerRecord.topic(), consumerRecord.partition(), consumerRecord.offset());
}
}
I want to implement publisher confirms functionality into my code, gone through the http://www.rabbitmq.com/blog/2011/02/10/introducing-publisher-confirms/ but was unable to understand
I am using QueueBuilder as follows
#Bean
public Queue salesQueue() {
return QueueBuilder.durable(salesQueue)
.withArgument("x-max-length", 1)
.withArgument("x-overflow", "reject-publish")
.build();
}
RabbitTemplate creation
#Bean
public AmqpTemplate rabbitTemplate(ConnectionFactory connectionFactory)
{
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
System.out.println("Message with correlation ID {} confirmed by
broker."+ correlationData.getId());
} else {
System.out.println("Broker is unable to handle message with
correlation ID {} : {}"+correlationData.getId()+","+
cause);
}
});
return rabbitTemplate;
}
sending message to queue
#Service
public class RabbitMQSender {
private static long sentMessageCount =0L;
#Autowired
private AmqpTemplate rabbitTemplate;
#Value("${rabbitmq.queue}")
String queueName;
public void send() {
long x=++sentMessageCount;
rabbitTemplate.convertAndSend(queueName,"
{'empId':'"+x+"','empName':'YYYYYY'}");
}
Presently I have set the queue length as 1, whenever I send messages greater than 1, I am expecting it to inform publisher about rejection of message via a basic.nack message which is not happening...
I request you to help me with this
Thank you