I have written a producer application that enqueue JMS messages by using Executer Service in activeMQ and it is working finely but the problem is it's taking long time to enqueue messages .
there are three files:
1. ExecutePushServer.java
2. ActiveMQProducer.java
3. SendPush.java
ExecutePushServer.java:
package com.rh.pushserver;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
public class ExecutePushServer {
/**
* #uthor ankit
*/
static int maxThread = 0;
static BufferedReader br = null;
static String fileLocation = null;
static List<String> tokenList = new ArrayList<String>();
private static String txt;
static Properties configFile = new Properties();
private final static Logger logger = Logger
.getLogger(ExecutePushServer.class.getName());
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
configFile.load(ExecutePushServer.class.getClassLoader()
.getResourceAsStream("config.properties"));
maxThread = Integer.valueOf(configFile.getProperty("POOL_SIZE"));
fileLocation = configFile.getProperty("LOCATION");
txt = configFile.getProperty("txt");
logger.info("Message text is : " + txt);
br = new BufferedReader(new FileReader(fileLocation));
ActiveMQProducer mqProducer = new ActiveMQProducer();
tokenList = getList(br);
logger.info("tokenList created.");
ExecutorService executor = Executors.newFixedThreadPool(maxThread);
for (String id : tokenList) {
Runnable work = new SendPush(mqProducer, id);
executor.execute(work);
}
// This will make the executor accept no new threads
// and finish all existing threads in the queue
logger.info("All Ids Entered in Pool.");
executor.shutdown();
while (!executor.awaitTermination(10, TimeUnit.MINUTES)) {
logger.info("Inside awaitTermination");
}
mqProducer.closeConnection();
} catch (IOException e) {
logger.error("Error in Reading File" + e);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
logger.error("Error in termination of executer" + e);
} finally {
try {
if (br != null)
br.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private static List<String> getList(BufferedReader br) {
// TODO Auto-generated method stub
String currentLine;
try {
while ((currentLine = br.readLine()) != null) {
tokenList.add(currentLine);
}
return tokenList;
} catch (IOException e) {
logger.error("Error occured in creating tokenList !" + e);
return null;
}
}
}
ActiveMQProducer.java
package com.rh.pushserver;
import java.io.IOException;
import java.util.Properties;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.log4j.Logger;
public class ActiveMQProducer {
/**
* #uthor ankit
*/
private final String url = ActiveMQConnection.DEFAULT_BROKER_URL;
private final String subject = "PUSH_NOTIFICATION";
private Connection connection;
private Session session;
private String txt=null;
private MessageProducer producer;
private MapMessage mapMessage;
static Properties configFile = new Properties();
private final static Logger logger=Logger.getLogger(ActiveMQProducer.class.getName());
public ActiveMQProducer() {
try {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
connection = connectionFactory.createConnection();
connection.start();
logger.info("Connection Created.");
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(subject);
producer = session.createProducer(destination);
logger.info("Producer generated");
configFile.load(ActiveMQProducer.class.getClassLoader().getResourceAsStream("config.properties"));
txt=configFile.getProperty("txt");
mapMessage = session.createMapMessage();
} catch (JMSException e) {
// TODO Auto-generated catch block
logger.error("Error JMS Exception occured in creating connection"+e);
} catch (IOException e) {
// TODO Auto-generated catch block
logger.error("Exception occured while opening file "+e);
}
}
public MessageProducer getProducer() {
return producer;
}
public void enqueueMessage(String id){
try {
mapMessage.setString("ID", id);
mapMessage.setString("DISPLAY_STRING", txt);
mapMessage.setInt("BADGE_COUNT", 1);
mapMessage.setString("DEVICE_TYPE", "ANDROID");
producer.send(mapMessage);
logger.info("Sent on : "+id);
} catch (JMSException e) {
// TODO Auto-generated catch block
logger.error("Error while Enqueue"+e);
}
}
public void closeConnection(){
try {
connection.close();
logger.info("Connection closed");
} catch (JMSException e) {
// TODO Auto-generated catch block
logger.error("Error in connection closer"+e);
}
}
}
SendPush.java
package com.rh.pushserver;
public class SendPush implements Runnable {
/**
* #uthor ankit
*/
private String id;
private ActiveMQProducer mqProducer;
public SendPush(ActiveMQProducer mqProducer,String id) {
this.id=id;
this.mqProducer=mqProducer;
}
#Override
public void run() {
mqProducer.enqueueMessage(id);
}
}
please help me !!
The first thing I'd look at is your thread usage; you're creating a new thread for each message, which could definitely be a big part of why your performance isn't faster. Why can't you have your threads run a loop that sends messages till they run out of messages to send, and then have N threads that split the work?
You'll also probably want to run your reader logic in a separate thread, where it reads the file as fast as it can and hands off the things it reads to the threads, so you don't have to wait while the file is read to even get started. Make sure you make the data structures that you use to pass data between the reader threads and the message threads are thread-safe!
Once you do that, if the speed isn't where you want it to be, look at the broker's configuration. (And post it here, if you want someone to look at it.) In particular, if your messages are persistent, then look at where they're being persisted and see if there are other options that would be faster. (JDBC storage is generally the slowest persistence option, so consider other options.) Or you might even be able to make your messages non-persistent to make them faster; you'll have to decide if you can live with that trade-off. Figure out whether your messages are being passed asynchronously or synchronously; if sync, you might want to enable async instead. And make sure that producer flow control isn't kicking in (check the broker logs); if it is, then your consumers are probably too slow and are slowing down your producers.
Related
I am trying to interact with RabbitMQ server using RabbitMQ-java client API.
I read from the java client api guide:
As a rule of thumb, sharing Channel instances between threads is something to be avoided. Applications should prefer using a Channel per thread instead of sharing the same Channel across multiple threads.
I am trying to use a ThreadPoolExecutor with corePoolSize 1 and adding Runnable tasks to save messages in RabbitMQ queues. Here is the code that I am using:
package common;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
import com.rabbitmq.client.BlockedListener;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;
public class RabbitMQUtil {
private static Logger log= LoggerFactory.getLogger("logger");
private static RabbitMQUtil gmInstance;
private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10000));
private final String PROPERTIES_FILE_NAME = "config/rabbitmq.properties";
private final Properties properties = new Properties();
private String host = null;
private int port = 0;
private String username = null;
private String password = null;
private String useSSL = "false";
private ConnectionFactory factory;
private Connection connection;
private Channel channel;
private RabbitMQUtil() throws IOException, TimeoutException, Exception {
try {
InputStream stream = RabbitMQUtil.class.getClassLoader().getResourceAsStream(PROPERTIES_FILE_NAME);
if(stream != null) {
properties.load(stream);
}
} catch (Exception ex) {
log.error("Exception while loading the rabbitmq properties file:", ex);
}
host = properties.getProperty("rabbitmq.host", "localhost");
port = Integer.parseInt(properties.getProperty("rabbitmq.port", "5672"));
username = properties.getProperty("rabbitmq.username", "guest");
password = properties.getProperty("rabbitmq.password", "guest");
useSSL = properties.getProperty("rabbitmq.usessl", "false");
factory = new ConnectionFactory();
factory.setHost(host);
factory.setPort(port);
factory.setUsername(username);
factory.setPassword(password);
if("true".equalsIgnoreCase(useSSL)) {
try {
factory.useSslProtocol();
} catch (KeyManagementException | NoSuchAlgorithmException e) {
log.error("Exception while applying the tls for rabbitmq:", e);
}
}
connection = factory.newConnection();
connection.addBlockedListener(new RabbitMQBlockedListener());
connection.addShutdownListener(new RabbitMQShutDownListener());
channel = connection.createChannel();
}
public static RabbitMQUtil getInstance() {
if(gmInstance == null) {
synchronized (RabbitMQUtil.class) {
if(gmInstance == null) {
try {
gmInstance = new RabbitMQUtil();
} catch (IOException | TimeoutException e) {
log.error("Exception in getInstance:", e);
} catch (Exception e) {
log.error("Exception in getInstance:", e);
}
}
}
}
return gmInstance;
}
public static void saveErrorMessagesInLogs(JsonObject obj, String queueName) {
log.info("data to be saved for :"+queueName+" is:"+obj.toString());
}
public void saveMsgInQueue(JsonObject gson, String queueName) {
this.executor.execute(new RabbitMQData(gson, queueName));
}
private class RabbitMQBlockedListener implements BlockedListener {
#Override
public void handleBlocked(String arg0) throws IOException {
log.warn("blocked listener called:", arg0);
}
#Override
public void handleUnblocked() throws IOException {
log.warn("unblocked listener called:");
}
}
private class RabbitMQShutDownListener implements ShutdownListener {
#Override
public void shutdownCompleted(ShutdownSignalException cause) {
log.error("Shutdown event listener called:", cause);
log.error("shutdown event listener:"+cause.isHardError());
}
}
private class RabbitMQData implements Runnable{
JsonObject obj;
String queueName;
public RabbitMQData(JsonObject obj, String queueName) {
Thread.currentThread().setName("RabbitMQ Thread:"+obj.get("userid")+" -->"+queueName);
this.obj = obj;
this.queueName = queueName;
}
#Override
public void run() {
try {
channel.queueDeclare(this.queueName, true, false, false, null);
channel.basicPublish("", this.queueName, MessageProperties.PERSISTENT_BASIC, this.obj.toString().getBytes());
} catch (Exception e) {
log.info("Error while running the scheduled rabbitmq task:", e);
log.info("data to be saved for :"+this.queueName+" is:"+this.obj.toString());
}
}
}
public static void saveRabbitMQData(JsonObject obj, String queueName) {
RabbitMQUtil util = RabbitMQUtil.getInstance();
if(util != null)
util.saveMsgInQueue(obj, queueName);
else
RabbitMQUtil.saveErrorMessagesInLogs(obj, queueName);
}
}
I would like to know the following things:
Is is fine to use a single channel when a threadpool of only 1 thread is used ?
How should connection and channel objects be handled when blocked/unblocked and shutdown events are triggered ? Although the API automatically establishes connection when RabbitMQ server is up again.
Any other feedback will be appreciated.
Thank you
1.- Is is fine to use a single channel when a threadpool of only 1 thread is used ?
yes, it is fine. that is the way you should do it. only one thread must use a Channel instance. Otherwise, confirmations might be lost (see here: https://www.rabbitmq.com/releases/rabbitmq-java-client/v3.1.1/rabbitmq-java-client-javadoc-3.1.1/com/rabbitmq/client/Channel.html)
2.- How should connection and channel objects be handled when blocked/unblocked and shutdown events are triggered ? Although the API automatically establishes connection when RabbitMQ server is up again.
when the application is shutting down, you should close the channel, then close the connection to RabbitMQ.
channel.close();
conn.close();
about blocking/unblocking, please read here (https://www.rabbitmq.com/api-guide.html) :
Callbacks to Consumers are dispatched in a thread pool separate from the thread that instantiated its Channel. This means that Consumers can safely call blocking methods on the Connection or Channel, such as Channel#queueDeclare or Channel#basicCancel.
Each Channel has its own dispatch thread. For the most common use case of one Consumer per Channel, this means Consumers do not hold up other Consumers. If you have multiple Consumers per Channel be aware that a long-running Consumer may hold up dispatch of callbacks to other Consumers on that Channel.
Disk.class implementation
package server;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import services.CustomerData;
public class Disk implements MessageListener{
private int index;
private FileWriter f;
private BufferedWriter b;
public Disk(int i){
this.index=i;
try {
f = new FileWriter("disk"+i+".txt",true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
b = new BufferedWriter(f);
}
#Override
public void onMessage(Message m) {
try {
if(m instanceof ObjectMessage){
CustomerData c = (CustomerData) ((ObjectMessage) m).getObject();
b.write(c.getSurname()+" "+c.getName()+" "+c.getAge());
b.newLine();
b.flush();
System.out.println("disk"+index+".txt saved");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
So, what happens is that every message received by every message listener is saved in the same file (the last indexed disk.txt file) but I want to save them in every single file, from 0 to N. N txt files are created but they are not modified except the last one.
EDIT: I added the FileWriter and BufferedWriter in the Disk contructor but it will create N files but modify the last one only.
Main class there Disk is created:
package server;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.Hashtable;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class StorageServer {
public static final int N = 10;
public static void main(String[] args) throws RemoteException {
Hashtable<String,String> prop = new Hashtable<String,String>();
prop.put("java.naming.factory.initial", "org.apache.activemq.jndi.ActiveMQInitialContextFactory");
prop.put("java.naming.provider.url", "tcp://127.0.0.1:61616");
prop.put("topic.req", "requests");
System.setProperty("org.apache.activemq.SERIALIZABLE_PACKAGES","*");
try {
Context jndiCon = new InitialContext(prop);
TopicConnectionFactory tConnFact = (TopicConnectionFactory) jndiCon.lookup("TopicConnectionFactory");
TopicConnection tConn = tConnFact.createTopicConnection();
TopicSession tSess = tConn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = (Topic) jndiCon.lookup("req");
TopicSubscriber subscriber = tSess.createSubscriber(topic);
tConn.start();
for(int i=0; i<N; i++){
//FileWriter file = new FileWriter("disk"+i+".txt",true);
subscriber.setMessageListener(new Disk(i));
System.out.println("New disk"+i+" started");
}
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
You have a single TopicSubscriber which has a single MessageListener (hence the setMessageListener and not addMessageListener). You need to create a separate TopicSubscriber for each listener with
for(int i=0; i<N; i++){
TopicSubscriber subscriber = tSess.createSubscriber(topic);
subscriber.setMessageListener(new Disk(i));
System.out.println("New disk"+i+" started");
}
I'd also recommend avoiding using the FileWriter (and FileReader) class, because it uses the platform encoding. This can cause surprises when platform (or its encoding) changes. The equivalent, but longer and safer way is:
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("whatever.txt"), "UTF-8"));
With UTF-8 being a safe encoding to use.
I would like to know how can i get list of destinations (queues or topics) on a remote broker in ActiveMQ in Java without using JMX. I tried to get topics by DestinationSource class, but it doesn't work. In this thread I found that advisory support have to be enabled on the client and the broker. I found that advisory support is enabled by default.
Here is my broker
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.apache.activemq.api.core.TransportConfiguration;
import org.apache.activemq.core.config.Configuration;
import org.apache.activemq.core.config.impl.ConfigurationImpl;
import org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory;
import org.apache.activemq.core.server.ActiveMQServer;
import org.apache.activemq.core.server.ActiveMQServers;
public class Server {
public static void main(final String arg[]) throws Exception
{
try
{
// Step 1. Create the Configuration, and set the properties accordingly
Configuration configuration = new ConfigurationImpl();
//we only need this for the server lock file
configuration.setJournalDirectory("target/data/journal");
configuration.setPersistenceEnabled(false); // http://activemq.apache.org/what-is-the-difference-between-persistent-and-non-persistent-delivery.html
configuration.setSecurityEnabled(false); // http://activemq.apache.org/security.html
/**
* this map with configuration values is not necessary (it configures the default values).
* If you want to modify it to run the example in two different hosts, remember to also
* modify the client's Connector at {#link EmbeddedRemoteExample}.
*/
Map<String, Object> map = new HashMap<String, Object>();
map.put("host", "localhost");
map.put("port", 61616);
// https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/HornetQ_User_Guide/ch14s04.html
TransportConfiguration transpConf = new TransportConfiguration(NettyAcceptorFactory.class.getName(),map);
HashSet<TransportConfiguration> setTransp = new HashSet<TransportConfiguration>();
setTransp.add(transpConf);
configuration.setAcceptorConfigurations(setTransp); // https://github.com/apache/activemq-6/blob/master/activemq-server/src/main/java/org/apache/activemq/spi/core/remoting/Acceptor.java
// Step 2. Create and start the server
ActiveMQServer server = ActiveMQServers.newActiveMQServer(configuration);
server.start();
}
catch (Exception e)
{
e.printStackTrace();
throw e;
}
}
}
And client, where I want to get created topic
import java.util.Set;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.advisory.DestinationSource;
import org.apache.activemq.command.ActiveMQTopic;
public class MessageConsumer implements ExceptionListener{
ActiveMQConnection connection = null;
javax.jms.MessageConsumer consumer = null;
Session session = null;
public MessageConsumer(){
try {
// create a new intial context, which loads from jndi.properties file
javax.naming.Context context = new javax.naming.InitialContext();
// Create a ConnectionFactory
ActiveMQConnectionFactory connectionFactory = (org.apache.activemq.ActiveMQConnectionFactory)context.lookup("ConnectionFactory");
// Create a Connection
connection= (ActiveMQConnection) connectionFactory.createConnection();
DestinationSource destSource = connection.getDestinationSource();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Topic or Queue)
Destination destination = new ActiveMQTopic("MyTopic");
// Create a MessageConsumer from the Session to the Topic or Queue
consumer = session.createConsumer(destination);
connection.start();
Thread.sleep(3000);
Set<ActiveMQTopic> b = destSource.getTopics();
MessageListener listener = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message"
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
};
consumer.setMessageListener(listener);
} catch (Exception e) {
System.out.println("Caught: " + e);
e.printStackTrace();
}
}
#Override
public void onException(JMSException exception) {
System.out.println("JMS Exception occured. Shutting down client.");
}
public void close(){
// Clean up
try {
consumer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
destSource.getTopics() returns set of 0 topics.
I am trying to use multiple threads to consume jms queue.
I know that there should be a separate JMS Session for each Thread and that what I did in my code as shown below. But I am getting a weird exception
here is the exception stack trace:
javax.jms.IllegalStateException: Forbidden call on a closed connection.
at org.objectweb.joram.client.jms.Connection.checkClosed(Connection.java:404)
at org.objectweb.joram.client.jms.Connection.createSession(Connection.java:530)
at MessageWorker.run(ReceiveJmsDemoMultiThreaded.java:96)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
I need your help because this is a blocking issue for me
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class ReceiveJmsDemoMultiThreaded {
public static void main(String[] args) {
Context context = null;
ConnectionFactory factory = null;
Connection connection = null;
Destination destination = null;
try {
context = getInitialContext();
factory = (QueueConnectionFactory) context.lookup("JQCF");
destination = (Destination) context.lookup("sampleQueue");
connection = factory.createConnection();
final ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new MessageWorker(connection, destination) );
executor.submit(new MessageWorker(connection, destination) );
executor.submit(new MessageWorker(connection, destination) );
executor.submit(new MessageWorker(connection, destination) );
connection.start();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (context != null) {
try {
context.close();
} catch (NamingException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
private static InitialContext getInitialContext() throws NamingException {
Properties prop = new Properties();
prop.put("java.naming.provider.url", "rmi://localhost:1099");
prop.put("java.naming.factory.initial",
"org.objectweb.carol.jndi.spi.MultiOrbInitialContextFactory");
return new InitialContext(prop);
}
}
class MessageWorker extends Thread {
Connection connection = null;
Destination dest = null;
Session session = null;
Destination destination = null;
public MessageWorker(Connection connection, Destination dest) {
this.connection = connection;
this.destination = dest;
}
#Override
public void run() {
try {
MessageConsumer receiver = null;
System.out.println("Starting Thread "+currentThread().getName());
while (true) {
try {
System.out.println("Waiting for next msg "+currentThread().getName());
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
receiver = session.createConsumer(destination);
Message msg = receiver.receive();
if (msg instanceof Message && msg != null) {
System.out.println("STARTING consuming "+msg.toString()+" by thread "+currentThread().getName() );
Thread.sleep(2000);//some work here
System.out.println("ENDING consuming "+msg.toString()+" by thread "+currentThread().getName() );
}
} catch (JMSException e) {
e.printStackTrace();
System.exit(1);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
Many thanks
You are seeing this problem because, in the main thread, after submitting the jobs to the Executor Service, the connection is closed using:
connection.close();
So, when the threads try to create a Session using this shared connection (which just got closed), they are getting this exception. Nothing unexpected here.
Just for testing, you can make your main thread sleep for a long time until all your threads are done receiving messages. This way, you can confirm that you dont receive this exception.
A real solution may be to shutdown the Executor service and make the main thread awaitTermination() to wait for completion of the submitted jobs.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
i try to program a part of a university research project about multi client - server socket programming.my code works as well as so that i give valide result but the problem is that evalutor of our group said that my code have not a good speed on connection for data transfer.i will be thankfull if you found the problem(s) in my code that cause this issue.
server part:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import j
ava.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
/**
*
*/
/**
* #author Sina
*
*/
public class BoxServer {
ServerSocket serversocket;
static ThreadHandler t[]=new ThreadHandler[100];
static int size=0;
static ArrayList<Message> messagebox=new ArrayList<Message>();
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(79);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while(true)
{
try{
//InetAddress inetadress=InetAddress.getLocalHost();
//System.out.println(inetadress);
//System.out.println(inetadress.getHostName());
//System.out.println(inetadress.getHostAddress());
Socket socket=serverSocket.accept();
if(socket==null)
{
System.out.println("null");
}
t[size]=new ThreadHandler(socket,"username");
size++;
t[size-1].start();
}
catch(UnknownHostException e){
System.out.println("salam s");
System.out.println(e.getMessage());
}
catch (IOException e) {
System.out.println("bye s");
System.out.println(e.getMessage());
}
}
}
}
class ThreadHandler extends Thread{
private String socname;
Socket mySocket;
ObjectInputStream inp;
ObjectOutputStream outp;
public ThreadHandler(Socket s,String socketName)
{
this.mySocket=s;
this.socname=socketName;
}
public void run()
{
try {
inp=new ObjectInputStream(mySocket.getInputStream());
outp=new ObjectOutputStream(mySocket.getOutputStream());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while(true)
{
System.out.println("thread run");
System.out.println(mySocket.getLocalPort());
System.out.println(mySocket.getLocalAddress());
try {
// System.out.println("my socket:"+mySocket.getOutputStream());
System.out.println(mySocket.isConnected());
System.out.println(inp.available());
System.out.println("inp = "+inp);
System.out.println("reeead "+ inp.readObject());
Message mess=(Message)inp.readObject();
System.out.println("dsd");
System.out.println("mess: "+mess);
BoxServer.messagebox.add(mess);
if(mess.getReceiver().equals("system-use:code=1"))
{
System.out.println(mess.getSender()+" wants to see his/her Inbox");
}
//mySocket.close();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("bug dar thread");
e.printStackTrace();
}
}
}
}
client part
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import javax.swing.Timer;
public class Main {
/**
* #param args
*/
static Socket socket=new Socket();
public static void main(String[] args) {
System.out.println("newuser(n) or login(l)");
Scanner scanner=new Scanner(System.in);
String typeOfOperation=scanner.nextLine();
if(typeOfOperation.equals("n"))
{
}
else
if(typeOfOperation.equals("l"))
{
try {
socket = new Socket("127.0.0.1",79);
final ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in=new ObjectInputStream(socket.getInputStream());
while(true)
{
Thread timer=new Thread()
{
public void run()
{
while(true)
{
Message temp=new Message();
temp.setReceiver("system-use:code=1");
temp.setSender("username");
try {
out.writeObject(temp);
sleep(5000);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
};
timer.start();
String username=scanner.nextLine();
String to=scanner.nextLine();
String body=scanner.nextLine();
Message all=new Message();
all.setText(body);
all.setReceiver(to);
all.setSender(username);
System.out.println("you connected to system");
System.out.println(socket);
System.out.println("now should write");
out.writeObject(all);
System.out.println("ghable threAD");
}
// socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
System.out.println("salaam c");
System.out.println(e.getMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("bye c");
System.out.println(e.getMessage());
}
}
else
{
System.out.println("bad operation. try again!");
}
}
}
Message class(Entity only not important i think!):
import java.io.Serializable;
public class Message implements Serializable{
String sender;
String receiver;
String text;
boolean delivered=false;
public void delived()
{
this.delivered=true;
}
private String tostringOfClass;
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public void setSender(String sender) {
this.sender = sender;
}
public void setText(String text) {
this.text = text;
}
public String getReceiver() {
return receiver;
}
public String getSender() {
return sender;
}
public String getText() {
return text;
}
public String toString()
{
tostringOfClass="sender : "+sender+" \n"+"receiver : "+receiver+" \n"+"message: "+text;
return tostringOfClass;
}
}
Your evaluator missed something much more important: it doesn't work. You are calling readObject() twice per loop in the server, but all you do with the first result is print it out with System.out.println(). So your code is missing every odd object.
There isn't much you could do to improve the speed of this. He probably wants you to interpose a BufferedOutputStream between the ObjectOutputStream and the socket, and similarly for BufferedInputStream. However the object streams already run their own buffers, so this is probably a waste of time. He might also want you to use large socket send and receive buffers, if you've been taught about those: see Socket.setXXXBufferSize(). Set them to at least 32k. He might also be anti-Serialization, but for this application I don't see that it makes much difference. This is an interactive application, and the messages are small, so the speed over the network is basically irrelevant. You can only type so fast.
You should also close in the client when the user types whatever it is that tells the program to stop, and in the server you must catch EOFException, before IOException, and close the socket and break out of the read loop when you get it.
Also printing out Socket.isConnected() yields no useful information. The socket is always connected at the points you print it at. This method is not a health-check for the connection, it only tells you about the state of your Socket. Not the same thing.
Your evaluator seems to me to be focussing on entirely the wrong thing.
I'm not sure how much time you would want to invest in optimizing your Socket code, but ZMQ http://www.zeromq.org/ has helped greatly in removing latency and optimizing the Bandwidth usage.
However, in simpler notes. Try not to use ObjectOutputStream they are one layer above. Go immediately for DataInputStream and DataOutputStream (Maybe also BufferedInputStream) It's been a while for me, so I'm rusty. But since you are sending strings, you don't need to do Object Serialization along with it.
You said, your time is being lost in data transfer as well. But consider not creating a new thread on each connection and use thread pools.
and Salam.