Archive for April, 2009
Parallel, F# e no fim, de volta ao básico
Recentemente, reescrevemos aqui na empresa onde trabalho um serviço de instalações. O fluxo era mais ou menos o seguinte (ligeiramente simplificado):
1 – Identificar se o pedido foi aprovado
2 – Obter a lista de serviços inclusos no pedido
3 – Chamar o WS de instalação correspondente para cada serviço
4 – Logar todo o processo para gerar estatísticas e identificar erros de instalação
As instalações devem ser processadas ininterruptamente e o volume diário é imenso. Após um spike sobre o projeto, resolvemos utilizar o Parallels na camada de infra-estrutura para simplificar o processamento simultâneo por pacotes de serviços e escalar melhor aproveitando os múltiplos cores do servidor.
O desenvolvimento foi tranquilo, o projeto foi pro ar e observamos uma melhora significativa no ciclo de instalações. Agora a parte boa, cadê o problema? Monitorando o negócio depois de uns dias no ar, percebemos que as chamadas para os Web Services externos viraram o freio-de-mão puxado da aplicação.
“Mas como, isso é tão básico!” – pois é, tão sem graça que resolvemos variar e implementar em F# para fazer as chamadas assíncronas, saiu algo mais ou menos assim:
1: /// Executa um POST para a url especificada.
2: let AsyncPost (url:string, httpMethod:PostType, contentType:string, postData:string) =
3: Async.Run(AsyncHttpPost(url, httpMethod, contentType, postData))
4:
5: /// Immplementação para o método AsyncPost.
6: let private AsyncHttpPost(url:string, httpMethod:PostType, contentType:string, postData:string) =
7: async {
8: let asyncRequest = WebRequest.Create(url)
9: asyncRequest.Method <- httpMethod.ToString()
10:
11: do
12: match httpMethod with
13: |PostType.POST | PostType.PUT ->
14: asyncRequest.ContentType <- contentType
15: use writeStream = asyncRequest.GetRequestStream()
16: let encoding = new UTF8Encoding()
17: let bytes = encoding.GetBytes(postData)
18: writeStream.Write(bytes, 0, bytes.Length)
19: |_ -> null
20:
21: use! response = asyncRequest.AsyncGetResponse()
22:
23: use stream = response.GetResponseStream()
24: use reader = new StreamReader(stream)
25:
26: return! reader.AsyncReadToEnd()
27: }
28:
29: /// Verbos aceitos para atualizações HTTP
30: type PostType =
31: | POST = 1
32: | PUT = 2
33: | DELETE = 3
Teoricamente não havia motivos para as chamadas http se tornarem o gargalo do fluxo, então decidimos rever do básico como as aplicações .Net processam chamadas http.
Depois de perder um tempo no MSDN, o Google me fez parar neste e neste post. Ambos têm a resposta que eu procurava, mas no primeiro ainda têm uma mega aula de debug e o segundo dá várias outras dicas importantes. Enfim, a solução principal estava no atributo do app.config:
<add address="*" maxconnection="20" />
Onde 20 é o número máximo de conexões simultâneas permitidas por url.
A configuração padrão no Framework permite apenas duas conexões simultâneas por endereço, e aí não têm sistema mega-blaster-multi-thread que resolva – quando chegar nas chamadas http vai enfileirar tudo neste limite.
Como o autor do segundo post explica, esse limite é uma cautela para proteger endereços de ataques DoS não intencionais e para seguir a RFC 2616, que diz que “um cliente não deve manter mais de duas conexões abertas para o mesmo servidor ou proxy”.
Ok… mas precisava uma engenheira de escalabilidade com poderes de debug sobrenaturais – da própria Microsoft, e mais um monte de gente passar pela mesma saga de “caça ao gargalo” pra chegar nessa informação? O MSDN melhorou muito nos últimos anos, mas ainda está longe de ser o ideal.
No commentsI18N + datetime_select = erro “can’t convert Symbol into String”
Não acontece se especificar a ordem:
<%= f.datetime_select :date_of_birth,
rder => [:year, :month, :day] %>
Rake temperamental com Mysql no Vista
Configurar o Rails pra rodar no Windows Vista não foi de primeira. Depois de uma briga com o RubyGems porque não tinha configurado a variável de ambiente GEM_PATH, o rake que ficou temperamental e sempre dava a seguinte mensagem:
rake aborted!
Mysql::Error: Commands out of sync; you can't run this command now: SHOW TABLES
Pra corrigir: precisa adicionar ao diretório C:\ruby\bin a DLL libmysql.dll. Achei a resposta aqui, e pra minha sorte eu também tinha o Wamp instalado com uma versão da DLL.
Ctrl+C –> Ctrl+V, rake feliz.
No comments