Postfix: balanceando carga de e-mails de saída entre vários links.
Existem várias razões para se balancear a carga de saída de e-mails. Por exemplo, você pode ser um grande ESP (Email Service Provider) e cada Postfix seu fica atrelado à um só link e o mesmo já está “topado”. Ou você quer uma situação que garanta que as mensagens irão sair, mesmo se um dos links caírem. Com o método que irei demonstrar neste artigo, você poderá quantos links de saída quiser.
Para tal, iremos precisar do Postfix compilado com suporte a MySQL. Mas por que MySQL? Porque nele está a solução!
Como isso funciona?
O Postfix permite que você crie vários transports de saída e entrada, mas pode originalmente sair apenas por um transport padrão, e o resto terá que ser mapeado na rasão ‘dominio -> transport’ através da diretiva transport_maps. O que iremos fazer, é criar um banco de dados simples, com uma Stored Function que irá dizer ao Postfix via transport_maps por qual transport a mensagem irá sair, e tal transport estará usando um link especifico.
Coletando as informações necessárias:
Quantos links você tem, por quais você irá querer que as mensagens sejam disparadas? Para este artigo, imaginei a seguinte situação:
Link 1 – IP: 1.1.1.1
Link2 – IP: 1.1.1.2
Link3 – IP: 1.1.1.3
Mãos à obra!
Considerando que você já tem Postfix e MySQL devidamente instalados, iremos configurar o Postfix, primeiramente o arquivo máster.cf para definir os transports de saída. No caso, eu quero usar meus três links disponíveis para enviar mensagens, então, devo criar três transports independentes para tal:
L1 unix – – n – – smtp
-o smtp_bind_address=1.1.1.1
L2 unix – – n – – smtp
-o smtp_bind_address=1.1.1.2
L3 unix – – n – – smtp
-o smtp_bind_address=1.1.1.3
Pronto, criei três transports, são eles L1, L2 e L3.
Agora iremos criar um banco de dados chamado Postfix com duas tabelas: current e transports.
CREATE DATABASE `postfix`;
CREATE TABLE `current` (
`transportId` int(10) DEFAULT NULL
) ENGINE=MyISAM;
CREATE TABLE `transports` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`transport` varchar(50) NOT NULL DEFAULT ‘0’,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
Criadas as tabelas, iremos agora criar uma stored function, é ela quem irá dizer ao Postfix por onde a mensagem deve ser disparada, efetuando então, o balanceamento de carga.
CREATE FUNCTION `f_transport`() RETURNS varchar(50) CHARSET latin1
BEGIN
DECLARE current_transport INT(11);
DECLARE count_transport INT(11);
DECLARE result varchar(50);
SELECT * INTO @current_transport FROM current;
SELECT count(*) INTO @count_transport FROM transports;
IF @current_transport < @count_transport THEN
SELECT @current_transport + 1 INTO @current_transport;
ELSE
SET @current_transport = 1;
END IF;
UPDATE current SET transportId = @current_transport;
SELECT transports.transport INTO @result
FROM transports,current
WHERE transports.id=current.transportId
AND transports.id = @current_transport;
RETURN @result;
Feito isso, iremos agora cadastrar no MySQL, os transports criados la no master.cf. O nome do transport deverá ser sempre finalizado com ‘:’.
INSERT INTO transports (transport) VALUES (‘L1:’);
INSERT INTO transports (transport) VALUES (‘L2:’);
INSERT INTO transports (transport) VALUES (‘L3:’);
Uma vêz terminada a configuração do MySQL, voltamos ao Postfix, mais especificamente no arquivo main.cf, onde iremos adicionar a diretiva:
transport_maps=mysql:/etc/postfix/mysql-transports.cf
Logo após, criamos o arquivo mysql-transports.cf em /etc/postfix com o seguinte conteúdo:
hosts=ip_do_mysql
user=usuario_do_mysql
dbname=postfix
query=SELECT f_transport() as transport
Uma vez feito, reinicie o Postfix para que as alterações surtam efeito:
# postfix stop
# postfix start
Pronto, se você tiver seguido este artigo de forma criteriosa, seu Postfix já deverá estar balanceando a carga entre os links. Basta monitorar as suas interfaces de rede. Não se esqueça de conferir os logs pra ver se tudo está indo como o esperado.
Bom dia amigo, e como fica a situaçao do dns reverso, ja que a identificação sera diferente para cada link e no postfix só ha como ter um myhostname?
Obrigado.
Rafael
Bom dia. O Postfix permite que você configure variáveis myhostname diferentes por tranports usando -o myhostname=host.que.vc.quiser no master.cf.
Obrigado
Solução interessante, por isso gosto muito do postfix ele é dinâmico a ponto que você pode fazer as coisas de maneiras diferentes!
Muito interessante a solução, só não consegui visualizar, por que ela é melhor que um round-robin de link, no caso de um provedor de internet. Mas é bem interessante, ja utilizei algo do tipo em ldap, mas gerenciando o transport por conta de e-mail, direcionando o email para a instancia do postifix que casava com as diretivas da conta.
Se você leu o código fonte da Stored Function provavelmente deve ter visto que essa é uma solução em round-robin também. Round-robin de link depende de protocolos de roteamento e até talvez equipamentos mais caros como roteadores de borda (nem sempre aplicavel em 100% dos casos) além de não permitir que se personalize N fatores e parametros por transport. Neste caso, a Stored Function diz ao Postfix por qual transport a mensagem terá de sair, e esse transport pode ser personalizado de acordo com suas necessidades, é muito importante por exemplo o myhostname do transport estar alinhado com o reverso do IP dele. Quanto ao uso de LDAP, não acho que para este caso, seja aplicável, já que o mesmo é uma base estática baseada em objetos e não suporta Stored Functions que por exemplo exibem resultados dinâmicos.
Muito bom Ramon,
Tive apenas um dificuldade, enquanto estou enviando email para um provedor, o ip não é alterado, somente altera quando envio pelo menos 1 para outro dominio.
Como faço para garantir a troca de Ip por email ou alterar antes de uma quantia grande. Qual a melhor opção para o scache.
Obrigado amigo pela ajuda
Poderia colocar com fica o master.cf e o main.cf. Fiz o procedimento mas continua saindo por um link apenas. E se deixo apenas como colocou aqui no tutorial nao fica escutando a porta 25 o servidor.
Olá,
Essa SQL é compatível com qual MYSQL? estou tetando em um MYSQL 5.1.61 e sempre dá erro de sintaxe. Outra dúvida, essa seria uma boa opção para e-mail marketing? vc aconselha?
Olá Bruno,
Fiz esta stored function utilizando o MySQL 5.5. E sim, é super aconselhável para e-mail marketing.
Ramon, você faria este serviço para mim? Pode fazer um orçamento?
Opa Gadelha!! Tudo tranquilo? Pois é cara eu nao consigo nem a pau criar a segunda tabela Tranports, sempre o mesmo erro:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘’0′,
PRIMARY KEY (`id`)
) ENGINE=MyISAM’ at line 3
onde estou errado? estou fazendo direto no SSH já tentei usar o phpmyadmin… heidisql…. a primeira cria normalmente. Você não teria essa sql exportada ? Muito obrigado.
bom consegui criar as duas tabelas, no caso da segunda tive que mudar as aspas simples ’0′, para duplas…. agora novos problemas com o resto
Bruno,
Uma dica, é você modificar os delimitadores de código do MySQL temporariamente. Exemplo:
DELIMITER $$
AQUI VEM O CODIGO SQL
$$
DELIMITER ;
Tenta dessa forma 🙂
Estou tentando criar a função no Mysql 5.1.63-0+squeeze1-log – (Debian), mas está dando erro de syntaxe, será que tem como adaptar algo na função para esta versão ?
Erro
consulta SQL:
CREATE FUNCTION `f_transport` (
) RETURNS VARCHAR( 50 ) CHARSET latin1 BEGIN DECLARE current_transport INT( 11 ) ;
Mensagens do MySQL : Documentação
#1064 – You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ” at line 3
Rafael,
Isso foi devido ao wordpress, basta substituir os caracteres “`” por aspas simples do tipo “‘” que o comando será executado sem problemas. Qualquer dúvida, comenta aqui ou me manda um email. ramongadelha_AT_gmail.com