¿Cómo diseñar servicios web altamente escalables en Java?

15

Estoy creando algunos servicios web que tendrían 2000 usuarios concurrentes. Los servicios se ofrecen de forma gratuita y, por lo tanto, se espera que obtengan una gran base de usuarios. En el futuro, puede ser necesario escalar hasta 50,000 usuarios.

Ya hay algunas otras preguntas que abordan el problema, como: /programming/2567254/building-highly-scalable-web-services

Sin embargo, mis requisitos difieren de la pregunta anterior.

Por ejemplo: mi aplicación no tiene una interfaz de usuario, por lo que las imágenes, CSS y JavaScript no son un problema. Está en Java, por lo que sugerencias como usar HipHop para traducir PHP a código nativo son inútiles.

Por eso decidí hacer mi pregunta por separado.

Esta es la configuración de mi proyecto:

  1. Descanse servicios web basados ​​en Apache CXF
  2. Hibernate 3.0 (con optimizaciones relevantes como carga lenta y HQL personalizado para afinar)
  3. Tomcat 6.0
  4. MySql 5.5

¿Cuáles son las mejores prácticas a seguir para que una aplicación basada en Java sea escalable?

Kshitiz Sharma
fuente
Si está exponiendo un servicio REST, usar un proxy inverso como Varnish sería de gran ayuda. ¿Qué tan frescos deben ser los datos? ¿Estás seguro de que necesitas una base de datos relacional? ¿Podría particionar los datos? Con la pila de tecnología que está describiendo, me concentraría en asegurarme de que la menor cantidad posible de solicitudes llegue a su punto final. ¿Has pensado en hacer esto en la memoria con soluciones como Hazel cast / Gigaspaces, etc.?
ebaxt
@ebaxt gracias por tus sugerencias. Gigaspaces parece ser de código abierto. Pero el elenco de Hazel parece interesante.
Kshitiz Sharma
1
@ebaxt "¿Está seguro de que necesita una base de datos relacional?" La adopción de nosql tendría cambios drásticos en la arquitectura de la aplicación. Estamos tratando de mantener la complejidad al mínimo. Sin embargo, el costo no es un factor para nosotros. Así que nos quedaremos con el enfoque relacional.
Kshitiz Sharma
1
Puedes usar Postgres, MySQL o lo que sea. ¿Qué hay de tu infraestructura? ¿Se pueden usar matrices de discos? ¿Los servidores están alojados en la misma ubicación? ¿Puedes conectar tu clúster con latidos, etc.? ¿Puedes ponerlos en la misma subred?
edze
1
Yo también soy programador. Pero si su base de datos relacional es el cuello de botella, tenderá a terminar con estas preguntas. Existen bases de datos en el mercado, algunas funcionan mejor que otras en algunas situaciones. Pero están utilizando diferentes niveles predeterminados de aislamiento de transacciones y concurrencia optimista frente a concurrencia pesimista, etc.
edze

Respuestas:

8

Me ocupé del problema en el pasado, pero aún siento que tengo mucho que aprender en el campo. Considero que este es uno de los campos más interesantes que hay en el desarrollo de software hoy en día, aquí hay algunas ideas al respecto:
MySQL es una base de datos bastante justa a menos que esté trabajando con una gran cantidad de datos, y en este caso podría considerar NoSQL base de datos, pero debe examinar cuidadosamente cuál es la mejor base de datos NoSQL para sus necesidades.

Debe implementar el almacenamiento en caché en su sistema; intente almacenar en caché la mayor cantidad de datos de solo lectura tanto como sea posible, o defina algunas estrategias de almacenamiento en caché; por ejemplo, tuvimos un escenario en el que era válido para un usuario ver "datos antiguos" como siempre y cuando la actualización reciente haya tenido lugar en la última hora.
Consideraría JBoss Cache, o tal vez Infinispan (que es más como una estructura de datos distribuidos) u otro marco de almacenamiento en caché popular para esto.
Además, como mencionó tomcat, supongo que trabaja en algún módulo de solicitud-respuesta. Intente considerar el uso de un caché que existe en el alcance de una solicitud dada, esto puede ser incluso un simple HashMap que está asociado con el almacenamiento local de subprocesos .
Mi idea aquí se parece bastante al caché de primer nivel en Hibernate .

Debe recordar que los archivos, las transacciones y otros recursos son caros en términos de mantenerlos abiertos. Asegúrese de cerrar los archivos y las transacciones lo antes posible, o terminará con errores que se reproducirán en configuraciones a gran escala

Además, debe comprender qué 2000 usuarios simultáneos, ¿significa esto que 2000 usuarios están accediendo a su servidor a la vez o están utilizando su sistema? Distinga entre los casos en que 2000 usuarios intentan abrir un socket a su servidor, y un caso en el que solo 500 están, y 1500 actualmente están buscando resultados, de completar la entrada en el lado del cliente.

Debe considerar el uso de la agrupación en clúster: tendrá que lidiar con problemas como el equilibrio de carga , la sesión fija (lo que significa que el equilibrador de carga redirigirá una solicitud al mismo servidor para la misma sesión) y más.

Si necesita tener un código de sincronización, elija cuidadosamente la estrategia de sincronización. Vi algunos sistemas en los que se usaba un bloqueo simple, pero un ReaderWriterLockpodría haber mejorado las cosas, ya que la mayoría del acceso era de solo lectura.

Considere tener el almacenamiento en caché y la validación del lado del cliente si es posible, intente guardar las llamadas al servidor y enviar solo diferencias de datos, en caso de que la mayor parte de su respuesta a una solicitud con el mismo parámetro no cambie.
Por ejemplo, en el proyecto de código abierto oVirt solicitamos obtener estadísticas de una máquina virtual determinada. algunos de los datos de la máquina virtual rara vez cambian, por lo que enviamos solo MD5, si los datos cambian, el valor de MD5 también cambia, realizamos una solicitud para obtener los datos completos, y no solo el MD5.

Mencioné hibernate antes, le recomendaría que considere usarlo cuidadosamente, si necesita realizar muchas escrituras y menos lecturas, Hibernate podría no ser ideal para usted, y debería considerar trabajar con Spring-JDBC como envoltorio JDBC

Indexe su base de datos sabiamente y use un esquema de base de datos correcto. Considere usar una capa de procedimientos almacenados ya que están precompilados y optimizados.

Me gustaría decir que en el pasado, traté con un sistema (nodo único) en mysql (principalmente acceso de solo lectura) con jboss 4.2.1 y logré llegar a 2000 concurrentes los usuarios
(no accediendo a la vez en términos de abrir 2000 sockets contra nuestro servidor), pero usando / navegando en nuestro sistema, usando JBoss Cache y precargando en la caché algunos de los datos más accedidos, o datos que nos dimos cuenta que serán "populares y populares" "pero nuestra solución fue buena para nuestra arquitectura y nuestros flujos,
por lo que , como digo en estos casos,
hay más consejos y trucos, pero realmente depende de su arquitectura y de los flujos que necesita tener en su sistema". ¡Buena suerte!


fuente
Estoy de acuerdo, excepto para los procs almacenados, no use procs almacenados. Y puede usar un hashmap concurrente y valores atómicos, para que sea seguro para hilos
NimChimpsky
3

Buena pregunta. Probablemente sea difícil decir cuál es el mejor enfoque, pero lo intentaré desde mi experiencia.

La mejor manera de escalar la aplicación web basada en Java es escribirla lo más sin estado posible (si es posible). Esto le permite escalar horizontalmente la aplicación, donde puede agregar servidores tomcat si hay más usuarios concurrentes.

Sin embargo, como notó, podría haber problemas con las conexiones de la base de datos. Pero la pregunta que tengo es, ¿cómo está obteniendo los datos? ¿Es generado por el usuario o usted obtiene los datos de un tercero? Esto es muy importante porque, si está prestando un servicio a su usuario con los datos agregados de una aplicación de terceros (por ejemplo, FB, Twitter, etc.), lo que puede seguir es escribir en la base de datos maestra y replicar los datos en bases de datos esclavas que se asignan a cada instancia de tomcat. Entonces cada servidor Tomcat puede obtener de su propia base de datos esclava.

 Are there faster alternatives to Mysql?

Puede optar por el clúster MySQL que tiene un almacén de datos en memoria. Pero tenga cuidado con el hecho de que la aplicación puede necesitar algunos cambios. No sql joinsestán bien soportados en el clúster MySQL, aunque en la última versión hay mejoras para el mismo. Si el costo no es un factor, puede probar Oracle.

La solución de almacenamiento en caché definitivamente mejorará el rendimiento. Pero entonces, todo depende de la arquitectura de toda la aplicación. Debe saber cuándo insertar datos en la memoria caché, cuándo ensuciarlos (eliminar de la memoria caché).

Con respecto a la distribución de la carga en un entorno multiservidor, le sugiero que use un equilibrador de carga que usar Apache para el equilibrio de carga.

Chandra
fuente
"Te sugiero que uses un equilibrador de carga que usar Apache para el equilibrio de carga" ¿Qué enfoque / software sugerirías si no es Apache?
Kshitiz Sharma
Básicamente estaba recomendando hardware de equilibrador de carga, que su administrador de red debería poder configurar. Esto por supuesto tiene un costo adicional para el proyecto. Este equilibrador de carga tendrá su propia IP (también llamada IP virtual) y básicamente asignará esta IP a su dominio. Cuando llega la solicitud, esto lo enrutará a todos los servidores conectados en modo round robin (también otros algoritmos disponibles). Puede usar apache para este propósito si el hardware no es una opción, pero preferiría el hardware ya que no necesita ajustar apache solo para este propósito.
Estamos usando un servidor dedicado con httpd para hacer lo mismo. El hardware no es un problema.
Kshitiz Sharma
Puede usar httpd y mod_cluster, si no recuerdo mal. Consideraría cuidadosamente antes de ir a la solución "overkill" de hardware LB, antes de verificar httpd y mod_cluster
@zaske: probablemente tenga razón en que el equilibrador de carga de hardware puede ser una exageración. Pero en caso de que necesite escalar, es fácil hacerlo agregando más servidores.
2

Actualmente estoy configurando un sistema similar (a nivel profesional) y este es el diseño que he elegido:

  • Dos balanceadores de carga Nginx (ambos activos, ambos failover para el otro, balanceados con DNS round robin)
  • Dos bases de datos MySQL en modo maestro maestro de replicación
  • Dos instancias de Tomcat como un clúster de tomcat
  • Dos instancias de Memcached para el almacenamiento en caché y el estado de sesión compartido para el clúster Tomcat

Esto logrará una solución redundante, de alta disponibilidad y escalable.

Los equilibradores de carga (en hardware decente) equilibrarán fácilmente una línea saturada de 1 gbit cada uno. Este también es un gran lugar para la descarga de SSL.

Puede guardar la información de su sesión en memcached. En caso de que una instancia de tomcat falle, otra instancia de tomcat puede recuperar información relevante de la sesión y los clientes no notarán nada. No olvides combinar esto con sesiones adhesivas también. (Para mantener el tráfico de red bajo)

El clúster de Tomcat también tiene una opción para compartir información de sesión entre el clúster en tiempo real, sin usar memcached. Aunque creo que el rendimiento es inteligente, usar Memcached será mejor.

Si necesita más potencia en cualquiera de estas aplicaciones:

  • Nginx: agregue más balanceadores de carga, aunque no creo que este sea el cuello de botella muy pronto.
  • Tomcat: puede aumentar fácilmente el tamaño del clúster Tomcat o agregar más clústeres
  • Mysql: agregue algunos esclavos de solo lectura o aumente el tamaño del clúster (dependiendo de su aplicación, pero dado que escribió una aplicación basada en REST, esto no debería ser un problema)
  • Memcached: Agregue más nodos, Memcached escala bastante bien, creo.

No sé cómo se compila su aplicación y cuáles son los grandes recursos de recursos, pero si ve una gran carga de base de datos (¡durante sus pruebas de carga!), Agregar un caché entre la aplicación y la base de datos ciertamente podría mejorar mucho el rendimiento. Pero no olvide que no todo es almacenable en caché, si sus consultas son siempre diferentes, el almacenamiento en caché no ayudará (mucho)

Mi consejo sería descargar VMware Workbench (o un software de virtualización similar) e intentar crear una configuración simple. Sin equilibrio de carga ni agrupamiento, solo lo básico y el trabajo a partir de ahí. Una por una, agregue más funciones (equilibrio, almacenamiento en caché, agrupamiento, etc.) y asegúrese de investigar un poco sobre cada tema, para que sepa que ha elegido correctamente.

Si sigue ejecutando las mismas pruebas de rendimiento durante este proceso, puede ver por sí mismo si usar X es mejor que usar Y en su configuración, o qué impacto tendrá el almacenamiento en caché, etc.

Al final, una configuración como esta realmente depende de los requisitos de su aplicación y sus clientes, todo se puede hacer de varias maneras, cada una con sus propias fortalezas y debilidades.

¿Alguna pregunta más?

¡Buena suerte!

Wesley

Wesley
fuente
avellana? hazelcast.com
NimChimpsky
¿Utiliza un marco para la capa de almacenamiento en caché, o simplemente un montón de hashes manuales en consultas SQL?
djechlin