I was trying to connect to JedisCluster (ElastiCache Redis) from java. But I was getting JedisConnectionException with No reachable node in the cluster.
Here was my code to connect to JedisCluster
public static void main(String[] args) throws IOException{
final GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxWaitMillis(2000);
poolConfig.setMaxTotal(300);
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("mycachecluster.eaogs8.0001.usw2.cache.amazonaws.com",6379));
jedisClusterNode.add(new HostAndPort("mycachecluster.eaogs8.0002.usw2.cache.amazonaws.com",6379));
JedisCluster jedisCluster = new JedisCluster(jedisClusterNode,poolConfig);
System.out.println("Cluster Size...." + jedisCluster.getClusterNodes().size());
try{
jedisCluster.set("foo", "bar");
jedisCluster.get("foo");
}
catch(Exception e){
e.printStackTrace();
}
finally{
jedisCluster.close();
}
}
The exception I got after running this
redis.clients.jedis.exceptions.JedisNoReachableClusterNodeException: No reachable node in cluster
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnection(JedisSlotBasedConnectionHandler.java:57)
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnectionFromSlot(JedisSlotBasedConnectionHandler.java:74)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:116)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:31)
at redis.clients.jedis.JedisCluster.set(JedisCluster.java:103)
I have checked
telnet mycachecluster.eaogs8.0001.usw2.cache.amazonaws.com 6379
as mentioned in AWS Doc I got the reply as Connected.
What is the issue here and why I am not able to connect to the JedisCluster using java?
Note :
I am using jedis version 2.9.0
Update:
In AWS Encryption in-transit and Encryption at-rest are activated
So
Jedis jedis = null;
try{
jedis = new Jedis(URI.create("rediss://mycachecluster.eaogs8.0001.usw2.cache.amazonaws.com:6379"));
System.out.println(jedis.ping());
System.out.println("XXXXX: "+jedis.get("c"));
}
catch(Exception exception){
exception.printStackTrace();
}
finally{
jedis.close();
}
works fine. But not the jedis cluster.
From URI.create("rediss://..."), it's evident that you are using Redis SSL Scheme to create a successful connection by Jedis. But JedisCluster doesn't have SSL support yet.
There is a pending feature request regarding this.
The JedisCluster had some problems while connecting the redis cluster server with SSL enabled. Even with the latest revision(as of July 2020) we were getting the exception JedisNoReachableClusterNodeException. There are very less articles on the configurations required for various server requirements.
We needed the library in 2 languages, one in Java and the other in Python. For python I used the python redis-py-cluster. While for Java initially we tried with Jedis and then Jedis Cluster but both were not helpful.
So another library I found is
Lettuce
For a redis cluster server with SSL support the configuration is pretty straight forward and supports a builder pattern to construct the connection object with optional parameters. Here is the sample to create and connect to redis-cluster server
RedisURI redisURI = RedisURI.Builder.redis("<<Redis Server primary endpoint>>", 6379).withSsl(true).withVerifyPeer(false).build();
RedisClusterClient redisClient = RedisClusterClient.create(redisURI);
StatefulRedisClusterConnection<String, String> conn = redisClient.connect();
List<KeyValue<String, String>> res_1= conn.sync().mget(keys...)_
conn.close();
But note that if the redis server is a single node instance then even Jedis library is also good to use.
Related
I have created a Storm topology which connects to Redis Cluster using Jedis library. Storm component always expects that Redis is up and running and only then it connects to Redis and subscribes the events.Currently we use pub-sub strategy of Redis.
Below is the code sample that explains my Jedis Connectivity inside Storm to for Redis.
try {
jedis.psubscribe(listener, pattern);
} catch(Exception ex) {
//catch statement here.
} finally {
pool.returnResource(jedis);
}
....
pool = new JedisPool(new JedisPoolConfig(), host, port); //redis host port
ListenerThread listener = new ListenerThread(queue, pool, pattern);
listener.start();
EXPECTED BEHAVIOUR
Once Redis dies and comes back online, Storm is expected to identify the status of Redis. It must not need a restart in case when Redis die and come online.
ACTUAL BEHAVIOUR
Once Redis restarts due to any reason, I always have to restart the Storm topology as well and only then it starts listening back to Redis.
QUESTION
How can I make Storm listen and reconnect to Redis again after Redis is restarted? any guidance would be appreciated, viz. docs, forum answer.
Catch the exception for the connection lost error and set the pool to null
(Assume that you doing this in Spout) Use an if-else statement to check if pool is null then create a new instance of JedisPool() assigning to the pool like in your code:
pool = new JedisPool(new JedisPoolConfig(), host, port); //redis host port
If pool not null (means connected) then continue your work
This is a common issue with apache-storm where connection thread is alivein stale condition, although the source from where you are consuming is down/restarted. Ideally it should retry to create new connection thread instead reusing the existing one. Hence the Idea is to have it automated it by by detecting the Exception (e.g. JMSConnectionError in case of JMS).
refer this Failover Consumer Example which will give you brief idea what to do in such cases.(P.S this is JMS which would be JMS in redis your case.)
The Steps would be something like this.
Catch Exception in case of ERROR or connection lost.
Init connection (if not voluntarily closed by program) from catch.
If Exception got to step 1.
I just find this code and I use to check if MYSQL Server is running after every ten second...by call this method
public static boolean hostAvailabilityCheck(String address, int port) {
try (Socket s = new Socket(address, port)) {
return true;
} catch (IOException ex) {
/* ignore */
}
return false;
}
so do I do it right, or there is other better way for check for MYSQL if still running after some second? please help me.
The port can be open without any application listening to it. If you want to see also that your mySQL application is listening on the port, get mySQL connector for JAVA and look how to establish a connection - with user name and password.
Instead of doing this i think it is a better approach to use Hibernate or other ORM capabilities for create Datasources and Connection Pools, because without it you will have to deal with resource management yourself.
Check this link,for an example of how to create and use a Session and the associated connection.
Hibernates uses c3p0 to handle datasource creation and connections, you can configure using properties all the parameters, and this will handle reconnections for you keeping a conection pool ready for use. Also it can validate the connection with a validation query
I have setup a JMS listener for IBM MQ . When the listener is running on single JVM like tomcat on local machine it runs fine. But when i deploy it to the Cloud where there are 2 VM's , it's running fine on one of the VM and connects to MQ but it says below on the other one.
Is there any limitation from IBM MQ on using a ID,password from multiple clients in order to connect to the Queue manager?
RROR> com.ssc.ach.mq.JMSMQReceiver[main]: errorMQJMS2013: invalid security authentication supplied for MQQueueManager
javax.jms.JMSSecurityException: MQJMS2013: invalid security authentication supplied for MQQueueManager
at com.ibm.mq.jms.MQConnection.createQM(MQConnection.java:2050)
at com.ibm.mq.jms.MQConnection.createQMNonXA(MQConnection.java:1532)
at com.ibm.mq.jms.MQQueueConnection.<init>(MQQueueConnection.java:150)
at com.ibm.mq.jms.MQQueueConnectionFactory.createQueueConnection(MQQueueConnectionFactory.java:185)
at com.ibm.mq.jms.MQQueueConnectionFactory.createConnection(MQQueueConnectionFactory.java:1066)
at
I am starting the listener on VM startup using Servlet init method
public void init(ServletConfig config) throws ServletException {
logger.info("App Init");
try {
boolean isListnerOn = Boolean.parseBoolean(System.getProperty("listner", "false"));
logger.info(" startReceiver , listner flag is "+isListnerOn);
if(isListnerOn){
if (mqReceive == null) {
MyMessageListener jmsListner = new MyMessageListener();
mqReceive = new JMSMQReceiver(jmsListner);
}
if (mqReceive != null) {
try{
mqReceive.start();
} catch (Exception e) {
logger.error("Error starting the listner ", e);
}
}
}else{
logger.info(" listner not started as flag is "+isListnerOn);
}
} catch (Exception e) {
logger.error(e, e);
}
}
private MQQueueConnectionFactory mqQueueConnectionFactory;
public MQReceiver(MyMessageListener listner) {
userName=System.getProperty( "mqId","");
pwd=System.getProperty("mqId","");
host = System.getProperty(PREFIX + ".host");
port = Integer.parseInt(System.getProperty(PREFIX + ".port"));
qManager = System.getProperty(PREFIX + ".qManager");
channel = System.getProperty(PREFIX + ".channel");
queueName = System.getProperty(PREFIX + ".achqueueName");
logger.info("HOST:" + host + "\tPORT:" + port + "\tqManager:"+ qManager + "\tchannel:" + channel + "\tqueueName:"+ queueName);
try {
mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName(host);
mqQueueConnectionFactory.setChannel(channel);//communications link
mqQueueConnectionFactory.setPort(port);
mqQueueConnectionFactory.setQueueManager(qManager);//service provider
mqQueueConnectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
queueConnection = mqQueueConnectionFactory.createConnection(trustUserName, trustID);
session = queueConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
queue = session.createQueue(queueName);
((MQDestination) queue).setTargetClient(WMQConstants.WMQ_CLIENT_NONJMS_MQ);
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(listner);
logger.info(" Connect MQ successfully.");
} catch (JMSException e) {
logger.error("error" + e.getMessage(), e);
}
}
public void start() {
logger.info("Starting MQListener... ");
//mqListener.start();
try {
queueConnection.start();
logger.info("MQListener start successfully");
} catch (JMSException e) {
logger.error("error" + e.getMessage(), e);
}
}
The IBM MQ Classes for JMS client error JMSWMQ2013 can be caused by quite a few issues.
IBM Support Technote "WMQ 7.1 / 7.5 / 8.0 / 9.0 queue manager RC 2035 MQRC_NOT_AUTHORIZED or AMQ4036 or JMSWMQ2013 when using client connection as an MQ Administrator" has a good write up on diagnosing and resolving issues like this.
If you want more specific help, to start with please provide the following details by editing and adding them to your question.
Version of IBM MQ Classes for JMS used by the client application.
Version of IBM MQ installed on the IBM MQ queue manager
Errors in the queue manager's AMQERR01.LOG that happen at the same time as the error you receive in the IBM MQ Classes for JMS client application on the second VM..
It is strange that it works on the first VM and fails on the second. If the trustUserName, trustID are the same for both VMs, IBM MQ should accept them equally.
If you are using IBM MQ v8 or later native connection authentication it is possible the OS or PAM is denying the second connection. I have only seen this where pam_tally had a limit of 5 and more than 5 connection hit at the same time. It could be possible there is some sort of login limit of one login per user.
Per you comment it appears that you had a CHLAUTH ADDRESSMAP rule missing, the first VM's IP was allowed and the second VM's IP was not allowed. Depending on how the queue manager is configured and how the CHLAUTH rule is blocking the connection the queue manager may return MQRC 2035 to the client. On a IBM MQ Classes for JMS client this is returned as MQJMS2013. This could happen for instance if your queue manager is using ADOPTCTX(NO) and has a CHLAUTH rule that maps ADDRESS(*) to a MCAUSER that does not exist (ex: *NOACCESS) and other CHLAUTH rules that map connections from specific IPs to a user that does have access.
A more secure setup is to use ADOPTCTX(YES) which will tell MQ to set the MCAUSER to the id that is authenticated by CONNAUTH. You could also have a ADDRESSMAP rule with ADDRESS(*) USERSRC(NOACCESS) to block by default and then other rules with specific IPs and USERSRC(CHANNEL to allow those IPs you want to whitelist.
com.ssc.ach.mq.JMSMQReceiver[main]: errorMQJMS2013: invalid security authentication supplied for MQQueueManager
javax.jms.JMSSecurityException: MQJMS2013: invalid security authentication supplied for MQQueueManager
Is because:
queueConnection = mqQueueConnectionFactory.createConnection(trustUserName, trustID);
If you don't supply a valid UserId and Password that can be authenticated by the queue manager then your connection will rejected. I have no idea what you are passing into the method but it is supposed to be a valid UserId and Password for the remote system.
Also, I wish people would stop using the term 'MQ Listener' because you are not creating an 'MQ Listener', you are creating a consumer that is receiving messages.
An MQ listener is a component of MQ that accepts and handles incoming connections. See here.
The application I am working on needs to communicate to an IBM MQ server in a remote location. We currently have a working system using active MQ which uses a broker, and a bridge to connect to this remote IBM MQ server and is working fine.
Due to some new enhancement, we are now trying to achieve the same using IBM client jars instead of Active MQ.
The problem I am facing is that I can connect to the remote server's inboundQ and send messages.But I am always receiving null from the remote servers outbound Queue. But I have no way to check if the messages are received at the remote location. But the same message if sending through the old ActiveMQ system will get a response from remote MQ server.
Old Active MQ internally uses the bridge to connect to remote IBM MQ server which is configured exactly like the new code I am using.
I have tried multiple codes from internet and stack overflow itself and always I am able to connect but not getting any responses.
Also, I get no errors or exceptions while trying to send or receive from remote IBM MQ.
I will paste a sample code which I am trying to get to work. I have changed some configuration values in the code.
My doubts are the following.
All I am doing for this is copying IBM MQ client jars into the application and using the code to send messages to remote MQ. I have not installed any other application. Will such a system work or should there always be some intermediate program like active MQ?
The same code is able to send and receive from an IBM MQ server which I installed in our local network but fails to get a response from the remote server? This leads me to believe if I am missing anything in configuration ? should anything else be configured other than in code?
I see no errors or exceptions. Always message is sent but the response is null. I have not seen the usage of any username-password or public-private key authentication. Is there any authentication used normally to check the source. ?
I am using IBM MQ client 5.3 version which I know is old. But used that since they working active MQ setup uses the same and is working correctly. I have no way of knowing which version on IBM MQ server is present on the remote machine. Is there a problem if we use a different client MQ version than the version of server MQ version. ?
Sample code which works for me in local environment ie is able to send and receive from an IBM MQ server I have installed in another machine in the local network. The same code will fetch null response when I am trying to use it with remote IBM MQ server.
import javax.jms.*;
import javax.jms.JMSException;
import com.ibm.mq.jms.*;
import com.ibm.jms.JMSMessage;
import javax.jms.TextMessage;
public class SendReceive {
private MQQueueConnectionFactory connectionFactory;
private MQQueueConnection connection;
private void runTest() {
try {
connect();
connection.start();
MQQueueSession session = (MQQueueSession) connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
MQQueue queue = (MQQueue) session.createQueue("INBOUND_QUEUE"); /* values replaced with correct values in deployment server */
MQQueue queue2 = (MQQueue) session.createQueue("OUTBOUND_QUEUE"); /* values replaced with correct values in deployment server */
MQQueueSender sender = (MQQueueSender) session.createSender(queue);
MQQueueReceiver receiver = (MQQueueReceiver) session.createReceiver(queue2);
//TextMessage message = session.createTextMessage("yesyesyes");
String stt = "Test Message"; //
TextMessage message = session.createTextMessage(stt);
message.setJMSReplyTo(queue2);
sender.send(message);
System.out.println("Sent: " + message);
Message msg1 = receiver.receive(5000);
if(msg1!=null){
String responseMsg = ((TextMessage) msg1).getText();
System.out.println("Received: " + responseMsg);
}else{
System.out.println("Message received is null");
}
}catch(Exception e){
System.out.println("Exception caught in program : " + e);
e.printStackTrace();
}
}
public boolean connect() {
boolean connected = false;
try {
/* values below are replaced with correct values in deployment server */
connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setPort(1515);
connectionFactory.setHostName("192.168.1.23"); //
connectionFactory.setQueueManager("QCCMGR");
connectionFactory.setChannel("QCHANNEL");
connectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
connection = (MQQueueConnection) connectionFactory.createQueueConnection();
connected = true;
} catch (Exception e) {
connected = false;
}
return connected;
}
public static void main(String[] args) {
new SendReceive().runTest();
}
}
MQ v5.3 was released November 29th 2002 and has been out of support since September 28th 2007 (almost 9 years). The version may not have anything to do with your issue but I would strongly suggest that you move to a supported version of the MQ client. Newer MQ client versions can connect to older MQ queue managers. You can download a java only install of MQ 8.0 or MQ 9.0 jar files at the links below:
IBM MQ v8.0 Client
IBM MQ v9.0 Client
I read on some old threads that having the sender and receiver on the same session caused problems when a timeout was specified. Try adding another session for the receiver.
private void runTest() {
try {
connect();
connection.start();
MQQueueSession session = (MQQueueSession) connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
MQQueueSession session2 = (MQQueueSession) connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
MQQueue queue = (MQQueue) session.createQueue("INBOUND_QUEUE"); /* values replaced with correct values in deployment server */
MQQueue queue2 = (MQQueue) session2.createQueue("OUTBOUND_QUEUE"); /* values replaced with correct values in deployment server */
MQQueueSender sender = (MQQueueSender) session.createSender(queue);
MQQueueReceiver receiver = (MQQueueReceiver) session2.createReceiver(queue2);
//TextMessage message = session.createTextMessage("yesyesyes");
String stt = "Test Message"; //
TextMessage message = session.createTextMessage(stt);
message.setJMSReplyTo(queue2);
sender.send(message);
System.out.println("Sent: " + message);
Message msg1 = receiver.receive(5000);
if(msg1!=null){
String responseMsg = ((TextMessage) msg1).getText();
System.out.println("Received: " + responseMsg);
}else{
System.out.println("Message received is null");
}
}catch(Exception e){
System.out.println("Exception caught in program : " + e);
e.printStackTrace();
}
}
Try taking a JMS trace by adding the following to the execution of the java app:
-DMQJMS_TRACE_LEVEL=base
-DMQJMS_TRACE_DIR=/tracedirectory
ex: java -DMQJMS_TRACE_LEVEL=base -DMQJMS_TRACE_DIR=/tracedirectory JavaApp
This should produce a file that I believe ends in .trc in the directory you specify.
You can review this for errors that might help point you in the right direction.
Suggestions:
Try changing your program to force it to send a blank username:
connection = (MQQueueConnection) connectionFactory.createQueueConnection("", "");
Try closing the sender after sender.send is called
sender.close();
With out further information it is difficult to determine the cause. The more information you can gather the better.
It could be some network related configuration issue or maybe Windows Messaging not configured well. You may consider giving WebSphere MQ - Message Test Utility by IBM to ensure that there are no such system or network configuration issues are present.
I need to use Azure Redis Cluster, with password, with SSL, with pipelining support.
I was using Jedis until now but it lacks support for cluster+ssl+password+pipelining combo.
I tried lettuce (https://github.com/mp911de/lettuce/releases/tag/4.1.2.Final) and currently hit a connection issue I am not able to solve on my own.
Connecting to an Azure Redis Cluster (2 * P4) works without SSL but not with.
Also I can connect to a single node with SSL but without cluster support.
Problem is when combining cluster+ssl, the auth call times out (the command is sent over the wire but times out).
The cluster without SSL working code looks like this:
RedisURI redisURI = RedisURI.Builder.redis(host, 6379)
.withPassword(password)
.build();
RedisClusterClient client = RedisClusterClient.create(redisURI);
RedisAdvancedClusterCommands<String, String> connection = client.connect().sync();
connection.set("a", "1");
System.out.println(connection.get("a"));
Output is 1
Enabling SSL:
RedisURI redisURI = RedisURI.Builder.redis(host, 6380)
.withPassword(password)
.withSsl(true)
.build();
RedisClusterClient client = RedisClusterClient.create(redisURI);
RedisAdvancedClusterCommands<String, String> connection = client.connect().sync();
connection.set("a", "1");
System.out.println(connection.get("a"));
It hangs during 1 minute and log4j logs looks like this:
2016-05-26 14:25:17,110 | TRACE | lettuce-nioEventLoop-3-1 | CommandEncoder | [/{CLIENT} -> {HOST}/{IP}:6380] Sent: *2
$4
AUTH
$44
{PASSWORD}
2016-05-26 14:26:17,134 | WARN | main | ClusterTopologyRefresh | Cannot connect to RedisURI [host='***', port=6380]
com.lambdaworks.redis.RedisCommandTimeoutException: Command timed out
at com.lambdaworks.redis.LettuceFutures.await(LettuceFutures.java:95)
at com.lambdaworks.redis.LettuceFutures.awaitOrCancel(LettuceFutures.java:74)
at com.lambdaworks.redis.AbstractRedisAsyncCommands.auth(AbstractRedisAsyncCommands.java:64)
at com.lambdaworks.redis.cluster.RedisClusterClient.connectToNode(RedisClusterClient.java:342)
at com.lambdaworks.redis.cluster.RedisClusterClient.connectToNode(RedisClusterClient.java:301)
at com.lambdaworks.redis.cluster.ClusterTopologyRefresh.getConnections(ClusterTopologyRefresh.java:240)
at com.lambdaworks.redis.cluster.ClusterTopologyRefresh.loadViews(ClusterTopologyRefresh.java:132)
at com.lambdaworks.redis.cluster.RedisClusterClient.loadPartitions(RedisClusterClient.java:468)
at com.lambdaworks.redis.cluster.RedisClusterClient.initializePartitions(RedisClusterClient.java:445)
at com.lambdaworks.redis.cluster.RedisClusterClient.connectClusterImpl(RedisClusterClient.java:359)
at com.lambdaworks.redis.cluster.RedisClusterClient.connect(RedisClusterClient.java:244)
at com.lambdaworks.redis.cluster.RedisClusterClient.connect(RedisClusterClient.java:231)
at com.ubikod.ermin.reach.tools.Test.main(Test.java:20)
Exception in thread "main" com.lambdaworks.redis.RedisException: Cannot retrieve initial cluster partitions from initial URIs [RedisURI [host='***', port=6380]]
at com.lambdaworks.redis.cluster.RedisClusterClient.loadPartitions(RedisClusterClient.java:471)
at com.lambdaworks.redis.cluster.RedisClusterClient.initializePartitions(RedisClusterClient.java:445)
at com.lambdaworks.redis.cluster.RedisClusterClient.connectClusterImpl(RedisClusterClient.java:359)
at com.lambdaworks.redis.cluster.RedisClusterClient.connect(RedisClusterClient.java:244)
at com.lambdaworks.redis.cluster.RedisClusterClient.connect(RedisClusterClient.java:231)
at com.ubikod.ermin.reach.tools.Test.main(Test.java:20)
Keeping SSL and disabling cluster works:
RedisURI redisURI = RedisURI.Builder.redis(host, 6380)
.withPassword(password)
.withSsl(true)
.build();
RedisClient client = RedisClient.create(redisURI);
RedisCommands<String, String> connection = client.connect().sync();
connection.set("a", "1");
System.out.println(connection.get("a"));
So that's not just an SSL issue, its a SSL + cluster combo issue.
I tried to use withStartTls, disabling peer verification, raising the timeout, any combination of those without luck.
Any idea if it's a library bug or an Azure Redis bug?
I inspected the wiki page of lettuce, and I noticed the issue was not caused by a library bug or an Azure Redis bug, unfortunately, just only the lettuce not support Redis Cluster with SSL, please see the content below from the subsection Connecting to Redis using String RedisURI of the wiki page.
lettuce supports SSL only on regular Redis connections. Master resolution using Redis Sentinel or Redis Cluster are not supported since both strategies provide Redis addresses to the native port. Redis Sentinel and Redis Cluster cannot provide the SSL ports.