I'm new in Java. I understand the example in this site and applied it.
What I try to understand is how can I define /:module/:action kind of customization where :module or :action can be anything instead of calling a static url like /test
I read the documentation but it seems that createContext accepts static string instead of a regex.
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/test", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
}
Related
I am creating a simple micro service using vertx and when i start my server it only create one event thread when available is 12.
My code to start server is
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
int processorCounts = Runtime.getRuntime().availableProcessors();
DeploymentOptions options = new DeploymentOptions().setInstances(processorCounts);
vertx.deployVerticle( HttpRouter.class.getName(),options);
}
And my http router looks like this
#Override
public void start() throws Exception {
super.start();
Router router = Router.router(vertx);
router.get("/").handler(event -> {
event.response().end("Hello World");
});
vertx.createHttpServer().requestHandler(router::accept).listen(8001);
}
What is your process for testing? I assume you opened a browser and hit refresh on the same page. Then yes, the same verticle instance will handle the requests. The reason is Vert.x load balances connections among verticles instances, not requests.
Open a different browser and you should see different event loop names.
I want to create a communication system with two clients and a server in Netty nio. More specifically, firstly, I want when two clients are connected with the server to send a message from the server and after that to be able to exchnage data between the two clients. I am using the code provided from this example. My modifications in the code can be found here: link
It seems that the channelRead in the serverHandler works when the first client is connceted so it always return 1 but when a second client is connected does not change to 2. How can I check properly from the server when both clients are connected to the server? How can I read this value dynamically from my main function of the Client? Then which is the best way to let both clients communicate?
EDIT1: Apparently it seems that the client service is running and close directly so every time that I am running a new NettyClient is connected but the connection is closed after that. So the counter is always chnages from zero to one. As I was advised in the below comments I tested it using telnet in the same port and the counter seems to increasing normally, however, with the NettyClient service no.
EDIT2: It seems that the issue I got was from future.addListener(ChannelFutureListener.CLOSE); which was in channelRead in the ProcessingHandler class. When I commented it that out it seems that the code works. However, am not sure what are the consequences of commented that out. Moreover, I want from my main function of the client to check when the return message is specific two. How, could I create a method that waits for a specific message from server and meanwhile it blocks the main functionality.
static EventLoopGroup workerGroup = new NioEventLoopGroup();
static Promise<Object> promise = workerGroup.next().newPromise();
public static void callClient() throws Exception {
String host = "localhost";
int port = 8080;
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(), new ClientHandler(promise));
}
});
ChannelFuture f = b.connect(host, port).sync();
} finally {
//workerGroup.shutdownGracefully();
}
}
I want inside the main function to call the method and return the result and when it is 2 to continue with the main functionality. However, I cannot call callClient inside the while since it will run multiple times the same client.
callBack();
while (true) {
Object msg = promise.get();
System.out.println("Case1: the connected clients is not two");
int ret = Integer.parseInt(msg.toString());
if (ret == 2){
break;
}
}
System.out.println("Case2: the connected clients is two");
// proceed with the main functionality
How can I update the promise variable for the first client. When I run two clients, for the first client I always received the message :
Case1: the connected clients is not two
seems that the promise is not updated normally, while for the second client I always received the:
Case2: the connected clients is two
If my memory is correct, ChannelHandlerContext is one per channel and it can have multiple ChannelHandlers in it's pipeline. Your channels variable is an instance variable of your handler class. And you create a new ProcessingHandler instance for each connection. Thus each will have one and only one connection in channels variable once initialized - the one it was created for.
See new ProcessingHandler() in initChannel function in the server code (NettyServer.java).
You can either make channels variable static so that it is shared between ProcessingHandler instances. Or you can create a single ProcessingHandler instance elsewhere (e.g. as a local variable in the run() function) and then pass that instance to addLast call instead of new ProcessingHandler().
Why the size of ChannelGroup channels is always one. Even if I connect
more clients?
Because child ChannelInitializer is called for every new Channel (client). There you are creating new instance of ProcessingHandler, so every channel see its own instance of ChannelGroup.
Solution 1 - Channel Attribute
Use Attribute and associate it with Channel.
Create attribute somewhere (let's say inside Constants class):
public static final AttributeKey<ChannelGroup> CH_GRP_ATTR =
AttributeKey.valueOf(SomeClass.class.getName());
Now, create ChannelGroup which will be used by all instances of ProcessingHandler:
final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
Update your child ChannelInitializer in NettyServer :
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new RequestDecoder(),
new ResponseDataEncoder(),
new ProcessingHandler());
ch.attr(Constants.CH_GRP_ATTR).set(channels);
}
Now you can access instance of ChannelGroup inside your handlers like this:
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
final ChannelGroup channels = ctx.channel().attr(Constants.CH_GRP_ATTR).get();
channels.add(ctx.channel());
This will work, because every time new client connects, ChannelInitializer will be called with same reference to ChannelGroup.
Solution 2 - static field
If you declare ChannelGroup as static, all class instances will see same ChannelGroup instance:
private static final ChannelGroup channels =
new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
Solution 3 - propagate shared instance
Introduce parameter into constructor of ProcessingHandler:
private final ChannelGroup channels;
public ProcessingHandler(ChannelGroup chg) {
this.channels = chg;
}
Now, inside your NettyServer class create instance of ChannelGroup and propagate it to ProcessingHandler constructor:
final ChannelGroup channels = new
DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new RequestDecoder(),
new ResponseDataEncoder(),
new ProcessingHandler(channels)); // <- here
}
Personally, I would choose first solution, because
It clearly associate ChannelGroup with Channel context
You can access same ChannelGroup in other handlers
You can have multiple instances of server (running on different port, within same JVM)
I'm developing a HTTP server using HttpServer class. The code is like the following.
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8989), 0);
server.createContext("/", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
}
static class MyHandler implements HttpHandler {
public void handle(HttpExchange httpExchange) throws IOException {
/*
Some code here
*/
}
}
What I want is to find something (an id variable or an object) that identifyies the actual connection in the handler function.
If I make a break-point in the handler, I debug the server, then I run a client, I can see the content of httpExchange:
I think that connection attribute is a good choice. But I can't find how to get it from httpExchange, or its id.
Is there any suggestion?
In one package I have two different classes Client.java and Server.java
I want to make this package jar, i mean executable.
First I want the Server class to run and after 2-3 seconds I want Client method to run. Is it possible?
Thank you
You have to leave only one main method and run your server and client in separate threads from it.
To do it, take a look at Runnable interface. Your server class and client class should implement it. Then you have to move the logic, used to start server and client to it's run() method.
class Server implements Runnable {
#Override
public void run() {
//your server starting logic here
}
}
class Client implements Runnable {
#Override
public void run() {
//your client starting logic here
}
}
After that, you can modify your main method, to start server and client, like:
public static void main(String args[]) throws InterruptedException {
Server server = new Server();
Client client = new Client();
Thread tServer = new Thread(server);
tServer.start();
//here you can wait some time to Server started
Thread tClient = new Thread(client);
tClient.start();
}
I have built a simple HttpServer following tutorials i have found online, using Sun's lightweight HttpServer.
Basically the main function looks like this:
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
//Create the context for the server.
server.createContext("/", new BaseHandler());
server.setExecutor(null); // creates a default executor
server.start();
}
And I have implemented the BaseHandler Interface's method to process the Http request and return a response.
static class BaseHandler implements HttpHandler {
//Handler method
public void handle(HttpExchange t) throws IOException {
//Implementation of http request processing
//Read the request, get the parameters and print them
//in the console, then build a response and send it back.
}
}
I have also created a Client that sends multiple requests via threads. Each thread sends the following request to the server:
http://localhost:8000/[context]?int="+threadID
On Each client run, The requests seem to arrive in different order to the server, but they are served in a serial manner.
What i wish to acomplish is for the requests to be processed in a parallel manner if that is possible.
Is it possible, for example, to run each handler in a seperate thread, and if so, is it a good thing to do.
Or should i just drop using Sun's lightweight server altogether and focus an building something from scratch?
Thanks for any help.
As you can see in ServerImpl, the default executor just "run" the task :
157 private static class DefaultExecutor implements Executor {
158 public void execute (Runnable task) {
159 task.run();
160 }
161 }
you must provide a real executor for your httpServer, like that :
server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
and your server will run in parallel.
Carefull, this is a non-limited Executor, see Executors.newFixedThreadPool to limit the number of Thread.
You used server.setExecutor(null) that runs the handler in the same caller thread. In this case, the main thread which runs the server.
You only need to change the line as
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
//Create the context for the server.
server.createContext("/", new BaseHandler());
server.setExecutor(Executors.newCachedThreadPool());
server.start();
}