Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Mikhail Salosin (en diante - MS): - Ola a todos! Chámome Michael. Traballo como programador de backend en MC2 Software e falarei sobre o uso de Go no backend da aplicación móbil Look+.

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

A alguén aquí lle gusta o hóckey?

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Entón esta aplicación é para ti. É para Android e iOS e úsase para ver emisións de varios eventos deportivos en liña e gravadas. A aplicación tamén contén varias estatísticas, emisións de texto, táboas para conferencias, torneos e outra información útil para os fans.

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Tamén na aplicación hai momentos de vídeo, é dicir, podes ver os momentos máis importantes dos partidos (goles, pelexas, tiroteos, etc.). Se non queres ver a emisión completa, só podes ver as máis interesantes.

Que utilizaches no desenvolvemento?

A parte principal foi escrita en Go. A API coa que se comunicaban os clientes móbiles escribiuse en Go. Tamén se escribiu en Go un servizo para enviar notificacións push aos teléfonos móbiles. Tamén tivemos que escribir o noso propio ORM, do que poderemos falar algún día. Ben, algúns pequenos servizos foron escritos en Go: redimensionar e cargar imaxes para os editores...

Usamos PostgreSQL como base de datos. A interface do editor foi escrita en Ruby on Rails usando a xoia ActiveAdmin. A importación de estatísticas dun provedor de estatísticas tamén está escrita en Ruby.

Para as probas da API do sistema, usamos Python unittest. Memcached úsase para limitar as chamadas de pago da API, "Chef" úsase para controlar a configuración, Zabbix úsase para recoller e supervisar as estatísticas internas do sistema. Graylog2 é para recoller rexistros, Slate é documentación da API para clientes.

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Selección do protocolo

O primeiro problema que atopamos: necesitabamos escoller un protocolo de interacción entre o backend e os clientes móbiles, en función dos seguintes puntos...

  • O requisito máis importante: os datos dos clientes deben estar actualizados en tempo real. É dicir, todos os que estean vendo a emisión neste momento deberían recibir actualizacións case ao instante.
  • Para simplificar as cousas, supuxemos que os datos que están sincronizados cos clientes non se eliminan, senón que se ocultan mediante bandeiras especiais.
  • Todo tipo de solicitudes raras (como estatísticas, composicións do equipo, estatísticas do equipo) obtéñense mediante solicitudes GET ordinarias.
  • Ademais, o sistema tiña que soportar facilmente 100 mil usuarios ao mesmo tempo.

En base a isto, tiñamos dúas opcións de protocolo:

  1. Websockets. Pero non necesitabamos canles do cliente ao servidor. Só necesitabamos enviar actualizacións do servidor ao cliente, polo que un websocket é unha opción redundante.
  2. Os eventos enviados polo servidor (SSE) xurdiron ben! É bastante sinxelo e basicamente satisface todo o que necesitamos.

Eventos enviados polo servidor

Unhas palabras sobre como funciona esta cousa...

É executado sobre unha conexión http. O cliente envía unha solicitude, o servidor responde con Content-Type: text/event-stream e non pecha a conexión co cliente, pero segue escribindo datos na conexión:

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Os datos pódense enviar nun formato acordado cos clientes. No noso caso, enviámolo deste formulario: o nome da estrutura modificada (persoa, xogador) enviouse ao campo do evento e enviouse JSON con campos novos e modificados para o xogador ao campo de datos.

Agora imos falar de como funciona a interacción en si.

  • O primeiro que fai o cliente é determinar a última vez que se realizou a sincronización co servizo: mira a súa base de datos local e determina a data do último cambio rexistrado por el.
  • Envía unha solicitude con esta data.
  • Como resposta, enviámoslle todas as actualizacións que se produciron desde esa data.
  • Despois diso, fai unha conexión coa canle en directo e non se pecha ata que precisa destas actualizacións:

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Enviámoslle unha lista de cambios: se alguén marca un gol, cambiamos o marcador do partido, se se lesiona, este tamén se envía en tempo real. Así, os clientes reciben ao instante datos actualizados no feed do evento de partido. Periódicamente, para que o cliente entenda que o servidor non morreu, que non lle pasou nada, enviamos unha marca de tempo cada 15 segundos, para que saiba que todo está en orde e non hai que volver conectarse.

Como se atende a conexión en directo?

  • En primeiro lugar, creamos unha canle na que se recibirán actualizacións almacenadas no búfer.
  • Despois diso, subscribímonos a esta canle para recibir actualizacións.
  • Establecemos a cabeceira correcta para que o cliente saiba que todo está ben.
  • Envía o primeiro ping. Simplemente gravamos a marca de tempo da conexión actual.
  • Despois diso, lemos desde a canle nun bucle ata que se peche a canle de actualización. A canle recibe periodicamente a marca de tempo actual ou os cambios que xa estamos escribindo para abrir conexións.

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

O primeiro problema que atopamos foi o seguinte: por cada conexión aberta co cliente, creamos un temporizador que marcaba unha vez cada 15 segundos; resulta que se tiñamos 6 mil conexións abertas cunha máquina (cun ​​servidor API), 6 creáronse mil temporizadores. Isto fixo que a máquina non aguante a carga necesaria. O problema non era tan obvio para nós, pero conseguimos un pouco de axuda e solucionalo.

Como resultado, agora o noso ping procede da mesma canle da que procede a actualización.

En consecuencia, só hai un temporizador que marca unha vez cada 15 segundos.

Aquí hai varias funcións auxiliares: enviar a cabeceira, ping e a propia estrutura. É dicir, o nome da táboa (persoa, partido, tempada) e a información sobre esta entrada transmítense aquí:

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Mecanismo de envío de actualizacións

Agora un pouco sobre de onde veñen os cambios. Temos varias persoas, editores, que ven a emisión en tempo real. Crean todos os eventos: alguén foi expulsado, alguén resultou ferido, algún tipo de substitución...

Usando un CMS, os datos entran na base de datos. Despois diso, a base de datos notifica isto aos servidores da API mediante o mecanismo Listen/Notify. Os servidores de API xa envían esta información aos clientes. Así, esencialmente só temos uns poucos servidores conectados á base de datos e non hai ningunha carga especial na base de datos, porque o cliente non interactúa directamente coa base de datos de ningún xeito:

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

PostgreSQL: Escoitar/Notificar

O mecanismo Escoitar/Notificar en Postgres permítelle notificar aos subscritores do evento que algún evento cambiou; creouse algún rexistro na base de datos. Para iso, escribimos un simple disparador e función:

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Ao inserir ou modificar un rexistro, chamamos á función de notificación da canle data_updates, pasando alí o nome da táboa e o identificador do rexistro que foi modificado ou inserido.

Para todas as táboas que deben estar sincronizadas co cliente, definimos un disparador que, despois de modificar/actualizar un rexistro, chama á función indicada na seguinte diapositiva.
Como se subscribe a API a estes cambios?

Créase un mecanismo Fanout: envía mensaxes ao cliente. Recopila todas as canles de clientes e envía as actualizacións recibidas a través destas canles:

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Aquí a biblioteca estándar pq, que se conecta á base de datos e di que quere escoitar a canle (data_updates), comproba que a conexión está aberta e todo está ben. Estou omitindo a comprobación de erros para aforrar espazo (non comprobar é perigoso).

A continuación, configuramos Ticker de forma asíncrona, que enviará un ping cada 15 segundos e comezamos a escoitar a canle á que nos subscribimos. Se recibimos un ping, publicamos este ping. Se recibimos algún tipo de entrada, publicaremos esta entrada para todos os subscritores deste Fanout.

Como funciona Fan-out?

En ruso, isto tradúcese como "divisor". Temos un obxecto que rexistra os subscritores que queiran recibir algunhas actualizacións. E en canto chega unha actualización a este obxecto, distribúe esta actualización a todos os seus subscritores. O suficientemente sinxelo:

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Como se implementa en Go:

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Hai unha estrutura, está sincronizada mediante Mutexes. Ten un campo que garda o estado da conexión de Fanout á base de datos, é dicir, está escoitando e recibirá actualizacións, así como unha lista de todas as canles dispoñibles: mapa, cuxa clave é a canle e a estrutura en forma de valores (esencialmente non se usa de ningún xeito).

Dous métodos - Conectado e Desconectado - permítennos dicir a Fanout que temos unha conexión coa base, que apareceu e que a conexión coa base se rompeu. No segundo caso, cómpre desconectar todos os clientes e dicirlles que xa non poden escoitar nada e que se volven conectar porque se pechou a conexión con eles.

Tamén hai un método de subscrición que engade a canle aos "oíntes":

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Existe un método de cancelación de subscrición, que elimina a canle dos oíntes se o cliente se desconecta, así como un método de publicación, que permite enviar unha mensaxe a todos os subscritores.

Pregunta: – Que se transmite por esta canle?

SEÑORITA: – Transmítese o modelo que cambiou ou ping (esencialmente só un número, un número enteiro).

SEÑORITA: - Podes enviar calquera cousa, enviar calquera estrutura, publicala; só se converte en JSON e xa está.

SEÑORITA: – Recibimos unha notificación de Postgres: contén o nome e o identificador da táboa. Segundo o nome e o identificador da táboa, obtemos o rexistro que necesitamos e, a continuación, enviamos esta estrutura para a súa publicación.

A Infraestrutura

Como se ve isto dende o punto de vista das infraestruturas? Contamos con 7 servidores de hardware: un deles está completamente dedicado á base de datos, os outros seis executan máquinas virtuais. Hai 6 copias da API: cada máquina virtual coa API execútase nun servidor de hardware separado; isto é para fiabilidade.

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Temos dous frontends con Keepalived instalado para mellorar a accesibilidade, de xeito que se ocorre algo, un frontend pode substituír ao outro. Tamén: dúas copias do CMS.

Tamén hai un importador de estatísticas. Hai un esclavo de base de datos do que se fan copias de seguridade periodicamente. Hai Pigeon Pusher, unha aplicación que envía notificacións push aos clientes, así como cousas de infraestrutura: Zabbix, Graylog2 e Chef.

De feito, esta infraestrutura é redundante, porque 100 mil poden ser atendidos con menos servidores. Pero había ferro - usámolo (dixéronnos que era posible - por que non).

Pros de Go

Despois de traballar nesta aplicación, xurdiron vantaxes tan obvias de Go.

  • Genial biblioteca http. Con el podes crear moito fóra da caixa.
  • Ademais, canles que nos permitiron implementar moi facilmente un mecanismo de envío de notificacións aos clientes.
  • O marabilloso que o detector de carreiras permitiunos eliminar varios erros críticos (infraestrutura de posta en escena). Ponse en marcha todo o que funciona na posta en escena, compilado coa clave Carreira; e nós, en consecuencia, podemos mirar a infraestrutura de escenificación para ver que problemas potenciais temos.
  • Minimalismo e sinxeleza da linguaxe.

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

Buscamos desenvolvedores! Se alguén quere, por favor.

preguntas

Pregunta do público (en diante – B): – Paréceme que perdeu un punto importante sobre Fan-out. Teño razón ao entender que cando envías unha resposta a un cliente, bloqueas se o cliente non quere ler?

SEÑORITA: - Non, non estamos bloqueando. En primeiro lugar, temos todo isto detrás de nginx, é dicir, non hai problemas con clientes lentos. En segundo lugar, o cliente ten unha canle cun búfer; de feito, podemos poñer alí ata cen actualizacións... Se non podemos escribir na canle, entón elimínaa. Se vemos que a canle está bloqueada, simplemente pecharemos a canle e xa está: o cliente volverá conectarse se xurde algún problema. Polo tanto, en principio, aquí non hai ningún bloqueo.

EN: – Non podería ser posible enviar inmediatamente un rexistro a Listen/Notify, e non unha táboa de identificadores?

SEÑORITA: – Listen/Notify ten un límite de 8 mil bytes na precarga que envía. En principio, sería posible enviar se estiveramos lidando cunha pequena cantidade de datos, pero paréceme que así [a maneira de facelo] é simplemente máis fiable. As limitacións están no propio Postgres.

EN: – ¿Os clientes reciben actualizacións sobre coincidencias que non lles interesan?

SEÑORITA: - En xeral, si. Como regra xeral, hai 2-3 partidos en paralelo, e aínda así en poucas ocasións. Se un cliente está a ver algo, normalmente está a ver o partido que está a suceder. Entón, o cliente dispón dunha base de datos local na que se engaden todas estas actualizacións e, mesmo sen conexión a Internet, o cliente pode ver todas as coincidencias pasadas para as que ten actualizacións. Esencialmente, sincronizamos a nosa base de datos no servidor coa base de datos local do cliente para que poida traballar fóra de liña.

EN: – Por que fixeches o teu propio ORM?

Alexey (un dos desenvolvedores de Look+): – Daquela (era hai un ano) había menos ORM que agora, cando hai bastantes. O meu favorito da maioría dos ORM é que a maioría deles funcionan con interfaces baleiras. É dicir, os métodos destes ORM están preparados para asumir calquera cousa: unha estrutura, un punteiro de estrutura, un número, algo completamente irrelevante...

O noso ORM xera estruturas baseadas no modelo de datos. Eu mesmo. E, polo tanto, todos os métodos son concretos, non usan a reflexión, etc. Aceptan estruturas e esperan usar aquelas estruturas que veñen.

EN: - Cantas persoas participaron?

SEÑORITA: – Na fase inicial participaron dúas persoas. Comezamos nalgún lugar en xuño, e en agosto estaba lista a parte principal (a primeira versión). Houbo un lanzamento en setembro.

EN: – Onde describe SSE, non utiliza tempo de espera. Por que é iso?

SEÑORITA: – Para ser honesto, SSE segue sendo un protocolo html5: o estándar SSE está deseñado para comunicarse cos navegadores, polo que eu entendo. Ten características adicionais para que os navegadores poidan volver conectarse (e así por diante), pero non as necesitamos, porque tiñamos clientes que podían implementar calquera lóxica para conectarse e recibir información. Non fixemos SSE, senón algo semellante ao SSE. Este non é o protocolo en si.
Non había necesidade. Polo que entendo, os clientes implementaron o mecanismo de conexión case desde cero. Realmente non lles importaba.

EN: - Que utilidades adicionais utilizaches?

SEÑORITA: – Usamos máis activamente govet e golint para unificar o estilo, así como gofmt. Non se utilizou nada máis.

EN: - Que usaches para depurar?

SEÑORITA: – A depuración realizouse en gran parte mediante probas. Non usamos ningún depurador nin GOP.

EN: – Podes devolver a diapositiva onde está implementada a función Publicar? Os nomes de variables dunha soa letra confunden?

SEÑORITA: - Non. Teñen un ámbito de visibilidade bastante "estreito". Non se usan en ningún outro lugar, excepto aquí (excepto para os internos desta clase), e é moi compacto: só leva 7 liñas.

EN: - Dalgunha maneira aínda non é intuitivo...

SEÑORITA: - Non, non, este é un código real! Non se trata de estilo. É unha clase tan utilitaria e moi pequena: só hai 3 campos dentro da clase...

Mikhail Salosin. Encontro de Golang. Usando Go no backend da aplicación Look+

SEÑORITA: – En xeral, todos os datos que se sincronizan cos clientes (partidos da tempada, xogadores) non cambian. A grandes liñas, se facemos outro deporte no que necesitemos cambiar o partido, simplemente teremos todo en conta na nova versión do cliente, e as versións antigas do cliente serán prohibidas.

EN: – Hai paquetes de xestión de dependencias de terceiros?

SEÑORITA: –Utilizamos go dep.

EN: – Había algo sobre vídeo no tema da reportaxe, pero no reportaxe nada sobre vídeo.

SEÑORITA: – Non, non teño nada no tema sobre o vídeo. Chámase "Look+": ese é o nome da aplicación.

EN: – Dixeches que se transmite aos clientes?...

SEÑORITA: – Non estivemos implicados na transmisión de vídeo. Isto foi totalmente feito por Megafon. Si, non dixen que a aplicación fose MegaFon.

SEÑORITA: – Go – para enviar todos os datos – sobre o marcador, eventos de partidos, estatísticas... Go é o backend completo da aplicación. O cliente debe saber desde algún lugar que ligazón usar para o xogador para que o usuario poida ver o partido. Temos ligazóns a vídeos e emisións que se prepararon.

Algúns anuncios 🙂

Grazas por estar connosco. Gústanche os nosos artigos? Queres ver máis contido interesante? Apóyanos facendo un pedido ou recomendando a amigos, Cloud VPS para desenvolvedores desde 4.99 $, un análogo único de servidores de nivel de entrada, que inventamos nós para ti: Toda a verdade sobre VPS (KVM) E5-2697 v3 (6 núcleos) 10 GB DDR4 480 GB SSD 1 Gbps desde 19 dólares ou como compartir un servidor? (dispoñible con RAID1 e RAID10, ata 24 núcleos e ata 40 GB DDR4).

Dell R730xd 2 veces máis barato no centro de datos Equinix Tier IV en Amsterdam? Só aquí 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV desde $199 nos Países Baixos! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - desde $ 99! Ler sobre Como construír a infraestrutura corp. clase co uso de servidores Dell R730xd E5-2650 v4 por valor de 9000 euros por un centavo?

Fonte: www.habr.com

Engadir un comentario