Probando servidores fáciles para recibir datos vía TCP usando Grizzly

23 07 2009

Grizzly. El motor del nuevo Glassfish

Hace unos días me surgió la necesidad de hacer un servidor capaz de recibir solicitudes TCP concurrentes con un buen rendimiento y de manera fácil. Entre las herramientas que surgieron, se encontró Grizzly: el cual en su página principal nos explica que es un proyecto que surge por la necesidad de escribir aplicaciones para servidores escalables y robustas en lenguaje Java, de una manera mucho más fácil usando la librería Java New I/O API (NIO).

Después de mucho leer y en mi proceso de aprender este framework, he encontrado dos ejemplos de servidores muy sencillos, los cuales he probado usando un cliente que envía una cadena de texto cada milisegundo con el fin de probar el performance de los mismos.
El primer ejemplo que probé lo encontré en el blog de Chad Gallemore el cual tiene el siguiente código:

public class TCPProcessor {
public static void main(String[] args) throws Exception {
int port = Integer.getInteger(args[0]);
Controller controller = new Controller();
TCPSelectorHandler tcpHandler = new TCPSelectorHandler();
final MyProtocolFilter filter = new MyProtocolFilter();
tcpHandler.setPort(port);
controller.setProtocolChainInstanceHandler(new DefaultProtocolChainInstanceHandler() {
public ProtocolChain poll() {
ProtocolChain protocolChain = protocolChains.poll();
if (protocolChain == null) {
protocolChain = new DefaultProtocolChain();
protocolChain.addFilter(new ReadFilter());
protocolChain.addFilter(filter);
}
return protocolChain;
}
});
controller.addSelectorHandler(tcpHandler);
controller.start();
}
}
public class MyProtocolFilter implements ProtocolFilter {
public boolean execute(Context context) {
final WorkerThread workerThread = ((WorkerThread)Thread.currentThread());
String message = "";
ByteBuffer buffer = workerThread.getByteBuffer();
buffer.flip();
if(buffer.hasRemaining()) {
byte[] data = new byte[buffer.remaining()];
int position = buffer.position();
buffer.get(data);
buffer.position(position);
message = new String(data);
}
System.out.println("New message being read, message is: " + message);
buffer.clear();
return false;
}
public boolean postExecute(Context context) throws IOException {
return true;
}
}

Para probar este código, cree un cliente que envía una cadena de texto cada milisegundos y lo ejecuté en 4 computadoras diferentes. Se comenzó portando bastante bien, pero en un par de minutos el servidor se puso muy lento y los clientes empezaron a mostrar errores ya que no lograban realizar la conexión al servidor. Se presentaron alrededor de unos 40 errores entre todos los clientes.

Por ende, decidí buscar un nuevo servidor, que fuera igual de sencillo para poder estudiarlo: entonces encontré éste en las páginas de ayuda de Grizzly, el cual presenta el siguiente código:

/*
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*/
package se.phlogiston.grizzly;
import com.sun.grizzly.*;
import com.sun.grizzly.filter.ReadFilter;
import com.sun.grizzly.filter.EchoFilter;
import com.sun.grizzly.filter.LogFilter;
/**
* Simple EchoServer using grizzly, copied from somewhere on grizzly.dev.java.net
*/
public class Server {
final ProtocolFilter read = new ReadFilter();
final ProtocolFilter echo = new EchoFilter();
final TCPSelectorHandler tcp_handler = new TCPSelectorHandler();
final Controller controller = new Controller();
public static void main(String[] args) {
int port = 0;
for (int i = 0; i < args.length;i++) {
if (args[i].equals("-p")) {
if (args[++i] != null) {
port = Integer.parseInt(args[i]);
System.out.println("Port set to "+port);
} else {
System.out.println("No port set!");
}
}
}
Server s = new Server();
s.execute(port);
}
void execute(int pPort) {
if (pPort != 0) {
tcp_handler.setPort(pPort);
} else {
System.out.println("No port given!");
System.exit(1);
}
controller.setSelectorHandler(tcp_handler);
controller.setProtocolChainInstanceHandler(
new DefaultProtocolChainInstanceHandler() {
public ProtocolChain poll() {
ProtocolChain protocol_chain = protocolChains.poll();
if (protocol_chain == null) {
protocol_chain = new DefaultProtocolChain();
protocol_chain.addFilter(read);
//protocol_chain.addFilter(log);
protocol_chain.addFilter(echo);
}
return protocol_chain;
}
}); // end overridden method
try {
controller.start();
} catch(Exception e) {
System.out.println("Exception in controller...");
}
}
}

Después de realizar la misma prueba, éste servidor solo arrojó 8 errores en los clientes. Esto muestra una mejora sustancial en éste servidor.
Espero seguir mejorando el mismo para realizar todas las tareas que requiero, pero lo más importante es que con estas pruebas, se puede ver que aunque ambos servidores son bastante sencillos, el último es mucho más eficiente que el primero.

Aún con este buen resultado, no me sentí conforme y seguí buscando otro pero con el mismo objetivo: que fuera igual de sencillo para poder estudiarlo. Encontré otro en los tutoriales de inicio de Grizzly. El código es mucho más extenso por lo que no lo colocaré en el blog para no hacer el post aún más extenso de lo que se ha convertido. Sin embargo colocaré el proyecto de Netbeans donde monte el servidor para que lo puedan descargar y probar

Con éste servidor sólo se presentaron 2 errores al realizar la misma prueba (tuve que cambiar el mensaje que envía el cliente para que al final tuviera la cadena “[eoq]”). Se pudieron manejar 30 conexiones concurrentes por segundo. Sin lugar a duda fue el mejor resultado.

Seguiré leyendo para seguir aprendiendo de Grizzly y comentando en futuros posts

Anuncios

Acciones

Information

2 responses

27 07 2009
fuelusumar

Bastante util el codigo y todo, yo fui uno de los que ayudo a moroco a probar el servidor, bombardiando con el cliente, y en verdad se comporto muy bien, la mayoria de los problemas fueron de parte del cliente

27 07 2009
moroco

Gracias diez, sin ti ni Luiso hubiera podido hacer las pruebas de resistencia del servidor… Gracias Totales!!!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s




A %d blogueros les gusta esto: