美文网首页大数据 爬虫Python AI Sql大数据Java 杂谈
分布式专题(2)- 分布式 Java通信

分布式专题(2)- 分布式 Java通信

作者: Java程序员YY | 来源:发表于2019-08-12 14:02 被阅读1次

    Java实现系统间的通信概览

    我们知道,所谓分布式,无非就是“将一个系统拆分成多个子系统并散布到不同设备”的过程而已。在微服务的大潮之中, 我们把系统拆分成了多个服务,根据需要部署在多个机器上,这些服务非常灵活,可以随着访问量弹性扩展。本质上而言,实现一个分布式系统,最核心的部分无非有两点:

    如何拆分——可以有很多方式,核心依据一是业务需求,二是成本限制。这是实践中构建分布式系统时最主要的设计依据。

    如何连接——光把系统拆开成各个子系统还不够,关键是拆开后的各个子系统之间还要能通信,因此涉及通信协议设计的问题,需要考虑的因素很多,好消息是这部分其实成熟方案很多。

    分布式系统并非灵丹妙药,解决问题的关键还是看你对问题本身的了解。通常我们需要使用分布式的常见理由是:

    为了性能扩展——系统负载高,单台机器无法承载,希望通过使用多台机器来提高系统的负载能力。

    为了增强可靠性——软件不是完美的,网络不是完美的,甚至机器本身也不可能是完美的,随时可能会出错,为了避免故障,需要将业务分散开保留一定的冗余度。

    本篇要讲的是分布式应用中解决“如何连接”的问题,即Java是如何实现系统间的通信的。先上一张总图:

    上图中,我们看到图片左边的【网络通信】,是由协议和网络IO组成。协议如TCP/IP等在上一篇文章中已经介绍过,多出的Multicast(组播)此处也不再延伸介绍,有需要的同学另外自行了解即可。上一篇文章在介绍传输层的TCP协议时,已经提到了“TCP提供全双工通信,会话双方都可以同时接收和发送数据。都设有接收缓存和发送缓存,用来临时存放双向通信的数据”。发送缓存也就是写缓存,接收缓存也就是读缓存。在客户端与服务器经过三次握手建立连接后,在二者之间就相当于打开了一条可以互相传送数据的道路,道路的两端就是各自的读写缓存和我们所说的套接字Socket,每一个socket都有一个输出流和一个输入流。这种跨越网络的数据IO流,就是我们说的网络IO。然后可以看到网络IO还分为了BIO、NIO和AIO,这个我们可以先不管,后面我会再细说。所以TCP连接差不多就是下图这个样子。

    在了解了Socket和网络IO的含义之后,我们看回第一张图的右边,可以看到Java实现系统间的通信方式有基于Java API、基于开源框架、基于远程通信技术等。下面,我们用Java代码来一起实现一下这几种方式。

    Socket:socket本身并不是协议,它是应用层与TCP/IP协议族通信的中间软件抽象层,是一组调用接口(TCP/IP网络的API函数)。可以看做是对TCP/IP协议的封装,它把复杂的TCP/IP协议族隐藏在Socket接口后面,它的出现只是使得程序员更方便地使用TCP/IP协议栈而已。

    基于Java API

    java.net 包中的 API 包含有网络编程相关的类和接口。java.net 包中能够找到对TCP协议、UDP协议、Multicast协议的支持。我们仍以基于TCP协议的网络编程为例。

    在编程开始前,我们再次简单回顾一下计算机网络中的传输层和TCP协议。

    传输层为应用进程之间提供端口到端口的通信

    TCP提供全双工通信,会话双方都可以同时接收和发送数据。

    (在看API的具体实现之前,思考一个有意思的问题:如果是交给你去实现客户端与服务器的通信,你会设计多少个对象?如何设计它们的关系?如何做到面向对象设计?多看,多想,多换位思考,如果是你的话,你怎么处理,这是对提高自己水平很有裨益的事,无论是做人还是做事。)

    官方文档提到:以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:

    服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。

    服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。

    服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。

    Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。

    在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

    连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。

    上述流程有空就多看几遍,我们后面讲的所有通信都是基于上述流程。各种技术和框架不过是对这些流程不断封装、抽象、扩展而已,但是主流程仍是不变的。

    现在,打开我们的IDEA或者Eclipse,按照API中的实现步骤,一起来实现下面的小目标。

    (终于要回到我们熟悉的代码部分了,Code Time Begin !)

    小目标:对基于Java API的网络编程有初步的了解。具体需求如下:

    1)从客户端把“Hello, I am xxx. Here is Client.”这条消息传送给服务端;

    2)从服务端读取该消息,并给客户端返回响应消息:“Hello, xxx, nice to meet you! Here is Server.”

    我们可以按照以下步骤实现上述需求:

    第一步:建项目

    我们先新建一个项目distributed,再建一个名为mysocket的包。为了以后方便添加Jar包,我们建的是maven项目。

    第二步:建服务端类

    然后建一个服务端类:HelloServer,代码如下:

    package socket;

    import java.io.*;

    import java.net.*;

    public class HelloServer {

    // 选择一个端口作为服务端端口

    private static int port = 8888;

    public static void main(String[] args) throws Exception {

    // 创建ServerSocket对象,相当于在服务端(本机)打开一个监听

    ServerSocket serverSocket = new ServerSocket(port);

    System.out.println("开始监听端口:" + port + "...");

    // accept方法阻塞等待客户端连接,连接成功后得到socket对象

    Socket socket = serverSocket.accept();

    // 获取服务端socket的输入流,客户端通过这个输入流给服务端传递消息

    DataInputStream in = new DataInputStream(socket.getInputStream());

    // 通过服务端socket的输入流,输出客户端发送过来的消息

    System.out.println("客户端消息:"+in.readUTF());

    // 获取服务端socket的输出流,服务端端通过这个输出流给客户端传递消息

    DataOutputStream out = new DataOutputStream(socket.getOutputStream());

    // 通过服务端socket的输出流,给客户端发送消息

    out.writeUTF("Hello,jvxb, nice to meet you!Here is Server。");

    // 关闭服务端socket。

    socket.close();

    // 关闭监听

    serverSocket.close();

    }

    }

    第三步:建客户端类

    然后建一个客户端类:HelloClient,代码如下:

    package socket;

    import java.io.*;

    import java.net.*;

    public class HelloClient {

    // 需连接的服务端IP或域名,此例中本机即为服务端。一般都是通过配置文件来设置。

    private static String serverName = "127.0.0.1";

    // 需连接的服务端端口

    private static int port = 8888;

    public static void main(String[] args) throws Exception {

    // 通过指定服务端IP、服务端端口,连接到服务端,连接成功后获得客户端socket.

    Socket clientSocket = new Socket(serverName, port);

    // 通过客户端socket,获得客户端输出流。

    DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream());

    // 通过客户端输出流,向服务端发送消息。

    out.writeUTF("Hello,I am jvxb! Here is Client.");

    // 通过客户端输出流,读取服务端发送过来的消息。

    DataInputStream in = new DataInputStream(clientSocket.getInputStream());

    // 输出服务端发送过来的消息

    System.out.println("服务器响应: " + in.readUTF());

    // 关闭客户端socket

    clientSocket.close();

    }

    }

    第四步:测试

    1)运行服务端类

    2)运行客户端类

    3)查看输出结果

    可以看到结果如下:

    通过以上的例子我们可以看到,只需要简单的几行Java代码,通过Java API我们就能够实现基于TCP协议的客户端/服务端通信。同理,通过DatagramSocket对象也能很快速地实现基于UDP协议的客户端/服务端通信,此处不再展开。当然,我们上面举的例子只是最基础的。一般来说服务端不会只与一个客户端连接,服务端需要监听多个客户端连接的话,就得让accept()方法在while中持续循环,所以服务端的代码一般都是配合多线程来使用,传统做法是一个客户端连接过来就开一个线程去单独处理,这种处理是比较简单容易实现,但很明显客户端连接一多,性能方面就跟不上了,因为光是线程的切换开销就挺大的,更不用说每个线程都会占用挺大的资源。那要怎么解决性能的问题呢?功力深厚者,可以自己去设计出自己的一套东西去解决,像小兵我这种水平未到家的,我觉得用人家东西也挺不错的。。比如我们可以直接使用Netty框架。

    最新免费java,架构,大数据AI编程资料获取添加

    薇信:18410263200

    通过验证填写“111”(备注必填)

    相关文章

      网友评论

        本文标题:分布式专题(2)- 分布式 Java通信

        本文链接:https://www.haomeiwen.com/subject/abqujctx.html