¿Por qué es bueno Akka para la concurrencia?

9

Soy nuevo en Akka y en el marco de actores: estoy seguro de que me falta algo obvio, acepte mis disculpas de antemano.

Sigo leyendo que uno de los puntos principales para elegir Akka es la forma en que gestiona la concurrencia.

No me queda claro por qué Akka es tan especial; Entiendo que hay muchos pequeños actores que son muy ligeros y rápidos; sin embargo, ¿cómo puede ayudarme esto cuando dos usuarios guardan un formulario al mismo tiempo?

¿Todavía no necesitaría algún tipo de bloqueo de concurrencia (pesimista / optimista / etc.)?

abx78
fuente
¿Se pregunta por qué los actores son buenos para la concurrencia o específicamente Akka?
scriptin
Wouldn't I still need some sort of concurrency lock (pessimistic/optimistic/etc..)?- Sí, pero es responsabilidad del RDBMS subyacente, no del actor. Es muy probable que el actor de Akka no esté hablando directamente con su usuario. Más bien, el actor Akka y el usuario (otro actor, esencialmente) interactúan con la base de datos, directamente.
Robert Harvey

Respuestas:

7

Una de las ventajas de los modelos de procesamiento de mensajes, como actores y agentes, es que los problemas tradicionales de concurrencia (principalmente la sincronización del estado compartido) ya no son un problema. El actor puede mantener el estado privado y actualizarlo libremente sin bloqueos. El marco del actor garantiza que solo se procese un mensaje a la vez. Con el procesamiento en serie, el código se puede escribir de forma libre de bloqueo.

En su ejemplo de usuarios que guardan un formulario, suponiendo que el actor mantiene una Lista de algunos datos de cada formulario, el actor puede actualizar la lista sin bloqueos, porque el marco garantiza que solo se procesará un formulario a la vez. Tradicionalmente, tendría que bloquear los accesos a la Lista o usar una lista concurrente.

La estrategia de concurrencia es un asunto ligeramente diferente y sigue siendo su responsabilidad (sin que la estrategia sea la más común). Para cambiar un poco su ejemplo, supongamos que ambos usuarios intentan actualizar la misma instancia de formulario al mismo tiempo. Sin una estrategia de concurrencia, los cambios de uno sobrescribirán al otro (probablemente el último gane). Eso está bien, pero en el mejor de los casos esto resulta en un comportamiento inesperado para el usuario cuyos cambios se sobrescribieron. Si ven el formulario que acaban de cambiar, tendrá valores inesperados (del otro usuario). En el peor de los casos (cuando no solo estamos hablando de actualizaciones de formularios, sino también de pedidos de envío), podría ocasionar pérdidas de varios tipos (tiempo, ingresos, etc.).

El uso de una estrategia de concurrencia ayuda a identificar estos casos y a poder resolverlos según las reglas comerciales. Por ejemplo, Optimistic Concurrency hace que el usuario envíe la versión del formulario que está actualizando. Cuando el actor va a procesar el cambio, se da cuenta de que el segundo usuario piensa que está actualizando la Versión 5 cuando el formulario está realmente en la Versión 6 debido a la actualización del primer usuario. Ahora al menos podemos notificar al segundo usuario que el formulario ya ha cambiado desde que comenzaron a editarlo. O cualesquiera que sean las reglas que el negocio quiera imponer allí.

En el caso de actualizar un formulario, probablemente no le importe tanto la concurrencia (depende, supongo). Pero en otros casos, puede ser algo muy importante al menos poder verificar y manejar las violaciones. Es posible que incluso desee ignorar la infracción de concurrencia, como si los usuarios cambiaran diferentes secciones (para continuar con la analogía del formulario). O si el cambio tiene un gran impacto en el negocio (un pedido grande), desea aceptarlo y resolver conflictos menores más tarde (por ejemplo, la actualización anual de información de contacto no se ha completado).

Creo que Akka tiene otras dimensiones como la forma en que maneja fallas, supervisores, etc., que son consideraciones importantes para los desarrolladores.

Kasey Speakman
fuente
9

Voy a escribir sobre el Modelo de actor en general (no solo Akka) en comparación con otros modelos de concurrencia, como la concurrencia clásica basada en bloqueo y la memoria transaccional ordenada.

Ventajas

  1. Concepto más fácil de entender y usar

    • La concurrencia basada en bloqueo es difícil; en particular, es muy difícil hacerlo bien porque hay muchos conceptos que se deben entender y usar para ser correctos y eficientes: bloqueos, semáforos, hilos, sincronización, exclusión mutua, barrera de memoria, etc.

    • Los actores, por otro lado, son un concepto más abstracto; tienes actores que envían y reciben mensajes. No es necesario comprender y utilizar conceptos de bajo nivel como la barrera de la memoria.

  2. Hace cumplir la inmutabilidad

    • La mutabilidad es una fuente de muchos errores y errores en la programación, especialmente en aplicaciones de subprocesos múltiples. El modelo de actor resuelve este problema haciendo cumplir la inmutabilidad.
  3. Menos propenso a errores

    • Debido a las dos razones anteriores
  4. Eficiente

    • No es tan eficiente como un buen bloqueo escrito, pero en general es más eficiente que la memoria transaccional de software.
  5. Fácilmente escalable

    • Al menos en teoría, la aplicación debería escalar bastante bien agregando más actores para realizar sus trabajos. Con bloqueo basado es bastante difícil de escalar.

Desventajas

  1. No todos los idiomas imponen fácilmente la inmutabilidad;

    • Erlang, el lenguaje que primero popularizó a los actores tiene la inmutabilidad en su núcleo, pero Java y Scala (en realidad, la JVM) no imponen la inmutabilidad.
  2. Sigue siendo bastante complejo

    • Los actores se basan en un modelo de programación asincrónico que no es tan sencillo y fácil de modelar en todos los escenarios; Es particularmente difícil manejar errores y escenarios de falla.
  3. No previene el estancamiento o el hambre

    • Dos actores pueden estar en el estado que esperan el mensaje uno del otro; por lo tanto, tiene un punto muerto al igual que con las cerraduras, aunque es mucho más fácil de depurar. Sin embargo, con la memoria transaccional se le garantiza un punto muerto libre.
  4. No tan eficiente

    • Debido a la inmutabilidad forzada y a que muchos actores tienen que cambiar usando el mismo hilo, los actores no serán tan eficientes como la concurrencia basada en el bloqueo.

Conclusión

La concurrencia basada en bloqueo es la más eficiente, pero es difícil de programar y propensa a errores; La memoria transaccional de software es la más clara, fácil de programar y menos propensa a errores, pero también es la menos eficiente. Los actores se encuentran en algún punto intermedio entre estos dos modelos con todos los aspectos: fácil programación, eficiencia y propensión a errores.

m3th0dman
fuente
Para su ventaja número 2, ¿no debería ser "La mutabilidad es una fuente de muchos errores ..." y "... resuelve este problema al prohibir la mutabilidad " en lugar de la " inmutabilidad "? Además, la segunda oración tiene dos menciones redundantes para resolver el problema.
8bittree
@ 8bittree Sí, tienes razón; Escribí mal. En caso de que no lo sepa, puede editar las respuestas para corregir tales problemas.
m3th0dman
Soy consciente de eso, solo soy reacio a hacer cambios que inviertan o cambien drásticamente el significado de algo en caso de que sea un malentendido de mi parte.
8bittree
¿Puedes dar más detalles sobre el interbloqueo? Parece que tendría que construir una configuración de actor muy incómoda (comunicación bidireccional) para darse cuenta de eso. Si está utilizando un patrón de estilo de preguntar (A le pide a B una respuesta, B procesa la solicitud y responde al remitente de la solicitud, pero nunca contacta a A directamente) No veo cómo es posible un punto muerto
Daenyth el
1
@Daenyth Estoy de acuerdo en que tendrías que usar una construcción extraña para lograr un punto muerto con los actores; pero desde una perspectiva similar, tendría que usar una construcción extraña para lograr un punto muerto con concurrencia basada en bloqueo (aunque estoy de acuerdo en que es mucho más fácil crear un punto muerto con un mutex que con un actor). De todos modos, la idea es que solo la memoria transaccional garantiza que no pueda tener un punto muerto, sin importar la construcción extraña que elija implementar.
m3th0dman