分布式(三)——分布式架构网络通信

原创
09/21 15:47
阅读数 3.5K

一:分布式架构网络通信

在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技 术,例如:RMI、Hessian、SOAP、ESB和JMS等

1.1:基本原理

要实现网络机器间的通讯,需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现。

  • 其中传输协议比较出名的有tcp、udp等,tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议。
  • 网络IO,主要有BIO、NIO、AIO三种方式

1.2:RPC

RPC全称为remote procedure call,即远程过程调用。借助RPC可以做到像本地调用一样调用远程服务,是一种进程间的通信方式。常见的RPC框架有:Hessian、gRPC、Thrift、HSF、Dubbo等。

注意:需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程;对 于RPC框架而言,核心模块就是通讯序列化

远程过程调用分为两种,现在在服务间通信的方式也基本以这两种为主:

  • 基于HTTP的restful形式的广义远程调用,以spring could的feign和restTemplate为代表,采用的协议是HTTP的7层调用协议,并且协议的参数和响应序列化基本以JSON格式和XML格式为主。

  • 基于TCP的狭义的RPC远程调用,以阿里的Dubbo为代表,主要通过netty来实现4层网络协议,NIO来异步传输,序列化可以是JSON或者hessian2以及java自带的序列化等,可以配置。

1.2.1 RPC架构

一个完整的RPC架构里面包含了四个核心的组件,分别是Client,Client Stub,Server以及Server Stub,这个Stub 可以理解为存根。

  • 客户端(Client),服务的调用方。
  • 客户端存根(Client Stub),存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
  • 服务端(Server),真正的服务提供者。
  • 服务端存根(Server Stub),接收客户端发送过来的消息,将消息解包,并调用本地的方法。

1.2.2 RPC调用过程

注意:无论是何种类型的数据,最终都需要转换成二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流(序列化),而数据的接收方则需要把二进制流再恢复为对象(反序列化)

1.3:RMI

Java RMI 指的是远程方法调用 (Remote Method Invocation),是java原生支持的远程调用 ,采用JRMP(Java Remote Messageing protocol)作为通信协议,可以认为是纯java版本的分布式远程调用解决方案, RMI主要用 于不同虚拟机之间的通信,这些虚拟机可以在不同的主机上、也可以在同一个主机上,因此这里的通信可以理解为一个 虚拟机上的对象调用另一个虚拟机上对象的方法。

1.3.1 RMI简介

注册表:
注册表(Registry):以URL形式注册远程对象,并向客户端回复对远程对象的引用。

客户端:

  1. 存根/桩(Stub):远程对象在客户端上的代理;
  2. 远程引用层(Remote Reference Layer):解析并执行远程引用协议;
  3. 传输层(Transport):发送调用、传递远程方法参数、接收远程方法执行结果。

服务端:

  1. 骨架(Skeleton):读取客户端传递的方法参数,调用服务器方的实际对象方法,并接收方法执行后的返回值;
  2. 远程引用层(Remote Reference Layer):处理远程引用后向骨架发送远程方法调用;
  3. 传输层(Transport):监听客户端的入站连接,接收并转发调用到远程引用层。

1.3.2 RMI调用过程

远程调用过程:
1)客户端从远程服务器的注册表中查询并获取远程对象引用。
2)桩对象与远程对象具有相同的接口和方法列表,当客户端调用远程对象时,实际上是由相应的桩对象代理完成的。
3 ) 远程引用层在将桩的本地引用转换为服务器上对象的远程引用后,再将调用传递给传输层(Transport),由传输层通 过TCP协议发送调用;
4)在服务器端,传输层监听入站连接,它一旦接收到客户端远程调用后,就将这个引用转发给其上层的远程引用层;
5)服务器端的远程引用层将客户端发送的远程应用转换为本地虚拟机的引用后,再将请求传递给骨架(Skeleton);
6)骨架读取参数,又将请求传递给服务器,最后由服务器进行实际的方法调用。

1.4:BIO、NIO、AIO

BIO:同步阻塞的;NIO:同步非阻塞的;AIO:异步非阻塞的。

同步与异步(针对应用程序与内核的交互):

  • 同步:用户进程触发IO操作时,使用等待或者轮训的方式查看IO操作是否就绪
    例如:银行取钱,我自己去取钱,取钱的过程中等待。
  • 异步:当一个异步进程调用发出之后,调用者不会立刻得到结果。而是在调用发出之后,被调用者通过状态、通知来通知调用者,或者通过回调函数来处理这个调用。
    例如:我请朋友帮我取钱,他取到钱后返回给我.

阻塞与非阻塞(针对进程访问数据(读写操作)的时候,根据IO操作的就绪状态采取不同的方式):

  • 阻塞:阻塞方式下读取和写入将一直等待
    例: ATM机排队取款,你只能一直等待排队取款
  • 非阻塞:使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成
    例:柜台取款,取个号,然后坐在椅子上做其他事,等广播通知,没到你的号你就不能去,但你可以不断的问大堂经理排到了没有。

1.4.1 BIO(同步阻塞)

BIO:同步阻塞IO,B代表blocking。服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

简单实现:

  1. 服务端代码
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress("127.0.0.1",8888));
        while (true){
            // 同步阻塞
            Socket socket = serverSocket.accept();
            new Thread(()->{
                try {
                    byte[] bytes = new byte[1024];
                    int len = socket.getInputStream().read(bytes);
                    System.out.println(new String(bytes,0,len));
                    socket.getOutputStream().write(bytes,0,len);
                    socket.getOutputStream().flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
  1. 客户端代码
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",8888);
        socket.getOutputStream().write("hello".getBytes());
        socket.getOutputStream().flush();
        System.out.println("server send back data =====");
        byte[] bytes = new byte[1024];
        int len = socket.getInputStream().read(bytes);
        System.out.println(new String(bytes,0,len));
    }

1.4.2 NIO(同步非阻塞)

同步非阻塞IO (non-blocking IO / new io)。服务器实现模式为一个请求对应一个通道(Channel),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。

  • 通道(Channels):Channel 数据连接的通道。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中 .
  • 缓冲区(Buffers):通道channel可以向缓冲区Buffer中写数据,也可以像buffer中存数据。
  • 选择器(Selector):使用选择器,借助单一线程,就可对数量庞大的活动 I/O 通道实时监控和维护。

NIO的特点

当一个连接创建后,不会需要对应一个线程,这个连接会被注册到多路复用器,所有的连接只需要一个线程就可以操作,该线程的多路复用器会轮询,发现连接有请求时,才开启一个线程处理。

NIO模型中selector的作用,一条连接来了之后,现在不创建一个while死循环去监听是否有数据可读了,而是直接把这条连接注册到selector上,然后,通过检查这个selector,就可以批量监测出有数据可读的连接,进而读取数据,

NIO的使用

nio的使用示例

1.4.3 AIO(异步非阻塞)

异步非阻塞IO。A代表asynchronize

特点:

  • 当有流可以读时,操作系统会将可以读的流传入read方法的缓冲区,并通知应用程序,
  • 对于写操作,OS将write方法的流写入完毕时操作系统会主动通知应用程序。因此read和write都是异步 的,完成后会调用回调函数。

使用场景: 连接数目多且连接比较长(重操作)的架构,比如相册服务器。重点调用了OS参与并发操作,Java7开始支持。

展开阅读全文
打赏
0
4 收藏
分享
加载中
那么问题来了 为什么没有 异步阻塞的.
我请朋友帮我去取钱, 然后我在原地站着等朋友, 他不回来我哪都不去
这么先进的设计思路我马上就去写源码[doge]
09/24 17:41
回复
举报
更多评论
打赏
1 评论
4 收藏
0
分享
返回顶部
顶部