La Ingeniería detrás de Uber (PARTE I)

El principio que acompaña la ingeniería detrás de Uber se basa en algo que ya he comentado en otras oportunidades en este mismo blog: ¿Cómo escalar a partir de una arquitectura monolítica simple? ¿Cómo romper este esquema de trabajo para formar una arquitectura que soporte grandes cantidades de datos y tráfico y además nos provea de seguridad?.  La respuestas a estas preguntas las abordé en detalle en mi tesis de Magister en la cuál realicé un trabajo de investigación de las que en ese tiempo eran las principales plataformas de redes sociales: Facebook, Twitter, Instagram, Flirck. Y aunque Uber no entra directamente en la categoría de “red social”, usa los principios sociales y por supuesto los de ingeniería del software para hacer que su plataforma tecnológica vaya creciendo de la mano de su modelo de negocios.

La Ingeniería detrás de Uber (PARTE I)

La Arquitectura inicial monolítica de Uber

https://www.uber.com/en-CL/blog/service-oriented-architecture

Inicialmente Uber partió en la ciudad de San Francisco, California, teniendo todo su código base y los repositorios diseñados para trabajar en esta parte geográfica del mundo, lo cuál les permitía resolver los problemas de negocio de manera “sencilla”, lo que incluía: conectar a los pasajeros con los transportistas, el sistema de pagos y el sistema de facturación.  Para cumplir con estos requerimientos de alto nivel era razonable contar con toda esa lógica de negocios en un solo lugar, pero en la medida en que comenzaron a expandirse a otras ciudades y agregar nuevas funcionalidades, esto ya no era tan razonable.

En la medida en que los “domain models” comenzaron a crecer en conjunto con la adición de nuevas características, los componentes se comenzaron a tornar ALTAMENTE ACOPLADOS (recordemos que uno de los principios fundamentales de ingeniería de software es mantener los componentes lo menos acoplados posible), y esto logró que la encapsulación y la separación de intereses se tornara un trabajo difícil.  Por lo tanto la tarea primordial del equipo de ingeniería de Uber era encontrar una forma en que rápidamente se pudieran integrar piezas de código al modelo de negocio; y acá es donde entra la integración continúa.

¿Cómo lograr un buen escalamiento cuando tenemos diseñada nuestra plataforma para un solo repositorio?

El cambio a SOA

Uber decidió seguir el ejemplo de empresas como: Amazon, Twitter, Netflix, SoundCloud y romper el esquema monolítico base para formar una arquitectura orientada a servicios (SOA).  En concreto, SOA puede significar una variedad de cosas distintas (como término, algo que abordé en el artículo Distinguiendo entre SaaS y SOA), sin embargo Uber decidió abordar SOA a partir del uso del patrón de microservicios.  Para no inventar la rueda, voy a recurrir a la genial explicación de Martín Fowler:

Una arquitectura monolítica pone toda su funcionalidad en un único proceso, y la forma en que tiene de escalar (crecer) es agregando múltiples servidores.

Una arquitectura de microservicios pone cada funcionalidad (lo más atómica posible) en un servicio separado, y la forma que tiene de crecer es distribuyendo estos servicios a través de los servidores, replicando de acuerdo a las necesidades del negocio.

Cada servicio puede ser escrito en su propio lenguaje y puede o no tener su propia base de datos.

Esto resuelve varios problemas, pero también aparecen algunos nuevos, los cuales están clasificados en las siguientes áreas:

  1. Obviousness
  2. Safety
  3. Resilience

PD: uso los términos en inglés porque a veces es difícil encontrar una correcta traducción al texto sin que esto vaya en desmedro del contexto.

OBVIOUSNESS

Con más de 500 servicios, encontrar el servicio adecuado puede tornarse en una ardua tarea.  Toda vez identificado el servicio, la forma en que se utilizará este servicio no es tan obvia, ya que cada microservicio tiene estructurado su propio camino.  Los servicios que ofrecen REST o RPC (donde es posible acceder a su funcionalidad dentro de su propio dominio -lo que se conoce como stateless-), se caracterizan por ofrecer contratos débiles, y en el caso de Uber, los contratos varían mucho entre microservicios.  La adición de un esquema JSON a una API REST puede mejorar la seguridad y el proceso de desarrollo contra el servicio, pero no es trivial de escribir o mantener.  Por último, estas soluciones no ofrecen ninguna garantía con respecto a la tolerancia a fallos o la latencia. No hay manera estándar para manejar los tiempos de espera del lado del cliente y las interrupciones, o garantizar que una interrupción de un servicio no cause interrupciones en cascada. La capacidad de recuperación global del sistema se vería afectada negativamente por estas debilidades. Como un desarrollador escribió,  “convertimos nuestra API monolítica en una API monolítica distribuida”.

Se ha hecho evidente que necesitamos una forma estándar de comunicación que ofrezca la seguridad de tipos, la validación y la tolerancia a fallos. Otros objetivos incluyen:

  • Formas sencillas para proporcionar bibliotecas de cliente
  • Soporte “Cross Language”
  • Tiempos de espera sintonizables con políticas de reintento
  • Desarrollo y Testing Eficiente

En esta etapa de nuestro hiper-crecimiento, los ingenieros de Uber continúan evaluando tecnologías y herramientas para adaptarse a nuestras metas. Una cosa que sí sabemos es que el uso del ya existente Lenguaje de definición de interfaz (IDL) que proporciona una gran cantidad de herramientas pre-construidas desde el primer día es ideal.

Se evaluaron las herramientas existentes y encontramos que Apache Thrift (popularizada por Facebook y Twitter) cumplió con nuestras necesidades. Thrift es un conjunto de bibliotecas y herramientas para la construcción de servicios de lenguajes cruzados altamente escalables. Para lograr esto, los tipos de datos y las interfaces de servicios se definen en un archivo que es agnóstico del lenguaje de programación. A continuación, el código se genera para abstraer el transporte y la codificación de mensajes RPC entre los servicios escritos en todos los lenguajes que usamos (Python, Node, Go, etc.)

SAFETY

El argumento más convincente para Thrift es su seguridad. Thrift garantiza la seguridad de la unión (binfing) de los servicios al utilizar contratos estrictos. El contrato describe cómo interactuar con ese servicio incluyendo como llamar a los procedimientos de ese servicio, lo que proporcionan entradas y salida de lo que se espera al momento de ejecutarlo.

La adhesión a un estricto contrato significa menos tiempo que se dedica a encontrar la manera de comunicarse con un servicio y hacer frente a la serialización. Además, como microService evoluciona no tenemos que preocuparnos acerca de las interfaces que cambian repentinamente, y somos capaces de desplegar servicios de forma independiente de los consumidores. Esta es una muy buena noticia para los ingenieros de Uber. Estamos en condiciones de pasar a otros proyectos y herramientas desde Thrift resolviendo el problema de la seguridad fuera de la caja.

RESILIENCE

Por último, nos inspiramos en la tolerancia a fallos y las bibliotecas de latencia en otras empresas que se enfrentan a problemas similares, tales como Netflix que usa la biblioteca Hystrix y de Twitter la biblioteca Finagle , para abordar el problema de la resistencia. Con esas bibliotecas en mente, escribimos bibliotecas que aseguran los clientes son capaces de hacer frente a situaciones de fallo con éxito (que se discutirá con más detalle en un próximo post).

¿Hacia dónde se dirige Uber?

Claramente a mejorar la escalabilidad organizacional y proveer mayor resistencia y tolerancia a fallos usando la arquitectura de microServicios. Ellos entienden que hasta este punto la solución no es perfecta y requiere de cambios, desafortunadamente existen pocas herramientas alternativas para Python y Node que son parte del stack de desarrollo de Uber y se requiere de buena inversión de tiempo para construirlas.  Lo interesante, es ver que si es posible migrar un modelo monolítico a uno de mayor escalabilidad como SOA (implementado a través de micro servicios) considerando la evolución de nuestro modelo de negocios, ya lo ha hecho Facebook y Twitter en el pasado, y los chicos de Uber no quisieron quedar fuera.

LO QUE SE VIENE EN LA PARTE II:

Ya nos adentraremos completamente en el stack de Uber, hablaremos tanto de su backend como de casi todo su stack tecnológico pasando por la arquitectura lógica y física, plataformas, lenguajes de programación, entre otros.  Créanme que lo que estos chicos realizan a nivel de Frontend y Backend es muy interesante.

FUENTES DE INFORMACIÓN:

  1. https://eng.uber.com/soa/
  2. http://highscalability.com/
Visited 27 times, 1 visit(s) today
Please follow and like us:
La Ingeniería detrás de Uber (PARTE I)
La Ingeniería detrás de Uber (PARTE I)
La Ingeniería detrás de Uber (PARTE I)
La Ingeniería detrás de Uber (PARTE I)
La Ingeniería detrás de Uber (PARTE I)

Leave a Comment

URL has been copied successfully!
URL has been copied successfully!
RSS
Follow by Email
Facebook
X (Twitter)
Visit Us
Follow Me
Tweet
Youtube
Youtube
Whatsapp
Reddit
Copy link