How can I detect network disconnection in Apache C

2019-09-12 06:18发布

问题:

I need to log when a client is disconnected from Apache Camel connection. How can I detect a netty-tcp disconnection?

    from("{{uri.client.address}}")

回答1:

Its a bit more complicated to log individual client disconnects but I will try to share what I had to do.

You need to extend SimpleChannelInboundHandler with your own class and add the methods needed. For an example class see here:

https://github.com/apache/camel/blob/master/components/camel-netty4/src/main/java/org/apache/camel/component/netty4/handlers/ServerChannelHandler.java

The main method there to look at which I have changed to include client disconnect is this one:

 @Override
  public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    //command to send when there is a disconnect from client side
    String command = "clientDisconnect";
    if (LOG.isTraceEnabled()) {
      LOG.trace("Channel closed: {}", ctx.channel());
    }
    LOG.info("Channel has disconnected: affects ", uniqueClientId);
    //create Exchange and let the consumer process it
    final Exchange exchange = consumer.getEndpoint().createExchange(ctx, command);
    //create inOnly as there is no client waiting for a response
    exchange.setPattern(ExchangePattern.InOnly);
    // set the exchange charset property for converting
    if (consumer.getConfiguration().getCharsetName() != null) {
      exchange.setProperty(Exchange.CHARSET_NAME, IOHelper.normalizeCharset(consumer.getConfiguration().getCharsetName()));
    }
    // we want to handle the UoW
    consumer.createUoW(exchange);
    //set the uniqueClientId
    beforeProcess(exchange, ctx, command);
    processAsynchronously(exchange, ctx, command);
    // to keep track of open sockets
    consumer.getNettyServerBootstrapFactory().removeChannel(ctx.channel());
    super.channelInactive(ctx);
}

Note the part where I log if the channel has connected.

You can also log the ip address of different client if you want to do different things for different clients in your main route. This should help you.

Edit: Once you have extended that class, call the extended class TCPServerHandler. Then you need to create a TCPServerHandlerFactory to refer to previous class and set your encoder/decoder. This class can look like this:

public class TCPServerInitializerFactory extends ServerInitializerFactory  {
  private NettyConsumer consumer;

  public TCPServerInitializerFactory(NettyConsumer consumer) {
    this.consumer = consumer;
  }
  @Override
  protected void initChannel(Channel channel) throws Exception {
    ChannelPipeline pipeline = channel.pipeline();
    pipeline.addLast("string-encoder", new StringEncoder(CharsetUtil.UTF_8));
    pipeline.addLast("string-decoder", new StringDecoder(CharsetUtil.UTF_8));
    **pipeline.addLast("handler", new TCPServerHandler(consumer));**
  }

  @Override
  public ServerInitializerFactory createPipelineFactory(NettyConsumer nettyConsumer) {
    return new TCPServerInitializerFactory(nettyConsumer);
  }
}

The line marked with ** is where you are creating the TCPServerHandler class.

Then back in camel in your route class you need to add the factory to your registry like this:

TCPServerInitializerFactory serverFactory = new TCPServerInitializerFactory(null);
    final CamelContext camelContext = getContext();
    final org.apache.camel.impl.SimpleRegistry registry = new org.apache.camel.impl.SimpleRegistry();
    final org.apache.camel.impl.CompositeRegistry compositeRegistry = new org.apache.camel.impl.CompositeRegistry();
    compositeRegistry.addRegistry(camelContext.getRegistry());
    compositeRegistry.addRegistry(registry);
    ((org.apache.camel.impl.DefaultCamelContext) camelContext).setRegistry(compositeRegistry);
registry.put("spf", serverFactory);

Then refer to it as such:

from("netty4:tcp://0.0.0.0:3000?serverInitializerFactory=#spf&sync=true")...

Now you may wondering, all of this to register a log for a disconnect. This is because to be able to distinguish when a client disconnects and actually get the ip of the client you need to the Netty code. You can save some value in the exchange and then later in your route do something.



回答2:

Use an ErrorHandler, e.g. the LoggingErrorHandler:

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        // use logging error handler
        errorHandler(loggingErrorHandler("com.mycompany.foo"));

        // here is our regular route
        from("{{uri.client.address}}").to("mock:out");
    }
};