Como conectar um script a um site de terceiros

Olá Habr! Este é o primeiro post em nosso blog. Muitas pessoas nos conhecem como um chat para o site, foi com ele que começamos e agora ocupamos posições de liderança no campo dos mensageiros de negócios. Gradualmente, evoluímos para uma solução comercial abrangente que oferece muitas oportunidades para os clientes: retorno de chamada, comunicação com clientes por meio de mensagens instantâneas, redes sociais, aplicativos móveis, PBX virtual, funções de CRM e muito mais.

Por vários anos, resolvemos com sucesso muitos problemas técnicos, acumulamos muitas coisas interessantes e, em alguns lugares, uma experiência única, é claro, escrevemos nossas muletas e bicicletas. Com este post, começamos uma série de artigos nos quais compartilharemos nossa experiência no desenvolvimento, construção de processos em uma equipe totalmente remota, falar sobre nossa arquitetura, soluções técnicas que nos permitem atender efetivamente centenas de milhares de clientes em todo o mundo.

imagem

Jivosite hoje é:

  • 250 mil clientes em todo o mundo;
  • 150 milhões de impressões de widgets por dia;
  • 3,5 milhões de mensagens por dia;
  • 10 milhões de bate-papos por mês;
  • 1M conexões simultâneas;
  • Mais de 250 servidores em produção.

Como a maioria das pessoas nos conhece como bate-papo para um site, provavelmente começaremos com ele. Neste artigo, mostraremos como conectar seu código a um site de terceiros e a que você deve prestar atenção como exemplo de nossos muitos anos de experiência trabalhando com bate-papo. Este artigo será útil para aqueles que estão planejando ou já estão desenvolvendo um serviço de plug-in e simplesmente para todos os interessados ​​neste tópico.

Ponto de entrada


O teatro começa com um cabide e o serviço conectado com um código de inserção. É o ponto de entrada para qualquer serviço ou módulo no site. Como regra, ele pode ser encontrado nas instruções de instalação, após as quais é necessário adicioná-lo ao código HTML do site e, em seguida, existe a "mágica", que carrega e inicializa o script de uma certa maneira.

<script src="https://site.com/file.js"></script> 

Parece que seria mais fácil conectar o script ao site? Por padrão, você só precisa adicionar uma tag de script ao código HTML da página. Mas, de fato, esta é uma etapa importante, escondendo muitas armadilhas. Por exemplo, identificação do usuário, implementação de um canal de carregamento de script de backup, personalização de aparência ou lógica, velocidade de carregamento da página e assim por diante. Mas vamos falar sobre tudo em ordem.

Identificação


Só porque não é muito interessante alguém conectar um script, com certeza o script executa algum tipo de lógica, e essa lógica está ligada ao usuário. Por exemplo, o ID do contador, APP_ID da rede social, no nosso caso, esse é o ID do canal de comunicação criado. Ou seja, o script deve identificar o usuário em solicitações ao servidor. Para identificar o cliente através do código de inserção, existem três opções de implementação.

Opção 1

 <script async src="https://site.com/file.js?id=123"></script> 

Passe o ID diretamente no link para o arquivo e, no servidor, de alguma forma, jogue-o no script. Nesse caso, o servidor precisará gravar o ID no arquivo rapidamente ou formar uma string JS com o ID que carregará file.js. Essa lógica é semelhante à implementação de solicitações JSONP.


Durante muito tempo, trabalhamos com esse princípio, mas as desvantagens dessa abordagem são que a carga "inativa" no servidor e a necessidade de implementar o cache do servidor são adicionadas.

Opção # 2

 <script src="https://site.com/file.js" [async]></script> <script type=”text/javascript”> window.serviceNameId = “123”; // ServiceNameModule.init({id: “123”}); </script> 

Atributo assíncrono - informa ao navegador que não é necessário aguardar o carregamento do script para criar o DOM, o script deve ser executado imediatamente após o carregamento. Isso reduz o tempo de carregamento da página, mas também há um outro lado da moeda: o script pode ser executado antes que o DOM esteja pronto para funcionar.

Uma das implementações mais populares, incluindo grandes serviços, faz exatamente isso, apenas a sintaxe é diferente, mas a essência de tudo é a mesma.


Essa abordagem tem duas principais desvantagens: a primeira - o código de incorporação é complicado e a segunda - a ordem de execução desse código é muito importante, caso contrário nada funcionará. Além disso, você precisa escolher entre velocidade (assíncrona) e estabilidade (sem assíncrona); a maioria escolhe a segunda opção.

Opção nº 3

 <script async src="https://site.com/file.js?id=123"></script> 

Da mesma forma que a primeira opção, transfira o ID no link para o arquivo, mas recupere-o no navegador e não no servidor. Não é tão simples quanto parece, mas é possível. A API do navegador possui uma propriedade document.currentScript, retorna um link para um script carregado e atualmente em execução no navegador. Sabendo disso, é possível calcular o ID; para isso, é necessário obter a propriedade document.currentScript.src e extrair regularmente o ID dela.


Há uma coisa, mas: document.currentScript não é suportado por todos os navegadores. Para navegadores que não suportam essa propriedade, criamos um hack interessante. No código file.js, você pode lançar uma exceção "falsa" especial envolvida em try / catch, após o qual a URL do script no qual o erro ocorreu na pilha de erros será lançada. O URL conterá o ID obtido com a mesma regularidade.

Esse tipo de mágica é obtida, mas funciona. Não há problemas com a ordem de execução, o código de inserção parece simples e não há sobrecarga no servidor. Nos últimos dois anos, temos usado exatamente essa abordagem, embora o código de inserção em si seja diferente, mas o princípio seja o mesmo.

Configurações


Na maioria dos casos, os scripts de plug-in possuem configurações responsáveis ​​pela aparência ou lógica do trabalho. Essas configurações devem ser "lançadas" no script do plug-in, pois existem duas abordagens fundamentalmente diferentes.

Abordagem # 1

 <script async src="https://site.com/file.js"></script> <script type=”text/javascript”> window.serviceName = {color: “red”, title: “”, ...}; // ServiceNameModule.init({color: “red”, title: “”, ...}); </script> 

Essa abordagem também inclui passar as configurações nos parâmetros GET para o URL do script, semelhante à opção 1 da seção "Identificação". A abordagem é que, se o cliente deseja alterar as configurações, ele precisa editar o código de incorporação e atualizá-lo no site.


Isso é bom porque todas as configurações são armazenadas no cliente e elas não precisam ser armazenadas no servidor, desenvolvem e mantêm toda a lógica de negócios relacionada a isso. A principal desvantagem dessa abordagem é o inconveniente para o cliente, ele precisa fazer tudo manualmente e, se houver muitas configurações, o código de incorporação se tornará uma planilha difícil de manter, na qual é fácil cometer um erro. E para que as atualizações entrem em vigor, você precisa atualizar o site, esses são os gestos extras de desenvolvedores e administradores.

Abordagem # 2

 <script async src="https://site.com/file.js?id=123"></script> 

A segunda abordagem é que, se for necessário alterar as configurações, o cliente não precisa modificar o código de inserção, todas as configurações são armazenadas no servidor. Para alterar as configurações, vá ao painel gráfico, altere os parâmetros necessários e clique no botão "Salvar". Depois disso, as configurações serão aplicadas automaticamente ao site dele!


Não é necessário entender o código e fazer uma implantação para isso. Isso pode ser feito por uma pessoa que está longe do JavaScript, por exemplo, um gerente. Obviamente, essa opção é muito mais conveniente e mais simples para os usuários, e é por isso que a usamos. Mas você precisa pagar por conveniência, essa abordagem requer o desenvolvimento e o suporte da lógica no servidor e implica uma carga adicional nele. Nos artigos a seguir, mostraremos definitivamente como processamos 150 milhões dessas solicitações diariamente.

Compatibilidade com versões anteriores


É muito importante obter uma versão madura do código de incorporação o mais rápido possível. Porque a atualização dos códigos de inserção já instalados será extremamente difícil. Um exemplo de nossa prática: nas primeiras versões, usamos IDs numéricos, mas, por razões de segurança, os substituímos por alfanuméricos. Acontece que é muito difícil conseguir uma alteração no código de incorporação já instalado. Muitas pessoas nem sabem o que é HTML e como os sites são projetados. Por exemplo, um site foi criado por freelancers, um estúdio ou site foi criado por meio de um CMS / construtor, etc. Na maioria dos casos, nossos clientes trabalham apenas com o painel de configurações do widget. Desde então, ainda temos no nginx um mapa de reescrever IDs antigos para novos, com cerca de 40 mil registros.

 .... /script/widget/config/15**90 /script/widget/config/bqZB**rjW5; /script/widget/config/15**94 /script/widget/config/qtfx**xnTi; /script/widget/config/15**95 /script/widget/config/fqmpa**4YX; /script/widget/config/15**97 /script/widget/config/Vr21g**nuT; /script/widget/config/15**98 /script/widget/config/8NXL5**F8E; /script/widget/config/15**00 /script/widget/config/Th2HN**6RJ; .... 

Devido a esse recurso, somos forçados a manter a compatibilidade com versões anteriores do código de incorporação para todas as refatorações, das quais havia cerca de 5 em nossa memória.

Isolamento de código


Como o script está conectado a um site de terceiros que já possui código JavaScript e CSS para o site e outros serviços, o objetivo principal não é prejudicar o site para que nosso código não mude a lógica, muito menos quebre. Pode ser um erro de JavaScript que interrompe o fluxo de execução ou estilos que substituem os estilos de site. Mas o código do site também pode afetar o script conectado, por exemplo, é usada uma biblioteca que modifica a API do navegador, após a qual o código para de funcionar ou não funciona conforme o esperado.

 <script type="text/javascript"> //  mootools.js var JSON = new Hash({ encode: function () {}, decode: function () {} // ... }); //    JSON.parse(json); // Uncaught TypeError: JSON.parse is not a function </script> 

 <style type="text/css"> //      body * { padding: 20px; } form input { display: block; border: 2px solid red; } </style> 


Existem diferentes opções para isolar o código. Por exemplo, você pode usar prefixos em variáveis ​​JS, closures, para não entupir o contexto global, usar algo como BEM para estilos. Mas a maneira mais fácil é executar o código em um iframe, pois resolve a maioria dos problemas de isolamento, mas impõe certas restrições. Nós usamos uma versão híbrida, falaremos mais sobre o isolamento de código nos seguintes artigos.

Bloquear site de carregamento




Evento Onload - ocorre depois que a página da Web é totalmente carregada, incluindo imagens, estilos e scripts externos. Um recurso importante é que, na maioria dos sites, a lógica JS, scripts de terceiros e anúncios começam a trabalhar na ocorrência deste evento. Um ponto muito importante para todos os scripts conectados é evitar um impacto negativo nesse evento.

Isso acontece nos casos em que o servidor do qual o script é carregado responde por um longo período ou não responde: o evento onload é atrasado e o carregamento adicional da página é essencialmente bloqueado. No caso em que o servidor não estiver disponível, o evento onload ocorrerá somente após o tempo limite da solicitação, que é superior a 60 s. Portanto, problemas no servidor de upload de scripts essencialmente "interrompem" os sites, o que é inaceitável.

Experiência pessoal
No passado, eu trabalhava para uma empresa que tinha um site com 100K on-line simultâneo, namoro online. Naqueles dias, os botões "Compartilhar nas redes sociais" eram populares. Para eles aparecerem no site, era necessário conectar um script (sdk) a partir das redes sociais desejadas. Um dia, colegas correram para nós e disseram que nosso site não estava funcionando! Analisamos o monitoramento, no qual tudo estava normal, e a princípio não entendemos qual era o problema. Quando começaram a se aprofundar, perceberam que os servidores cdn do Twitter estavam caídos e o SDK não podia carregar, isso nos impediu de carregar o site por ~ 1,5 minutos. Ou seja, depois de abrir o site, um pouco de HTML foi carregado (o restante do SPA) e somente após 1,5 minutos tudo foi carregado, o tempo limite da solicitação funcionou. Precisamos organizar urgentemente um hotfix e remover o script deles do site. Após repetir essa situação, decidimos remover o bloco Compartilhar.

Nas primeiras versões do código de inserção, não levamos isso em consideração e, no caso de problemas técnicos do nosso lado, para dizer o mínimo, incomodamos nossos clientes, mas com o tempo corrigimos.

Solução

 <script type='text/javascript'> (function(){ var initCode = function () { // insert script tag }; document.readyState === 'complete' ? initCode() : w.addEventListener('load', initCode, false); })(); </script> 

A solução é simples, você precisa se inscrever no evento de uma carga completa do site e, em seguida, carregar o script. Para isso, é necessário usar o código de incorporação, e não a tag de script.

Velocidade de página do Google




Análise da versão móvel do habr.com

A maioria deles presta atenção à velocidade de carregamento do site, segundo muitos estudos, isso afeta diretamente o lucro, além de algoritmos de busca quando o ranking começou a levar em consideração o tempo de carregamento da página. Nesse sentido, os proprietários de sites geralmente usam ferramentas semelhantes para avaliar o desempenho do site. Portanto, é muito importante conectar o código de maneira ideal ao site, pois isso afeta diretamente o tempo de download.

Isso significa que você deve usar técnicas modernas para otimizar o carregamento da página. Por exemplo, use Gzip, armazene em cache arquivos e solicitações estáticas, use carregamento de script assíncrono, comprima estática com algoritmos modernos, como WebP / Brotli / etc, e use outras otimizações. Realizamos auditorias regularmente e respondemos a avisos e recomendações para atender aos requisitos atuais.

Cdn


Nas primeiras versões, baixamos estática dos servidores de aplicativos. Mas essa abordagem tem desvantagens: tráfego caro, afastamento dos visitantes do site e carga excessiva no canal do servidor. Você pode entupir facilmente o canal dos servidores de aplicativos com o efeito habr dos sites, pois o tráfego estático é muito "pesado".

Para economizar orçamento, estabilidade e reduzir a latência da rede, é ideal carregar estatísticas de servidores especialmente projetados para essa finalidade. Você pode usar provedores de CDN prontos, mas em larga escala não é barato e precisa ser limitado pelos recursos que esse ou aquele provedor oferece.



Nós o implementamos de forma simples e encomendamos servidores baratos na Rússia, Europa e América com tráfego ilimitado e um amplo canal. É barato, não nos impõe restrições, podemos personalizar tudo por nós mesmos, e a tolerância a falhas é garantida pelo mecanismo que funciona no navegador. Atualmente, 1 TB de estática é carregado diariamente de nossos servidores CDN.

Tolerância a falhas


Infelizmente, o mundo não é perfeito, incêndios ocorrem, uplinks caem, DCs ficam completamente submersos, o ILV bloqueia sub-redes e as pessoas cometem erros. No entanto, é necessário ser capaz de lidar com essas situações e continuar trabalhando.

Monitoramento
Primeiro você precisa entender que algo deu errado. Obviamente, você pode esperar até que os usuários venham reclamar, mas é melhor configurar monitoramento e alertas e, após os lançamentos, verificar se tudo está em ordem. Monitoramos muitos parâmetros diferentes, servidor e cliente, e se algo der errado, nós o vemos imediatamente. Por exemplo, o número de downloads de widgets ou um aumento anormal no tráfego nos servidores CDN diminuiu.


O número total de downloads de widgets para cada versão

Coleta de erro
O JavaScript é uma linguagem muito específica e é fácil cometer um erro. Além disso, o zoológico de navegadores na web moderna é muito grande; o que funciona no Chrome mais recente não é um fato que funcione no Safari ou Firefox. Portanto, é muito importante configurar a coleta de erros no navegador e responder a picos no tempo. Se o seu código funcionar em um iframe, você poderá fazer isso rastreando o manipulador global window.onerror e, no caso de um erro, enviar dados para o servidor. Se o código funcionar fora do iframe, será muito difícil implementar a coleta de erros.


O número total de erros de todos os sites e navegadores


Informações de erro específicas

Failover de CDN
Eu já escrevi acima que tudo tem a propriedade de cair, por isso é importante lidar com essas situações e melhor - automaticamente. Passamos por vários estágios do fallback dos servidores CDN, iniciados no manual e, finalmente, encontramos uma maneira de fazer isso de forma automática e otimizada para o navegador.

No modo manual, isso funcionou simplesmente: o SMS recebeu administradores dizendo que a CDN estava inativa, eles executaram certas manipulações, após o que o widget começou a carregar dos servidores de aplicativos. Isso pode levar de 5 minutos a 2 horas.

Para implementar o fallback automático, você precisa detectar de alguma forma que o script começou a carregar, mas isso não é tão fácil quanto parece. O navegador não fornece a capacidade de monitorar estados intermediários do carregamento da tag de script, como o evento onprogress em XMLHttpRequest, mas relata apenas o evento quando o script é carregado e executado. Também é impossível por um tempo aceitável descobrir que o servidor está indisponível no momento, o único evento onerror é acionado após o tempo limite da solicitação expirar, mais de 1 minuto. Em um minuto, o visitante já pode sair da página, mas o script não será carregado.

Tentamos opções diferentes, simples e complexas, mas, no final, criamos uma solicitação de ping para um servidor CDN. Funciona assim: primeiro fazemos ping no servidor CDN, se ele respondeu, depois carregamos o widget a partir dele. Para implementar esse esquema de maneira otimizada para o navegador e nossos servidores, usamos uma solicitação HEAD leve (sem um corpo) e, para downloads subsequentes, não o fazemos até que a versão do widget seja atualizada, porque o widget já está no cache do navegador.


Assim, recebemos uma detecção muito rápida e automática da disponibilidade do servidor estático e, no caso de uma queda, passamos para o servidor de backup quase sem demora.

Carregador


Para fazer upload do seu script para um site de terceiros, é necessário levar em consideração muitos pontos, mas é difícil implementar essa lógica no código de incorporação, pois ele simplesmente se transforma em "carne". Mas você ainda precisa fazer isso, para isso criamos um pequeno módulo que gerencia toda essa lógica "por baixo do capô" e carrega o código principal do widget. Ele carrega primeiro e implementa CDN Failover, armazenamento em cache, compatibilidade com códigos antigos incorporados, testes A / B, o layout gradual da nova versão do widget e muitas outras funções.


Assim, em etapas, chegamos a um esquema que abrange os principais casos de carregamento e inicialização do widget. Ela provou sua eficácia ao longo dos anos de uso em um grande número de sites diferentes. Ao mesmo tempo, o código de inserção permanece simples e universal, pois não há lógica nele e podemos alterá-lo a qualquer momento, sem forçar os usuários a alterar o código de inserção.

Serviços de Terceiros


E, finalmente, vale a pena mencionar serviços de terceiros que se conectam ao site ou de alguma forma interagem com sites: bots de pesquisa, análises, vários analisadores e assim por diante. Esses serviços deixam uma marca no trabalho; você também não deve esquecer isso. Vou contar alguns casos de nossa prática.

Googlebot
O aplicativo de nossa operadora possui a função "Visitantes", na qual é possível ver os visitantes que estão visualizando o site no momento, e várias informações sobre eles: horário no site, página, número de páginas visualizadas etc. Em algum momento, os clientes começaram a reclamar que estavam "pendurando" visitantes de outros sites, ou seja, no site que vende iPhones, um cliente que supostamente tinha uma página chamada "Comprar creme para o rosto". Quando eles começaram a descobrir, era o GoogleBot, que, ao mudar de site para site, armazenava em cache primeiro o LocalStorage e posteriormente transferia dados incorretos para o servidor.

A solução é simples, o servidor começou a ignorar dados do GoogleBot.

Yandex.Metrica
Há um recurso maravilhoso na métrica - um navegador da web, que permite ver o que o usuário viu e fez na forma de screencast. Para fazer isso, a métrica registra todas as ações do usuário e, após um bot de métrica especial percorrer os sites, executa as mesmas ações e registra isso. O problema era que, para simular o navegador móvel do usuário, de acordo com nossos dados, o Firefox estava ativado no modo de emulação móvel, mas o userAgent no bot era de desktop.

Isso levou ao fato de que, ao visualizar sessões de usuários móveis no navegador da web, a versão para desktop do widget foi aberta nas gravações, embora, na verdade, os usuários tenham aberto a versão móvel. Nossos clientes pensaram que sim e nos bombardearam com reclamações. , , , , .

, , , .


, . , . , , NodeJS , 270 - , .

, !

Source: https://habr.com/ru/post/pt452802/


All Articles