2017-03-07 1 views
0

J'étudie un moyen de dupliquer le trafic d'un proxy netty vers deux serveurs. -à-dire au lieu de la mise en œuvre générale:Netty Proxy pour dupliquer le trafic

Serveur1 -> Proxy -> Serveur 2

Je voudrais faire ce qui suit:

Server 1 -> Proxy -> Serveur 2 et le serveur 3

Server3-> Proxy est tombé

Chaque message est envoyé au serveur 2 et au serveur 3.

Je n'ai qu'une contrainte: la communication entre le proxy et le serveur 2 ne doit pas être bloquée à cause du serveur 3 (au cas où le serveur 3 est lent, etc.) .

Je commence à partir du code ci-dessous: https://github.com/dawnbreaks/TcpProxy

Malheureusement, je ne suis pas trop familier avec Netty, mais la mise en œuvre semble très optimale pour mon but. Je voudrais comprendre:

  1. Comment créer un nouveau canal pour le serveur 3
  2. Quelle API pour passer outre pour la communication au serveur 3
  3. Comment lire et déposer les messages du serveur 3

Répondre

3

a vu votre conversation dans IRC#netty.

Un certain nombre de choses ici. Votre proxy doit avoir un côté serveur auquel le serveur 1 se connecte. Ensuite, le serveur 2 et le serveur 3 doivent soit être en dehors d'une connexion à partir du proxy, soit utiliser UDP (en fonction de) pour recevoir des données du proxy.

Netty a un exemple de serveur proxy. Cela fonctionnerait dans votre cas et c'est vraiment facile pour la troisième partie. Autrement dit, vous utiliserez l'exemple existant et ouvrirez une nouvelle connexion qui sera vers le serveur 3. Maintenant, vous pouvez prendre les deux canaux du proxy (connexions client vers les serveurs 2 et 3). Mettez-les dans un groupe de canaux et écrivez-en un temps à deux serveurs !. Mon exemple de code éditer va permettre les communications du serveur 1 vers le serveur 2 via le proxy mutuellement et permettre une conversation mutuelle tandis que le serveur 3 ne peut recevoir que des données mais si le serveur 3 répond au proxy, le proxy ne fait rien . Vous voudrez peut-être ajouter un gestionnaire pour libérer des tampons ou gérer des données écrites qui ne devraient pas provenir du serveur 3. De là, vous devriez commencer mais vérifiez les documents netty, api, exemples et ppts ils sont super utiles!

Je vais joindre un code modifié pour vous montrer et voici un lien vers les exemples.

Netty Proxy Server Examples

Donc, pour l'exemple, vous modifier le HexDumpProxyFrontendHandler.class et il suffit d'ajouter un deuxième Bootstrap pour le nouveau client pour le serveur 3.

Code actuel

41  @Override 
42  public void channelActive(ChannelHandlerContext ctx) { 
43   final Channel inboundChannel = ctx.channel(); 
44 
45   // Start the connection attempt. 
46   Bootstrap b = new Bootstrap(); 
47   b.group(inboundChannel.eventLoop()) 
48   .channel(ctx.channel().getClass()) 
49   .handler(new HexDumpProxyBackendHandler(inboundChannel)) 
50   .option(ChannelOption.AUTO_READ, false); 
51   ChannelFuture f = b.connect(remoteHost, remotePort); 
52   outboundChannel = f.channel(); 
53   f.addListener(new ChannelFutureListener() { 
54    @Override 
55    public void operationComplete(ChannelFuture future) { 
56     if (future.isSuccess()) { 
57      // connection complete start to read first data 
58      inboundChannel.read(); 
59     } else { 
60      // Close the connection if the connection attempt has failed. 
61      inboundChannel.close(); 
62     } 
63    } 
64   }); 
65  } 

Modifié code

import io.netty.bootstrap.Bootstrap; 
import io.netty.channel.Channel; 
import io.netty.channel.ChannelFuture; 
import io.netty.channel.ChannelFutureListener; 
import io.netty.channel.ChannelHandlerContext; 
import io.netty.channel.ChannelOption; 

/* 
* Copyright 2012 The Netty Project 
* 
* The Netty Project licenses this file to you under the Apache License, 
* version 2.0 (the "License"); you may not use this file except in compliance 
* with the License. You may obtain a copy of the License at: 
* 
* http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
* License for the specific language governing permissions and limitations 
* under the License. 
*/ 
package io.netty.example.proxy; 

import io.netty.buffer.Unpooled; 
import io.netty.channel.ChannelInboundHandlerAdapter; 
import io.netty.channel.group.ChannelGroup; 
import io.netty.channel.group.DefaultChannelGroup; 
import io.netty.util.concurrent.GlobalEventExecutor; 

public class HexDumpProxyFrontendHandler extends ChannelInboundHandlerAdapter { 

    private final String remoteHost; 
    private final int remotePort; 

    // As we use inboundChannel.eventLoop() when buildling the Bootstrap this does not need to be volatile as 
    // the server2OutboundChannel will use the same EventLoop (and therefore Thread) as the inboundChannel. 
    private Channel server2OutboundChannel; 
    private Channel server3OutboundChannel; 

    // TODO You should change this to your own executor 
    private ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 

    public HexDumpProxyFrontendHandler(String remoteHost, int remotePort) { 
     this.remoteHost = remoteHost; 
     this.remotePort = remotePort; 
    } 

    @Override 
    public void channelActive(ChannelHandlerContext ctx) { 
     final Channel inboundChannel = ctx.channel(); 

     // Start the connection attempt to SERVER 3 
     Bootstrap server3Bootstrap = new Bootstrap(); 
     server3Bootstrap.group(inboundChannel.eventLoop()) 
       .channel(ctx.channel().getClass()) 
       // You are only writing traffic to server 3 so you do not need to have a handler for the inbound traffic 
       .handler(new DiscardServerHandler()) // EDIT 
       .option(ChannelOption.AUTO_READ, false); 
     ChannelFuture server3Future = server3Bootstrap.connect(remoteHost, remotePort); 
     server3OutboundChannel = server3Future.channel(); 


     // Start the connection attempt to SERVER 2 
     Bootstrap server2Bootstrap = new Bootstrap(); 
     server2Bootstrap.group(inboundChannel.eventLoop()) 
       .channel(ctx.channel().getClass()) 
       .handler(new HexDumpProxyBackendHandler(inboundChannel)) 
       .option(ChannelOption.AUTO_READ, false); 
     ChannelFuture server2Future = server2Bootstrap.connect(remoteHost, remotePort); 
     server2OutboundChannel = server2Future.channel(); 
     server2Future.addListener(new ChannelFutureListener() { 
      @Override 
      public void operationComplete(ChannelFuture future) { 
       if (future.isSuccess()) { 
        // connection complete start to read first data 
        inboundChannel.read(); 
       } else { 
        // Close the connection if the connection attempt has failed. 
        inboundChannel.close(); 
       } 
      } 
     }); 

     // Here we are going to add channels to channel group to save bytebuf work 
     channels.add(server2OutboundChannel); 
     channels.add(server3OutboundChannel); 
    } 

    // You can keep this the same below or use the commented out section 
    @Override 
    public void channelRead(final ChannelHandlerContext ctx, Object msg) { 
     // You need to reference count the message +1 
     msg.retain(); 
     if (server2OutboundChannel.isActive()) { 
      server2OutboundChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() { 
       @Override 
       public void operationComplete(ChannelFuture future) { 
        if (future.isSuccess()) { 
         // was able to flush out data, start to read the next chunk 
         ctx.channel().read(); 
        } else { 
         future.channel().close(); 
        } 
       } 
      }); 
     } 
     if (server3OutboundChannel.isActive()) { 
      server3OutboundChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() { 
       @Override 
       public void operationComplete(ChannelFuture future) { 
        if (future.isSuccess()) { 
         // was able to flush out data, start to read the next chunk 
         ctx.channel().read(); 
        } else { 
         future.channel().close(); 
        } 
       } 
      }); 
     } 


     // Optional to the above code instead channel writing automatically cares for reference counting for you 
//  channels.writeAndFlush(msg).addListeners(new ChannelFutureListener() { 
// 
//   @Override 
//   public void operationComplete(ChannelFuture future) throws Exception { 
//    if (future.isSuccess()) { 
//     // was able to flush out data, start to read the next chunk 
//     ctx.channel().read(); 
//    } else { 
//     future.channel().close(); 
//    } 
//   } 
//  }); 
    } 

    @Override 
    public void channelInactive(ChannelHandlerContext ctx) { 
     if (server2OutboundChannel != null) { 
      closeOnFlush(server2OutboundChannel); 
     } 
     if (server3OutboundChannel != null) { 
      closeOnFlush(server3OutboundChannel); 
     } 


     // Optionally can do this 
//  channels.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); 
    } 

    @Override 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
     cause.printStackTrace(); 
     closeOnFlush(ctx.channel()); 
    } 

    /** 
    * Closes the specified channel after all queued write requests are flushed. 
    */ 
    static void closeOnFlush(Channel ch) { 
     if (ch.isActive()) { 
      ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); 
     } 
    } 
} 

Jeter Handler

Cela pourrait être ajouté au serveur 3 en tant que gestionnaire pour rejeter tout ce qui a été écrit dans le proxy par le serveur 3. Par défaut, SimpleInboundHandlers supprimera les messages après leur traitement en décrémentant le nombre de références.

Discard Handler Code

+0

merci beaucoup pour votre réponse, je vais aller sur tout cela et de revenir à vous ... – tsar2512

+0

fait cela automatiquement que si assurer le serveur 3 est bloqué, ou a un ralentissement, il ne sera pas l'impact ou ralentir la communication entre le serveur 1 -> le serveur 2? – tsar2512

+0

La seule charge supplémentaire est que les données quittent votre socket vers le serveur 3 en plus du serveur 2. Le traitement interne utilisant le groupe de canaux ne devrait pas beaucoup affecter les performances. Le serveur 3 peut encore écrire techniquement sur le proxy, mais les données ne seront pas traitées. Vous pouvez insérer un gestionnaire de rejet. Modifié la réponse pour inclure un gestionnaire de suppression pour la connexion sur le proxy au serveur 3, toutes les données du serveur 3 sont maintenant supprimées si elles sont écrites sur le proxy. – Underbalanced