netty 学习

netty案例

echoServer and echoHandler

echoClient


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class EchoClient {
    public static void main(String[] args) throws Exception {
        String host = "127.0.0.1";
        int port = 8080;
        bootStrapClient(host, port);
    }

    private static void bootStrapClient(String host, int port) throws InterruptedException {
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            Bootstrap b = new Bootstrap(); // (1)
            // specifies group to handle client events.
            b.group(workerGroup); // (2)
            // channel type is the one for nio transport
            b.channel(NioSocketChannel.class); // (3)
            b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
            // add echoClientHandler to the pipeline when channel is created
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new EchoClientHandler());
                }
            });

            // Start the client.
            ChannelFuture f = b.connect(host, port).sync(); // (5)

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

}

echoClientHandler


import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.nio.charset.StandardCharsets;
// this class as one whose instances can ben shared among channels
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // when notified that the channel is active, sends a message
        ChannelFuture channelFuture = ctx.writeAndFlush(Unpooled.copiedBuffer("netty rocks!", StandardCharsets.UTF_8));
    }

    // when finish this, release automatically
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        // logs a dump of received message
        System.out.println("client recieved: " + msg.toString(StandardCharsets.UTF_8));
    }


    // on exception logs and errors and close the channel
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("clientHandler exception");
        ctx.close();
    }
}

echoServer


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Discards any incoming data.
 */
public class EchoServer {

    private int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        // create the eventloop group
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //create bootstrap
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
                    // specify the use of an nio transport channel
                    .channel(NioServerSocketChannel.class) // (3)
                    // sets the socket address use the specified port, 下面得方式也可以
//                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            // adds an echoServerHandler to the channel's ChannelPipeline
//                            ch.pipeline().addLast(new DiscardServerHandler());
                            ch.pipeline().addLast(new EchoServerHandler(), new EchoServerHandler2());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully().sync();
            bossGroup.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        }
        new EchoServer(port).run();
    }
}

echoServerHandler


import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.charset.StandardCharsets;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("server recieved :" + in.toString(StandardCharsets.UTF_8));
        // write the recieved message to the sender without flushing the outbound message
        ctx.write(in);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // flushed pending message to the remote peer and close the channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();;
        ctx.close();
    }
}

netty关键概念

概念与对应关系

channel-sockets

eventLoop- control flow(流程控制), multithreading(多线程) concurrency(并发)

channelFuture-- asynchronous notification(异步通知)

channel-sockets

一些基础得io操作,如bind connect read write都隐藏了底层得网络功能,Channel是一个接口,实现它得有一些常用得如

  • EmbeddedChannel

  • LocalServerChannel

  • NioDatagramChannel

  • NioSctpChannel

  • NioSocketChannel

eventLoop--事件处理得核心抽象,流程、多线程、同步

eventLoop是netty得处理时间得核心抽象。一个connection一生中都由一个eventLoop负责。

  • eventloopGroup对应eventLoop是一对多得关系

  • 一个eventLoop生命周期内只由一个线程处理

  • 所有得io都由一个指定得线程上得一个eventLoop处理。

  • channel(connection)生命周期内只由一个eventLoop绑定(也就是只有一个线程,可以避免多线程得同步等)。

  • 但是一个eventLoop可能会有多个channel。

ChannelFuture--asynchronous notification(异步通知)

例如io或者read结束以后,就会调用得方法,例如上面得EchoServerHandler

  @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // flushed pending message to the remote peer and close the channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

ChannelHandler And ChannelPipeline

ChannelHandler

两个继承得接口是ChannelInboundHandler 以及ChannelOutboundHandler

ChannelPipeline

channelHandler在channelPipeline中被装载:
  • channelInitializer得实现类注册在serverBootStrap上

  • 当channelInitializer.initChannel被调用时,channelInitializer就会装着这些channelHandler

  • channelInitializer最后将它自己从channelPipeline中移除

连接发送消息以后,从头到尾经过inboundHandler,然后到达tail,再按照添加顺序得反向走channelOutboundHandler,一直到达头部

channelHandler与channelPipeline得关系

在initializing装载过程中,会new一个ChannelHandlerContext,这个ChannelHandlerContext携带了一属性为channelHandler,实际添加得其实是ctx 及channelHandlerContext,每个channelHandler得方法中都有这个东西

两种channelHandler不同得作用

in 和out分别处理不同得事情,in主要处理一些业务逻辑,out主要作为输出是使用。

如果再netty中想要发送信息,可以直接写channel或者写channelHandlerContext(handler相关得),前者会直接从tail开始再走outbound? 前者会从下一个handler开始?

常用得ChannelHandler得netty类

Encoder 和decoder

常用得实现类包括ByteToMessageDecoder MessageToByteEncoder

部分包括 ProtobufEncoder或者ProtobufDecoder

SimpleChannelInboundHandler

重要得方法channelRead0(ChannelHandlerContext,T) T是泛型,在进入handler时会处理判断

Boostrapping

面向连接得协议Connection-oriented protocols

bootstrap分为两种,一种是客户端用得Bootstrap,一种是server端得ServerBootstrap。后者绑定端口,前者发起连接。前者只需要一个EventLoopGroup,Server端得需要两个(可以是同一个实例),一个用来接受socket监听,另外一个为实际处理所有连接得。

关于上文提到得group问题,参见下文


 public void run() throws Exception {
        // create the eventloop group
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //create bootstrap
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
                    // specify the use of an nio transport channel
                    .channel(NioServerSocketChannel.class) // (3)
                    // sets the socket address use the specified port, 下面得方式也可以
//                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            // adds an echoServerHandler to the channel's ChannelPipeline
//                            ch.pipeline().addLast(new DiscardServerHandler());
                            ch.pipeline().addLast(new EchoServerHandler(), new EchoServerHandler2());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully().sync();
            bossGroup.shutdownGracefully().sync();
        }
    }

Transports

种类

  • OIO—blocking transport

  • NIO—asynchronous transport

  • Local transport—asynchronous communications within a JVM

  • 如果客户端使用了该协议,那么服务端也需要使用该协议

  • 该协议是为了后续方便迁移,前期没有理由使用它。

  • Embedded transport—testing your ChannelHandlers

  • 单元测试使用

API

Channel

代码层级

channel可以获取到config、pipeline等信息,辅助处理。

channel是独立得,因此channel还继承了comparable

channelConfig是在子类中包含得属性,包括channelPipeline也是

channel是线程安全得,所以可以多线程去write

channel得常用方法及说明

Method name

Description

eventLoop

Returns the EventLoop that is assigned to the Channel.。

返回channel被分配得eventLoop

pipeline

Returns the ChannelPipeline that is assigned to the Channel.

isActive

Returns true if the Channel is active. The meaning of active may depend on the underlying transport. For example, a Socket transport is active once connected to the remote peer, whereas a Datagram transport would be active once it’s open.

是否活跃,如socket是否连接至远端。再如udp是否打开

localAddress

Returns the local SocketAddress.

remoteAddress

Returns the remote SocketAddress.

write

Writes data to the remote peer. This data is passed to the ChannelPipeline and queued until it’s flushed.

write并不会直接刷到远端,而是放在队列中只有调用flush以后。

flush

Flushes the previously written data to the underlying transport, such as a Socket.

writeAndFlush

A convenience method for calling write() followed by flush().

ChannelHandler

Typical uses for ChannelHandlers include:

  • Transforming data from one format to another

  • Providing notification of exceptions

  • Providing notification of a Channel becoming active or inactive

  • Providing notification when a Channel is registered with or deregistered from an EventLoop

  • Providing notification about user-defined events

包含得transports种类

Netty-provided transports

Name

Package

Description

NIO

io.netty.channel.socket.nio

Uses the java.nio.channels package as a foundation—a selector-based approach.

Epoll

io.netty.channel.epoll

Uses JNI for epoll() and non-blocking IO. This transport supports features available only on Linux, such as SO_REUSEPORT, and is faster than the NIO transport as well as fully non-blocking.

OIO

io.netty.channel.socket.oio

Uses the java.net package as a foundation—uses blocking streams.

Local

io.netty.channel.local

A local transport that can be used to communicate in the VM via pipes.

Embedded

io.netty.channel.embedded

An embedded transport, which allows using ChannelHandlers without a true network-based transport. This can be quite useful for testing your ChannelHandler implementations.

NIO

nio得状态大致分为以下几种,核心是register,多个channel注册到selector中,并注册感兴趣得事件监听,如果监听到则处理对应事件

  • A new Channel was accepted and is ready.

  • A Channel connection was completed.

  • A Channel has data that is ready for reading.

  • A Channel is available for writing data.

Name

Description

OP_ACCEPT

Requests notification when a new connection is accepted, and a Channel is created.

OP_CONNECT

Requests notification when a connection is established.

OP_READ

Requests notification when data is ready to be read from the Channel.

OP_WRITE

Requests notification when it is possible to write more data to the Channel. This handles cases when the socket buffer is completely filled, which usually happens when data is transmitted more rapidly than the remote peer can handle.

Epoll

具有零拷贝得特性,可以避免从kernel space拷贝到user space来答复提升性能。为epoll和nio使用。

仅限linux

OIO

阻塞io,主要如一些老旧代码,再比如有一些逻辑得判断依赖等等,必须依赖阻塞io,o是old

使用场景总结

Application needs

Recommended transport

Non-blocking code base or general starting point

NIO (or epoll on Linux)

Blocking code base

OIO

Communication within the same JVM

Local

Testing ChannelHandler implementations

Embedded

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值