Cómo NO hacer un web service

He leído incontables blogs acerca de cómo hacer web services, cómo exponer funcionalidad existente en forma de web service, las distintas maneras de exponerlo en Java (Axis2, CXF, la funcionalidad integrada de Java 6, etc).

Pero todos esos artículos (incluyendo uno que yo mismo escribí hace tiempo) se enfocan únicamente a la parte de exponer la funcionalidad, es decir, poner una interfaz SOAP que se puede invocar desde otra aplicación, probar que responde, y listo. Algunos artículos que he visto incluso no pasan de ser un tutorial de cómo crear el esqueleto del web service en Eclipse, NetBeans, Visual Studio (para los .NETeros) lo cual hace ver a los artículos que detallan herramientas de línea de comando como algo innecesariamente complicado.

Supongo que mucha gente lee estos artículos y se llevan la impresión de que es muy fácil hacer un web service. Simplemente siguen el tutorial o blog, y con eso tienen el esqueleto. El paso siguiente seguramente es invocar un método de un componente que hicieron o que ya tenían hecho, y luego probar que funciona, creando un cliente del web service y haciendo un par de invocaciones. ¡Listo! ¡Que lo pasen a producción!

NO. Faltan muchas cosas! Solamente han resuelto la parte más sencilla, que es exponer la funcionalidad. Pero ¿hicieron pruebas de concurrencia? ¿Invocaron el servicio desde dos clientes al mismo tiempo, con distintos valores, para ver que no se "baten" los datos, o que se atora? Ah claro, hay algunos programadores más hábiles, ya les podemos decir ninjas, que sí hicieron esa prueba, y se dieron cuenta de que la funcionalidad expuesta realmente no es thread-safe, y se baten los datos, porque el componente expuesto está guardando estado, o porque hay secciones críticas de código que utilizan recursos que no se deben compartir.

¿Cómo se resuelve lo anterior? ¡Fácil! De la misma manera que se resolvían esos mismos problemas hace muchos años, como lo hicieron nuestros ancestros cuando tenían broncas así en los servlets: usando la palabra mágica synchronized. Cuando un ninja de antaño tenía problemas de concurrencia en un servlet, simplemente le agregaba synchronized a su método doGet() o doPost y listo! problema resuelto. ¿Qué dicen? ¿Que funciona muy lento? Ese es problema del hardware, pónganle más memoria al servidor, hagan un cluster, no sé, no es mi problema.

Y ahí es donde queda todo, y ahí es donde sufre la calidad de ese sistema, y ahí es donde sobre todo sufren los pobres diablos que tienen que consumir ese web service, porque es un cuello de botella inoptimizable que afecta el desempeño de su aplicación y no hay nada que puedan hacer al respecto. Y aquí es donde yo me he encontrado ya en varias ocasiones. Así que decidí escribir esto, con la esperanza de que los ninjas, rockstars y hackers que se la pasan publicando web services, pongan un poco de atención a estos simples consejos para mejorar el desempeño de sus web services.

No es algo nuevo

Los puntos a considerar al momento de publicar un web service, no son algo nuevo. Existen desde que hace varios años. Son los mismos que también aplican para aplicaciones web.

Concurrencia
Hay que metérselo en la cabeza y tenerlo presente todo el tiempo cuando se desarrolla un web service: se van a estar procesando varias peticiones al mismo tiempo. Repítanlo: se van a estar procesando varias peticiones al mismo tiempo. Para eso es un web service, y así hay que diseñar todo el servicio, no solamente la interfaz expuesta. Hay que considerar la concurrencia en el diseño de todos los componentes involucrados.
Memoria
Hasta suena tonto, no? Hay que cuidar el uso de memoria. Suena fácil. Cómo hacer esto? Pues una manera es cuidar mucho las consultas que se realizan a base de datos. Sin importar la base de datos que usemos, no importa si es la más veloz del mundo, si es SQL o NoSQL, si es un cluster de 1000 computadoras con todos los datos en memoria; si el equipo donde se está procesando una petición de web service hace una consulta que obtiene un millón de registros, hay un problema, porque se van a estar procesando varias peticiones al mismo tiempo, y si una puede hacer una búsqueda que trae un millón de registros a memoria, todas lo pueden hacer.
CPU
El mismo principio que para la memoria. Si una petición hace un cálculo muy complicado, varias lo harán en algún momento.
Otros recursos
Hay que identificar muy bien qué recursos son utilizables por varios procesos al mismo tiempo y cuáles no; qué componentes pueden ser singletons porque sólo están procesando datos y cuáles clases van a ser objetos que guardan valores (y por tanto forman parte de los datos procesados); qué recursos pueden tenerse en un pool y qué recursos hay que estar renovando constantemente; cuáles recursos tienen límites fuera del control de la aplicación, como el número de archivos o file descriptors abiertos, que depende del sistema operativo, el número máximo de hilos que puede tener la aplicación, etc.

OK, pero ¿cómo?

Algunos de estos aspectos son relativamente sencillos de implementar bien, mientras que otros son más complejos o difíciles de diseñar bien, y sobre todo, de depurar.

El uso de memoria, por ejemplo, en varios casos no es tan difícil. La mayoría de los web services de hoy en día son de dos tipos: consultas de datos, o realizar una transacción con datos muy específicos. Los servicios de consultas de datos, cuando no son datos muy específicos (como consultar el saldo de una cuenta), sino que pueden devolver varios resultados según los parámetros (tipo "todas las personas cuyo nombre empieza con E"), son los que muchas veces tienen problemas de memoria. Hay que ser muy cuidadosos de ponerles un límite a los resultados devueltos, usar ese límite en la consulta a la base de datos, e indicar en la respuesta si es que hay más resultados pero no se está devolviendo (para que el cliente pueda realizar otra consulta de esos datos, de modo que se hace un tipo de paginación de los resultados).

A veces podemos aventarnos una búsqueda que parece muy eficiente porque la hicimos con muy pocas líneas de código; jalamos unos registros de esta tabla luego unos de esta otra tabla, hacemos una intersección en memoria y devolvemos el resultado. Pero, ¿qué pasa si los dos conjuntos de resultados son muy largos? Estaremos usando mucha memoria. A veces es mejor dejar que esa intersección la haga la base de datos, aunque sea un query más complejo, o filtrar los datos de una mejor manera para traernos únicamente los que realmente necesitamos para generar el resultado.

Otra veces, detalles tan simples como estar convirtiendo entre tipos de datos (cadenas y fechas, o cadenas y números), o estar concatenando cadenas con el operador +, puede llegar a ocupar bastante memoria (tomando en cuenta que hay muchas concatenaciones ocurriendo, porque se están procesando varias peticiones al mismo tiempo). Es mejor apoyarnos en clases como StringBuilder, o usar String.format en vez de estar concatenando con el operador +.

En cuanto a CPU, primero que nada hay que recordar que la optimización prematura es la raíz de todos los males, por lo que no voy a aconsejar que se pongan a optimizar todos y cada uno de los algoritmos involucrados en su web service. Pero sí es importante que identifiquen si hay algún cuello de botella y, si lo encuentran, buscar la mejor manera de eliminarlo o al menos mejorar su desempeño. No en todos los casos se trata de hacer que un ciclo sea más rápido por las instrucciones que se ejecutan en él; a veces simplemente la optimización puede darse con una mejor utilización de los recursos involucrados en el proceso.

Por ejemplo: si vamos a cifrar datos, o hacer una digestión de datos dentro de un ciclo que va a iterar sobre miles de registros, lo primero que debemos hacer es analizar si realmente tenemos que iterar sobre miles de registros (según lo primero que mencioné, de no jalar tantos datos). Si es algo ya inescapable, entonces podemos optimizar la manera en que vamos a cifrar los datos: utilizar una sola instancia de Cipher y de ser posible, un solo buffer de bytes temporal; instanciamos eso antes del ciclo, en vez de crear una instancia por cada iteración.

En ocasiones, necesitamos utilizar algún objeto que es un poco "caro" de crear (en el sentido de que consume mucha memoria y CPU su creación), pero que necesitamos para una parte del proceso y no podemos tener una sola instancia sino que necesitamos una para cada petición. Pero a veces no se necesita exactamente una para cada petición, sólo que el tener una sola instancia va a obligarnos a tener una sección crítica de código (un bloque synchronized) que sólo un hilo pueda ejecutar a la vez. En estos casos, podemos usar un pool de objetos. Podemos implementar esto de dos maneras: Una es usando algo como commons-pool de Apache, y tener un pool configurable de objetos de la misma clase; ya en producción podremos ajustar parámetros como el tamaño inicial, el tamaño máximo, el tiempo de espera para obtener un objeto del pool, etc. Por ejemplo podemos encontrarnos con que un pool de 10 objetos es suficiente para despachar hasta 100 peticiones simultáneas porque el objeto se usa solamente por una pequeña porción del proceso completo y 10 peticiones pueden usar el mismo objeto sin decremento notable en el desempeño, pero en contraste si cada una de esas 10 peticiones creara su propia instancia, el desempeño sería peor por el tiempo que toma la creación y configuración de dicho objeto.

En este punto, siempre es muy recomendable tener un pool de conexiones a base de datos. Hoy en día es muy fácil de tener, porque lo puede proveer el contenedor JEE que utilicemos, o se puede tener uno propio para el servicio, implementado con commons-dbcp BoneCP o c3p0.

Otra manera de tener disponibles objetos compartidos para varias peticiones, es ponerlos en un variables tipo ThreadLocal, pero entonces hay que tener cuidado de que los hilos que ya no se usan realmente se destruyan, y que los objetos que se crean en el ThreadLocal no empiecen a guardar referencias a otros objetos que no se destruyen, porque tendremos otros problemas debido a fugas de memoria por objetos que no se eliminan nunca por el recolector de basura.

¿Y las transacciones apá?

Un punto muy importante en web services que realizan operaciones (como uno que afecte el saldo de una cuenta por ejemplo) son las transacciones. Hay que asegurarse que sólo un proceso pueda afectar el saldo de una cuenta a la vez, por lo que lo mejor es utilizar transacciones ya sea de JTA o de la base de datos. Hoy en día podemos implementar la transaccionalidad por código, creando una transacción en la base de datos, manejando todos los posibles casos de error, haciendo rollback o commit al final, etc, o podemos simplemente aprovechar cosas como la anotación @Transactional de Spring, que simplemente le ponemos a un método y eso causará que se ejecute dentro de una transacción de JTA o de base de datos, según hayamos configurado la aplicación.

Pero hay que tener cuidado porque las transacciones nos pueden llevar a dos problemas distintos: primero que nada, pueden afectar el desempeño, porque anotar un método con @Transactional es casi como marcarlo synchronized; si es un método que hace muchas cosas y lo primero que hace es bloquear un registro, entonces si hay otras peticiones queriendo utilizar ese mismo registro en la base de datos, tendrán que esperar a que termine el método que ya se está ejecutando. Aún así esto es mucho mejor que ponerle synchronized al método porque entonces sólo un hilo podría ejecutarlo a la vez, sin importar si quieren afectar los mismos datos o no.

Por lo tanto, es importante identificar bien las partes del proceso que realmente necesitan ser transaccionales; a veces todo el proceso necesita ser una transacción, ni modo; si la transacción no afecta datos que sean susceptibles de ser utilizados por muchas peticiones a la vez, no debe haber problema.

El otro problema de las transacciones son los deadlocks. Los deadlocks transaccionales son muy similares a los deadlocks de programación concurrente, es el mismo principio: Un proceso bloquea un registro de la tabla A y luego bloquea un registro de la tabla B, mientras que otro proceso distinto bloquea primero un registro de la tabla B y luego un registro en la tabla A. Si estos son dos métodos de nuestro web service, estaremos en problemas cuando lleguen peticiones simultáneas que ejecuten ambos procesos sobre los mismos datos, ya que un proceso bloqueará la tabla A mientras el otro bloquea la tabla B, y entonces el primer proceso quedará bloqueado esperando leer el dato de la tabla B mientras que el segundo proceso está bloqueado esperando leer el dato de la tabla A (y hasta entonces no soltará el dato de la tabla B, que es el que está esperando obtener el primer proceso).

Por eso es muy importante al manejar transacciones, hacer siempre las cosas en el mismo orden. El segundo proceso debería de bloquear primero el dato de la tabla A y luego el de la tabla B, al igual que el primer proceso; de ese modo no puede ocurrer un deadlock.

Pruebas

Es importantísimo en un web service realizar no solamente pruebas unitarias y de integración, sino pruebas de stress. Desde una simple prueba de invocar el servicio desde 2 clientes distintos al mismo tiempo, para ver que realmente funciona bien de manera concurrente, hasta crear cientos de clientes que cada uno invoque al servicio cientos de veces, para detectar degradaciones significativas en el desempeño del servicio, o incrementos dramáticos en el uso de CPU y memoria.

Monitoreo

Por último, ya que se tiene hecho el web service es importante monitorear su desempeño, para saber si ya se está forzando demasiado al equipo que lo hospeda; ahí ya hay otro tipo de soluciones, como poner el web service en varios equipos y hacer balanceo de cargas, etc. Pero siempre es bueno que esto se haga porque ya no hay otra opción; no es una buena práctica hacer un web service muy ineficiente que solamente puede atender una petición al mismo tiempo y dejarle a los sysadmin la bronca de replicarlo en varios equipos, porque aparte del gasto innecesario que representa el clustering, esa solución permitirá que el web service sí se pueda ejecutar de manera simultánea aunque sea en equipos distintos, y si todos usan la misma base de datos o algún otro recurso compartido entre las instancias del servicio, entonces van a empezar a surgir problemas que no se detectaron en las pruebas, aunque se hayan hecho pruebas de concurrencia.

En fin, espero que les sirvan estos consejos para implementar web services que sean más rápidos y escalables. A mi al menos me sirvió para desahogarme, después de conectarme al enésimo web service con un tiempo de respuesta de varios segundos y que en horas pico no puedo usar porque no me acepta las conexiones (señal de que está saturado).

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de OscarRyz

Se oye que estas opiniones

Se oye que estas opiniones vienen desde la trinchera!!! Ouch!!

Otro punto a agregar:

En el momento de diseñar el servicio es importante recordar que la mayor parte del tiempo de ejecución se pierde en la transmisión de datos ( ya sea de la base de datos al servidor o del servidor al cliente ) Un programa en Java puede procesar 2 mb de información en milisegundos, pero enviar esa información por un WS puede durar varios minutos dependiendo de la velocidad de la red. Generalmente la conexion entre el servidor y la bd es dedicada por lo que ahí no hay mucho problema, pero la del servidor al cliente no.

Entonces hay que agregar:

NO MANDAR ( ni en la petición ni en la respuesta ) MÁS INFORMACIÓN QUE LA ESTRICTAMENTE NECESARIA!

Por ejemplo si el cliente sabe el id de una persona ( o cosa ) enviarlo solamente, no tiene sentido enviar todo el objeto con sus dependencias solo porque generador así lo expuso. De igual manera en la repuesta si no voy a necesitar todo el arreglo de objetos relacionados a esa persona ( o cosa ) , entonces NO hay que devolverlo!

y

HACER EL NÚMERO MÍNIMO DE PETICIONES POSIBLE!!

No tiene caso por ejemplo, primero insertar un objeto, luego esperar su id, y luego insertar todas sus dependencias, hay que enviar todo de un solo golpe.

¡¡Ouchh!!

Todo esta perfecto pero una consulta ¿donde va la palabra synchronizeden el metodo del webservice o en el metodo de las operaciones?

Imagen de ezamudio

Oscar

Idealmente, la mayor parte del tiempo de respuesta se va en la transmisión de los datos (recepción y envío). Pero no siempre es así, precisamente porque hay muchos web services mal hechos que funcionan muy lento y se les va una buena parte en bloqueos y en tiempos de espera internos.

Imagen de ezamudio

Caches

Otra técnica para optimizar el uso de algunos recursos es el manejo de caches. Esto olvidé mencionarlo en el artículo, me lo recordó el buen domix.

La manera de implementarlos pues depende mucho de la aplicación, y como siempre, hay varias opciones. Por ejemplo, Hibernate puede usar (que yo sepa) al menos dos mecanismos de cache y se configura a nivel entidad o a nivel búsqueda los datos a cachear. Esto es útil porque permite que algunas tablas que tienen pocos datos y que no cambian muy seguido (tipo catálogos y cosas así), se puedan manejar con caches; la primera vez que se piden en cualquier sesión, se leen de la base de datos, pero posteriormente se guardan en memoria y en búsquedas posteriores se devuelve la copia que se quedó en memoria, sin necesidad de ir nuevamente por ellos a la base de datos (por eso es importante saber usar un cache; si se le pone cache a una tabla que tiene miles de registros, es una buena receta para quedarse sin memoria pronto).

Otro cache muy popular últimamente es memcached, del cual ya he escrito antes. Ese cache es muy bueno por varias razones:

1. Se puede tener distribuido en varios equipos, armar un cluster, o tener un equipo dedicado a memcached
2. Es muy bueno para almacenar datos con fecha de expiración y devolverlos muy rápido.
3. Puede haber varios procesos distintos conectados al mismo cache. El proceso 1 puede guardar algo en el cache y el proceso 2 puede leerlo de ahí.

Como qué se puede guardar en memcached? De manera simplista, memcached es como tener un servidor aparte que tiene un java.util.Map gigantesco. Entonces podemos guardar un valor bajo una llave y luego pedirlo por esa misma llave. La única diferencia es que al momento de guardarlo hay que darle un tiempo de vida al dato; el cache "solito" va a olvidar ese dato cuando llegue la fecha de expiración.

OJO: memcached (y cualquier otro cache similar) no es un sustituto de base de datos; es un complemento. Si tienen un sitio de comercio electrónico con un catálogo extenso de productos, cuando hacen búsquedas a la base de datos de un producto específico pueden usar memcached, para guardar ahí los datos del producto por unas horas y usar esa info, aliviando un poco la carga a la base de datos. El mecanismo es así:

SIN CACHE
1. Buscar en la tabla Producto por el ID del producto a obtener (esto lo pueden estar haciendo muchísimos hilos al mismo tiempo).

CON CACHE
1. Buscar en el cache el producto por su ID. Si existe, usar ese dato (ya nos ahorramos la lectura a la base de datos)
2. Si no existe el producto en el cache, ir por el producto a la base de datos.
3. Meter el producto al cache (y así cualquier otro hilo que siga este procedimiento encontrará el producto en su paso 1)

Imagen de gorlok

SOA

Es muy común que algunos programadores novatos piensen que con poner un synchronized se resuelven todos los problemas de concurrencia. Pero si hay más de una instancia de la JVM, o se despliega en un cluster, pronto se verán otros problemas.

Muy relacionado con web services, es el diseño de una arquitectura SOA, como puede ser una basada en ESB. Además de la auditoría y control de toda la arquitectura, esto nos brinda una capa adicional de independencia sobre el versionado y ubicación de los web services "reales", pudiéndose hacer adaptaciones entre protocolos y distintas versiones de clientes y servicios, orquestación de servicios incluso vía workflows y sistemas de reglas de negocio atendiendo a las necesidades cambiantes del negocio, etc.

Consulta: ¿qué significa "batear" datos? No estoy familiarizado con este término. ¿Es "corrupción de datos" o tiene otros significados?

Imagen de ezamudio

batear?

No sé qué signifique, supongo que rechazar datos? Yo mencioné algo de datos que se baten, no que se batean. Del verbo batir, o sea que se revuelven todos, cuando la petición 1 obtiene datos de la petición 2, etc.

synchronized

¿Un synchronized no tiene un alcance (SCOPE)?

Es decir un web server multihilos implementa hilos transparentes para un programa de tal forma que se ejecuta al mismo tiempo pero por otro lado si yo pongo un metodo synchronized los hilos del servidor lo alcanzan o solo tiene el ambito del propio programa.

En la forma que tengo implementado un webservice tengo 3 @webmethods por la complejidad todos ellos hacen uso de otras clases y otros metodos, uno es de operaciones chonchas que ingresa una tanda enorme de datos a una BD

Crei que con un synchronized resolveria mi problema de perdida de datos y todas las peticiones se atenderian pero me di cuenta que no es asi los datos ingrasan intercalados osea un peticiony otra y no atiende una peticion unicamente a la vez.

Si, a lo mejor es que no domino el tema de concurrencia o paralelismo o etc, pero no sean despectivos, en esta pagina solo identifico a 3 personas que se ve dominan la "mayoria" de los temas de programacion por los demas he visto desde preguntas muy absurdas hasta algunas "pro"

Imagen de ezamudio

despectivos?

No sé dónde estuvo lo despectivo. La idea aquí es compartir conocimiento, creo que despectivo sería soltar unos insultos y criticar sin proponer maneras de mejorar.

No sé a qué te refieres con el alcance de synchronized, ni entendí lo de los hilos transparentes... la cosa es que un bloque de código marcado con synchronized solamente puede ser ejecutado por un hilo a la vez. Si marcas un método es como si metieras todo el código del método en un bloque synchronized usando al mismo objeto como candado. Es decir:

public void metodo1() {
  synchronized(this) {
    //codigo
  }
}

es lo mismo que

public synchronized void metodo1() {
  //codigo
}

PERO hay una gran diferencia cuando marcas dos o más métodos en la misma clase: TODOS los métodos marcados como synchronized usan el mismo candado (el receptor del método), de modo que solamente se puede ejecutar uno de esos métodos a la vez. O sea, si tienes 3 métodos en una clase y los 3 están marcados como synchronized, y luego quieres invocar cada método desde un hilo distinto, solamente se va a ejecutar uno de los 3 métodos a la vez, porque todos usan el mismo candado.

Si un proceso externo (como un web server, digamos apache httpd) se conecta a tu aplicación (digamos, corriendo en Tomcat, via AJP) y se ejecuta un método synchronized, los hilos de Apache obviamente se ven afectados por la sincronización de tu método, aunque no se "vea" en qué momento.

Si tienes un web service con 3 métodos como dices, y un método lo marcaste como synchronized, solamente un hilo podrá ejecutar ese método a la vez. Los demás hilos que quieran ejecutar el método tendrán que esperar a que termine la ejecución. Si se hacen 10 invocaciones simultáneas a dicho método, y tu método tarda 1 segundo en ejecutarse, entonces el primer hilo que pueda ejecutar dicho método podrá terminar en 1 segundo, pero el último tardará 10 segundos (9 segundos estuvo esperando, y el último segundo fue su tiempo de ejecución).

Entiendo que tu método synchronized es el de las operaciones chonchas. Eso empeora la situación porque seguramente es el método que más se tarda en correr. Ahí tienes que identificar si realmente se atora o se pierden datos por el tema de la concurrencia, o por alguna otra cosa. Si es concurrencia, estás seguro que el problema es a nivel aplicación? Porque podría ser tema de concurrencia en la base de datos (por cosa de transacciones como mencioné en el artículo). En fin, tal vez valga la pena que pongas un post en los foros para tratar el tema de manera específica.

Imagen de Abaddon

No exponer la capa de dominio

Estoy completamente de acuerdo con lo que se comenta, y ademas, relacionado a lo que comento Oscar yo agregaría: NO EXPONER LA CAPA DE DOMINIO DIRECTAMENTE.

Por ahí leí alguna vez que uno de los mayores enemigos de los sistemas es el tiempo. Ya que con el tiempo una tabla o repositorio de datos puede crecer tanto que una consulta que en un principio se desempeñaba bien después ya puede tardar muchísimo. También, una estructura de datos o un objeto de dominio al principio puede tener dos que tres propiedades que conforme va pasando el tiempo de desarrollo puede crecer el numero de propiedades y asociaciones a otros objetos de dominio, y si estamos usando frameworks ORM y otros framework para exponer un servicio web (SOAP o REST) si hacemos que se mapeen nuestros objetos de dominio a XML o JSON este framework lo que hace es empezar a llamar los metodos getXXX de nuestro objeto y empezar a cargar las asociaciones (que incluso podrían estar como lazy) y el desempeño se vería afectado.

Lo mejor seria tener una capa de DTOs entre los servicios que se exponen como sevicios web y nuestra capa de dominio solo con los datos necesarios.

Imagen de MachinesAreUs

Las recomendaciones no solo son para WS

Es muy buen artículo. Sin embargo creo que la mayoría (si no es que todas) las recomendaciones son adecuadas no solo a la implementación de servicios web, sino a cualquier tipo de sistema que atienda peticiones concurrentes, por ejemplo, aplicaciones web, servicios expuestos mediante otras tecnologías de integración (JMS, AMPQ, hessian, etc, etc). En otras palabras, le cambiaría el título =)

También es importante mencionar que Spring ofrece muy buenos mecanismos para hacer ajustes al comportamiento de las transacciones y evitar que se comporten casi igual que usar synchronized. Específicamente cada método que participa en una transacción puede configurarse con dos atributos, que indican 1) la manera en que se propagarán las transacciones ya existentes y 2) el nivel de aislamiento requerido en la BD respecto a otras transacciones concurrentes.

También creo que el problema de la mayoría de los tutoriales sobre servicios web (y otras tantas tecnologías) es un énfasis en la tecnología por sí misma, sin un sustento en los principios de diseño que la acompañan y soportan. En mi opinión la culpa es tanto de escritores como lectores de dichos artículos. El resultado por lo general es software con fallas tan graves y fundamentales como la que mencionas que implementan servicios que 'baten' los resultados por mantener estado compartido.. ¿que diablos les pasa?! Sin entender los principios de diseño adecuados, no importa que te den la mejor herramienta del mundo, seguirás escribiendo software mantequilla (como dice ), y muy posiblemente, en lugar de beneficiar al mundo (o mínimo a tu cliente) con dicha tecnología, le causarás más problemas que los que solucionas.

Imagen de Sr. Negativo

Malas prácticas

Las malas prácticas existen, muchos tutoriales confunden más que ayudar. Se enfocan en resaltar la tecnología, pero olvidan muchos otros aspectos:

  • Plan de trabajo
  • Diseño de las bases de datos
  • Ciclo de vida del sistema
  • etc.

Excelente post!

Imagen de greeneyed

Web services empieza por web

De acuerdo con MachinesAreUs, lo que se comenta es aplicable tanto a Web Services como a aplicaciones web. De hecho, aplicaciones web se hacen desde bastante antes y las técnicas están ahí para aprender de ellas. El error en muchos casos es el tipo de enseñanza que se ha hecho de los Web Services, donde en vez de tratarlos como web, se tratan como "publicar fácilmente metodos de objetos en remoto", de forma transparente, sin problemas... ja, ja y ja.
El problema de las peticiones concurrentes, utilización de memoria, saturación de servidores... no es nuevo, lo que ocurre es que lanzan a los programadores a hacer servicios web sin el know-how necesario y la transparencia y simplicidad son un engaño.

En cuanto al tema de concurrencia en sí, como ya se ha mencionado programar correctamente cuando hay concurrencia por en medio no es sencillo y no basta con aplicar un synchronized aquí y allá. Hay que programar teniendo en cuenta el tema desde el principio, por ejemplo evitando miembros compartidos, evitando contención de recursos por exceso de sincronzación... Es un tema complejo en el que no se puede correr antes de andar, y desafortunadamente hoy en día te piden record en los 100m lisos en cuanto te levantas del suelo.

Imagen de luxspes

Syncronized NO es la solucion

Todo esta perfecto pero una consulta ¿donde va la palabra synchronizeden el metodo del webservice o en el metodo de las operaciones?

@hugo.garcia: Mmmm.... o no entendiste el articulo... o estas siendo sarcastico....

ni no entendi ni estoy siendo sarcastico

A ver esta es mi situacion, implemente un web service mas menos asi

@WebService()
public class Service {

@WebMethod()
        public int login(@WebParam(name = "usuario")
        String usuario, @WebParam(name = "password")
        String password, @WebParam(name = "ip")
        String ip) {

        Usuario u = new Usuario();
        if (u.validaAcceso(usuario, password, idSolucion, ip)) {
                return u.getIdUsuario();
        }

@WebMethod
        public int insertarDatos(
        @WebParam(name = "datos") Datos datos,//
        @WebParam(name="lib") int lib,
        @WebParam(name="chksum") byte[] chkSum ){

        Procesador ps = new Procesador(datos,lib, chkSum);

                if (ps.registra()) {
                        return 1
                } else {
                        log.info("ERROR AL REGISTRO Archivo "+datos.getNombreArchivo());
                        return 2;
                }
        }

}

Bueno insertarDatos() es el metodo que me trae de cabeza

public class Procesador{
        .//operaciones
        .
        .
public boolean registra(){
        .      
        .//despues de operaciones
        .
        ProcesadorDAO psDAO = new ProcesadorDAO(datos,lib,chkSum);
        if(psDAO.insertarBD()){
                return true;
        }
        else{
                return false;
        }

       
}
       

}

y la otra clase

 public class ProcesadorDAO{
.
.
.//despues de algunas validaciones y operaciones
        insertarBD(){
        //y aqui va las operaciones para israter los datos que quiero  

        }
.
.
.      
}

Esto mas menos es lo que tengo (michisimo menos que mas)

Ahora mas que conceptos, teoria,buenos y malos deseos quisiera que me dieran tips practicos de mejorar este WebService y
su implementacion

Porque hice una prueba y le puse un synchronized al metodo insertarBD pero mi duda porque realmente la tengo es si seria correcto ponerlo ahi o aqui
@WebMethod
public int insertarDatos(

Aqui van algunos datos tecnicos
Lo tengo pubicado en JBoss,
base de datos es MS SQL Server
La tabla a la que entran los datos es enorme hacia abajo es decir tiene millones de registros y cada dia entran en promedio
1.5 millones de registros y estoy "perdiendo"entre el 10% y 15% (realmente no los pierdo pero tengo que recuperarlos)
aumente el numero de conexiones por parte de JBoss a 100 antes tenia 40 y el historico me indica que se llenan todas las conexiones

@luxspes: no uso select max; uso un identity, tiene indices, las bookmarks estan como deben, la BD no la administro yo sino un DBA

todo esto viene de este otro post

Cualquier comentario o ayuda de antemano gracias

Imagen de ezamudio

Otro post

Regresemos entonces al otro post a continuar con esa discusión. Este foro se va a convertir en la ayuda para tu web service y no es el caso.

Imagen de jiturbide

Tomarse el tiempo para diseñar

Una buena practica es tomarse el tiempo para hacer un poco de diseño. La decision de que y como exponer no debe ser del programador, debe ser una decision concensada desde muy atras.

Tambien considerar que los servicios web no son operaciones de llamadas entre clases o componentes de mi aplicacion, son una herramienta de integracion de sistemas de otras organizaciones u areas, por lo tanto se debe tomar un tiempo para platicar con otros equipos de desarrollo tanto de la necesidad funcional hoy y futura como de aspectos no funcionales como la concurrencia, seguridad, granularidad, etc.

Creo que esto no lo mencionaron...
Granularidad del servicio: muy fino, el cliente tendra que hacer varias llamadas para completar el caso de uso y tendras un impacto en las conexiones. Muy grueso? tal vez una llamada sea suficiente pero si es demasiado general y no satisface las necesidades de un sistema nuevo en particular te veras obligado a crear una version 2 o redefinirlo pegandole a tus clientes actuales.

Patrones de diseño - Aplicando un patron muy basico, Application Service o Web Service Broker, mejoras la reusabilidad y performance.
Al mover la logica de negocio del endpoint al AS o WSB el scope de la transaccion se reduce, el componente se puede reutilizar por otra operacion de negocio, etc, al final el endpoint se limita realizar validaciones y preparacion de la respuesta.

Nomenclatura adecuada - Tu Ws debe ser autodescriptivo, tener una nomenclatura adecuada para el nombre de la operacion y de los tipos de dato que definas dentro. Les ha tocado ver namespaces del tipo www.prueba.org ?

Manejo de errores, si falla tu WS no le dejes el problema a las Excepciones remotas, maneja el problema y envia en la respuesta un objeto con los detalles del problema. Si no puedes modificar los argumentos de regreso envia esta informacion en los header de la respuesta.

My two cents.

Imagen de beto.bateria

Esta buena la info, gracias.

Esta buena la info, gracias.

Imagen de poloche

sip

Interesante lo vamos a tomar encuenta a la hora de desarrollos

Strings u Objetos

Tengo una duda sobre web services que no he podido resolver talvez ustedes me pueden asistir.
El problema es que creando una aplicación JEE con web services con un compañero de facultad tenemos la discusión de si los métodos del web service para crear un objeto del dominio debe recibir los parámetros del objeto (solo tipos básicos) o debe recibir todo el objeto.
Por ejemplo:

Pasando el objeto

      @WebService()
      public class ContenidoWS {

             @WebMethod()
             public void crearEntradaBlog(@WebParam(name = "entradablog") EntradaBlog entradablog) {
                       ...
             }
      }

Pasando los parémetros del objeto

      public class EntradaBlog {
             private Date fecha;
             private String titulo;
             private String contenido;

             //get y set
      }
      @WebService()
      public class ContenidoWS {

             @WebMethod()
             public void crearEntradaBlog(@WebParam(name = "fecha") Date fecha,
                                                                   @WebParam(name = "titulo") String titulo,
                                                                   @WebParam(name = "contenido") String contenido) {
                       ...
             }
      }

Gracias por el post.

Son unos maestros... Hace

Son unos maestros... Hace tiempo estaba buscando como optimizar recursos al momento de programar con java, tenia la idea de como hacerlo, pero con esto me quedo todo mas claro, saludos... desde Guanajuato....