Blog
Nginx, SPDY agora é real.
Já faz um tempo, postei aqui no Blog falando sobre o suporte ao módulo SPDY que estava próximo de ser real no Nginx, venho acompanhando o assunto desde 2012, a discussão e testes no Google e no chromium.
Após ler sobre o assunto e começar a ver que também o Nginx estava iniciando testes com um módulo que daria suporte ao protocolo me entusiasmei a também inciar testes, então após algum tempo, cá estou eu para finalizar o post. De fato ainda não é um protocolo muito usado, não é um módulo que todos os SysAdmin estão avaliando e usando, creio que por que não saberem, por ainda não ser tão bem divulgado, mas acredito que de fato este se torne um padrão para acelerar conexões SSL, dado que o Google já está usando como padrão no seu buscador.
Desde a versão 1.3.15 do Nginx já temos suporte não nativo do módulo, isso significa que podemos compilar o Nginx e habilitar o suporte ao SPDY desde que tenhamos os requerimentos para isso.
Para quem deseja instalar e começar a utilizar, aqui vai a dica de como fazer, antes de mais nada gostaria de reforçar que, infelizmente tanto o IE quanto o Safari não suportam SPDY. Você poderá verificar a tabela de compatibilidade para suporte de protocolo de rede SPDY em navegadores de desktop e móveis.
Requerimentos.
- libssl 1.0.1+.
- libpcre 3, libpcre3-dev.
- Compilador C (ex. GNU C Compiler)
- Navegador com suporte ao SPDY (ex. Google Chrome)
Garantindo que o sistema atenda os requisitos podemos seguir instalando o Nginx com o suporte ao módulo, então façamos:
$ cd /tmp $ wget http://nginx.org/download/nginx-1.4.2.tar.gz $ tar -xzf nginx-1.4.2.tar.gz
$ cd nginx-1.4.2 $ ./configure --with-http_spdy_module --with-http_ssl_module $ make $ sudo make install
server { listen 127.0.0.1:443 ssl spdy; server_name mysite.com; access_log logs/ssl-access.log; error_log logs/ssl-error.log; ssl_certificate ssl/mysite.crt; ssl_certificate_key ssl/mysite.key; ...
Para testar você pode instalar no Chrome ou no Firefox uma extensão.
Discutindo a relação com Varnish 3 e Plone 4
Pois é, já é a segunda vez que dou palestra e posto aqui falando sobre o tema, dessa vez pude palestrar no Plone Symposium, o encontro da comunidade sulamericana de Plone que este ano aconteceu em Brasília, no Auditório do Interlegis.
Vejo no assunto tratado pontos positivos e negativos, infelizmente pelo que pude perceber como ponto negativo é que muitos ainda desconhecem o Varnish, e pior, desconhecem o que é um cache, muitos já ouviram falar, mas não estão tão preocupados em saber se tem um servidor de cache por trás do site.
Alo interessante é que com o assunto Diazo em questão, todo o resto do Plone parece desnecessário :) pelo menos para mim é o que ficou muito visível, e isso é bom claro, apesar da minha palestra não ter a ver com o Diazo, o Plone por padrão não vem com suporte a ESI / SSI (Edge side includes / Server side include), dai minha citação sobre o Diazo.
"Hoje temos algumas formas que adicionar cache em partes de nosso site, isso mesmo, imagine você poder colocar um cache diferente para cada parte da sua Home, 5 minutos de cache em um portlet de imagens, 1 mês de cache para o rodapé do site, banners sem cache e assim vai."
Não quero escrever aqui toda a palestra então você pode assistir a palestra ou e também visualizar os slides abaixo.Suporte SPDY no Nginx se aproxima.
Recentemente um dos desenvolvedores do Nginx Valentin Bartenev, anunciou o suporte SPDY para o servidor web Nginx. SPDY (pronunciado speedy), trata-se de um protocolo de rede experimental desenvolvido essencialmente no Google para transporte de conteúdo web e focado em fazer a Web mais rápida.
Apesar de não ser atualmente um protocolo padrão, o grupo que está desenvolvendo o SPDY afirmou publicamente que está trabalhando em direção a uma padronização, e tem implementações de referência no momento disponíveis em ambos Google Chrome e Mozilla Firefox. O protocolo é similar ao HTTP, tendo como objetivo reduzir o tempo de carga de páginas da internet. Isso é conseguido priorizando e multiplexando a transferência dos sub-recursos da página web para que somente uma conexão por cliente seja necessária.
Encriptação TLS é praticamente onipresente nas implementações do SPDY, e a transmissões são comprimidas com gzip ou DEFLATE em contraste ao HTTP, em que os cabeçalhos não são comprimidos. Além disso, o servidor indica ou até mesmo envia conteúdo ao invés de esperar requisições individuais para cada recurso de uma página. O nome não é um sigla, mas um versão reduzida da palavra "speedy" do inglês.
Este trabalho foi patrocinado pela Automattic que desde 2008 utilizam Nginx e desde então tem feito parte de quase todas as peças de sua infra.
Desde 2011 a empresa já conversava com o pessoal da Nginx para fechar um acordo, onde a Automattic iria patrocinar o desenvolvimento e a integração do SPDY no Nginx, tendo como única exigência final que que o código resultante deva ser liberado sob uma licença de código aberto para que outros possam se beneficiar de todo o trabalho.
Já existem extensões para as versões recentes dos navegadores Google Chrome e Mozilla Firefox que permitem saber se um site já implementam o SPDY, visitando o próprio Google ou o Twitter já podemos perceber que utilizam o protocolo.
Andrew Alexeev – Nginx, Inc.
E melhor que usso, percebi que apenas este site https://barry.wordpress.com implementa o SPDY no Nginx, quando acessado através de SSL e que trata-se do blog de Barry, o atual chefe de sistemas Wrangler na Automattic e responsável pela execução da infra-estrutura distribuída globalmente, no qual deu nota e alguns detalhes da implementação do SPDY no Blog.
O futudo do SPDY será fazer parte da fonte oficial do servidor web Nginx, e confesso, estou ansioso para já testar tudo :) então nos próximos dias irei postar novidades sobre os testes, é claro que para quem desejar já testar pode já pegar os patches beta em http://goo.gl/SlaiA.
Para você que deseja também testar o SPDY com o Nginx, leia o link http://nginx.org/patches/spdy/README.txt.
Desvantagens
- O conteúdo enviado mesmo que já haja cache é um desperdício de banda.
- Softwares de filtragem que dependem do HTTP não funcionarão mais.
- Muito provável que o IE vai demorar para implementar o suporte a este protocolo.
Você sabia?
- O navegador Google Chrome e o Chromium já utilizam o SPDY quando se comunicam com os serviços do Google, como o Google Search, Gmail, Chrome sync e quando exibindo anúncios do Google. O Google reconhece que o uso do SPDY é habilitado em comunicações entre o Chrome e os servidores do Google que usam SSL.
- A partir da versão 11 do Mozilla Firefox e SeaMonkey V2.8 existe o suporte a SPDY, apesar de não ser habilitado por padrão. O suporte pode ser habilitado através da preferencia network.http.spdy.enabled preference em about:config.
- No Firefox 13 o SPDY é habilitado por padrão.
- Também no Google Chrome há um parâmetro de linha de comando (--enable-websocket-over-spdy) que habilita uma implementação experimental do WebSocket sobre SPDY.
- E também podemos utilizar o Google Chrome para inspecionar as sessões do SPDY, acessando uma URL especial:
chrome://net-internals/#events&q=type:SPDY_SESSION%20is:active
JavaScript Minify com Python
Trabalhando em um projeto recentemente precisei gerar um arquivo menor do jQuery que eu havia modificado, e com isso comecei a me perguntar como o jquery.com o fazia com seus arquivos por exemplo:
- jquery-1.7.1.js 244K
- jquery-1.7.1.min.js 92K
$ mkdir cobaia $ curl -O http://code.jquery.com/jquery-1.4.4.js $ ls -hs *.js
$ easy_install jsmin
$ pip install jsmin
$ python
>>> from os import path >>> from os.path import join >>> >>> # Usei o cStringIO por ser mais rápido que o StringIO implementado em Python >>> from cStringIO import StringIO >>> >>> # As linha abaixo é o que nos garante o uso do jsmin >>> import jsmin >>> jmin = jsmin.JavascriptMinify() >>> >>> base = path.dirname(path.abspath(path.realpath(__name__))) >>> >>> # Abrimos o arquivo original apenas como leitura >>> filejs = open(join(base, 'jquery-1.4.4.js'),'r') >>> >>> # Pegamos o nome do arquivo original e concatenamos com .min.js >>> filename_out = str(str(filejs.name.split('/')[-1]).split('.js')[0] + '.min.js') >>> >>> file_out = StringIO() >>> file_out.write("") >>> >>> # Gerando um novo arquivo com modo de escrita >>> file_out = open(join(base, filename_out),'wb') >>> >>> # Por fim passamos para o minify um arquivo de entrada e o de saída >>> jmin.minify(filejs,file_out) >>> file_out.close()
$ ls -hs *.js
Outra forma de fazer.
$ gcc jsmin.c -o jsmin $ ./jsmin < jquery-1.4.4.js > jquery-1.4.4.min.js
O pulo do gato.
VMODs: O lego do Varnish 3
VMODs, ou simplesmente Varnish Modules é, segundo da documentação de modificações, um dos grandes sucessos obtidos na versão 3: "VMODs on the other hand, was an instant success, because they make it much easier for people to extend Varnish with new functionality".
E realmente, é o mesmo que tenho achado, como bem sabemos desde o Varnish 2.1 podemos usar C inline para criar e estender funcionalidades que por padrão o Varnish não trás, e com a chegada dos módulos essa brincadeira tona-se ainda melhor, por tudo que podemos fazer na VCL.
Sabermos ainda que existem coisas que não temos como fazer na VCL, por exemplo: Procurar um número de IP em um arquivo de banco de dados. Usando código C inline até temos como resolver este problema, já que o Varnish nos provê isso, e lá você pode fazer tudo, mas não é uma forma conveniente ou mesmo legível para resolver tais problemas. Eis o lugar onde VMODs entram em cena.
O que é a VMOD?
Trata-se de uma biblioteca compartilhada com algumas funções C que pode ser chamado a partir do código VCL.
A interface entre o VMOD o compilador VCL ("VCC") e o tempo de execução VCL ("VRT") é definido em um arquivo de nome vmod.vcc que um script python de nome "vmod.py" que faz todo o trabalho duro
Executando o vmod.py no arquivo vmod.vcc, será produzido dois arquivos, que são: "vcc_if.c" e "vcc_if.h", no qual devemos usar para construir o nosso arquivo de biblioteca compartilhada.
o vcc_if.c até pode ser esquecido em nosso VMOD, mas o vcc_if.h é importante, ele contém os protótipos para as funções que desejamos exportar para a VCL.
vmod_dir Default: ${VARNISH:DIR}/lib/varnish/vmods
Na prática.
- 1º Exemplo.
import example; sub vcl_deliver { # Vamos definit o resp.http.hello para "Hello, World" set resp.http.hello = example.hello("World"); }
- 2º Exemplo
Módulo: https://github.com/leed25d/geoip-vmod
Autor: David Newhall
Descrição: Provê a capacidade de retornar a localização geográfica de um endereço IP.
Nota: Após instalar o módulo (como instalar em README) basta executar o seguinte trecho de código em sua VCL.
import geoip;
sub vcl_recv {
# Definimos um cabeçalho de solicitação X-GeoIP
# para o geo do solicitante (ou desconhecido).
set req.http.X-Forwarded-For = client.ip;
set req.http.X-GeoIP = geoip.country(req.http.X-Forwarded-For)
}
- 3º Exemplo
Autor: Tollef Fog Heen
Descrião: Este vmod é um dos que mais gosto, ele provê o curls para o Varnish, então podemos usar o varnish como um cliente HTTP e buscar cabeçalhos e corpo nos backends.
Nota: Após instalar o módulo (como instalar em README) basta executar o seguinte trecho de código em sua VCL.
import curl; sub vcl_recv { curl.fetch("http://example.com/test"); if (curl.header("X-Foo") == "bar") { … } curl.free(); }
Um pouco mais antes de terminar.
- Adicionando o std.collect(), coletamos vários cabeçalhos HTTP para um único cabeçalho.
import std; sub vcl_recv { std.collect(req.http.foo); } sub vcl_fetch { std.collect(beresp.http.bar); }
- Retornando uma URL em caixa alta ou uma string.
sub vcl_deliver { set resp.http.foo = std.toupper(req.url); }
set beresp.http.x-scream = std.toupper("yes!");
- Retorna o contrário do toupper, ou seja, strings todas em caixa baixa.
set beresp.http.x-nice = std.tolower("VerY");
Ainda temos random, log, set_up_tos, syslog, fileread, duration e integer, no qual adoraria escrever sobre, mas fica a dica para quem desejar conhecer, o VMOD é realmente uma verdadeira caixa de legos, e o mais legal é que você ainda pode criar novas peças e encaixar nas já existentes. Espero que tenha sido u bom post para você entender e começar a montar a sua VMOD, e espero que post aqui sobre a experiência.
Até a próxima.
Leia também:
Migrando Varnish 2.1 para Varnish 3.0
Nas últimas semanas tenho tido a oportunidade de ficar mais próximo do Varnish, e nas últimas três semanas comecei a migrar do varnish 2.1 para Varnish 3, posso dizer desde já que teve um grande salto de melhorias e também de implementações. Mas neste post irei tratar apenas de como preparar sua configuração de vcl para não ser pego de supresa quando fazer um upgrade da versão.
O exemplo de vcl a seguir foi retirado do svn do Varnish: https://www.varnish-cache.org/trac/browser/etc/zope-plone.vcl e será com base neste vcl que iremos efetuar a migração.
Breve resumo das mudanças de 2.1.5 para 3.0.0
- Suporte ao módulo VMODs [3].
- Suporte a Compressão e descompressão, incluindo compressão de fragmentos ESI.
- Suporte a carregamento preliminar de streaming, tanto em cache ou não.
- Melhor documentação.
- Melhor valores padrão para parâmetros.
- varnishncsa agora com suporte de log com formatos personalizado.
- varnishlog, varnishncsa e varnishhist agora com suporte a filtros de registros que correspondem múltiplas expressões.
Alterações na VCL.
- log foi movido para o vmod std.[4]
log "Olá mundo";
import std; std.log "Olá mundo";
- purge agora torna-se o chamadas ban, ou como dito na VCL, são funções ban.
purge(“req.url = ” req.url);
ban(“req.url = ” + req.url);
purge continua existindo porém não leva nenhum argumento mais, e ainda pode ser usado em vcl_hit ou vcl_miss para fazer purge de itens do cache, onde você iria reduzir o ttl a 0 no Varnish 2.1.
sub vcl_hit { if (req.request == “PURGE”) { set obj.ttl = 0s; error 200 “Purged.”; } }
sub vcl_hit { if (req.request == “PURGE”) { purge; error 200 “Purged.”; } }
- beresp.cacheable está fora.
beresp.cacheable está fora do Varnish 3, mas pode ser substituído por beresp.ttl> 0s
- returns agora é feito com a função return()
pass, pipe, lookup, deliver, fetch, hash, pipe e restart não são mais palavras-chave, mas argumentos para a função return(), então:
sub vcl_pass { pass; }
torna-se
sub vcl_pass { return(pass); }
- req.hash foi substituído por hash_data()
set req.hash += req.url;
hash_data(req.url);
- esi substituído por beresp.do_esi
esi;
set beresp.do_esi = true;
pass em vcl_fetch foi renomeado para hit_for_pass
A diferença no comportamento do pass em vcl_recv e vcl_fetch confundem as pessoas, apenas para torna mais claro as coisas, eles são diferentes :) e agora devemos usar return(hit_for_pass) onde usava-se pass no vcl_fetch.
- NOTA.
O Varnish 3 também teve uma mudança de comportamento, isso significa que ele irá retornar um erro quando cabeçalhos forem muito grandes ao em vez de apenas ignorá-los. E se os limites são muito baixos, retornará HTTP 413, então deve-se alterar limits por http_req_hdr_len e http_req_size. Essas alterações devem ser feitas em tempo de execução, o que significa adicionar como parâmetro do executável varnishd, exemplo (-p http_req_hdr_len=4096 -p http_req_size=1024).
Para maiores informações sobre parâmetros em tempo de execução acesse a documentação[5].
Migrando.
Eis aqui nosso exemplo retirado do svn do Varnish, porém convertido para a versão 3.0, retirei quais quer comentários da VCL e efetuei a migração, acredito que depois da explicação dos parâmetros acima você seja capaz de migrar a sua configuração.
backend default { .host = "127.0.0.1"; .port = "9673"; } acl purge { "localhost"; "192.0.2.0"/24; } sub vcl_recv { if (req.http.host ~ "(www.)?example.com") { set req.http.host = "example.com"; set req.url = regsub(req.url, "^", "/VirtualHostBase/http/example.com:80/example.com/VirtualHostRoot"); } elsif (req.http.host ~ "(www.)?example.org") { set req.http.host = "example.org"; set req.url = regsub(req.url, "^", "/VirtualHostBase/http/example.org:80/example.org/VirtualHostRoot"); } else { error 404 "Unknown virtual host."; } if (req.request != "GET" && req.request != "HEAD") { if (req.request == "POST") { return(pass); } if (req.request == "PURGE") { if (!client.ip ~ purge) { error 405 "Not allowed."; } return(lookup); } } if (req.http.Cookie && req.http.Cookie ~ "__ac(|_(name|password|persistent))=") { if (req.url ~ "\.(js|css)") { remove req.http.cookie; return(lookup); } return(pass); } } sub vcl_hit { if (req.request == "PURGE") { purge; error 200 "Purged"; } } sub vcl_miss { if (req.request == "PURGE") { error 404 "Not in cache"; } } sub vcl_fetch { if (beresp.ttl < 3600s) { set beresp.ttl = 3600s; } }