Start workin on async server to be able to support many concurrently open connections for long polling.

This commit is contained in:
marcins78@gmail.com
2013-01-09 00:26:28 +00:00
parent 0055bd72bc
commit 5fcb3a1d68
5 changed files with 211 additions and 1 deletions

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gemp-lotr</artifactId>
<groupId>com.gempukku.lotro</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.gempukku.lotro</groupId>
<artifactId>gemp-lotr-async</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>gemp-lotr-web-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.6.1.Final</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,104 @@
package com.gempukku.lotro.async;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.handler.codec.http.*;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected;
import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import org.jboss.netty.util.CharsetUtil;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class HttpRequestHandler extends SimpleChannelUpstreamHandler {
private HttpRequest _request;
private boolean _readingChunks;
private String _uri;
private Map<Type, Object> _objects;
public HttpRequestHandler(Map<Type, Object> objects) {
_objects = objects;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
_request = (HttpRequest) e.getMessage();
if (is100ContinueExpected(_request)) {
send100Continue(e);
}
String uri = _request.getUri();
if (uri.indexOf("?")>-1)
_uri = uri.substring(0, uri.indexOf("?"));
else
_uri = uri;
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(_request.getUri());
Map<String, List<String>> queryStringParams = queryStringDecoder.getParameters();
HttpMethod httpMethod = _request.getMethod();
if (_request.isChunked()) {
send400Error(e);
} else {
ChannelBuffer content = _request.getContent();
if (content.readable()) {
}
writeResponse(e);
}
}
private void writeResponse(MessageEvent e) {
// Decide whether to close the connection or not.
boolean keepAlive = isKeepAlive(_request);
// Build the response object.
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
response.setContent(ChannelBuffers.copiedBuffer("Response: " + _uri, CharsetUtil.UTF_8));
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection.
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
}
// Write the response.
ChannelFuture future = e.getChannel().write(response);
// Close the non-keep-alive connection after the write operation is done.
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
private void send400Error(MessageEvent e) {
boolean keepAlive = isKeepAlive(_request);
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST);
ChannelFuture future = e.getChannel().write(response);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
private void send100Continue(MessageEvent e) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
e.getChannel().write(response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
e.getCause().printStackTrace();
e.getChannel().close();
}
}

View File

@@ -0,0 +1,44 @@
package com.gempukku.lotro.async;
import com.gempukku.lotro.packs.PacksStorage;
import com.gempukku.lotro.server.provider.DaoBuilder;
import com.gempukku.lotro.server.provider.PacksStorageBuilder;
import com.gempukku.lotro.server.provider.ServerBuilder;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.http.HttpContentCompressor;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class HttpServerPipelineFactory implements ChannelPipelineFactory {
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = Channels.pipeline();
// Uncomment the following line if you want HTTPS
//SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
//engine.setUseClientMode(false);
//pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("decoder", new HttpRequestDecoder());
// Uncomment the following line if you don't want to handle HttpChunks.
//pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
pipeline.addLast("encoder", new HttpResponseEncoder());
// Remove the following line if you don't want automatic content compression.
pipeline.addLast("deflater", new HttpContentCompressor());
Map<Type, Object> objects = new HashMap<Type, Object>();
objects.put(PacksStorage.class, PacksStorageBuilder.createPacksStorage());
DaoBuilder.fillObjectMap(objects);
ServerBuilder.fillObjectMap(objects);
ServerBuilder.constructObjects(objects);
pipeline.addLast("handler", new HttpRequestHandler(objects));
return pipeline;
}
}

View File

@@ -0,0 +1,26 @@
package com.gempukku.lotro.async;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
public class LotroAsyncServer {
public static void main(String[] server) {
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ServerBootstrap bootstrap = new ServerBootstrap(factory);
bootstrap.setPipelineFactory(new HttpServerPipelineFactory());
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.bind(new InetSocketAddress(8080));
}
}

View File

@@ -7,6 +7,7 @@ import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.logic.decisions.AwaitingDecision;
import com.gempukku.lotro.logic.timing.GameStats;
import java.util.Date;
import java.util.List;
public class GameEvent {
@@ -18,9 +19,10 @@ public class GameEvent {
AT, RT,
M, W,
GS,
CAC, EP, CA, D
CAC, EP, CA, D, CH
}
private Date _date;
private String _message;
private String _side;
private Type _type;
@@ -43,6 +45,15 @@ public class GameEvent {
_type = type;
}
public Date getDate() {
return _date;
}
public GameEvent date(Date date) {
_date = date;
return this;
}
public Integer getIndex() {
return _index;
}