Based on the suggested solusion and following the example, I'm trying to delete a key right after I get a notification that another key has expired.
The problem is that under stress test with heavy load of seting 600K new keys and setting half of them with expiration time of 2 seconds, I get the following exception:
Exception in thread "main" redis.clients.jedis.exceptions.JedisConnectionException: Unknown reply: t
The question is what will be the best practice to write such listener? (thread pool? if so in what context to implement it?)
Jedis version: 2.7.2
Redis version: 2.8.19
My code so far:
Subscriber class:
public class Subscriber {
public static void main(String[] args) {
JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
Jedis jedis = pool.getResource();
jedis.psubscribe(new KeyExpiredListener(), "__key*__:*");
}
}
Listener class:
public class KeyExpiredListener extends JedisPubSub {
private String generalKeyTimeoutPrefix = "TO_";
#Override
public void onPMessage(String pattern, String channel, String message) {
String originalKey = null;
try {
if(channel.endsWith("__keyevent#0__:expired") && message.startsWith(generalKeyTimeoutPrefix)) {
originalKey = message.substring(generalKeyTimeoutPrefix.length());
del(originalKey);
}
} catch(Exception e) {
logger.error("..", e);
}
}
private void del(String key) {
Jedis jedis = new Jedis("localhost");
jedis.connect();
try {
jedis.del(key);
} catch (Exception e) {
logger.error("...");
} finally {
jedis.disconnect();
}
}
}
Key generator:
public class TestJedis {
public static void main(String[] args) throws InterruptedException {
JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
Jedis jedis = pool.getResource();
String prefixForlKeys = "token_";
String prefixForTimeoutKeys = "TO_";
for (int i = 0; i < 300000; i++) {
String key = prefixForlKeys + i;
String timeoutKey = prefixForTimeoutKeys + key;
jedis.set(key, "some_data");
jedis.set(timeoutKey, "");
jedis.expire(timeoutKey, 2);
}
System.out.println("Finished to create the keys");
}
}
the problem is with your implementation of del() method: it does not use connection pooling, does not reuse connections, so it finally occupies all available local ports. Try to use something like this:
private void del(String key) {
Jedis jedis = pool.getResource();
jedis.connect();
try {
jedis.del(key);
} catch (Exception e) {
e.printStackTrace( );
} finally {
jedis.close();
}
}
instead of opening/closing a connection for each expired key.
Related
We're building a WCF server (.NET 4.5). It will only use net.pipe transport.
When a client closes the PIPE connection, the server gets unhandled CommunicationException, and terminates.
Q1. How do I handle the CommunicationException so the server does not terminate and continues serving other clients?
Q2. In the handler, how do I get SessionId of the session that was aborted? I need this to do clean up some session-specific data.
Thanks in advance!
contract
[ServiceContract(CallbackContract = typeof(IContractCallback))]
public interface IContractServer
{
[OperationContract(IsOneWay = true)]
void Connect(bool status);
[OperationContract(IsOneWay = false)]
void Disconnect(IContractServer _channelCallback);
[OperationContract(IsOneWay = true)]
void Play(bool status);
}
service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service : IContractServer
{
public List<IContractCallback> _channeList = new List<IContractCallback>();
public void Connect(bool status)
{
IContractCallback a = OperationContext.Current.GetCallbackChannel<IContractCallback>();
int call = 0;
foreach (var callBack in _channeList)
{
if (callBack == a)
{
call++;
}
}
if (call == 0)
{
_channeList.Add(a);
a.ConnectCallback(true);
}
else
{
a.ConnectCallback(false);
}
}
public void Disconnect(IContractServer _channelCallback)
{
foreach (var contractCallback in _channeList)
{
if (contractCallback == _channelCallback)
{
_channeList.Remove(contractCallback);
}
}
}
public void Play(bool status)
{
foreach (var contractCallback in _channeList)
{
contractCallback.PlayCallback(status);
}
}
}
client
using System.ComponentModel;
using System.ServiceModel;
using System.Windows;
using Host;
namespace VideoPlayer
{
public partial class MainWindow : Window, IContractCallback
{
private IContractServer Proxy = null;
public MainWindow()
{
InitializeComponent();
InstanceContext context = new InstanceContext(this);
DuplexChannelFactory<IContractServer> factory = new DuplexChannelFactory<IContractServer>(context, new NetNamedPipeBinding(), "net.pipe://localhost");
Proxy = factory.CreateChannel();
Proxy.Connect(true);
}
public void ConnectCallback(bool status)
{
MessageBox.Show(status ? "connected" : "no connected");
}
public void PlayCallback(bool status)
{
if (status)
{
MessageBox.Show("status true");
}
else
{
MessageBox.Show("status false");
}
}
private void ButtonPlay(object sender, RoutedEventArgs e)
{
Proxy.Play(true);
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
//хочу отправить сообщение о закрытии
Proxy.Disconnect(Proxy);
}
I faced with this problem before in my duplex services when an event raised from server side the exception occurred if there was no alive channel between client and server so server dropped to Fault state and all next requests won't be responded,
For this reason I came to this conclusion to check the channel and if it was alive then let call back methods to be raised.
In service side the trick would be like ↓
bool IsChannelAlive()
{
Logging logging = new Logging(LogFile);
try
{
if (((ICommunicationObject)_callbackChannel).State == CommunicationState.Opened)
{
logging.Log(LoggingMode.Prompt, "Channeld is still alive, can raise events...");
return true;
}
}
catch (Exception exp)
{
logging.Log(LoggingMode.Error, "IsChannelAlive()=> failed, EXP: {0}", exp);
}
logging.Log(LoggingMode.Warning, "Channeld is not alive so events won't raised...");
return false;
}
and in one of my events I use it like :
void stran_OperationTimedOut(object sender, EventArgs e)
{
if (IsChannelAlive())
_callbackChannel.OnOperationTimedOut();
}
But for a while I use this trick to know closed channel to do something:
public ImportService()
{
//Handle ContextClose to Audit all actions made in session
OperationContext.Current.InstanceContext.Closed += delegate
{
//Here
};
}
which is not reliable.
I am still using that IsAliveChannel() in my services.
Hope this answer resolve your problem or give you the clue.
I use RabbitMQ for connection between parts my program. Version of RMQ(3.3.5). It used with java client from repo.
// Connection part
#Inject
public AMQService(RabbitMQConfig mqConfig) throws IOException {
this.mqConfig = mqConfig;
connectionFactory.setHost(mqConfig.getRABBIT_HOST());
connectionFactory.setUsername(mqConfig.getRABBIT_USERNAME());
connectionFactory.setPassword(mqConfig.getRABBIT_PASSWORD());
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setPort(mqConfig.getRABBIT_PORT());
connectionFactory.setVirtualHost(mqConfig.getRABBIT_VHOST());
Connection connection = connectionFactory.newConnection();
channel = connection.createChannel();
channel.basicQos(1);
}
//Consume part
private static void consumeResultQueue() {
final QueueingConsumer consumer = new QueueingConsumer(channel);
Future resultQueue = EXECUTOR_SERVICE.submit((Callable<Object>) () -> {
channel.basicConsume("resultQueue", true, consumer);
while (true) {
try {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody(), "UTF-8");
resultListener.onMessage(message);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
});
}
I want leave use inifinty loop. Can RMQ notify client while message can read from queue? Without check?
You can create a class which extends DefaultConsumer and override handleDelivery.
public class MyConsumer extends DefaultConsumer {
public MyConsumer(Channel channel) {
super(channel);
}
#Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
// do your computation
}
}
And register this consumer with channel.basicConsume(queueName, myConsumerInstance);
Note that by doing this, handleDelivery will run inside rabbitmq client thread pool so you should avoid any long computation inside this function.
I have an application that sends AMQP messages via RabbitMQ. message sending is triggered on an http request. Recently I have noticed that some messages appear to be getting lost (as in never delivered). I also noticed that the list of channels being managed by the server is steadily increasing. The first thing I have corrected is to close channels after they are no longer required. However, I am still not sure my code is correctly structured to ensure delivery. Two sections of code are below; the first is a section of a singleton that manages the connection (does not recreate on every call), the second is the sending code. Any advice / guidance would be appreciated.
#Service
public class PersistentConnection {
private static Connection myConnection = null;
private Boolean blocked = false;
#Autowired ApplicationConfiguration applicationConfiguration;
#Autowired ConfigurationService configurationService;
#PostConstruct
private void init() {
}
#PreDestroy
private void destroy() {
try {
myConnection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public Connection getConnection( ) {
if (myConnection == null) {
start();
}
else if (!myConnection.isOpen()) {
log.warn("AMQP Connection closed. Attempting to start.");
start();
}
return myConnection;
}
private void start() {
log.debug("Building AMQP Connection");
ConnectionFactory factory = new ConnectionFactory();
String ipAddress = applicationConfiguration.getAMQPHost();
String password = applicationConfiguration.getAMQPUser();
String user = applicationConfiguration.getAMQPPassword();
String virtualHost = applicationConfiguration.getAMQPVirtualHost();
String port = applicationConfiguration.getAMQPPort();
try {
factory.setUsername(user);
factory.setPassword(password);
factory.setVirtualHost(virtualHost);
factory.setPort(Integer.parseInt(port));
factory.setHost(ipAddress);
myConnection = factory.newConnection();
}
catch (Exception e) {
e.printStackTrace();
}
myConnection.addBlockedListener(new BlockedListener() {
public void handleBlocked(String reason) throws IOException {
// Connection is now blocked
blocked = true;
}
public void handleUnblocked() throws IOException {
// Connection is now unblocked
blocked = false;
}
});
}
public Boolean isBlocked() {
return blocked;
}
}
/*
* Sends ADT message to AMQP server.
*/
private void send(String routingKey, String message) throws Exception {
String exchange = applicationConfiguration.getAMQPExchange();
String exchangeType = applicationConfiguration.getAMQPExchangeType();
Connection connection = myConnection.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exchange, exchangeType);
channel.basicPublish(exchange, routingKey, null, message.getBytes());
// Close the channel if it is no longer needed in this thread
channel.close();
}
Try this code:
#Service
public class PersistentConnection {
private Connection myConnection = null;
private Boolean blocked = false;
#Autowired ApplicationConfiguration applicationConfiguration;
#Autowired ConfigurationService configurationService;
#PostConstruct
private void init() {
start(); /// In this way you can initthe connection and you are sure it is called only one time.
}
#PreDestroy
private void destroy() {
try {
myConnection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public Connection getConnection( ) {
return myConnection;
}
private void start() {
log.debug("Building AMQP Connection");
ConnectionFactory factory = new ConnectionFactory();
String ipAddress = applicationConfiguration.getAMQPHost();
String password = applicationConfiguration.getAMQPUser();
String user = applicationConfiguration.getAMQPPassword();
String virtualHost = applicationConfiguration.getAMQPVirtualHost();
String port = applicationConfiguration.getAMQPPort();
try {
factory.setUsername(user);
factory.setPassword(password);
factory.setVirtualHost(virtualHost);
factory.setPort(Integer.parseInt(port));
factory.setHost(ipAddress);
myConnection = factory.newConnection();
}
catch (Exception e) {
e.printStackTrace();
}
myConnection.addBlockedListener(new BlockedListener() {
public void handleBlocked(String reason) throws IOException {
// Connection is now blocked
blocked = true;
}
public void handleUnblocked() throws IOException {
// Connection is now unblocked
blocked = false;
}
});
}
public Boolean isBlocked() {
return blocked;
}
}
/*
* Sends ADT message to AMQP server.
*/
private void send(String routingKey, String message) throws Exception {
String exchange = applicationConfiguration.getAMQPExchange();
String exchangeType = applicationConfiguration.getAMQPExchangeType();
Connection connection = myConnection.getConnection();
if (connection!=null){
Channel channel = connection.createChannel();
try{
channel.exchangeDeclare(exchange, exchangeType);
channel.basicPublish(exchange, routingKey, null, message.getBytes());
} finally{
// Close the channel if it is no longer needed in this thread
channel.close();
}
}
}
This could be enough, you have an connection with rabbitmq when the system starts.
If you an lazy singleton, the code is just a bit different.
I suggest to not use isOpen() method, please read here:
isOpen
boolean isOpen() Determine whether the component is currently open.
Will return false if we are currently closing. Checking this method
should be only for information, because of the race conditions - state
can change after the call. Instead just execute and try to catch
ShutdownSignalException and IOException Returns: true when component
is open, false otherwise
EDIT**
Question 1:
What are you looking for is the HA client.
RabbitMQ java client by default doesn't support this features, since the version 3.3.0 supports only the reconnect,read this:
...allows Java-based clients to reconnect automatically after network
failure. If you want be sure about your messages you have to create an
robust client able to resists to all fails.
Generally you should consider the fails, for example:
what happen if there is an error during the message publish?
In your case you simply lose the message,You should re-queue the message manually.
Question 2:
I don’t know your code, but connection == null shouldn’t happen, because this procedure is called for first:
#PostConstruct
private void init() {
start(); /// In this way you can initthe connection and you are sure it is called only one time.
}
Anyway you can raise an exception, the question is:
What do I have to do with the message that I was trying to send?
See the question 1
I’d like to suggest to read more about the HA, for example this:
http://www.rabbitmq.com/ha.html
https://www.rabbitmq.com/reliability.html
And this for the client:
https://github.com/jhalterman/lyra (I never used it)
Create a reliable system with rabbitmq is not complex, but you should know some basic concept.
Anyway .. Let me know!
some configurations here:
non-durable consumer,non-persistent message,disabled flow control,default prefetch size,optimizeAcknowledge = true,asynsend = true, use jms to connect ActiveMQ
for example,
one producer、one consumer,
Producer————Topic————consumer
the producer send rate can reach 6k/s
but,in this case:
one producer three consumer,
/——consumer
Producer——-Topic——-consumer
\——consumer
the producer send rate drop down to 4k/s
Here is my some of the key code:
sender class:
public class sender {
public Boolean durable=false;
public String clientID=null;
public Boolean transacted=false;
public int ackMode=Session.AUTO_ACKNOWLEDGE;
public int timeToLive=0;
public String queuename = "";
public int persistent = DeliveryMode.NON_PERSISTENT;
public Connection createConnection(String user,String pwd,String url) throws JMSException, Exception {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, pwd, url);
connectionFactory.setDispatchAsync(true);
//connectionFactory.setAlwaysSessionAsync(false);
Connection connection = connectionFactory.createConnection();
if (durable && clientID!=null) {
connection.setClientID(clientID);
}
connection.start();
return connection;
}
public Session createSession(Connection connection) throws Exception {
Session session = connection.createSession(transacted, ackMode);
return session;
}
public MessageProducer createProducer(Session session) throws JMSException {
Queue destination = session.createQueue(queuename);
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(persistent);
if( timeToLive!=0 )
producer.setTimeToLive(timeToLive);
return producer;
}
public void onMessage(Message message) {
//process message
}
}
sendmain method:
public static void main(String[] args) throws JMSException, Exception {
// TODO Auto-generated method stub
sender s = new sender();
s.persistent = DeliveryMode.NON_PERSISTENT;
Connection c = s.createConnection("","","tcp://localhost:61616?jms.useAsyncSend=true");
Session sess = s.createSession(c);
Topic topic = sess.createTopic("topic.test");
MessageProducer mp = sess.createProducer(topic);
StringBuffer tmpsb=new StringBuffer();
for (int j=0;j<1024;j++)
{
tmpsb.append("0");
}
Message m = sess.createTextMessage(tmpsb.toString());
long pre=System.currentTimeMillis();
for (int i=0;i<10000;i++)
{
mp.send(m);
}
long post=System.currentTimeMillis();
mp.close();
System.out.println("sendtime:"+(post-pre));
System.out.println("sendrate:"+10000000/(float)(post-pre));
System.out.println("timenow:"+(post));
}
receiver class code:
public class receiver implements MessageListener
{
public int receivetimes=0;
public long pretime;
public void onMessage(Message msg)
{
//TextMessage tm = (TextMessage) msg;
try {
if (receivetimes==0)
{
pretime=System.currentTimeMillis();
}
receivetimes+=1;
if (receivetimes==10000)
{
long now=System.currentTimeMillis();
System.out.println("time:"+(now-pretime)+"\nrecive rate:"+9999999/(float)(now-pretime));
System.out.println("timenow:"+(now));
receivetimes=0;
}
} catch(Throwable t) {
t.printStackTrace();
}
}
}
receiver class code here has hide some methods,such as createConnection,createSession or something just like sender class does.
receiver main method:
public static void main(String[] args) throws JMSException, Exception {
// TODO Auto-generated method stub
receiver s = new receiver();
Connection c = s.createConnection("","","tcp://localhost:6151?jms.optimizeAcknowledge=true");
Session sess = s.createSession(c);
Topic destination = sess.createTopic("topic.test");
MessageConsumer consumer = sess.createConsumer(destination);
consumer.setMessageListener(new receiver());
}
Every consumer is in a standalone process. I ran three consumer and one producer then I got a result of bad performance. Does any one knows why I get this?
Just as #TimBish said.
The issue is 'producer,consumer,activemq-server on same machine'.When separated,the issue never shows up.
It is so important to test something in a strict way.......
I am just trying to create a simple MINA server and client to evaluate. Here is my code.
public class Server {
private static final int PORT = 8080;
static class ServerHandler extends IoHandlerAdapter {
#Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
cause.printStackTrace();
}
#Override
public void sessionCreated(IoSession session) {
System.out.println("session is created");
session.write("Thank you");
}
#Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("session is closed.");
}
#Override
public void messageReceived(IoSession session, Object message) {
System.out.println("message=" + message);
session.write("Reply="+message);
}
}
/**
* #param args
*/
public static void main(String[] args) throws Exception {
SocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler(new Server.ServerHandler());
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind(new InetSocketAddress(PORT));
System.out.println("Listening on port " + PORT);
for (;;) {
Thread.sleep(3000);
}
}
}
public class Client {
private static final int PORT = 8080;
private IoSession session;
private ClientHandler handler;
public Client() {
super();
}
public void initialize() throws Exception {
handler = new ClientHandler();
NioSocketConnector connector = new NioSocketConnector();
connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.setHandler(handler);
for (;;) {
try {
ConnectFuture future = connector.connect(new InetSocketAddress(PORT));
future.awaitUninterruptibly();
session = future.getSession();
break;
} catch (RuntimeIoException e) {
System.err.println("Failed to connect.");
e.printStackTrace();
Thread.sleep(5000);
}
}
if (session == null) {
throw new Exception("Unable to get session");
}
Sender sender = new Sender();
sender.start();
session.getCloseFuture().awaitUninterruptibly();
connector.dispose();
System.out.println("client is done.");
}
/**
* #param args
*/
public static void main(String[] args) throws Exception {
Client client = new Client();
client.initialize();
}
class Sender extends Thread {
#Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.messageSent(session, "message");
}
}
class ClientHandler extends IoHandlerAdapter {
#Override
public void sessionOpened(IoSession session) {
}
#Override
public void messageSent(IoSession session, Object message) {
System.out.println("message sending=" + message);
session.write(message);
}
#Override
public void messageReceived(IoSession session, Object message) {
System.out.println("message receiving "+ message);
}
#Override
public void exceptionCaught(IoSession session, Throwable cause) {
cause.printStackTrace();
}
}
}
When I execute this code, the Client seems to keep sending a message instead of stopping after it sends. It looks to me that there is a recursive call in underlying MINA code. I know that I am doing something wrong.
Can somebody tell me how to fix this?
Thanks.
Try to initialize and start your sender and use the session within sessionOpened (ClientHandler)