I have an app which receives messages from some producer using RabbitMQ, parses them and sends somwhere else. The problem is that my app doesn't use the CPU power fully. it uses not more than 25% of the CPU. look at this screenshot from profiler:
Here is the code in which the biggest part of the processing executing:
public class Consumer {
private static final String QUEUE_NAME = "MdnaMessagesQueue";
private static int i = 1;
private final ConnectionFactory factory;
private final boolean autoAck = false;
private final Connection connection;
private final Channel channel;
private String response;
private Sender sender;
private Logger LOG = Logger.getLogger(Consumer.class);
private ExecutorService es;
public Consumer() throws IOException{
es = Executors.newFixedThreadPool(8);
factory = new ConnectionFactory();
factory.setHost("localhost");
connection = factory.newConnection(es);
channel = connection.createChannel();
sender = new Sender();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
channel.basicQos(25);
Properties props = new Properties();
try {
props.load(new FileInputStream("/home/mikhail/bzrrep/DLP/DLPServer/src/main/java/log4j.properties"));
} catch (Exception e){
LOG.error(e);
}
PropertyConfigurator.configure(props);
}
/**
* is used to receive messages from the queue
* #param customerId the id of current customer
* #throws IOException
* #throws InterruptedException
*/
public void receive(final String customerId) throws IOException,InterruptedException{
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
final QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(QUEUE_NAME, autoAck, "myConsumerTag",
new DefaultConsumer(channel){
#Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException{
BasicProperties replyProperties = new BasicProperties.Builder().correlationId(properties.getCorrelationId()).build();
long deliveryTag = envelope.getDeliveryTag();
try{
LOG.info(" [" +(i++) +"] Received from first queue ");
byte[] dataArr = body;
MimeParser mimeParser = new MimeParser(true);
Filter filter = new Filter();
ByteArrayInputStream bais1 = new ByteArrayInputStream(dataArr);
MessageInfo mi = mimeParser.parseMessages(bais1);
//checking for compliance with rules
boolean messageAccepted = filter.getMessageAcceptability(mi.getPlainText(), customerId);
response = filter.getResult();
if(messageAccepted){
//sending to the other queue
sender.send(dataArr);
channel.basicPublish("", properties.getReplyTo(), replyProperties, response.getBytes());//need to add responce
} else {
channel.basicPublish("", properties.getReplyTo(), replyProperties, response.getBytes());
}
} catch (Exception e){
LOG.error(e);
}finally {
channel.basicAck(deliveryTag, false);
}
}
});
}
}
Here is the snapshot from the profiler:
How to solve this problem?
Related
I'm trying to set up an amqp RabbitMQ consumer that is connected to a message queue from work. The code works just fine by itself, but as soon as I add it to my JavaFX application and open the channel, it closes with the message Closed due to exception from Consumer method handleDelivery for channel AMQChannel. I've tried using the manual acknowledgement as some suggest, but without success. This is the code that I use to connect to the queue:
#NoArgsConstructor
#Getter
#Setter
public final class QueueListener {
private static QueueListener INSTANCE;
public static QueueListener getInstance() {
if(INSTANCE == null) {
INSTANCE = new QueueListener();
}
return INSTANCE;
}
public static void setInstance(QueueListener q){
INSTANCE = q;
}
private MainController controller;
private ConnectionFactory factory;
private Connection conn;
private Channel channel;
private String queue;
private String host;
private String username;
private String password;
public void connect(String queue, String host, String username, String password) throws IOException, TimeoutException {
this.queue = queue;
this.host = host;
this.username = username;
this.password = password;
factory = new ConnectionFactory();
factory.setUsername(username);
factory.setPassword(password);
factory.setHost(host);
Map<String, Object> qmode = new HashMap<>();
qmode.put("x-queue-mode", "lazy");
qmode.put("x-single-active-consumer", true);
conn = factory.newConnection();
channel = conn.createChannel();
channel.addShutdownListener(cause -> System.out.println(cause.getReason()));
boolean durable = true;
channel.queueDeclare(queue, durable, false, false, qmode);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), StandardCharsets.US_ASCII);
message = message.replaceAll("\\u000D", "");
System.out.println(message);
MyLogger.logger.info(message);
controller.parseData(message);
};
boolean autoAck = true;
channel.basicConsume(queue, autoAck, deliverCallback, consumerTag -> {});
}
public void closeConnection() throws IOException, TimeoutException {
channel.close();
conn.close();
}
public void setController(MainController controller){
this.controller = controller;
}
}
The error was actually caused by the line controller.parseData(message), when setting the controller I was creating another instance of the MainController, which was causing the error
I am creating a client to communicate with APNs.
here is my requirement.
jdk 1.6
http/2
tls 1.3
ALPN
so I decided to make it using Netty.
I don't know if I set the header and data well.
Http2Client.java
public class Http2Client {
// static final boolean SSL = System.getProperty("ssl") != null;
static final boolean SSL = true;
static final String HOST = "api.sandbox.push.apple.com";
static final int PORT = 443;
static final String PATH = "/3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0";
// private static final AsciiTest APNS_PATH = new AsciiTest("/3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0");
private static final AsciiTest APNS_EXPIRATION_HEADER = new AsciiTest("apns-expiration");
private static final AsciiTest APNS_TOPIC_HEADER = new AsciiTest("apns-topic");
private static final AsciiTest APNS_PRIORITY_HEADER = new AsciiTest("apns-priority");
private static final AsciiTest APNS_AUTHORIZATION = new AsciiTest("authorization");
private static final AsciiTest APNS_ID_HEADER = new AsciiTest("apns-id");
private static final AsciiTest APNS_PUSH_TYPE_HEADER = new AsciiTest("apns-push-type");
public static void main(String[] args) throws Exception {
EventLoopGroup clientWorkerGroup = new NioEventLoopGroup();
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SslProvider provider = SslProvider.isAlpnSupported(SslProvider.OPENSSL) ? SslProvider.OPENSSL
: SslProvider.JDK;
sslCtx = SslContextBuilder.forClient()
.sslProvider(provider)
/*
* NOTE: the cipher filter may not include all ciphers required by the HTTP/2
* specification. Please refer to the HTTP/2 specification for cipher
* requirements.
*/
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.applicationProtocolConfig(new ApplicationProtocolConfig(
Protocol.ALPN,
// NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK
// providers.
SelectorFailureBehavior.NO_ADVERTISE,
// ACCEPT is currently the only mode supported by both OpenSsl and JDK
// providers.
SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_2,
ApplicationProtocolNames.HTTP_1_1))
.build();
} else {
sslCtx = null;
}
try {
// Configure the client.
Bootstrap b = new Bootstrap();
b.group(clientWorkerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.remoteAddress(HOST, PORT);
b.handler(new Http2ClientInit(sslCtx));
// Start the client.
final Channel channel = b.connect().syncUninterruptibly().channel();
System.out.println("Connected to [" + HOST + ':' + PORT + ']');
final Http2ResponseHandler streamFrameResponseHandler =
new Http2ResponseHandler();
final Http2StreamChannelBootstrap streamChannelBootstrap = new Http2StreamChannelBootstrap(channel);
final Http2StreamChannel streamChannel = streamChannelBootstrap.open().syncUninterruptibly().getNow();
streamChannel.pipeline().addLast(streamFrameResponseHandler);
// Send request (a HTTP/2 HEADERS frame - with ':method = POST' in this case)
final Http2Headers headers = new DefaultHttp2Headers();
headers.method(HttpMethod.POST.asciiName());
headers.path(PATH);
headers.scheme(HttpScheme.HTTPS.name());
headers.add(APNS_TOPIC_HEADER, "com.example.MyApp");
headers.add(APNS_AUTHORIZATION,
"bearer eyAia2lkIjogIjhZTDNHM1JSWDciIH0.eyAiaXNzIjogIkM4Nk5WOUpYM0QiLCAiaWF0IjogIjE0NTkxNDM1ODA2NTAiIH0.MEYCIQDzqyahmH1rz1s-LFNkylXEa2lZ_aOCX4daxxTZkVEGzwIhALvkClnx5m5eAT6Lxw7LZtEQcH6JENhJTMArwLf3sXwi");
headers.add(APNS_ID_HEADER, "eabeae54-14a8-11e5-b60b-1697f925ec7b");
headers.add(APNS_PUSH_TYPE_HEADER, "alert");
headers.add(APNS_EXPIRATION_HEADER, "0");
headers.add(APNS_PRIORITY_HEADER, "10");
final Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers, true);
streamChannel.writeAndFlush(headersFrame);
System.out.println("Sent HTTP/2 POST request to " + PATH);
// Wait for the responses (or for the latch to expire), then clean up the
// connections
if (!streamFrameResponseHandler.responseSuccessfullyCompleted()) {
System.err.println("Did not get HTTP/2 response in expected time.");
}
System.out.println("Finished HTTP/2 request, will close the connection.");
// Wait until the connection is closed.
channel.close().syncUninterruptibly();
} finally {
clientWorkerGroup.shutdownGracefully();
}
}
}
Http2ResponseHandler.java
public final class Http2ResponseHandler extends SimpleChannelInboundHandler<Http2StreamFrame> {
private final CountDownLatch latch = new CountDownLatch(1);
public void channelActive(ChannelHandlerContext ctx) {
String sendMessage = "{\"aps\":{\"alert\":\"hello\"}}";
ByteBuf messageBuffer = Unpooled.buffer();
messageBuffer.writeBytes(sendMessage.getBytes());
StringBuilder builder = new StringBuilder();
builder.append("request [");
builder.append(sendMessage);
builder.append("]");
System.out.println(builder.toString());
ctx.writeAndFlush(new DefaultHttp2DataFrame(messageBuffer, true));
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, Http2StreamFrame msg) throws Exception {
ByteBuf content = ctx.alloc().buffer();
System.out.println(content);
System.out.println("Received HTTP/2 'stream' frame : " + msg);
// isEndStream() is not from a common interface, so we currently must check both
if (msg instanceof Http2DataFrame && ((Http2DataFrame) msg).isEndStream()) {
ByteBuf data = ((DefaultHttp2DataFrame) msg).content().alloc().buffer();
System.out.println(data.readCharSequence(256, Charset.forName("utf-8")).toString());
latch.countDown();
} else if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) {
latch.countDown();
}
// String readMessage = ((ByteBuf) msg).toString(CharsetUtil.UTF_8);
//
// StringBuilder builder = new StringBuilder();
// builder.append("receive [");
// builder.append(readMessage);
// builder.append("]");
//
// System.out.println(builder.toString());
}
public void channelReadComplete(ChannelHandlerContext ctx) {
// ctx.flush();
ctx.close();
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
/**
* Waits for the latch to be decremented (i.e. for an end of stream message to be received), or for
* the latch to expire after 5 seconds.
* #return true if a successful HTTP/2 end of stream message was received.
*/
public boolean responseSuccessfullyCompleted() {
try {
return latch.await(5, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
System.err.println("Latch exception: " + ie.getMessage());
return false;
}
}
}
console log
Connected to [api.sandbox.push.apple.com:443]
Sent HTTP/2 POST request to /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0
PooledUnsafeDirectByteBuf(ridx: 0, widx: 0, cap: 256)
Received HTTP/2 'stream' frame : DefaultHttp2HeadersFrame(stream=3, headers=DefaultHttp2Headers[:status: 403, apns-id: eabeae54-14a8-11e5-b60b-1697f925ec7b], endStream=false, padding=0)
PooledUnsafeDirectByteBuf(ridx: 0, widx: 0, cap: 256)
Received HTTP/2 'stream' frame : DefaultHttp2DataFrame(stream=3, content=UnpooledSlicedByteBuf(ridx: 0, widx: 33, cap: 33/33, unwrapped: PooledUnsafeDirectByteBuf(ridx: 150, widx: 150, cap: 179)), endStream=true, padding=0)
Question
Did I send the header and data well?
How can i convert this part to String
DefaultHttp2DataFrame(stream=3, content=UnpooledSlicedByteBuf(ridx: 0, widx: 33, cap: 33/33, unwrapped: PooledUnsafeDirectByteBuf(ridx: 150, widx: 150, cap: 179)), endStream=true, padding=0)
If you know the solution, please help me.
Answer myself.
Http2ResponseHandler.java
public final class ResponseHandler extends SimpleChannelInboundHandler<Http2StreamFrame> {
private final CountDownLatch latch = new CountDownLatch(1);
#Override
protected void channelRead0(ChannelHandlerContext ctx, Http2StreamFrame msg) throws Exception {
if (msg instanceof Http2DataFrame && ((Http2DataFrame) msg).isEndStream()) {
DefaultHttp2DataFrame dataFrame = (DefaultHttp2DataFrame) msg;
ByteBuf dataContent = dataFrame.content();
String data = dataContent.toString(Charset.forName("utf-8"));
System.out.println(data);
latch.countDown();
} else if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) {
DefaultHttp2HeadersFrame headerFrame = (DefaultHttp2HeadersFrame) msg;
DefaultHttp2Headers header = (DefaultHttp2Headers) headerFrame.headers();
System.out.println(header.get("apns-id"));
latch.countDown();
}
}
/**
* Waits for the latch to be decremented (i.e. for an end of stream message to be received), or for
* the latch to expire after 5 seconds.
* #return true if a successful HTTP/2 end of stream message was received.
*/
public boolean responseSuccessfullyCompleted() {
try {
return latch.await(5, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
System.err.println("Latch exception: " + ie.getMessage());
return false;
}
}
}
Question
Did I send the header and data well?
-> Answer
final Http2Headers headers = new DefaultHttp2Headers();
headers.method(HttpMethod.POST.asciiName());
headers.scheme(HttpScheme.HTTPS.name());
headers.path(PATH + notification.getToken());
headers.add("apns-topic", topic);
headers.add("key", "value");
// if you have a data frame you have to put false.
final Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers, false);
How can i convert this part to String
-> Answer
DefaultHttp2DataFrame dataFrame = (DefaultHttp2DataFrame) msg;
ByteBuf dataContent = dataFrame.content();
String data = dataContent.toString(Charset.forName("utf-8"));
I hope it will be helpful to people who have the same curiosity as me.
In my application I have 3 classes:
- Company, which hires Workers for any of 3 jobs
- Workers, each can do 2 jobs
- Administrator, which receives copies of all messages in the program and can send messages to all companies, all workers or just everyone
I'm using work.companies.companyName for companies keys and work.workers.workerName for workers keys, they both use default exchange and queue for communication. The Administrator receives messages with admin Topic Exchange.
The problem is with the Administrator -> everyone else communication. It works exactly like Direct exchange - I can get Companies and Workers any names, even like "#", "company1.#" etc. and they won't receive anything, unless in the Administrator I explicitly send the message with key like "work.companies.company1".
I would like to be able to use just e. g. "work.companies.#" to send message to all companies. What am I doing wrong?
Administrator.java:
public class Administrator
{
public static void main(String[] args) throws IOException, TimeoutException
{
new Thread(new TopicListener("admin", ign -> {})).start();
TopicWriter writer = new TopicWriter();
// lots of code
TopicListener.java:
public class TopicListener implements Runnable
{
private final String EXCHANGE_NAME = "space";
private String key;
private Consumer<String> msgHandler;
public TopicListener(String key, Consumer<String> msgHandler)
{
this.key = key;
this.msgHandler = msgHandler;
}
#Override
public void run()
{
try
{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, key);
com.rabbitmq.client.Consumer consumer = new DefaultConsumer(channel)
{
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
{
String msg = new String(body, StandardCharsets.UTF_8);
System.out.println("Received: \"" + msg + "\"");
msgHandler.accept(msg);
}
};
channel.basicConsume(queueName, true, consumer);
}
catch (IOException | TimeoutException e)
{ e.printStackTrace(); }
}
}
TopicWriter.java:
public class TopicWriter
{
private final String EXCHANGE_NAME = "space";
private final Channel channel;
public TopicWriter() throws IOException, TimeoutException
{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
this.channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
}
public void send(String msg, String key) throws IOException
{
channel.basicPublish(
EXCHANGE_NAME,
key,
null,
msg.getBytes(StandardCharsets.UTF_8));
}
}
Company.java contains:
new Thread(new TopicListener("space.agencies." + agencyID, ign -> {})).start();
Worker.java contains:
new Thread(new TopicListener("space.carriers." + carrierID, consumer)).start();
I found out where the problem was: I was trying to send message to everyone using Topic, where in RabbitMQ Topic is used to specify who should receive the message. The "#" or "*" should be used in the queue key declaration, not while sending the message with a given key.
I am new to rabbitMQ.I have written a java consumer in the following way.Please advice me whether it is correct implementation of the Thread+RabbitMQ .I have created three threads which consumer data from the queue and do the processing.
public class TileJobs implements Runnable {
private static final String EXCHANGE_NAME = "fanout_logs";
Connection connection;
ConnectionFactory factory;
Channel channel;
String name;
int count = 0;
TileJobs(String name) throws IOException, TimeoutException {
factory = new ConnectionFactory();
factory.setHost("192.168.2.4");
factory.setUsername("manish");
factory.setPassword("mm#1234");
connection = factory.newConnection();
channel = connection.createChannel();
this.name = name;
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
channel.queueDeclare("test", true, false, false, null);
channel.queueBind("test", EXCHANGE_NAME, "");
channel.basicQos(1);
}
#Override
public void run() {
// TODO Auto-generated method stub
System.out.println("inside the run");
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(TileJobs.this.name);
TileJobs.this.count = TileJobs.this.count + 1;
System.out.println(TileJobs.this.count);
System.out.println(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
try {
channel.basicConsume("test", false, consumer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class ReceiveLogsDirect {
private static final String EXCHANGE_NAME = "fanout_logs";
public static void main(String[] argv) throws Exception {
TileJobs consumer = new TileJobs("manish");
Thread consumerThread = new Thread(consumer);
consumerThread.start();
TileJobs consumer1 = new TileJobs("manish1");
Thread consumerThread1 = new Thread(consumer1);
consumerThread1.start();
TileJobs consumer2 = new TileJobs("manish2");
Thread consumerThread2 = new Thread(consumer2);
consumerThread2.start();
}
}
Regards
Manish
You can make a class to get connection to rabbitmq server.In program use one connection multi channels to consume queue.Note that one channel for one consumer.
I have the following class:
public class QueueProcessor implements Runnable {
//private static final Logger logger = LogManager.getLogger(QueueProcessor.class.getCanonicalName());
private Session session;
private Queue queue;
private String queueName;
private JSONObject jaob;
public QueueProcessor(Session session, Queue queue, String queueName, JSONObject jaob) {
this.queue = queue;
this.session = session;
this.queueName = queueName;
this.jaob = jaob;
}
#Override
public void run() {
try {
customConsumer();
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void customConsumer() throws Exception {
MessageConsumer consumer = session.createConsumer(queue);
try {
while (true) {
Message msg = consumer.receive();
if (msg != null && msg instanceof TextMessage) {
TextMessage tm = (TextMessage) msg;
JobProcessing jobProcessing = new JobProcessing(tm, queueName, jaob);
String finalJson = jobProcessing.process();
// System.out.println("Json copied for posting: " + finalJson);
GlobalAsyncHttpClient.getINSTANCE().postData(finalJson);
msg.acknowledge();
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
consumer.close();
}
}
}
Now I have around 8 queues, for which I spawn 8 threads to read each queue and process through the above class using Executors / ThreadPool.
I am using Spark streaming custom to read from a JMS queue. As per the official example the code used goes like this:
public static void main(String[] args) {
try {
SparkConf sparkConf = new SparkConf().setAppName("JavaCustomReceiver");
JavaStreamingContext ssc = new JavaStreamingContext(sparkConf, new Duration(1000));
// Create an input stream with the custom receiver on target ip:port and count the
// words in input stream of \n delimited text (eg. generated by 'nc')
JavaReceiverInputDStream<String> lines = ssc.receiverStream(new TibcoReceiver());
JavaDStream<String> words = lines.flatMap(x -> Arrays.asList(SPACE.split(x)).iterator());
JavaPairDStream<String, Integer> wordCounts = words.mapToPair(s -> new Tuple2<>(s, 1))
.reduceByKey((i1, i2) -> i1 + i2);
wordCounts.print();
ssc.start();
ssc.awaitTermination();
} catch (Exception e) {
e.printStackTrace();
}
}
The line of interest here is:
JavaReceiverInputDStream<String> lines = ssc.receiverStream(new TibcoReceiver());
I want the values read in the QueueProcessor back here, but how do I do that as I am in a continuous while loop. I am not aware if there is any other design pattern I can use here. Please guide!