Efficient channelRead for Java Netty Server - java

I'm using netty to develop a proxy server and my proxy ProxyBackendHandler class is as follows. There on channelRead method I need to get the msg data and write to client as TextWebSocketFrame. To do that I have used a StringBuilder and a while loop to iterate the ByteBuf. Can anyone suggest me a better way to do this as it seems that above code has high perfomance overhead when the high data loads.
public class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
StringBuilder sReplyBuffer;
public ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
sReplyBuffer = new StringBuilder(4000);
}
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
// Please suggest a efficient implementation for read msg and pass it to writeAndFlush.
ByteBuf in = (ByteBuf) msg;
sReplyBuffer.setLength(0);
try {
while (in.isReadable()) {
sReplyBuffer.append((char) in.readByte());
}
} finally {
((ByteBuf) msg).release();
}
inboundChannel.writeAndFlush(new TextWebSocketFrame (sReplyBuffer.toString())).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
ctx.channel().read();
System.out.println("Sent To Client");
} else {
future.channel().close();
}
}
});
}
}

Maybe something like this:
public class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
public ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
inboundChannel.writeAndFlush(new TextWebSocketFrame((ByteBuf) msg)).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
ctx.channel().read();
System.out.println("Sent To Client");
} else {
future.channel().close();
}
}
});
}
}

I suggest not using a StringBuilder at all. Just use the buffer you already have. You don't state what a TextWebSocketFrame might be, or why you think you need it, but ultimately a proxy server only has to copy bytes. You don't need StringBuilders or extra classes for that. Or Netty, frankly.

Related

Spring WebFlux. How to add specific logic after controller call?

I have the next RestController:
#PostMapping("/play")
public Mono<PlayResponse> play(#RequestBody String body) {
//business logic here;
}
And I wanna add some additional logic after the controller call. For example, I want to add to my application some modes logic:
timeout mode - after the successful call, the response will wait until timeout happens
long answer mode - after the successful call, the response will wait a particular amount of ms
failure mode - after the successful call, the response will answer with FORBIDDEN code
etc.
I'm trying to achieve that through WebFilter:
#Component
public class OutgoingFilter implements WebFilter {
Mode mode = new TimeoutMode();
#NonNull
#Override
public Mono<Void> filter(#NonNull final ServerWebExchange exchange, final WebFilterChain chain) {
return chain.filter(exchange)
.doOnNext(this::onNext)
.map(this::onMap)
.doFinally(this::onFinally);
}
private Void onMap(final Void unused) {
mode.run();
return unused;
}
private void onNext(final Void unused) {
mode.run();
}
private void onFinally(final SignalType signalType) {
mode.run();
}
}
As you can see, I've tried onMap, doOnNext andonFinally methods and none of them seem not working.
Is it the right way to use WebFilter there? Maybe I'm doing something wrong?
How can I implement such logic in the Spring WebFlux application?
Update
There is my Mode interface:
public interface Mode {
void run() throws Forbidden;
}
Implementations:
public class Fail implements Mode {
#Override
public void run() throws Forbidden {
throw new Forbidden("Fail mode enabled");
}
}
public class Wait implements Mode {
private final int ms;
public Wait() {
this(0);
}
public Wait(final int ms) {
this.ms = ms;
}
#Override
public void run() throws Forbidden {
sleep();
}
private void sleep() {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
But the implementations aren't final. I will change them to a reactive style if it's needed.

Netty TCP Socket InputStream

Netty TCP Server is running at port 8000 receiving NMEA format data. It uses Marine API library to convert the gibberish to a meaningful information which needs input stream from the socket.
SentenceReader sentenceReader = new SentenceReader(socket.getInputStream());
sentenceReader.addSentenceListener(new MultiSentenceListener());
sentenceReader.start();
How can i get inputstream for netty server port being used?
SentenceReader does not have any method to accept "streamed in" data, however with subclassing, it can be made to accept the data.
The core of SentenceReader uses a DataReader for its data, normally this datareader is polled from a seperate thread SentenceReader itself, and we can modify this structure to get what we need.
First, we subclass SentenceReader with our own class, give it the proper constructor and methods we want, and remove the effect of the start and stop methods. We provide null as the file for now (and hope future versions provide a method to pass a datareader in directly)
public class NettySentenceReader extends SentenceReader {
public NettySentenceReader () {
super((InputStream)null);
}
#Override
public void start() {
}
#Override
public void stop() {
}
}
We now need to implement all functionality of the internal class DataReader inside our own Netty handler, to replicate the same behaviour
public class SentenceReaderHandler extends
SimpleChannelInboundHandler<String> {
private SentenceFactory factory;
private SentenceReader parent;
public SentenceReaderHandler (SentenceReader parent) {
this.parent = parent;
}
#Override
public void channelRegistered(ChannelHandlerContext ctx) {
if(!ctx.channel().isActive())
return;
//ActivityMonitor monitor = new ActivityMonitor(parent);
this.factory = SentenceFactory.getInstance();
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
//ActivityMonitor monitor = new ActivityMonitor(parent);
this.factory = SentenceFactory.getInstance();
}
#Override
// This method will be renamed to `messageReceived` in Netty 5.0.0
protected void channelRead0(ChannelHandlerContext ctx, String data)
throws Exception {
if (SentenceValidator.isValid(data)) {
monitor.refresh();
Sentence s = factory.createParser(data);
parent.fireSentenceEvent(s);
} else if (!SentenceValidator.isSentence(data)) {
parent.fireDataEvent(data);
}
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
//monitor.reset();
parent.fireReadingStopped();
}
#Override
public void channelUnregistered(ChannelHandlerContext ctx) {
if(!ctx.channel().isActive())
return;
//monitor.reset();
parent.fireReadingStopped();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
parent.handleException("Data read failed", e);
}
}
Finally, we need to integrate this into a Netty pipeline:
SentenceReader reader = new NettySentenceReader();
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
private static final StringDecoder DECODER = new StringDecoder();
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(DECODER);
pipeline.addLast(new SentenceReaderHandler(reader));
}
});
You can't easily as InputStream is blocking and netty is an async - non blocking API.

JAVA Synchronize cycle with callbacks

I have this function that downloads all files in the ArrayList and I want to make this "synchronous" , I mean that I want to download only one file at time.
How can I make the FOR cycle to wait until a file is downloaded and than take an other file to download?
public void downloadFiles(ArrayList<String> files, final String destinationFolder){
for(String file:files){
GoogleDrive.getInstance().readFile(file, GoogleDrive.FolderLocation.ROOT_FOLDER, new GoogleDrive.GoogleDriveReadFileCallback() {
#Override
public void successful(String title, byte[] content) {
try {
FileUtils.writeByteArrayToFile(new File(destinationFolder+File.pathSeparator+title), content);
} catch (IOException e) {
Log.e(TAG,"ERROR FileManager.downloadFiles: "+e.toString());
}
}
#Override
public void notFound() { }
#Override
public void error(String error) { }
});
}
}
The question sounds pretty simple; but turns out to be hard. Why is that? Because the given code is doing things in a wrong way. What do I mean with that?
I assume that
GoogleDrive.getInstance().readFile(file,
GoogleDrive.FolderLocation.ROOT_FOLDER,
new GoogleDrive.GoogleDriveReadFileCallback()
triggers an asynchronous read from Google Drive; and upon competition, that callback instance will be called. But when we have a closer look into that callback code - we find that it is missing essential parts:
it is not doing any kind of error handling (hint: you have no idea when something went wrong with this approach)
the callback has no means to "signal" to the outside world "i am done".
Thus: the solution is to rework that thing completely. You could create a real class implementing the required interface; and that callback implementation could have methods that tell you whether file reading is still ongoing, completed successfully or failed.
In other words: you build a wrapper around GoogleDrive readFile(); and that wrapper offers synchronous reading (probably successfull() gets called when the readFile() is done - so your wrapper can simply wait for that callback); or the wrapper could return some sort of Future.
24 hours later the answear was too easy, just implemented a listener that start a new download every time an old one is terminated(with success or not) and remove it from the list. I don't know if this is the correct way to do it but it works
interface FileManagerDownloadEvent{
void downloadSuccessful(String fileName);
void downloadNotFound(String fileName);
void downloadError(String fileName,String error);
}
public class FileManager implements FileManagerDownloadEvent{
private FileManagerDownloadEvent downloadEvent;
private ArrayList<String> filesToDownload;
private String destinationFolder;
public FileManager(){
this.downloadEvent=this;
}
private void download(){
if(filesToDownload.size()!=0) {
final String file=filesToDownload.get(0);
filesToDownload.remove(0);
GoogleDrive.getInstance().readFile(file, GoogleDrive.FolderLocation.ROOT_FOLDER, new GoogleDrive.GoogleDriveReadFileCallback() {
#Override
public void successful(String title, byte[] content) {
try {
FileUtils.writeByteArrayToFile(new File(destinationFolder+File.separator+title), content);
downloadEvent.downloadSuccessful(destinationFolder+File.separator+title);
} catch (Exception e) {
Log.e(TAG,"ERROR FileManager.downloadFiles: "+e.toString());
}
}
#Override
public void notFound() {
downloadEvent.downloadNotFound(file);
}
#Override
public void error(String error) {
downloadEvent.downloadError(file,error);
}
});
}
}
#Override
public void downloadSuccessful(String filePath) {
Log.d(TAG,"downloadSuccessful: "+filePath);
download();
}
#Override
public void downloadNotFound(String fileName) {
Log.e(TAG,"downloadNotFound: "+fileName);
download();
}
#Override
public void downloadError(String fileName,String error) {
Log.e(TAG,"downloadError: "+fileName+" --> "+error);
download();
}
}

How to store a list of Akka actors in Play framework 2 application?

I've got a Play framework 2 application that can receive data and send it to multiple clients via WebSockets. I use Akka actors to work with WebSockets, just like in this documentation. I also have a WebSocketRouter class that extends UntypedActor and contains routing logic (decides, which clients to pass the data the system receives to). I know that i can use the Router functionality of Akka, but that is not the issue at the moment for me. The issue is that i have to store a list of all active clients. Right now i store it in a static list of the WebSocketRouter class. That was the fastest way to write a proof-of-concept prototype, but it is not thread-safe and does not seem to be "the Akka way".
Below is a simplified code sample:
WebSocketController:
//This controller handles the creation of WebSockets.
public class WebSocketController extends Controller {
public static WebSocket<String> index() {
return WebSocket.withActor(new F.Function<ActorRef, Props>() {
public Props apply(ActorRef out) throws Throwable {
return MessageSender.props(out);
}
});
}
}
MessageSender :
//Hold a reference to the auto-created Actor that handles WebSockets
//and also registers and unregisters itself in the router.
public class MessageSender extends UntypedActor {
public static Props props(ActorRef out) {
return Props.create(MessageSender.class, out);
}
private final ActorRef out;
public MessageSender(ActorRef out) {
this.out = out;
}
#Override
public void preStart() {
WebSocketRouter.addSender(getSelf());
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
out.tell(message, getSelf());
}
else {
unhandled(message);
}
}
public void postStop() {
WebSocketRouter.removeSender(getSelf());
}
}
WebSocketRouter:
public class WebSocketRouter extends UntypedActor {
private static ArrayList<ActorRef> senders;
static {
senders = new ArrayList<>();
}
public static void addSender(ActorRef actorRef){
senders.add(actorRef);
}
public static void removeSender(ActorRef actorRef){
senders.remove(actorRef);
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
for (ActorRef sender : senders) {
sender.tell(message, getSelf());
}
}
}
}
Once again, i know this is a bad solution and i'm seeking a better one. I have thought of creating a thread-safe singleton class that would hold current connections. I also thought about holding the list of current connections in an instance of some Akka actor and modifying the list via Akka messages, but for this way to work I'd have to store an ActorRef to that actor statically, so that it could be accessed from different ActorSystems.
What is the best way to solve my problem that would fit best into Akka ideology?
Instead of having a static reference to an Actor (WebSocketRouter in your case), why not come up with some messages to send it? That way, the actor can maintain its own internal state in a consistent way. State change through messages is one of the main benefits of the Actor Model.
Before I get into code, I'm sorry if this isn't 100% accurate, I've only used the Scala version of Akka and am basing this off a quick scan of the Akka Documentation.
So in your case, I would define a few objects in order to express Join/Leave...
public class JoinMessage { }
public class ExitMessage { }
Note that ExitMessage is really only needed if you intend to keep your WebSocket open and have the user stop listening to the router. Otherwise, the router can detect when the Actor has been terminated.
And then you would change your MessageSender actor to send these messages whenever they join or leave a chat room....
public class MessageSender extends UntypedActor {
public static Props props(ActorRef out) {
return Props.create(MessageSender.class, out);
}
private final ActorRef out;
private final ActorRef router;
public MessageSender(ActorRef out) {
this.out = out;
this.router= getContext().actorSelection("/Path/To/WebSocketRouter");
}
#Override
public void preStart() {
router.tell(new JoinMessage(), getSelf());
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
out.tell(message, getSelf());
} else {
unhandled(message);
}
}
}
And then your router can change to manage state internally rather than exposing internal methods on the Actor (which as you know is not good)....
public class WebSocketRouter extends UntypedActor {
private final Set<ActorRef> senders = new HashSet<>();
private void addSender(ActorRef actorRef){
senders.add(actorRef);
}
private void removeSender(ActorRef actorRef){
senders.remove(actorRef);
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof JoinMessage) {
addSender(sender);
getContext().watch(sender); // Watch sender so we can detect when they die.
} else if (message instanceof Terminated) {
// One of our watched senders has died.
removeSender(sender);
} else if (message instanceof String) {
for (ActorRef sender : senders) {
sender.tell(message, getSelf());
}
}
}
}
Again, this code is to give you an idea of how to accomplish this task by taking advantage of the Actor Model. Sorry if the Java isn't 100% accurate, but hopefully you can follow my intent.

Looking for substitutes for some functionality while moving from Netty 3.x to 4.21 final

In Netty 3.5 we use SimpleChannelHandler, which provides method for both event types. How do I use the same approach in Netty 4.0.0?
To be more specific i m looking for a substitute of the method below
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
}
I am trying to send message to all clients connected to the server.
Here is the example for netty 3.x
ChannelGroup allConnected = new DefaultChannelGroup("all-connected");
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelConnected(ctx, e);
allConnected.add(e.getChannel());
}
and then to send messages to all channels connected
ChannelBuffer cb = ChannelBuffers.wrappedBuffer("hello".getBytes(Charset.forName("UTF-8")));
allConnected.write(cb);
This is what i need to do in Netty 4.21 final but i couldn't find a similar method which provided me the needed functionality.
I'm not really sure what you mean by both event types. I guess you mean client and server? I use ChannelInboundHandlerAdapter for that:
public class ServerCommunicationHandler extends ChannelInboundHandlerAdapter {
private final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
channels.add(ctx.channel());
ctx.fireChannelActive();
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
broadcastMessage(msg);
ctx.fireChannelRead(msg);
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
channels.remove(ctx.channel());
ctx.fireChannelInactive();
}
public void broadcastMessage(Object object) {
channels.writeAndFlush(object);
}
}

Categories