I'm making an input stream rate meter. It is basically a service that exposes a request stream call and counts how many messages per second it can handle.
As the client is totally async when it comes to sending messages, I use the ClientCallStreamObserver to start sending messages just when the stream is ready, to avoid memory overflow.
The client code looks like this:
public static void main(String[] args) throws Exception {
ManagedChannel channel = ManagedChannelBuilder.forAddress("server", 4242).usePlaintext(true).build();
ServerGrpc.ServerStub asyncStub = ServerGrpc.newStub(channel);
StreamObserver<MarketDataOuterClass.Trade> inputStream = asyncStub.reportNewTradeStream(new StreamObserver<Empty>() {
#Override
public void onNext(Empty empty) {
}
#Override
public void onError(Throwable throwable) {
logger.info("on error response stream");
}
#Override
public void onCompleted() {
logger.info("on completed response stream");
}
});
final ClientCallStreamObserver<MarketDataOuterClass.Trade> clientCallObserver = (ClientCallStreamObserver<MarketDataOuterClass.Trade>) inputStream;
while (!clientCallObserver.isReady()) {
Thread.sleep(2000);
logger.info("stream not ready yet");
}
counter.setLastTic(System.nanoTime());
while (true) {
counter.inc();
if (counter.getCounter() % 15000 == 0 ) {
long now = System.nanoTime();
double rate = (double) NANOSEC_TO_SEC * counter.getCounter() / (now - counter.getLastTic());
logger.info("rate: " + rate + " msgs per sec");
counter.clear();
counter.setLastTic(now);
}
inputStream.onNext(createRandomTrade());
}
}
My observation loop over isReady is never ending.
OBS: I'm using kubernetes cluster to serve my test, the server is receiving the call and returning a StreamObserver implementation.
isReady should eventually return true, as long as the RPC doesn't error/complete immediately. But the code is not observing flow control properly.
After each call to onNext() to send a request isReady() could begin returning false. Your while (true) loop should instead have the isReady() check at the beginning of each iteration.
Instead of polling, it is better to call serverCallObserver.setOnReadyHandler(yourRunnable) to be notified when the call is ready to send. Note that you should still check isReady() within yourRunnable as there can be spurious/out-of-date notifications.
Related
I am using GRPC-Java 1.1.2. In an active GRPC session, I have a few bidirectional streams open. Is there a way to clean them from the client end when the client is disconnecting? When I try to disconnect, I run the following look for a fixed number of times and then disconnect but I can see the following error on the server side (not sure if its caused by another issue though):
disconnect from client
while (!channel.awaitTermination(3, TimeUnit.SECONDS)) {
// check for upper bound and break if so
}
channel.shutdown().awaitTermination(3, TimeUnit.SECONDS);
error on server
E0414 11:26:48.787276000 140735121084416 ssl_transport_security.c:439] SSL_read returned 0 unexpectedly.
E0414 11:26:48.787345000 140735121084416 secure_endpoint.c:185] Decryption error: TSI_INTERNAL_ERROR
If you want to close gRPC (server-side or bi-di) streams from the client end, you will have to attach the rpc call with a Context.CancellableContext found in package io.grpc.
Suppose you have an rpc:
service Messaging {
rpc Listen (ListenRequest) returns (stream Message) {}
}
In the client side, you will handle it like this:
public class Messaging {
private Context.CancellableContext mListenContext;
private MessagingGrpc.MessagingStub getMessagingAsyncStub() {
/* return your async stub */
}
public void listen(final ListenRequest listenRequest, final StreamObserver<Message> messageStream) {
Runnable listenRunnable = new Runnable() {
#Override
public void run() {
Messaging.this.getMessagingAsyncStub().listen(listenRequest, messageStream);
}
if (mListenContext != null && !mListenContext.isCancelled()) {
Log.d(TAG, "listen: already listening");
return;
}
mListenContext = Context.current().withCancellation();
mListenContext.run(listenRunnable);
}
public void cancelListen() {
if (mListenContext != null) {
mListenContext.cancel(null);
mListenContext = null;
}
}
}
Calling cancelListen() will emulate the error, 'CANCELLED', the connection will be closed, and onError of your StreamObserver<Message> messageStream will be invoked with throwable message: 'CANCELLED'.
If you use shutdownNow() it will more aggressively shutdown the RPC streams you have. Also, you need to call shutdown() or shutdownNow() before calling awaitTermination().
That said, a better solution would be to end all your RPCs gracefully before closing the channel.
When I run the below locally (on my own computer) it works fine - I can send messages to it and it reads them in properly. As soon as I put this on a remote server and send a message, only half the message gets read.
try {
this.asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(80));
this.asynchronousServerSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
#Override
public void completed(AsynchronousSocketChannel asynchronousSocketChannel, Void att) {
try {
asynchronousServerSocketChannel.accept(null, this);
ByteBuffer byteBuffer = ByteBuffer.allocate(10485760);
asynchronousSocketChannel.read(byteBuffer).get(120000, TimeUnit.SECONDS);
byteBuffer.flip();
System.out.println("request: " + Charset.defaultCharset().decode(byteBuffer).toString());
} catch (CorruptHeadersException | CorruptProtocolException | MalformedURLException ex) {
} catch (InterruptedException | ExecutionException | TimeoutException ex) {
}
}
#Override
public void failed(Throwable exc, Void att) {
}
});
} catch (IOException ex) {
}
I've looked around at other questions and tried some of the answers but nothing worked so far. I thought the cause might be that it's timing out due to it being slower over the network when it's placed remotely but increasing the timeout didn't resolve the issue. I also considered that the message might be too large but allocating more capacity to the ByteBuffer didn't resolve the issue either.
I believe your issue is with the Asynchronous nature of the code you're using. What you have is an open connection and you've called the asynchronous read method on your socket.
This reads n bytes from the channel where n is anything from 0 to the size of your available buffer.
I firmly believe that you have to read in a loop. That is, with Java's A-NIO; you'd need to call read again from your completed method on your CompletionHandler by, possibly, passing in the AsynchronousSocketChannel as an attachment to a new completed method on a CompletionHandler you create for read , not the one you already have for accept methods.
I think this is the same sort of pattern you'd use where you'd call accept again with this as the completion handler from your completed method in the CompletionHandler you're using for the accept method call.
It then becomes important to put an "Escape" clause into your CompletionHandler for instance, if the result is -1 or if the ByteBuffer had read X number of bytes based on what you're expecting, or based on if the final byte in the ByteBuffer is a specific message termination byte that you've agreed with the sending application.
The Java Documentation on the matter goes so far as to say the read method will only read the amount of bytes on the dst at the time of invocation.
In Summary; the completed method call for the handler for the read seems to execute once something was written to the channel; but if something is being streamed you could get half of the bytes, so you'd need to continue reading until you're satisfied you've got the end of what they were sending.
Below is some code I knocked together on reading until the end, responding whilst reading, asynchronously. It, unlike myself, can talk and listen at the same time.
public class ReadForeverCompletionHandler implements CompletionHandler<Integer, Pair<AsynchronousSocketChannel, ByteBuffer>> {
#Override
public void completed(Integer bytesRead, Pair<AsynchronousSocketChannel, ByteBuffer> statefulStuff) {
if(bytesRead != -1) {
final ByteBuffer receivedByteBuffer = statefulStuff.getRight();
final AsynchronousSocketChannel theSocketChannel = statefulStuff.getLeft();
if (receivedByteBuffer.position()>8) {
//New buffer as existing buffer is in use
ByteBuffer response = ByteBuffer.wrap(receivedByteBuffer.array());
receivedByteBuffer.clear(); //safe as we've not got any outstanding or in progress reads, yet.
theSocketChannel.read(receivedByteBuffer,statefulStuff,this); //Basically "WAIT" on more data
Future<Integer> ignoredBytesWrittenResult = theSocketChannel.write(response);
}
}
else {
//connection was closed code
try {
statefulStuff.getLeft().shutdownOutput(); //maybe
}
catch (IOException somethingBad){
//fire
}
}
}
#Override
public void failed(Throwable exc, Pair<AsynchronousSocketChannel, ByteBuffer> attachment) {
//shout fire
}
The read is originally kicked off by a call from the completed method in the handler from the very original asynchronous accept on the server socket like
public class AcceptForeverCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, Pair<AsynchronousServerSocketChannel, Collection<AsynchronousSocketChannel>>> {
private final ReadForeverCompletionHandler readForeverAndEverAndSoOn = new ReadForeverCompletionHandler();
#Override
public void completed(AsynchronousSocketChannel result, Pair<AsynchronousServerSocketChannel, Collection<AsynchronousSocketChannel>> statefulStuff) {
statefulStuff.getLeft().accept(statefulStuff, this); //Accept more new connections please as we go
statefulStuff.getRight().add(result); //Collect these in case we want to for some reason, I don't know
ByteBuffer buffer = ByteBuffer.allocate(4098); //4k seems a nice number
result.read(buffer, Pair.of(result, buffer ),readForeverAndEverAndSoOn); //Kick off the read "forever"
}
#Override
public void failed(Throwable exc, Pair<AsynchronousServerSocketChannel, Collection<AsynchronousSocketChannel>> attachment) {
//Shout fire
}
}
I implementing websockets using Vert.x 3.
The scenario is simple: opening socket from client doing some 'blocking' work at the vertex verticle worker and when finish response with the answer to the client(via the open socket)
Please tell me if I am doing it right:
Created VertxWebsocketServerVerticle. as soon as the websocket is opening and request coming from the client I am using eventBus and passing the message to
EventBusReceiverVerticle. there I am doing blocking operation.
how I am actually sending back the response back to VertxWebsocketServerVerticle and sending it back to the client?
code:
Main class:
public static void main(String[] args) throws InterruptedException {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new EventBusReceiverVerticle("R1"),new DeploymentOptions().setWorker(true));
vertx.deployVerticle(new VertxWebsocketServerVerticle());
}
VertxWebsocketServerVerticle:
public class VertxWebsocketServerVerticle extends AbstractVerticle {
public void start() {
vertx.createHttpServer().websocketHandler(webSocketHandler -> {
System.out.println("Connected!");
Buffer buff = Buffer.buffer().appendInt(12).appendString("foo");
webSocketHandler.writeFinalBinaryFrame(buff);
webSocketHandler.handler(buffer -> {
String inputString = buffer.getString(0, buffer.length());
System.out.println("inputString=" + inputString);
vertx.executeBlocking(future -> {
vertx.eventBus().send("anAddress", inputString, event -> System.out.printf("got back from reply"));
future.complete();
}, res -> {
if (res.succeeded()) {
webSocketHandler.writeFinalTextFrame("output=" + inputString + "_result");
}
});
});
}).listen(8080);
}
#Override
public void stop() throws Exception {
super.stop();
}
}
EventBusReceiverVerticle :
public class EventBusReceiverVerticle extends AbstractVerticle {
private String name = null;
public EventBusReceiverVerticle(String name) {
this.name = name;
}
public void start(Future<Void> startFuture) {
vertx.eventBus().consumer("anAddress", message -> {
System.out.println(this.name +
" received message: " +
message.body());
try {
//doing some looong work..
Thread.sleep(10000);
System.out.printf("finished waiting\n");
startFuture.complete();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
I always get:
WARNING: Message reply handler timed out as no reply was received - it will be removed
github project at: https://github.com/IdanFridman/VertxAndWebSockets
thank you,
ray.
Since you are blocking your websocket handler until it receives a reply for the sent message to the EventBus, which will not, in fact, be received until the set up delay of 10s laps, you certainly will get warning since the reply handler of the event bus will timeout -> Message sent but no response received before the timeout delay.
Actually I don't know if you are just experimenting the Vert.x toolkit or you are trying to fulfill some requirement, but certainly you have to adapt your code to match in the Vert.x spirit:
First you should better not block until a message is received in your websocket handler, keep in mind that everything is asynchrounous when it comes to Vert.x.
In order to sleep for some time, use the Vert.x way and not the Thread.sleep(delay), i.e. vertx.setTimer(...).
The below code details an id read when a serial event happens,an id is generated every fews seconds when the device is powered on(Serial Event), and no serial data is received when it is powered off .problem is i need a url call to be sent once when the id is received and once when not visible(powered down).
I believe im close but cannot seem to get it right.I would be very grateful if someone could help with this and how to set flags and scheduler to achieve the above case and possibly explain where im going wrong.
int numberOfEmptyIds = 0;
int maxNumberOfAttempts = 5;
boolean urlSent = false;
long timeoutInMillis = 10000; // let's say 10000 millis, equivalent to 10 seconds
Timer timer = null;
public void connect(String portName) throws Exception {
...
scheduleTimer();
}
public void serialEvent(SerialPortEvent evt) {
if(evt.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
while(in.read(buffer) > -1) {
String asHexStr = DatatypeConverter.printHexBinary(buffer);
if(asHexStr.contains("FB1")) {
scheduleTimer();
numberOfEmptyIds = 0;
} else {
numberOfEmtyIds++;
if(numberOfEmptyIds == maxNumberOfAttempts && !urlSent) {
// send the url here
}
}
}
} catch (IOException ex) {
// Log the exception here
}
}
}
private void scheduleTimer() {
timer = new Timer("Timeout");
TimerTask task = new TimerTask() {
#Override
public void run() {
if(!urlSent) {
// send the url here
}
}
};
timer.schedule(task, timeoutInMillis);
}
Problem is i need a url call to be sent once when the id is received
and once when not visible(powered down).
The second part is done by the timer, if no data arrives to the serial port then the scheduled task will sent the URL (if not sent yet). In my answer to your previous question I forgot to cancel the timer when the task is re-scheduled :
private void scheduleTimer() {
if(timer != null) {
timer.cancel();
}
timer = new Timer("Timeout");
TimerTask task = new TimerTask() {
#Override
public void run() {
if(!urlSent) {
// send the url here
}
}
};
timer.schedule(task, timeoutInMillis);
}
This way there would be a single scheduled task. From Timer.cancel() javadoc:
Terminates this timer, discarding any currently scheduled tasks. Does
not interfere with a currently executing task (if it exists). Once a
timer has been terminated, its execution thread terminates gracefully,
and no more tasks may be scheduled on it.
Note that calling this method from within the run method of a timer
task that was invoked by this timer absolutely guarantees that the
ongoing task execution is the last task execution that will ever be
performed by this timer.
About the first part you can manage it with boolean flags just like urlSent. If you need to send the URL just a single time then you can have a flag for the URL sent by ID arriving and another flag for URL sent due no data (or empty ID's) received.
Edit
Based on the flow-chart you've posted here and shown below:
Enter description here http://dl6.fileswap.com/storage_previews/02112014/54cd147697479d29c43c530b93d5fa83/52fe9168/aW1hZ2UvanBlZw%3D%3D/4cf59787af56f18847df6235cdc20816.jpg
You maybe can change your current approach using Timer.scheduleAtFixedRate() method to check at a fixed rate of time a if the serial port stops reading the ID. As you need to send the ID received notification just once then you may set urlSent flag to true when this URL is efectively sent. Also I think you can get rid of check if the received data doesn't contain the expected ID. Something like this:
boolean urlSent = false;
long lastIdArrivalTime = 0;
long timeTolerance = 60000;
long timeoutInMillis = 300000; // 5 minutes
Timer timer = null;
public void connect(String portName) throws Exception {
...
scheduleTimer();
}
public void serialEvent(SerialPortEvent evt) {
if(evt.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
while(in.read(buffer) > -1) {
String asHexStr = DatatypeConverter.printHexBinary(buffer);
if(asHexStr.contains("FB100000010F0801")) {
lastIdArrivalTime = System.currentTimeMillis();
if(!urlSent) {
// send the URL notifying the ID
urlSent = true; // next time url will not be sent
}
}
}
} catch (IOException ex) {
// Log the exception here
}
}
}
private void scheduleTimer() {
timer = new Timer("Timeout");
TimerTask task = new TimerTask() {
#Override
public void run() {
long currentTime = System.currentTimeMillis();
if((currentTime - lastIdArrivalTime) >= timeTolerance) {
// sent the URL notifying the device is off
urlSent = false; // this way the next ID arrival will be notified
}
}
};
timer.scheduleAtFixedRate(task, timeoutInMillis, timeoutInMillis);
}
Some notes:
The timer is scheduled just once this time because it will execute the task every 5 minutes. If you need to shut down the connection don't forget to call timer.cancel() method.
The variable lastIdArrivalTime holds the last time in milliseconds when an ID arrives.
The variable timeTolerance is a max time tolerance to assume the connection is down. As you've said the device sends the ID at a seconds fixed period, so if spent 1 minute since the last ID arrival then you can assume the connection is down (or device is off).
Some hints on your code available here:
TimerTask implements Runnable interface and is intended to be used using Timer class which will create a separate thread to execute this task when the scheduled time comes, so don't use TimerTask in a new thread.
Don't mess with
Threads
unless you know exactly what are you doing. It's extremely easy make a
mistake and mess the things up.
I am a newbie to vert.x. I was trying out the vert.x "NetServer" capability. http://vertx.io/core_manual_java.html#writing-tcp-servers-and-clients and it works like a charm .
However , I also read that "A verticle instance is strictly single threaded.
If you create a simple TCP server and deploy a single instance of it then all the handlers for that server are always executed on the same event loop (thread)."
Currently, for my implementation, I wanted to receive the TCP stream of bytes and then trigger another component. But this should not be a blocking call within the "start" method of the Verticle. So, is it a good practice, to write an executor within the start method? or does vertx automatically handle such cases.
Here is a snippet
public class TCPListener extends Verticle {
public void start(){
NetServer server = vertx.createNetServer();
server.connectHandler(new Handler<NetSocket>() {
public void handle(NetSocket sock) {
container.logger().info("A client has connected");
sock.dataHandler(new Handler<Buffer>() {
public void handle(Buffer buffer) {
container.logger().info("I received " + buffer.length() + " bytes of data");
container.logger().info("I received " + new String(buffer.getBytes()));
//Trigger another component here. SHould be done in a sperate thread.
//The previous call should be returned . No need to wait for component response.
}
});
}
}).listen(1234, "host");
}
}
What should be mechanism to make this a non blocking call.
I don't think this is the way to go for vert.x.
A better way would be to use the event bus properly instead of Executor. Have a worker respond to the event on the bus, do the processing, and signal the bus when it's completed.
Creating threads defeats the purpose of going with vert.x.
The most flexible way is to create an ExecutorService and process requests with it. This brings fine-grained control over threading model of workers (fixed or variable number of threads, what work should be performed serially on a single thread, etc).
Modified sample might look like this:
public class TCPListener extends Verticle {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
public void start(){
NetServer server = vertx.createNetServer();
server.connectHandler(new Handler<NetSocket>() {
public void handle(final NetSocket sock) { // <-- Note 'final' here
container.logger().info("A client has connected");
sock.dataHandler(new Handler<Buffer>() {
public void handle(final Buffer buffer) { // <-- Note 'final' here
//Trigger another component here. SHould be done in a sperate thread.
//The previous call should be returned . No need to wait for component response.
executor.submit(new Runnable() {
public void run() {
//It's okay to read buffer data here
//and use sock.write() if necessary
container.logger().info("I received " + buffer.length() + " bytes of data");
container.logger().info("I received " + new String(buffer.getBytes()));
}
}
}
});
}
}).listen(1234, "host");
}
}
As duffymo mentioned creating threads defeats the purpose of using vertx. Best way would be to write a message into eventbus and create a new handler listening for messages from the eventbus. Updated the code to showcase this. Writing the messages to "next.topic" topic, and registered a handler to read message from "next.topic" topic.
public class TCPListener extends Verticle {
public void start(){
NetServer server = vertx.createNetServer();
server.connectHandler(new Handler<NetSocket>() {
public void handle(NetSocket sock) {
container.logger().info("A client has connected");
sock.dataHandler(new Handler<Buffer>() {
public void handle(Buffer buffer) {
String recvMesg = new String(buffer.getBytes());
container.logger().info("I received " + buffer.length() + " bytes of data");
container.logger().info("I received " + recvMesg);
//Writing received message to event bus
vertx.eventBus().send("next.topic", recvMesg);
}
});
}
}).listen(1234, "host");
//Registering new handler listening to "next.topic" topic on event bus
vertx.eventBus().registerHandler("next.topic", new Handler<Message<String>() {
public void handle(Message<String> mesg) {
container.logger.info("Received message: "+mesg.body());
}
};
}
}