当您设计一个将连接到许多服务器的客户端时,例如爬虫。
您将编写类似的代码:
// 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)的东西,你获得的唯一信息就是你连接的socketAdress,你可以在“public void channelConnected”中恢复它。 )“功能。
好的,但是如果我想恢复一些用户数据会怎么样,比如我在代码示例中看到的MyURL对象?
我使用脏黑客,我使用Map<“ip:port”,MyURL>所以我可以在channelConnected中检索相关数据,因为我知道ip:port我已连接。
这个hack非常脏,如果你同时连接到同一个服务器它将无法工作(或者你必须绑定到本地端口并使用像“localport:ip:remoteport”这样的键,但它是如此脏)。
所以我正在寻找将数据传递给CrawlerHander的好方法?
如果我们可以通过bootstrap的connect()方法传递这些数据,那将会很酷。我知道我可以在我的ChannelPipelineFactory.getPipeline()中传递参数,因为它是通过connect()调用的。但现在我们不能,所以这是我使用的另一个肮脏的黑客:
编辑:
// 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());
}
答案 0 :(得分:0)
您可以使用Channel未来侦听器使用ChannelLocal或ChannelHandlerContext(或最新Netty 3.x中的Channel it self)将数据传递到新连接的频道/频道处理程序。在下面的示例中,使用了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
}
}
注意:您可以使用
设置和获取数据,而不是ChannelLocalChannelHandlerContext.setAttachment()/ getAttachment()
Netty的最新3.x版本中的Channel.setAttachment()/ getAttachment()
但这两种方法都不支持类型安全。
答案 1 :(得分:0)
您可以通过Bootstrap将变量传递给Channel。
Netty.io 4.1和SO - Adding an attribute to a Channel before creation
很晚就更新到这个答案。