FastDFS网络连接问题分析

这是个一般性问题,参见这篇文章的描述:

中间件客户端配置中的域名与IP


先讲解一下FastDFS文件上传的执行流程:

  1. 获取trackerServer连接;

  2. 向trackerServer发送请求,获取一个可用的storageServer的IP;

    (请求指令为104,参见TrackerClient.getStoreStorage方法)

注意,每次上传请求,都要重新获取storageServer(以便达到负载均衡等目的)。

另外,每次上传请求,可以重新获取trackerServer,也可以指定固定的trackerServer。


获取storageServer的逻辑,是由trackerServer服务器后端决定的,应用程序没有干预。

但是获取trackerServer的逻辑,是应用自己决定的。fastdfs-java-client获取trackerServer的逻辑如下:

  public TrackerServer getConnection() throws IOException {
    return this.tracker_group.getConnection();
  }

  public TrackerServer getConnection() throws IOException {
    int current_index;

    synchronized (this.lock) {
      this.tracker_server_index++;
      if (this.tracker_server_index >= this.tracker_servers.length) {
        this.tracker_server_index = 0;
      }

      current_index = this.tracker_server_index;
    }
    return this.getConnection(current_index);
  }

    public TrackerServer getConnection(int serverIndex) throws IOException {
      InetSocketAddress address = this.tracker_servers[serverIndex];
      Socket sock = new SocketPool(pool, getConnection(address), address);
      return new TrackerServer(sock, address);
    }

以上代码的意思是:首先从用户配置的trackerServer地址里面去轮询、选择其中一个作为连接地址,然后跟这个地址建立网络连接(换句话说,客户端自己实现了一个简单的负载均衡机制)。这个设计主要是为 IP地址考虑的。如果trackerServer地址不是IP,而是域名,则存在问题:InetSocketAddress的来源是:

public InetSocketAddress(String hostname, int port) {
    checkHost(hostname);
    InetAddress addr = null;
    String host = null;
    try {
        addr = InetAddress.getByName(hostname);
    } catch(UnknownHostException e) {
        host = hostname;
    }
    holder = new InetSocketAddressHolder(host, addr, checkPort(port));
}

这段代码的关键是:InetAddress.getByName(hostname)。这个Java方法就是用来做域名解析的。

参见这篇文章:https://blog.csdn.net/weixin_34379433/article/details/93762404

    所以,如果trackerServer配置的不是IP,而是域名,则该域名只会在第一次解析,后续程序永远不会再次解析这个域名了。当然,如果要配置域名,也不是不可以,但是应该配置多个域名,且每个域名对应一个唯一的IP,例如:

fdfs01.inner.zollty.com,fdfs02.inner.zollty.com,fdfs03.inner.zollty.com

    像这样配置是正确的。但是配置成这样:fdfs.inner.zollty.com,然后DNS解析到3个IP,这就是错误的做法。


    另外,我们从上面的代码也可以看到,如果使用了网络连接池,那其实最好是每次都重新获取trackerServer和storageServer,因为获取trackerServer成本很低,只需要new一个TrackerServer即可,它没有网络连接的开销。但是,获取storageServer是有网络开销的,开销来源于 客户端程序会通过trackerServer去查询可用的storageServer的IP。

    相对于上传文件的开销成本,连接一次trackerServer的开销并不大,所以我认为这种逻辑是可以接受的。

    如果不去查询,每次都复用storageServer可不可以?看下面的代码:

public StorageServer getStorageServer(String ip_addr, int port, int store_path) throws IOException {
    InetSocketAddress address = new InetSocketAddress(ip_addr, port);
    Socket sock = new SocketPool(pool, getConnection(address), address);
    return new StorageServer(sock, address, store_path);
}
private Connection getConnection(InetSocketAddress address) {
    Connection conn;
    try {
        // 从连接池中获取连接
        conn = pool.borrowObject(address);
    } catch (Exception e) {
        throw new NestedRuntimeException(e, "从连接池中获取连接异常");
    }
    return conn;
}

    StorageServer有一个socket类属性,它持有一个Connection连接。其实没必要一直占用这个连接。用完直接归还就是。

    其实真正的复用,是复用StorageServer的InetSocketAddress,而非StorageServer本身。


© 2009-2020 Zollty.com 版权所有。渝ICP备20008982号