我正在使用 Camel 2.15.3 和 camel-netty4 ,并且自从camel-netty3升级后,我在通过UDP接收完整的JSON消息时遇到问题。每个JSON消息大约是3到5千字节,但我的 MessageToMessageDecoder 实现只给出了第一个2048(即2k字节)。从测试程序中,我发送一条UDP消息,从我的 MessageToMessageDecoder 中的调试打印中,它显示只调用一次decode()方法。
我目前正在阅读 Netty In Action ,但我在我的日志文件中看到了这一点: UnpooledUnsafeDirectByteBuf(ridx:0,widx:2048,cap:2048))
我迫切需要在生产中修复此问题,并且只需要能够通过UDP接收JSON消息并通过我的Camel路由发送它们。我对使用什么是最好的框架(如果有的话)感到困惑?
使用netty3,这工作正常,我有一个 UdpPacketDecoder实现ChannelUpstreamHandler ,调用 Channels.fireMessageReceived(ctx,message,me.getRemoteAddress())来触发消息到下一个处理程序,它似乎工作正常。
我的路线如下所示。它从netty4:udp消耗并生成到SEDA队列,现在只是在测试时:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<route startupOrder="104" customId="true" id="ROUTE_ID_RAW_CQMS_EVENTS" xmlns="http://camel.apache.org/schema/spring">
<from uri="netty4:udp://devserver-09.dev.s.mission.net:11111?serverPipelineFactory=#CQMS_SERVER_PIPELINE_FACTORY_ROUTE_ID_RAW_CQMS_EVENTS&keepAlive=true&sync=false&receiveBufferSize=26214400&sendBufferSize=26214400&allowDefaultCodec=false&disconnectOnNoReply=false&receiveBufferSizePredictor=8192"/>
<setProperty propertyName="CamelCharsetName" id="setProperty1">
<expressionDefinition>iso-8859-1</expressionDefinition>
</setProperty>
<threads poolSize="7" maxPoolSize="14" threadName="threads_ROUTE_ID_RAW_CQMS_EVENTS" callerRunsWhenRejected="true" id="threads1">
<to uri="seda:SEDA_INPUT_QUEUE_102?size=200000&concurrentConsumers=10&waitForTaskToComplete=Never&failIfNoConsumers=true&timeout=10000" id="to1"/>
<setProperty propertyName="CamelCharsetName" id="setProperty2">
<expressionDefinition>iso-8859-1</expressionDefinition>
</setProperty>
</threads>
</route>
我打印出收到的DatagramPacket,显示:UnpooledUnsafeDirectByteBuf(ridx:0,widx:2048,cap:2048))
这是我的MessageToMessageDecoder实现:
package com.mission.mplr.multiprotocollistenerrouter;
import com.vonage.mplr.utils.MiscUtils;
import io.netty.channel.ChannelHandlerContext; // Represents the "binding" between a ChannelHandler and the ChannelPipeline.
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.nio.charset.Charset;
import java.util.List;
import org.slf4j.Logger; // The org.slf4j.Logger interface is the main user entry point of SLF4J API.
import org.slf4j.LoggerFactory; // Utility class producing Loggers for various logging APIs, most notably for log4j.
public class UdpDatagramDecoder extends MessageToMessageDecoder<DatagramPacket> {
private static final Logger logger = LoggerFactory.getLogger(UdpDatagramDecoder.class);
private static final Logger errorLogger = LoggerFactory.getLogger("ERROR_LOGGER");
private final String CHARSET_NAME;
UdpDatagramDecoder(String charsetName) {
this.CHARSET_NAME = charsetName;
}
@Override
public boolean acceptInboundMessage(Object msg) throws Exception {
return true;
}
@Override
protected void decode(ChannelHandlerContext chc, DatagramPacket packet, List out) throws Exception {
logger.info("decode(): ENTER");
logger.info("decode(): Received datagram = {}", packet);
String packetAsString = packet.content().toString(Charset.forName(CHARSET_NAME));
if(packetAsString == null) {
return; // Nothing to do
} else {
out.add(packetAsString);
packet.retain();
}
logger.info("decode(): bodyBytesAsString[size={}] = {}", packetAsString.length(), packetAsString);
String bodyBytesAsHex = MiscUtils.stringAsHex(packetAsString, CHARSET_NAME);
logger.info("decode(): bodyBytesAsHex[size={}] = {}", bodyBytesAsHex.length(), bodyBytesAsHex);
logger.info("decode(): EXIT");
}
}
// ------------- end --------------
我的服务器管道有initChannel()实现:
@Override
protected void initChannel(Channel ch) throws Exception {
logger.trace("initChannel(): ENTER");
ChannelPipeline channelPipeline = ch.pipeline();
serverInvoked = true;
String theSourceRouteId = consumer.getRoute().getId();
logger.debug("initChannel(): consumer = {}, theSourceRouteId = {}", consumer.toString(), theSourceRouteId);
// -------------------------------------------------------------------
// Here we add the custom UDP datagram decoder. Decoders are typically
// stateful, thus we create a new instance with every pipeline.
// -------------------------------------------------------------------
String udpPacketDecoderName = "CQMS_UDP_DATAGRAM_DECODER_" + theSourceRouteId;
logger.debug("initChannel(): Adding {}", udpPacketDecoderName);
channelPipeline.addLast(udpPacketDecoderName, new UdpDatagramDecoder(CHARSET_NAME));
// -----------------------------------------------------------------------------------------
// Default Camel ServerChannelHandler for the consumer, to allow Camel to route the message.
// -----------------------------------------------------------------------------------------
String serverChannelHandlerName = "CQMS_SERVER_CHANNEL_HANDLER_" + theSourceRouteId;
logger.debug("initChannel(): Adding {}", serverChannelHandlerName);
channelPipeline.addLast(serverChannelHandlerName, new ServerChannelHandler(consumer));
logger.trace("initChannel(): EXIT");
}
答案 0 :(得分:1)
Netty默认使用2048作为数据报包的上限。您可以通过在Bootstrap上设置自己的FixedRecvByteBufAllocator实例来更改此设置。不知道如何通过Camel完成这项工作。
答案 1 :(得分:0)
非常感谢诺曼!以下是适用于Camel 2.15.3的解决方案。 基本上,我们从应用程序的配置中读取上限,并将其设置在ServerInitializerFactory的 initChannel(Channel ch)方法中。
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline channelPipeline = ch.pipeline();
serverInvoked = true;
// -------------------------------------------------------------------
// Here we add the custom UDP datagram decoder. Decoders are typically
// stateful, thus we create a new instance with every pipeline.
// -------------------------------------------------------------------
String udpDecoderName = "UDP_DECODER_" + theSourceRouteId;
channelPipeline.addLast(udpDecoderName, new UdpPacketDecoder_ADAPTER(CHARSET_NAME));
// ---------------------------------------------------------------------
// Netty4 has default of 2048 bytes as upper limit for datagram packets.
// Here we override the default upper limit based on a config param.
// ---------------------------------------------------------------------
if(ConfigManager.getInstance().getRecvByteBufAllocator() > 0) {
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(ConfigManager.getInstance().getRecvByteBufAllocator()));
}
// -----------------------------------------------------------
// Add string encoder (downstream) / string decoder (upstream)
// -----------------------------------------------------------
// For decoding from a ChannelBuffer to a String object
String stringDecoderName = "SERVER_PIPELINE_STRING_DECODER_" + theSourceRouteId;
channelPipeline.addLast(stringDecoderName, STR_DECODER);
// For encoding from a String object into a ChannelBuffer
String stringEncoderName = "SERVER_PIPELINE_STRING_ENCODER_" + theSourceRouteId;
channelPipeline.addLast(stringEncoderName, STR_ENCODER);
// For encoding from a String object into a DatagramPacket
String datagramPacketEncoderName = "SERVER_PIPELINE_DATAGRAM_PACKET_ENCODER_" + theSourceRouteId;
channelPipeline.addLast(datagramPacketEncoderName, DATAGRAM_PACKET_ENCODER);
// -----------------------------------------------------------------------------------------
// Default Camel ServerChannelHandler for the consumer, to allow Camel to route the message.
// -----------------------------------------------------------------------------------------
String serverChannelHandlerName = "SERVER_CHANNEL_HANDLER_" + theSourceRouteId;
channelPipeline.addLast(serverChannelHandlerName, new ServerChannelHandler(consumer));
}