1

クローラーなど、多数のサーバーに接続するクライアントを設計する場合。

あなたはそのようなものをコーディングします:

// the pipeline
public class CrawlerPipelineFactory  implements ChannelPipelineFactory {
    public ChannelPipeline getPipeline() throws Exception {
        return Channels.pipeline(new CrawlerHandler());
    }
}

// the channel handler
public class CrawlerHandler extends SimpleChannelHandler {
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        // ...
    }
}


// the main :
public static void main(){
    ChannelFactory factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool());

    ClientBootstrap scannerBootstrap = new ClientBootstrap(factory);
    scannerBootstrap.setPipelineFactory(new CrawlerPipelineFactory());

    while(true){
        MyURL url = stack.pop();
        ChannelFuture connect = scannerBootstrap.connect(url.getSocketAddress()); 
    }
}

これで、ApplicationHandlerにいるときに、SimpleChannelHandlerまたはWhatEverStreamHandler(例ではCrawlerHander)を実装するものだけが、「public voidchannelConnected()」関数で回復できる接続先のsocketAdressです。 。

わかりましたが、コード例にあるMyURLオブジェクトなど、一部のユーザーデータを復元したい場合はどうすればよいですか?

ダーティハックを使用しています。Map<"ip:port"、MyURL>を使用しているので、接続しているip:portがわかっているので、channelConnectedで関連データを取得できます。

このハックは本当に汚いです。同じサーバーに同時に接続している場合は機能しません(または、ローカルポートにバインドして、「localport:ip:remoteport」などのキーを使用する必要がありますが、とても汚いです)。

だから私はCrawlerHanderにデータを渡すための良い方法は何ですか?

ブートストラップのconnect()メソッドを介してこのデータを渡すことができれば素晴らしいでしょう。引数はconnect()を介して呼び出されるため、ChannelPipelineFactory.getPipeline()で引数を渡すことができます。しかし、今はできないので、ここに私が使用する別の汚いハックがあります:

編集:

// the main
while(!targets.isEmpty()){
    client.connect("localhost",111); // we will never connect to localhost, it's a hack
}

// the pipleline
public ChannelPipeline getPipeline() throws Exception {
    return Channels.pipeline(
        new CrawlerHandler(targets.pop()) // I specify each new host to connect here
    );
}

// in my channel handler
// Now I have the data I want in the constructor, so I m sure I get them before everything is called
public class CrawlerHandler extends SimpleChannelHandler {
    ExtraParameter target;
    public CrawlerHandler(ExtraParameter target) {
        this.target =  target;

// but, and it's the most dirty part, I have to abort the connection to localhost, and reinit a new connection to the real target
    boolean bFirstConnect=true;
    @Override
    public void connectRequested(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        if(bFirstConnect){
            bFirstConnect = false;
            ctx.getChannel().connect(target.getSocketAddr());
        }
4

2 に答える 2

0

ChannelLocal を使用するか、ChannelHandlerContext (または最新の Netty 3.x の Channel itself) で、connect future リスナーを使用して、新しく接続されたチャネル/チャネル ハンドラーにデータを渡すことができます。以下の例では、ChannelLocal が使用されています。

public class ChannelDataHolder {
  public final static ChannelLocal<String> CHANNEL_URL = new ChannelLocal<String>(true);
}

  // for each url in bootstrap

  MyURL url = ....; 
  ChannelFuture cf = scannerBootstrap.connect(url.getSocketAddress());

  final String urlString = url.getUrl();

  cf.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                ChannelDataHolder.CHANNEL_URL.set(future.getChannel(), urlString);
            }
   });



  //In the handler

 public class CrawlerHandler extends SimpleChannelHandler {
   @Override
   public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
      String urlString = ChannelDataHolder.CHANNEL_URL.get(ctx.getChannel()); 
      // ...use the data here
   }
 }

注: ChannelLocal の代わりに、次を使用してデータを設定および取得できます。

  • ChannelHandlerContext.setAttachment()/getAttachment()

  • Netty の最新 3.x バージョンの Channel.setAttachment()/getAttachment()

ただし、どちらのアプローチもタイプ セーフをサポートしていません。

于 2012-04-19T19:33:46.613 に答える