Netty设计模式

1 Reactor模式

  1.1 概述

•  单线程模型
           所有I/O操作都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor线程上完成的
适合于小容量的场景,高并发的应用场景无法承载压力,单线程无法同时处理高并发时的多条链路,包括编解码,接收响应,性能无法支撑,吞吐量小,而且客户端积压超时率一并提升,增加服务端的负载
        •  多线程模型
            一个Acceptor用于监听端口,负责接收客户端的TCP连接请求,一个Reactor Thread Pool负责处理I/O操作,包括IO读写及编解码和响应发送
        •  主从多线程模型
            一个Acceptor负责接收请求,一个Main Reactor Thread Pool负责连接,一个Sub Reactor Thread Pool负责处理I/O操作。Acceptor负责客户端的登陆握手认证,认证成功建立链路,后续由Sub Reactor负责

    1.2 Netty之Reactor线程模型实现

Netty工作流程:
        netty服务端启动时,通常会有两个NioEventLoopGroup:一个boss,一个worker。第一个NioEventLoopGroup正常只需要一个EventLoop,主要负责客户端的连接请求,然后打开一个Channel,交给第二个NioEventLoopGroup中的一个EventLoop来处理这个Channel上的所有读写事件。一个Channel只会被一个EventLoop处理,I/O操作、编解码都在同一个EventLoop中,确保这些操作线程安全,而一个EventLoop可能会被分配给多个Channel。netty客户端启动后与服务端建立连接,获得Channel后发送相应的请求数据。netty服务端和客户端启动代码如下

package netty;

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

import java.net.InetSocketAddress;

/**
 * @Auther: ANYO
 * @Date: 2019/3/12 23:01
 * @Description:
 */
public class EchoServer {

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }
    public static void main(String[] args) throws Exception {
        /*if (args.length != 1) {
            System.err.println(
                    "Usage: " + EchoServer.class.getSimpleName() +
                            " <port>");
            return;
        }*/
//        int port = Integer.parseInt(args[0]);
        new EchoServer(8082).start();
    }

    /**
     * Netty 创建服务端的流程
     * 1.	创建 ServerBootstrap 实例
     * 2.	设置并绑定 Reactor 线程池
     * 3.	设置并绑定服务端 Channel
     * 4.	创建并初始化 ChannelPipeline
     * 5.	添加并设置 ChannelHandler
     * 6.	绑定并启动监听端口
     * @throws Exception
     */
    public void start() throws Exception {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workGroup = new NioEventLoopGroup(10);
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
//                    .channel(OioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline().addLast(
                                    new EchoServerHandler());
                        }
                    });
            //绑定端口,同步等待成功
            ChannelFuture f = b.bind().sync();
            System.out.println(EchoServer.class.getName() + " started and listen on " +
                    f.channel().localAddress());
            //挂起服务,等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully().sync();
            workGroup.shutdownGracefully().sync();
        }
    }

}
package netty;

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

import java.net.InetSocketAddress;

/**
 * @Auther: ANYO
 * @Date: 2019/3/12 23:31
 * @Description:
 */
public class EchoClient {

    private final String host;
    private final int port;

    public EchoClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline().addLast(
                                    new EchoClientHandler());
                        }
                    });

            ChannelFuture f = b.connect().sync();
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws Exception {
        new EchoClient(host, port).start();
    }
}

2 责任链模式

  2.1 概述

  责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者与接受者之间的耦合关系,将这个对象形成一条链并沿着链传递请求,直到找到能处理它为止。

结构图
责任链模式结构图

  2.2 netty之责任链模式实现

pipeline管道保存所有的handler处理器,请求到达服务端后,开启入站事件的处理,从第一个handler开始进行传递,由上往下找到相应的处理器,处理完后根据实际判断是否要进行出站处理。

       责任链模式中的组件:

ChannelPipeline:采取责任链模式,将多个ChannelHandler组合在一起,形成链条,事件进行传递给相应ChannelHandler处理。ChannelHandler可实现动态添加和删除。事件传递的顺序:入站事件根据handler的注册顺序执行,出站事件根据handler的注册逆序执行
       ChannelHandler:业务处理组件,分为入站处理(ChannelInboundHandler)和出站处理(ChannelOutboundHandler)两种类型,处理传递到Channel对应的事件

ChannelInboundHandlerAdapter类的继承关系结构图
ChannelOutboundHandlerAdapter类的继承关系结构图


       ChannelHandlerContext:上下文信息,每个ChannelHandler加入到pipeline后都会有对应的ChannelHandlerContext,保存着相关数据channel,handler: pipeline,主要用于维护责任链的执行

ChannelHandlerContext的主要属性


3 单例模式

  3.1 单例模式概述

单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式的实现:

  1. 静态初始化,分为懒汉模式(第一次引用时实例化)和饿汉模式(自己加载时就将自己实例化)

  2. 双重校验锁法

  3.2 Netty单例模式源码实现

DefaultSelectStrategyFactory源码

/*
 * Copyright 2016 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.channel;

/**
 * Factory which uses the default select strategy.
 */
public final class DefaultSelectStrategyFactory implements SelectStrategyFactory {
    public static final SelectStrategyFactory INSTANCE = new DefaultSelectStrategyFactory();

    private DefaultSelectStrategyFactory() { }

    /**
     * 饿汉模式:类加载时实例化单例对象,线程安全
     * @return
     */
    @Override
    public SelectStrategy newSelectStrategy() {
        return DefaultSelectStrategy.INSTANCE;
    }
}

4  策略模式

  4.1 策略模式概述

策略模式:定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户

  4.2 netty策略模式实现

netty的选择器,使用的是策略者模式,将选择的行为设计成接口,不同的选择器根据自己不同的需求用不用的方式实现选择器接口

EventExecutorChooserFactory源码
/*
 * Copyright 2016 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.util.concurrent;

import io.netty.util.internal.UnstableApi;

/**
 * Factory that creates new {@link EventExecutorChooser}s.
 */
@UnstableApi
public interface EventExecutorChooserFactory {

    /**
     * Returns a new {@link EventExecutorChooser}.
     */
    EventExecutorChooser newChooser(EventExecutor[] executors);

    /**
     * Chooses the next {@link EventExecutor} to use.
     */
    @UnstableApi
    interface EventExecutorChooser {

        /**
         * Returns the new {@link EventExecutor} to use.
         */
        EventExecutor next();
    }
}
DefaultEventExecutorChooserFactory实现了EventExecutorChooserFactory接口,其实现了newChooser方法,返回不同的行为
/*
 * Copyright 2016 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.util.concurrent;

import io.netty.util.internal.UnstableApi;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Default implementation which uses simple round-robin to choose next {@link EventExecutor}.
 */
@UnstableApi
public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {

    public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();

    private DefaultEventExecutorChooserFactory() { }

    @SuppressWarnings("unchecked")
    @Override
    public EventExecutorChooser newChooser(EventExecutor[] executors) {
        if (isPowerOfTwo(executors.length)) {
            return new PowerOfTwoEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }

    private static boolean isPowerOfTwo(int val) {
        return (val & -val) == val;
    }

    private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }
    }

    private static final class GenericEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        GenericEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            return executors[Math.abs(idx.getAndIncrement() % executors.length)];
        }
    }
}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值