I'm trying to aply Redisson features for my project as message broker and I have a question. Is it possible to push Redisson to precceding recieved messages asynchronously? I have created a small example, sent 4 messages from different URL's. I expected, that Redisson proceeded them asynchronously, but it did it one by one.
Here the implementation:
public class RedisListenerServiceImpl implements MessageListener<String> {
private static final Logger log = LoggerFactory.getLogger(RedisListenerServiceImpl.class);
private final ObjectMapper objectMapper = new ObjectMapper();
#Override
public void onMessage(CharSequence channel, String stringMsg) {
log.info("Message received: {}", stringMsg);
MessageDto msg;
try {
msg = objectMapper.readValue(stringMsg, MessageDto.class);
} catch (final IOException e) {
log.error("Unable to deserialize message: {}", e.getMessage(), e);
return;
}
try {
//Do my stuff
} catch (Exception e) {
log.error("Unable to get service from factory: {}", e.getMessage(), e);
}
}
}
And the config:
#Configuration
public class RedisListenerConfig {
#Autowired
public RedisListenerConfig(RedissonClient redisClient,
MessageListener redisListenerService,
#Value("${redis.sub.key}") String redisSubKey) {
RTopic subscribeTopic = redisClient.getTopic(redisSubKey);
subscribeTopic.addListenerAsync(String.class, redisListenerService);
}
}
It's expected behavior. If you want your messages to be processed concurrently when the Listener onMessage() method is triggered, just use a thread pool to process it.
Since Redisson doesn't know how many threads you want to consume the triggered events, it leaves the implementation detail to you.
public class RedisListenerServiceImpl implements MessageListener<String> {
private static final Logger log = LoggerFactory.getLogger(RedisListenerServiceImpl.class);
private final ObjectMapper objectMapper = new ObjectMapper();
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
#Override
public void onMessage(CharSequence channel, String stringMsg) {
log.info("Message received: {}", stringMsg);
MessageDto msg;
try {
msg = objectMapper.readValue(stringMsg, MessageDto.class);
executorService.submit(()->{
System.out.println("do something with message: "+msg);
});
} catch (final IOException e) {
log.error("Unable to deserialize message: {}", e.getMessage(), e);
return;
}
try {
//Do my stuff
} catch (Exception e) {
log.error("Unable to get service from factory: {}", e.getMessage(), e);
}
}
Related
I have a controller. Obtain data from client and in my service I get messages from API, but using WEBSocket connection. I never worked with WEBSockets before, so I found code example and implemented it. It works fine but the problem is, I have no idea how can I return obtained messages to my controller and to send response to the client.
public void sendRequest(String trader, List<String> cryptoGroup) {
// String json = ...prepare JSON...
try {
// open websocket
final WebsocketClientEndpoint clientEndPoint = new WebsocketClientEndpoint(new URI(link));
// add listener
clientEndPoint.addMessageHandler(System.out::println);
// send message to websocket
clientEndPoint.sendMessage(json);
// wait 5 seconds for messages from websocket
Thread.sleep(5000);
} catch (InterruptedException ex) {
throw new RuntimeException("InterruptedException exception: " + ex.getMessage());
} catch (URISyntaxException ex) {
throw new RuntimeException("URISyntaxException exception: " + ex.getMessage());
}
}
public void addMessageHandler(MessageHandler msgHandler) {
this.messageHandler = msgHandler;
}
public static interface MessageHandler {
public void handleMessage(String message);
}
Maybe there is a some kind of a pattern for such cases, some kind of observer-listener or something... I'll be appreciate for the ideas
I have a Java service which is getting messages from an Oracle Advanced Queue. I can create the connection and listen and get messages OK. I can see that you can stop and start listening for messages, so I have implemented controls for that. However, I would like to be able to report on the current status of the listener. I can see if it's there, but how can I tell if it's stopped or started?
I have a container class along the lines of (Listener is my own class (implementing both MessageListener and ExceptionListener) which actually does something with the message)
public class QueueContainer {
private static final String QUEUE_NAME = "foo";
private final Connection dbConnection;
private final QueueConnection queueConnection;
private final QueueSession queueSession;
private final Queue queue;
private final MessageConsumer consumer;
private final Listener listener;
public QueueContainer(final Connection dbConnection ) {
try {
this.dbConnection = dbConnection;
queueConnection = AQjmsQueueConnectionFactory.createQueueConnection(dbConnection);
queueSession = queueConnection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
queue = ((AQjmsSession) queueSession).getQueue(context.getEnvironment(), QUEUE_NAME);
consumer = queueSession.createConsumer(queue);
listener = new Listener(QUEUE_NAME);
consumer.setMessageListener(listener);
queueConnection.setExceptionListener(listener);
} catch (JMSException | SQLException e) {
throw new RunTimeException("Queue Exception", e);
}
}
public void startListening() {
try {
queueConnection.start();
} catch (JMSException e) {
throw new RunTimeException("Failed to start listening to queue", e);
}
}
public void stopListening() {
try {
queueConnection.stop();
} catch (JMSException e) {
throw new RunTimeException("Failed to stop listening to queue", e);
}
}
public void close() {
if (queueConnection != null) {
try {
queueConnection.close();
} catch (JMSException e) {
throw new RunTimeException("Failed to stop listening to queue", e);
}
}
}
public boolean isRunning() {
try {
// This doesn't work - I can't distinguish between started and stopped
return queueConnection.getClientID() != null;
} catch (JMSException e) {
LOGGER.warn("Failed to get queue client ID", e);
return false;
}
}
I can't see what to put in isRunning that could distinguish between a stopped and started listener
The JMS API assumes you know yourself what you did. So why not add a boolean flag and keep track of this ?
private volatile boolean isListening = false;
...
public void startListening() {
try {
queueConnection.start();
isListening = true;
} catch (JMSException e) {
throw new RunTimeException("Failed to start listening to queue", e);
}
}
public void stopListening() {
try {
queueConnection.stop();
isListening = false;
} catch (JMSException e) {
throw new RunTimeException("Failed to stop listening to queue", e);
}
}
public void close() {
if (queueConnection != null) {
try {
queueConnection.close();
isListening = false;
} catch (JMSException e) {
throw new RunTimeException("Failed to stop listening to queue", e);
}
}
}
public boolean isRunning() {
return isListening;
}
There is no JMS API call to determine whether or not a javax.jms.Connection is started.
To be clear, the queue itself is not the entity that is started or stopped. The connection is started or stopped.
You may be able to get this information from the Oracle Advanced Queue implementation object, but I'm not familiar with that implementation so I can't say. Obviously any solution using an implementation object rather than the standard API will not be portable.
I read in the documentation that AsynchronousSocketChannel is threadsafe, so it is safe for single instance of it to be shared by multiple threads, but when I try to implement this single instance concept (in client side application) I failed to use write() method to send data to server.
Previously I had success doing it by calling shutdownOutput() or close() from channel after callingwrite(byteBuffer,attachment,completionHandler). But when I just want to use only single instance without callingclose()orshutdownOutput()` the message never reaches the server (I saw it from server log).
Do we need to close channel in order to make message reach the server? I use Spring boot to build this project.
Here is my code:
#Component
public class AgentStatusService {
private static final Logger log =
LoggerFactory.getLogger(AgentStatusService.class);
#Autowired
private SocketAddress serverAddress;
#Autowired
private AsynchronousSocketChannel channel;
public void consumeMessage() throws IOException {
try {
log.info("trying to connect to {}", serverAddress.toString());
channel.connect(serverAddress, channel, new SocketConnectCompletionHandler());
log.info("success connect to {}", channel.getRemoteAddress());
} catch (final AlreadyConnectedException ex) {
final ByteBuffer writeBuffer = ByteBuffer.wrap("__POP ".getBytes());
final Map<String, Object> attachm`enter code here`ent = new HashMap<>();
attachment.put("buffer", writeBuffer);
attachment.put("channel", channel);
writeBuffer.flip();
channel.write(writeBuffer, attachment, new SocketWriteCompletionHandler());
} catch (final Exception e) {
log.error("an error occured with message : {}", e.getMessage());
e.printStackTrace();
}
}
This is my socket connect completion handler class:
public class SocketConnectCompletionHandler
implements CompletionHandler<Void, AsynchronousSocketChannel> {
private static Logger log =
LoggerFactory.getLogger(SocketConnectCompletionHandler.class);
#Override
public void completed(Void result, AsynchronousSocketChannel channel) {
try {
log.info("connection to {} established", channel.getRemoteAddress());
final ByteBuffer writeBuffer = ByteBuffer.wrap("__POP ".getBytes());
final Map<String, Object> attachment = new HashMap<>();
attachment.put("buffer", writeBuffer);
attachment.put("channel", channel);
writeBuffer.flip();
channel.write(writeBuffer, attachment, new
SocketWriteCompletionHandler());
} catch (final IOException e) {
e.printStackTrace();
}
}
#Override
public void failed(Throwable exc, AsynchronousSocketChannel attachment) {
exc.printStackTrace();
try {
log.error("connection to {} was failed", attachment.getRemoteAddress());
} catch (final Exception e) {
log.error("error occured with message : {}", e.getCause());
}
}
}
This is my socket write completion handler class:
public class SocketWriteCompletionHandler
implements CompletionHandler<Integer, Map<String, Object>> {
private static final Logger log =
LoggerFactory.getLogger(SocketWriteCompletionHandler.class);
#Override
public void completed(Integer result, Map<String, Object> attachment) {
try {
final AsynchronousSocketChannel channel =
(AsynchronousSocketChannel) attachment.get("channel");
final ByteBuffer buffer = (ByteBuffer) attachment.get("buffer");
log.info("write {} request to : {}", new String(buffer.array()),
channel.getRemoteAddress());
buffer.clear();
readResponse(channel, buffer);
} catch (final Exception ex) {
ex.printStackTrace();
log.error("an error occured with message : {}", ex.getMessage());
}
}
#Override
public void failed(Throwable exc, Map<String, Object> attachment) {
log.error("an error occured : {}", exc.getMessage());
}
public void readResponse(AsynchronousSocketChannel channel, ByteBuffer
writeBuffer) {
final ByteBuffer readBuffer = ByteBuffer.allocate(2 * 1024);
final Map<String, Object> attachment = new HashMap<>();
attachment.put("writeBuffer", writeBuffer);
attachment.put("readBuffer", readBuffer);
attachment.put("channel", channel);
readBuffer.flip();
channel.read(readBuffer, attachment, new
SocketReadCompletionHandler());
}
}
If the server thinks it didn't receive the message, and it did when you were previously shutting down or closing the socket, it must be trying to read to end of stream, and blocking or at least not completing its read and so never logging anything.
Why you are using multiple threads in conjunction with asynchronous I/O, or indeed with any socket, remains a mystery.
I have a MessageDriven bean which consume messages and send it to somewhere else by REST API. Like this:
#MessageDriven(name = "n", activationConfig = {...})
public class SubmitMessageBean implements MessageListener {
#Resource
private MessageDrivenContext context;
#Override
public void onMessage(final Message message) {
try {
// Submit data to somewhere with REST API
} catch (IOException ex) {
this.context.getRollbackOnly();
} catch (JMSException ex) {
e.printStackTrace();
}
}
}
When error happens I want to retry sending in a minute. It there anyway to do that?
OK, I figured it out. You can also easily add delay to give the remote system more time to resolve the error.
#MessageDriven(name = "n", activationConfig = {...})
public class SubmitMessageBean implements MessageListener {
#Inject
JMSContext context;
#Resource(mappedName = "...")
Queue queue;
#Override
public void onMessage(Message message) {
Integer retry = 0;
try {
// track how many time we tried before
retry = message.getIntProperty("retry");
// Submit data to somewhere with REST API
} catch (IOException ex) {
// Put it back in queue again
// You can limit number of retry by keeping retry variable
if (retry < 5) {
JMSProducer producer = this.context.createProducer();
producer.setProperty("retry", r);
// Add some delay to start it again after a minute
producer.setDeliveryDelay(60000);
// Send it again with send()
}
} catch (JMSException ex) {
e.printStackTrace();
}
}
}
Of course you need to fill {...} with your own right values.
I wrote a little program that use activemq embedded broker. Program run on one machine successfully but does not work another one. Both of server have sun os 10 & java 7 and also activemq 5.5.1. I mean with does not work, new BrokerService() constructor call does not return for a while (a couple of minutes). Snippet code is like below.
Thanks for advices..
public static void main(String[] args) {
// ....
try {
p.start("tcp://192.168.4.2:61616");
} catch (MessagingException e) {
e.printStackTrace();
}
// ....
}
public void start(String brokerAddress) throws MessagingException {
try {
System.out.println(">>> initialize 2");
broker = new JmsBroker(brokerAddress, brokerAddress.substring(6));
System.out.println(">>> initialize 3");
broker.start();
System.out.println(">>> initialize 4");
messageProducer = new JmsProducer(brokerAddress,
"MESSAGING_IF_NAME",
false,
5000);
System.out.println(">>> initialize 5");
} catch (JMSException e) {
System.out.println(e);
}
}
public class JmsBroker extends Thread {
private BrokerService broker;
private Object lock;
private static final Logger logger = LoggerFactory.getLogger(JmsBroker.class);
private String connector;
public JmsBroker(String jmsAddress, String brokerName) throws MessagingException {
broker = new BrokerService(); // !!!!! PROBLEM
broker.setBrokerName(brokerName);
broker.setUseJmx(true);
broker.setUseLoggingForShutdownErrors(true);
broker.setSchedulerSupport(false);
broker.setPersistent(false);
connector = jmsAddress;
try {
System.out.println(">>> s1");
broker.addConnector(connector);
System.out.println(">>> s2");
broker.start(true);
System.out.println(">>> s3");
while (!broker.isStarted()) {
Thread.sleep(10);
System.out.println("BROKER NOT STARTED");
}
logger.info("JMS BROKER STARTED");
System.out.println("JMS BROKER STARTED");
} catch (InterruptedException e) {
throw new MessagingException(e);
} catch (Exception e) {
throw new MessagingException(e);
}
}
public void run() {
try {
lock = new Object();
synchronized (lock) {
lock.wait();
}
} catch (Exception e) {
logger.error("", e);
}
}
}
wrong record on etc/hosts file causes this problem. After removing problem is solved.