未为netty中的每个请求创建新处理程序
我注意到没有为每个http请求创建新的处理程序实例。我有几个在实例级别定义的变量。这些值是根据请求设置的。通过仔细检查,我发现这些值不是新设置的,而是来自第一个请求的值 这是我的处理程序代码未为netty中的每个请求创建新处理程序,netty,Netty,我注意到没有为每个http请求创建新的处理程序实例。我有几个在实例级别定义的变量。这些值是根据请求设置的。通过仔细检查,我发现这些值不是新设置的,而是来自第一个请求的值 这是我的处理程序代码 @Component @ChannelHandler.Sharable public class CustomHandler extends ChannelDuplexHandler { private final StringBuilder buf = new StringBuilder(); priv
@Component
@ChannelHandler.Sharable
public class CustomHandler extends ChannelDuplexHandler {
private final StringBuilder buf = new StringBuilder();
private final String foo;
private final String val;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//parse the request and set the variables
if (foo!=null) {
foo = request.getUri()
}
if (val!=null) {
val = getQueryParamsOf("key");
}
buf.append(val);
}
}
缓冲区没有被清除。对于每个新请求,我仍然看到旧的缓冲区。
i、 e如果我提出请求/foobar?key=netty
我在第一次通话中看到buf=netty。
后续调用,buf=nettynetty和buf=nettynettynetty等等。
此外,foo
和val
变量在第一次请求后从不为空
我的理解是,因为将为每个请求创建新的处理程序。但由于我使用了@ChannelHander.Sharable
可能会重复使用相同的处理程序
所以我注释掉了@ChannelHander.Sharable
,第一个请求通过了。从下一个请求中,我得到以下错误
io.netty.channel.ChannelPipelineException: my.example.handlers.CustomHandler is not a @Sharable handler, so can't be added or removed multiple times.
at io.netty.channel.DefaultChannelPipeline.checkMultiplicity(DefaultChannelPipeline.java:625)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:208)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:409)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:396)
at my.example.CustomInitializer.initChannel(CustomInitializer.java:35)
at my.example.CustomInitializer.initChannel(CustomInitializer.java:16)
at io.netty.channel.ChannelInitializer.initChannel(ChannelInitializer.java:113)
at io.netty.channel.ChannelInitializer.handlerAdded(ChannelInitializer.java:105)
at io.netty.channel.DefaultChannelPipeline.callHandlerAdded0(DefaultChannelPipeline.java:637)
这是我的初始化代码
定制者
public class CustomIniatializer extends ChannelInitializer<SocketChannel> {
@Autowired
private ChannelDuplexHandler customHandler;
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(8*1024, true));
p.addLast(customHandler);
}
}
public类custominizatizer扩展了ChannelInitializer{
@自动连线
专用通道DuplexHandler customHandler;
@凌驾
公共频道(SocketChannel ch){
ChannelPipeline p=通道管道();
p、 addLast(新的LoggingHandler(LogLevel.INFO));
p、 addLast(新的HttpServerCodec());
p、 addLast(新的HttpObjectAggregator(8*1024,true));
p、 addLast(customHandler);
}
}
使用通道初始值设定项时,必须记住的一点是,对于每个打开的新连接,都会调用方法initChannel
任何需要唯一状态的东西都应该在这个方法中构造
查看您的代码,我发现您正在正确地创建LoggingHandler
、HttpServerCodec
和HttpObjectAggregator
的新实例,但您引用的是customHandler
类的“共享”实例
虽然只需在initChannel
方法中使用new CustomHandler()
就可以解决问题,但使用springs autowire系统实际上显示了不同的意图
我们还可以使用另外两种解决方案:
工厂模式
与直接自动关联ChannelDuplexHandler的实例不同,您需要关联一个生成此类实例的工厂:
公共接口通道DuplexHandlerFactory{
公共ChannelDuplexHandler getChannelDuplexHandler();
}
@组成部分
公共类ChannelDuplexHandlerFactory实现实现ChannelDuplexHandlerFactory{
公共ChannelDuplexHandler getChannelDuplexHandler(){
返回新的CustomHandler();
}
}
公共类custominizatizer扩展了通道初始值设定项{
@自动连线
专用通道DuplexHandler customHandler;
@凌驾
公共频道(SocketChannel ch){
ChannelPipeline p=通道管道();
p、 addLast(新的LoggingHandler(LogLevel.INFO));
p、 addLast(新的HttpServerCodec());
p、 addLast(新的HttpObjectAggregator(8*1024,true));
p、 addLast(customHandler.getChannelDuplexHandler());
}
}
使用基于通道的字段,而不是类字段
您可以使用的另一种解决方案是存储在当前通道内的变量,这是一种更先进的技术,可用于某些情况:
@组件
@ChannelHandler.可共享
公共类CustomHandler扩展了ChannelDuplexHandler{
私有静态final AttributeKey BUF_KEY=AttributeKey.newInstance(“BUF_KEY”);
私有静态final AttributeKey FOO_KEY=AttributeKey.newInstance(“FOO_KEY”);
私有静态最终AttributeKey VAL_KEY=AttributeKey.newInstance(“VAL_KEY”);
@凌驾
public void channelRead(ChannelHandlerContext ctx,Object msg){
通道ch=ctx.Channel();
最终StringBuilder buf=ch.attr(buf_键).get();
字符串foo=ch.attr(foo_KEY).get();
字符串val=ch.attr(val_KEY).get();
//解析请求并设置变量
如果(foo!=null){
foo=request.getUri()
}
如果(val!=null){
val=getQueryParamsOf(“键”);
}
buf.追加(val);
ch.attr(FOO_KEY).set(FOO);
ch.attr(VAL_KEY).set(VAL);
}
}
即使使用新的Handler()实现,我也有同样的问题
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
.....
p.addLast("httprequest", new HttpServerHandler(config));
}
}
这个uuid对于每个调用都不是唯一的。我从实例到方法级范围删除了两个变量(buf和foo)。但我无法避免val,所以我使用了基于AttributeKey字段的方法,因为在我的用例中这看起来更优雅
private String uuid;
public HttpServerHandler(final StaticConfig staticConfig) {
this.uuid = UUID.randomUUID().toString();
}