Inicial > Uncategorized > Postfix: balanceando carga de e-mails de saída entre vários links.

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.

Categorias:Uncategorized
  1. Rafael
    outubro 14, 2011 às 10:40 am

    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

    • outubro 14, 2011 às 12:42 pm

      Bom dia. O Postfix permite que você configure variáveis myhostname diferentes por tranports usando -o myhostname=host.que.vc.quiser no master.cf.

      • Rafael
        outubro 17, 2011 às 11:09 pm

        Obrigado

  2. outubro 14, 2011 às 11:12 am

    Solução interessante, por isso gosto muito do postfix ele é dinâmico a ponto que você pode fazer as coisas de maneiras diferentes!

  3. Anderson Araújo
    outubro 15, 2011 às 2:20 am

    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.

    • outubro 15, 2011 às 3:20 am

      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.

  4. Nilton Cesar
    novembro 2, 2011 às 3:57 pm

    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

  5. Andre
    junho 21, 2012 às 2:52 pm

    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.

  6. julho 12, 2012 às 8:56 pm

    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?

    • julho 12, 2012 às 9:01 pm

      Olá Bruno,

      Fiz esta stored function utilizando o MySQL 5.5. E sim, é super aconselhável para e-mail marketing.

  7. Sergio
    agosto 17, 2012 às 10:02 pm

    Ramon, você faria este serviço para mim? Pode fazer um orçamento?

  8. agosto 23, 2012 às 7:40 pm

    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.

  9. agosto 23, 2012 às 8:04 pm

    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

    • agosto 23, 2012 às 8:24 pm

      Bruno,

      Uma dica, é você modificar os delimitadores de código do MySQL temporariamente. Exemplo:

      DELIMITER $$

      AQUI VEM O CODIGO SQL

      $$

      DELIMITER ;

      Tenta dessa forma 🙂

  10. Rafael Caviquioli
    maio 7, 2014 às 12:50 pm

    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

    • maio 7, 2014 às 4:37 pm

      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

  1. No trackbacks yet.

Deixe um comentário