001-核心技术-IO模型简介、BIO简介及示例

1、概述

  基础:java编程、OOP编程、多线程、IO编程、网络编程、经常使用设计模式(观察者、命令、职责链等)、经常使用数据结构(链表等)java

1.一、简介

  netty是一个java开源框架,是一个异步的,基于事件驱动的网络应用框架,用于快速开发高性能、高可靠性的网络IO程序。编程

  主要是针对在TCP协议下,面向Clients端的高并发应用,或者Peer-to-Peer场景下大量数据持续传输的应用。设计模式

  本质是一个NIO框架,适用于服务器通信相关的多种应用场景。 服务器

Netty
NIO【IO、网络】
JDK 原生IO[网络]
TCP/IP

1.二、应用场景

  互联网:在分布式系统中,各个节点之间须要远程服务调用,高性能的RPC框架不可缺乏,Netty做为异步高性能的通信框架,通常被做为基础通信组件被RPC框架使用。网络

  典型应用:dubbo、网游、大数据[AVRO数据文件共享]、Akka、Flink、Spark数据结构

  书籍:Netty in action、netty 权威指南多线程

2、IO模型

  IO模型简单理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通讯的性能并发

  java供支持3中网络编程模型IO模式:BIO、NIO、AIO框架

  BIO:同步并阻塞(传统阻塞型):服务器实现模式为一个链接一个线程,即客户端有链接请求时服务器端就须要启动一个线程进行处理,若是这个链接不作任何事情会形成没必要要的线程开销。异步

  NIO:同步非阻塞,服务器实现模式为一个线程处理多个请求(链接),即客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到链接有IO请求就进行处理

  AIO【NIO2.0】:异步非阻塞,AIO引入异步通道的概念,采用了Proactor模式,简化了程序编写,有效的请求才启动线程,他的特色是先有操做系统完成后成才通知服务端程序启动线程去处理,通常适用于链接数多且链接时间较长的应用。

        

3、BIO简介

   同步并阻塞(传统阻塞型):服务器实现模式为一个链接一个线程,即客户端有链接请求时服务器端就须要启动一个线程进行处理,若是这个链接不作任何事情会形成没必要要的线程开销。能够经过线程池机制改善。(实现多个客户链接服务器)

3.一、编程流程

  一、服务器端启动一个ServerSocket

  二、客户端自动启动Socket对服务器进行通讯,默认状况下服务器端须要对每一个客户创建一个线程与之通讯

  三、客户端发出请求后,先咨询服务器是否有线程响应,若是没有则会等待或者被拒绝

  四、若是有响应,客户端线程会等待请求结束后,在继续执行

3.二、 应用实例

  一、监听6666端口,有链接启动线程

  二、使用线程池机制改善,能够链接多个客户端

  三、客户端使用telnet 测试

代码:

public class BIOServer {
    public static void main(String[] args) throws IOException {
        //一、建立一个线程池
        //二、客户端链接,启动一个线程

        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        ServerSocket serverSocket = new ServerSocket(6666);

        System.out.println("服务启动了:6666端口,telnet 127.0.0.1 6666");
        while (true) {
            System.out.println("等待链接……");
            final Socket socket = serverSocket.accept();

            System.out.println("一个客户端被链接");
            newCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    handler(socket);
                }
            });
        }
    }

    public static void handler(Socket socket) {
        try {
            System.out.println("Thread id:" + Thread.currentThread().getId() +";name:"+ Thread.currentThread().getName());
            byte[] bytes = new byte[1024];
            //经过socket获取输入流
            InputStream inputStream = socket.getInputStream();
            //循环读取客户端数据
            while (true) {

                System.out.println("等待读取……");
                int read = inputStream.read(bytes);//将inputStream 读取到 bytes
                if (read != -1) {
                    System.out.println("Thread id:" + Thread.currentThread().getId() +";name:"+ Thread.currentThread().getName());

                    System.out.println("输出客户端发送的数据");
                    System.out.println(new String(bytes, 0, read));
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("关闭和client的链接");
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

测试 使用:

telnet 127.0.0.1 6666

3.三、BIO问题分析

  一、每一个请求都须要建立独立的线程,与对应的客户端进行数据Read,业务处理,数据Write

  二、当并发数较大时,须要建立大量线程来处理链接,系统资源占用较大

  三、当链接创建后,若是当前线程暂时没有数据可读,则线程就阻塞在Read上,形成线程资源浪费 

相关文章
相关标签/搜索