Serviço de referência de aplicativo móvel

Ruslan Aromatov, desenvolvedor chefe, CDI



Boa tarde, Khabrovites! Trabalho como desenvolvedor de back-end no Moscow Credit Bank e, desta vez, gostaria de falar sobre como organizamos a entrega de conteúdo em tempo de execução para nosso aplicativo móvel MKB Online. Este artigo pode ser útil para aqueles que estão envolvidos no design e desenvolvimento de servidores front-end para aplicativos móveis, nos quais é necessário fornecer constantemente uma variedade de atualizações, sejam documentos bancários, pontos de geolocalização, ícones atualizados etc. sem atualizar o aplicativo nas lojas. Aqueles que desenvolvem aplicativos móveis também não farão mal. O artigo não contém exemplos de código, apenas algumas discussões sobre o tópico.

fundo


Acho que qualquer desenvolvedor de aplicativos para dispositivos móveis encontrou o problema de atualizar parte do conteúdo de seus aplicativos. Por exemplo, altere a cláusula de contrato do usuário, o ícone ou as coordenadas da loja de um cliente que se mudou repentinamente. Parece que poderia ser mais fácil? Nós reconstruímos o aplicativo e o colocamos na loja. Os clientes são atualizados, todos estão felizes.

Mas esse esquema simples não funciona por um motivo simples - nem todos os clientes são atualizados. E, a julgar pelas estatísticas, existem muitos desses clientes.

No caso de um aplicativo bancário, a falha na entrega de informações relevantes pode custar dinheiro e a insatisfação do cliente. Por exemplo, no primeiro dia do próximo mês, as tarifas dos cartões são alteradas, novas regras do programa de bônus são incluídas ou novos tipos de destinatários de pagamento são adicionados. E se o cliente iniciar o aplicativo exatamente em 0 horas e 01 minutos, ele deverá ver o conteúdo atualizado.

"Elementar!" - você diz. - "Baixe esses dados do servidor e você será feliz."

E você estará certo. Nós fazemos isso. É isso aí, discordamos .

No entanto, nem tudo é tão simples. Temos aplicativos para iOS e Android. Cada plataforma possui várias versões diferentes que possuem funcionalidades e API diferentes.
Como resultado, pode ser que seja necessário atualizar o arquivo para um aplicativo Android com uma versão da API superior a 27, mas não toque no iOS e em versões anteriores.

Torna-se ainda mais interessante quando, por exemplo, precisamos atualizar os ícones dos destinatários de pagamento ou adicionar novos itens com novos ícones. Desenhamos cada instância do ícone em sete resoluções diferentes para cada tipo específico de tela: para Android, temos 4 delas (hdpi, xhdpi, xxhdpi, xxxhdpi) e 3 para iOS (1x, 2x, 3x). Qual deles devo enviar para um aplicativo específico?

"Bem, então envie os parâmetros de arquivo necessários para uma aplicação específica."

Corretamente! Ninguém sabe sobre qual arquivo o aplicativo precisa, exceto o aplicativo.
Entretanto, isso não é tudo. Nos aplicativos, existem muitos arquivos interconectados. Por exemplo, as listas de beneficiários (um arquivo json) são associadas aos detalhes dos beneficiários (outro arquivo json). E se recebermos o primeiro arquivo e, por algum motivo, não conseguirmos receber o segundo, os clientes não poderão pagar pelo serviço. E isso não é muito bom, francamente.

O segundo caso: atualizamos todo o conjunto de ícones de destinatários de pagamento (e existem mais de cem) ao entrar na página de pagamento. Dependendo da velocidade da Internet, pode levar de 10 segundos a vários minutos. Qual deve ser o comportamento correto da página? Por exemplo, você pode simplesmente exibir a versão anterior dos ícones, fazer o download de novas em segundo plano, armazenar em cache e mostrar apenas novas na próxima vez que o cliente visitar a página. De alguma forma, não realmente, certo?

Outra opção é substituir dinamicamente os ícones já baixados por novos. Não é muito bonito, certo? E se algum ícone não baixar? Em seguida, veremos uma bela série de novos ícones com uma peça do design antigo no meio.

Ícones de operação

"Faça o download de todo o conjunto de ícones em um arquivo na inicialização do aplicativo."

Belo pensamento. Não mesmo. Mas há uma nuance.

Muitas vezes acontece que um designer redesenha apenas algumas centenas de ícones e você só precisa substituí-los. Eles pesam 200 bytes e todo o arquivo temos 200 kilobytes. Será que o cliente terá que reabastecer o que já possui?

E ainda não calculamos o custo desse trabalho no servidor. Digamos que 10.000 clientes por hora venham até nós (esse é o valor médio, acontece mais). O início do aplicativo inicia a atualização em segundo plano dos diretórios(sim, agora você sabe como chamamos). Se um cliente precisar atualizar 1 kilobyte, em uma hora o servidor fornecerá mais de 10 megabytes. Moedas de um centavo, certo? E se o conjunto de atualizações pesar 1 megabyte? neste caso, teremos que dar 10 gigabytes. Em algum momento, chegamos à conclusão de que o tráfego deve ser considerado.

Então você precisa aprender a entender quais arquivos foram alterados e quais não foram, e faça o download apenas dos necessários.

Direita. Mas como entender quais arquivos foram alterados e quais não? Consideramos um hash para isso. Assim, um determinado cache de arquivo aparece no aplicativo, que contém um conjunto de arquivos de referência. Esses arquivos são usados ​​como recursos, conforme necessário. E no lado do servidor, finalmente nascemos ...

Serviço de Diretório


Em geral, este é um serviço da Web comum que envia arquivos via http, levando em consideração todos os requisitos do aplicativo. Ele consiste em vários contêineres de encaixe, dentro dos quais um aplicativo java trabalha com o servidor da web jetty a bordo. O back-end é o banco de dados Tarantool no mecanismo de vinil (não houve escolha dolorosa - havia apenas toda a ligação para esse banco de dados; você pode ler sobre isso no meu artigo anterior Serviço de cache inteligente baseado em ZeroMQ e Tarantool ) com replicação mestre-escravo. Para gerenciar arquivos, existe uma interface da web de serviço, também completamente auto-escrita.



Os detalhes de implementação técnica no tópico deste artigo não são particularmente significativos. Pode ser php + apache + mysql, C # + IIS + MSSQL ou qualquer outro pacote, incluindo sem um banco de dados.

O diagrama abaixo mostra como o serviço que chamamos de Woodside funciona. Os clientes móveis por meio do balanceador acessam instâncias de serviços da Web e, por sua vez, obtêm os arquivos necessários no banco de dados.

Esquema de trabalho

Mas neste artigo, falarei apenas sobre a estrutura do sistema de referência e como as usamos em aplicativos.

Arquivos necessários nas aplicações, dividimos em 3 tipos diferentes.

  1. Arquivos que devem sempre estar no aplicativo e independentes do tipo de sistema operacional. Por exemplo, este é um arquivo pdf com um contrato de serviço bancário.
  2. -, , ( ) . , .
  3. , , . - , , . , .

Programa de Afiliados

Os dois primeiros tipos de arquivos na forma de arquivos são imediatamente colocados no assembly do aplicativo - uma nova versão por padrão inclui o conjunto mais recente de diretórios. Eles se enquadram no sistema de atualização automática, que é executado em segundo plano quando o aplicativo é iniciado e funciona da seguinte maneira.

1. O serviço de diretório recebe automaticamente parte dos dados de vários locais: bancos de dados, serviços relacionados, bolas de rede - essas são algumas informações bancárias gerais importantes que são atualizadas por outros departamentos. A outra parte são diretórios criados dentro de nossa equipe por meio da interface da web e contendo arquivos destinados apenas a aplicativos móveis.

2. De acordo com a programação (ou pelo botão), o serviço percorre todos os arquivos de todos os diretórios e, com base nisso, forma um conjunto de arquivos de índice (dentro do json), tanto para arquivos do primeiro tipo (2 versões para iOS e android) quanto para arquivos de recursos do segundo tipo (7 versões para cada tipo de tela).
Parece algo como isto:

{
  "version": "43",
  "date": "04 Apr 2020 12:31:59",
  "os": "android",
  "screen": "any",
  "hashType": "md5",
  "ts": 1585992719,
  "files": [
    {
      "id": "WBRbDUlWhhhj",
      "name": "action-in-rhythm-of-life.json",
      "dir": "actions",
      "ts": 1544607853,
      "hash": "68c589c4fa8a44ded4d897c3d8b24e5c"
    },
    {
      "id": "o3K4mmPOOnxu",
      "name": "banks.json",
      "dir": "banks",
      "ts": 1583524710,
      "hash": "c136d7be420b31f65627f4200c646e0b"
    }
  ]
}

Os índices contêm informações sobre todos os arquivos de um determinado tipo, com base nos quais o mecanismo para atualizar diretórios nos aplicativos é construído.

3. Aplicativos na inicialização, a primeira coisa que eles baixam é indexar arquivos no diretório / new dentro do cache de arquivos. E no diretório / current , eles têm índices para o conjunto atual de arquivos junto com os próprios arquivos.

4. Com base nos arquivos de índice novos e antigos (com a participação de todos os arquivos atuais dos quais o hash é considerado), são criadas listas de arquivos que precisam ser atualizados ou excluídos, e a necessidade de atualização geralmente é estabelecida.

5. Depois disso, para o diretório / newos aplicativos baixam os arquivos necessários do servidor por meio de um link direto (a identificação do arquivo no índice é responsável por isso). Nesse caso, a presença e os hashes dos arquivos já existentes no diretório / new são levados em consideração , pois esse pode ser um resumo.

6. Assim que todo o conjunto de arquivos é recebido no diretório / new , eles são verificados no arquivo de índice (às vezes acontece que os arquivos não foram completamente baixados).

7. Se a verificação foi bem-sucedida, toda a árvore de arquivos é movida com a substituição no diretório / current . O novo arquivo de índice se torna atual.

8. Se a verificação não for bem-sucedida, as transferências de arquivos não ocorrerão e o aplicativo continuará usando o conjunto atual de diretórios. Na próxima vez que o aplicativo for iniciado, o mecanismo de atualização tentará corrigi-lo. Se houver uma falha global ao mover arquivos, seremos forçados a reverter para a primeira versão dos diretórios que acompanham o assembly. Até agora não houve precedentes.

Mas por que isso é tão difícil?

Na realidade, não é muito difícil. Mas o fato é que precisamos constantemente experimentar e encontrar compromissos entre o número de arquivos atualizados constantemente e os tempos de carregamento, entre economia de tráfego e velocidade. Um papel importante na escolha de um tipo de arquivo é desempenhado quando exatamente é necessário no aplicativo. Suponha que, se o ícone for exibido imediatamente na página principal após o logon, o aplicativo poderá carregar esse arquivo no tempo de execução imediatamente, e não colocá-lo em um mecanismo de atualização longo. Agora, o tamanho total do arquivo morto apenas com os arquivos principais é de 12 megabytes, sem incluir os recursos dependentes da tela. E como a atualização é essencialmente uma operação atômica, devemos esperar até que ela termine. Isso pode levar alguns minutos nos casos em que a conexão é ruim e há muitos arquivos novos.

Um ponto importante é economizar tráfego. Houve momentos em que utilizamos completamente um canal de 100 megabits após atualizações espessas. Eu tive que expandir para 300. Até agora, o suficiente. Em média, as métricas mostram que geralmente os clientes fazem o download de 25 a 50 gigabytes por hora durante o dia (isso ocorre porque temos arquivos muito grandes que são atualizados diariamente). Ainda há espaço para um maior desenvolvimento em termos de economia, mas os negócios também estão em alerta - o tempo todo estão adicionando uma variedade de novas belezas.

Concluindo, posso acrescentar que os próprios servidores frontais também usam o serviço, que na inicialização baixa os dados necessários para o processamento de solicitações de clientes.

E como você entrega atualizações de conteúdo para aplicativos?

All Articles