青训营笔记|大数据方向--分布式文件存储系统开发(3)

151 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的的第3天

 上回讲到我们的分布式文件存储系统所需的技术,这次我们就来了解一下其中的,用于网络通信的一个框架--Netty

 首先,我们先来看看百度给出的介绍:

Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

 这个解释作者感觉是不够通俗易懂的,我认为,不严谨的说,假如我们把让机器按照我们的要求进行网络通信比作建房子的话,Netty就像是建材批发商,我们需要什么,就去Netty的包下导入对应的类,然后像拼积木一样把各个功能部件拼起来,而且由于所有的材料都来自同一家批发商,所以他们的标准比较统一,系统比较高效

 下面我们来构建一个基础的Netty项目,前期的准备工作就不赘述了,首先建一个maven项目,然后在pom.xml文件下导入对应的包

 <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.79.Final</version>
 </dependency>

 Netty是区分服务端与客户端的,我们先创建对应的包与类,分别叫做NetServer.java与NetClient.java,目录结构如下

image.png  然后是NetServer.java的内容,如下

package com.li.server;

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

public class NetServer {
    //服务端使用ServerBootstrap引导的示例
  public static void main(String[] args) throws InterruptedException {
    //创建EventLoop
    NioEventLoopGroup master = new NioEventLoopGroup();
    //创建服务端引导
    ServerBootstrap bootstrap = new ServerBootstrap();
    try { 
      bootstrap.group(master);
      //指定使用NioServerSocketChannel作为Channel类型
      bootstrap.channel(NioServerSocketChannel.class);
      //注册一个ChannelInboundHandlerAdapter类型处理channel的连接
      bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
              //TODO 在有连接进入后进行ChannelHandler的编排
              System.out.println("连接建立......");
        }
      });
      //使用bind方法启动服务端的监听
      ChannelFuture future = bootstrap.bind(8888).sync();
      future.channel().closeFuture().sync();
    } catch (Exception e) {
      //TODO: handle exception
    }finally{
      master.shutdownGracefully().sync();
    }
  }
}

 下面是NetClient的内容

package com.li.client;

import io.netty.bootstrap.Bootstrap;
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.NioSocketChannel;

public class NetClient {

//客户端使用Bootstrap引导的示例
public static void main(String[] arg) throws InterruptedException {
    NioEventLoopGroup group=new NioEventLoopGroup();
    Bootstrap bootstrap = new Bootstrap();
    bootstrap.channel(NioSocketChannel.class);
    bootstrap.group(group);
    bootstrap.option(ChannelOption.SO_KEEPALIVE,true);
    //绑定远程服务地址与端口
    bootstrap.remoteAddress("127.0.0.1", 8888);
    bootstrap.handler(new ChannelInitializer<SocketChannel>() {
      @Override
      protected void initChannel(SocketChannel ch) throws Exception {
        //TODO 在有连接进入后进行ChannelHandler的编排
        System.out.println("NetClient 启动......");
      }
    });
    //连接远程服务器
    ChannelFuture channelFuture = bootstrap.connect().sync();
    channelFuture.channel().closeFuture().sync();
    group.shutdownGracefully();
  }
}

 我们先让NetServer先启动,然后启动NetClient,你可以看到命令行会打印出各自initChannel()方法中的内容,我们再来尝试解读这个demo的组成部分

 从我的角度来说,先得实例化客户端和服务端,其次,实现通信需要ip地址和端口号,可以看到两个文件中分别有ServerBootstrap和Bootstrap类,NetServer中有一个步骤bootstrap.bind(8888),而NetClient中有一个bootstrap.remoteAddress("127.0.0.1", 8888),这验证了我们的猜想。

 在我们使用的网络可能并不会一样,有些参数或属性需要根据实际要求设置,所以连接中应当有可供我们配置的部分,可以看到NetClient中有一个语句bootstrap.option(ChannelOption.SO_KEEPALIVE,true); 这一部分参考网友的解释ChannelOption.SO_KEEPALIVE_墨渧的博客-CSDN博客
个人理解是客户端会定时发送消息以确定服务端是否还活跃,如否则会尝试关闭连接。

 接下来连接顺利建立后,我们就需要特点的逻辑代码来实现业务,比如在本例中NetServer启动后会监听信道,如果连接建立则打印连接建立......,NetClient建立连接后会打印NetClient 启动......,bootstrap.handler()里面填充的内容就是我们的逻辑代码,为了清晰易懂我们也可以把这些代码写在另一个类中,这里只是为了粗略介绍所以没有那么做。

 再来看bootstrap.group(),可以看到里面放了一个NioEventLoopGroup,顾名思义,这是一个事件循环池,或者看作一个线程池,里面有很多线程,遇到I/O事件需要处理时,就按照某种方法选择一个线程完成任务。

 这个demo的最后一部分就是资源的关闭,这里有一点需要注意,这句channelFuture.channel().closeFuture().sync(); 当main方法执行到这里时,会让主线程卡在这里等待,不要关闭NetServer和NetClient,否则的话main方法会继续往下执行shutdownGracefully()我们的服务就会停止了,假如注释掉这一句

channelFuture.channel().closeFuture().sync();

可以看到我们启动后过了几秒进程就结束了,这是因为程序直接执行了后面的shutdownGracefully() image.png 另一种方法是把master.shutdownGracefully().sync()这一句也注释掉,这样程序就不会结束了