Day13.高性能RPC设计 学习笔记1

1、引言

  1. 系统架构演变
    随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用(MVC)架构已没法应对,分布式服务架构以及流动计算架构(伸缩性)势在必行,亟需一个治理系统确保架构有条不紊的演进。
    图01
  • 单一架构:例如早期servlet/jsp - ORM(对象关系映射) Hibernate|MyBatis
  • 垂直架构:将一个应用分层,实现协同开发,便于后期项目升级维护 - MVC Struts1/2|SpringMVC
  • 分布式服务:提高服务总体性能,将一些模块独立出去(SOA service orented architecture),独立运行,随着业务发展,不可避免须要调度第三方服务(物流信息、第三支付)。RPC - Remote Process Call (即时消息) webservice、Dubbo、SpringCloud
  • 流动式计算架构:要求服务提供方可以根据负载状况,动态增长或者减小。同时实现服务可视化,简化服务运维,提高系统资源的利用率。 - SOA治理框架 微服务 - 对服务进行集中管理调控。
  1. 高并发互联网设计原则 —— 高负载高存储高容错 也称 AKF拆分原则
    X轴:服务水平扩展
    服务具备可复制性 ,水平分割 ,解决一些并发问题
    (水平拓展,比性能的垂直提高成本低-摩尔定律)
    Y轴:服务间各司其职
    弱耦合,垂直分割,物理节点物尽其用
    (泳道设计)
    Z轴:对x,y打包,进行物理隔离。
    X中有Y,Y中有X x和y互相转换
    y中有x:对y轴作垂直拆分,层与层间独立,为了保证每一层的可用性,每一层作水平分割java

  2. 高并发的场景问题 —— 优化思路
    如何让服务支持x轴的水平扩展,利用分布式的思惟解决高并发;如何作y中的垂直拆分,是应用之间尽量隔离,弱化服务件耦合度;结合两个维度来解决高并发的场景问题。(Socket + IO)程序员

2、网络基础(TCP/IP)

  • 网络编程:经过编码的方式,让不一样计算机之间相互通讯(数据的传递)。web

  • 两个核心问题:
    寻址问题:ip地址+port端口号
    协议问题:数据传输格式面试

  • OSI 七层模型(标准,了解就好)
    图02编程

  • OSI 五层模型(七层的转变)tomcat

  • 协议
    http协议:应用层协议,超文本传输协议,不一样计算机间传输文本的协议(字符串),底层tcp协议
    tcp/ip协议:传输层协议安全

tcp协议:传输安全,效率低
udp协议:传输不安全,效率高服务器

  • 了解
    应用层(Http协议:WebService,tomcat协议)——普通程序员
    传输层(TCP/IP UDP:基于tcp/ip封装的ftp、smtp/pop三、websocket…定制协议rpc协议)——架构程序员 也是代码最终触及到的地方
    物理层(嵌入式开发)

3、传统 BIO 网络编程模型(TCP/IP)

  1. IO模型分类
  • BIO 同步阻塞IO
    传统IO 或者 Blocking IO
    特色:面向流Input | Output
    【每一个线程只能处理一个channel(同步的,该线程和该channel绑定)。
    线程发起IO请求,无论内核是否准备好IO操做,从发起请求起,线程一直阻塞,直到操做完成。】websocket

  • NIO 同步非阻塞IO
    New IO 或者Non Blocking IO
    特色:面向缓冲区Buffer(基于通道)
    【每一个线程能够处理多个channel(异步)。
    线程发起IO请求,当即返回;内核在作好IO操做的准备以后,经过调用注册的回调函数通知线程作IO操做,线程开始阻塞,直到操做完成 。】网络

  • AIO(Async Non Blocking IO) 异步非阻塞
    【线程发起IO请求,当即返回;内存作好IO操做的准备以后,作IO操做,直到操做完成或者失败,经过调用注册的回调函数通知线程作IO操做完成或者失败 。】

  1. BIO
    方向—— 输入、输出流(InputStream | OutputStream)
    类型—— 字节、字符流(Reader | Writer)
    功能—— 节点、过滤流(BufferedInputStream | BufferedOutputStream )

  2. BIO网络编程

  • 服务端:ServerSocket 【接收和响应,请求转发 - ServerSocket请求响应- Socket
    ①初始化服务器ServerSocket,绑定监听端口
    ②等待客户端链接serverSocket.accept();
    ③处理请求/响应sockect.getInputStream(); / socket.getOutputStream();
    ④关闭资源

  • 客户端:Socket 【发送和接收,发送请求|接收响应:Socket
    ①初始化客户端Socket,绑定服务器IP/端口
    ②发起请求/获取响应socket.getOutputStream(); / socket.getInputStream();
    ③关闭资源

    socket编程图

  1. BIO代码
  • Server
public class BIOBootstrapServer {
    public static void main(String[] args) throws IOException {
        server();
    }
    public static void server() throws IOException {
        //建立服务转发ServerSocket
        ServerSocket ss = new ServerSocket();
        ss.bind(new InetSocketAddress(9999)); //server能够省略好hostname

        ExecutorService threadPool= Executors.newFixedThreadPool(10);
        while(true) {
            //等待请求到来,转发产生Socket(系统内部资源)
            final Socket socket = ss.accept(); //没有请求就等待,阻塞
            //final 匿名内部类使用局部变量要final修饰
            threadPool.submit(new Runnable() {
                public void run() {
                    try {
                        //利用IO,读取用户请求
                        InputStream req = socket.getInputStream();
                        InputStreamReader isr = new InputStreamReader(req);
                        BufferedReader br = new BufferedReader(isr);

                        String line = null;
                        StringBuilder sb = new StringBuilder();
                        while ((line = br.readLine()) != null) {
                            sb.append(line);
                        }
                        System.out.println("服务器收到:" + sb.toString());
                        //给出用户响应
                        OutputStream res = socket.getOutputStream();
                        PrintWriter pw = new PrintWriter(res);
                        pw.println(new Date().toLocaleString());
                        pw.flush();
                        socket.shutdownOutput(); //告知客户端写结束
                        //关闭系统资源
                        socket.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}
  • Client
public class BIOBootstrapClient {
    public static void main(String[] args) throws IOException {
        send("这是一条来自client 的 message!");
    }
    public static void send(String msg) throws IOException {
        //建立Socket对象
        Socket s = new Socket();
        s.connect(new InetSocketAddress("127.0.0.1",9999)); //因此几乎给ip和端口的,底层走的就是socket

        //发送请求
        OutputStream req = s.getOutputStream();
        PrintWriter pw=new PrintWriter(req);
        pw.println(msg);
        pw.flush();
        s.shutdownOutput();//告知服务端写结束

        //接收响应
        InputStream res = s.getInputStream();
        InputStreamReader isr = new InputStreamReader(res);
        BufferedReader br = new BufferedReader(isr);
        String line=null;
        StringBuilder sb=new StringBuilder();
        while((line=br.readLine())!=null){
            sb.append(line);
        }
        System.out.println("客户端收到:"+sb.toString());
        //关闭资源
        s.close();
    }
}

思考下,BIO模型的缺点?
accept转发产生socket资源,一次请求转发等待请求响应的过程有阻塞。main线程做accept请求转发、子线程做socket读写IO处理。

  • 机器间通讯是靠传输 套接字 完成的。

套接字,是支持TCP/IP的网络通讯的基本操做单元,能够看作是不一样主机之间的进程进行双向通讯的端点,简单的说就是通讯的两方的一种约定,用套接字中的相关函数来完成通讯过程。
简单的举例说明下:Socket = Ip address + TCP/UDP + port。

每一个socket表明一个系统的随机端口,用来在服务和客户端之间创建链接。
在这里插入图片描述

  1. BIO网络编程问题
    经过将相应处理和转发进行分离,可是在转发的时候,服务器是经过先开启线程资源(计算),而后在计算中动用IO操做,IO存在总线占用问题,致使当前线程操做的IO资源没有就绪,致使系统计算资源的浪费。若是要解决该问题,必须先考虑让IO就绪,而后在开启线程处理或者理解为让线程去处理一些非阻塞的IO操做。

BIO编程诟病 :多线程模型解决服务端并发,可是没法判断当前处理的IO状态是否就绪,此时就会致使线程在作无畏等待,致使系统资源利用率不高。 先开线程 -> 在线程等待IO就绪->处理IO->线程资源释放

4、NIO编程模型

  • Channel(FileChannel-读写文件、ServerSocketChannel-请求转发、SocketChannel-响应处理)
  • ByteBuffer (字节缓冲区)
  1. 建立FileChannel
//可读的FileChannel
FileChannel fr=new FileInputStream("xxx路径").getChannel();
//可写的FileChannel
FileChannel fw=new FileOutputStream("xxx路径").getChannel();

文件操做

//读文件件
fr.read(缓冲区)
//写文件
fw.write(缓冲区)
  1. 字节缓冲区
    图03

flip:pos赋值给limit,post归0
clear:恢复变量为初始状态

  1. 一个基于NIO的文件拷贝【面试&笔试】
    【NIO没有流的概念,用通道来处理。】
public class FileCopyDemo {
    public static void main(String[] args) throws IOException {
        //建立读通道 磁盘读入数据 到 ByteBuffer
        FileInputStream in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\123.txt");
        FileChannel fcr = in.getChannel(); //fileChannelRead

        //建立写通道 将 ByteBuffer 数据写入磁盘
        FileOutputStream out = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\321.txt");
        FileChannel fcw= out.getChannel(); //fileChannelWrite

        //建立ByteBuffer
        ByteBuffer buffer = ByteBuffer.wrap(new byte[1024]);//.allocate()也能够

        while (true){
            buffer.clear();
            int n = fcr.read(buffer);
            if(n==-1) break;
            buffer.flip();
            fcw.write(buffer);
        }
        //关闭资源
        fcr.close();
        fcw.close();
    }
}
  1. “挂羊头,卖狗肉” —— NIO的API,BIO的编程模型
public class NIOBootstrapServer_Fake {
    public static void main(String[] args) throws IOException {
        server();
    }
    public static void server() throws IOException {
        //建立服务转发ServerSocket
        ServerSocketChannel ss = ServerSocketChannel.open();
        ss.bind(new InetSocketAddress(9999));
        ExecutorService threadPool= Executors.newFixedThreadPool(10);
        while(true) {
            //等待请求到来,转发产生Socket(系统内部资源)
            final SocketChannel socket = ss.accept();// 阻塞
            threadPool.submit(new Runnable() {
                public void run() {
                    try{
                        //读取客户端响应
                        ByteBuffer buffer=ByteBuffer.allocate(1024);

                        ByteArrayOutputStream baos=new ByteArrayOutputStream();//相似stringbuilder,用来暂存用

                        while (true){
                            buffer.clear();
                            int n = socket.read(buffer);
                            if(n==-1) break;
                            buffer.flip();
                            baos.write(buffer.array(),0,n);
                        }
                        System.out.println("服务器收到:"+new String(baos.toByteArray()));

                        //给出客户端响应

                        ByteBuffer respBuffer = ByteBuffer.wrap((new Date().toLocaleString()).getBytes());
                        socket.write(respBuffer);

                        socket.shutdownOutput();//告知客户端写结束
                        //关闭系统资源
                        socket.close();
                    }catch (Exception e){

                    }
                }
            });
        }
    }
}
  1. 通道选择器
    图
    在这里插入图片描述

NIO核心思想,将网络编程中全部操做(转发,读请求,写响应)封装成事件Event,以后用事件形式通知程序进行计算,事件发生则通知程序,这样处理的IO都是就绪的IO,保证计算资源的充分利用。这时候,引入通道选择器的概念。 咱们须要把通道注册到通道选择器的注册列表中,由通道选择器维护注册列表;一旦咱们所关注的事件发生了,他会把关注的事件封装到事件处理列表当中;为了防止处理空或无效IO,每一个事件处理结束后,要从列表中移除;移除不表明取消注册。只有两种可能才取消注册,一种是通道关闭,一种是主动注销。

相关文章
相关标签/搜索