美文网首页
为什么必须配置hosts文件映射-hostname问题整理

为什么必须配置hosts文件映射-hostname问题整理

作者: sheen口开河 | 来源:发表于2019-05-09 16:40 被阅读0次

    [TOC]

    1. 一些问题

    先来思考一下以下几个问题:

    • hostname和域名的区别是什么?
    • 为什么kafka服务端server.properties中如果配置了hostname,则客户端的/etc/hosts就必须配置服务端的ip和hostname的映射?
    • 为什么storm集群环境必须配置集群所有机器的ip和hostname的映射?

    2. 基本概念

    要回答以上几个问题,我们先来弄清楚几个基本概念。

    2.1 hostname

    • hostname也就是主机名,表示在一个特定网络范围内的一个计算机的名字
    • 每个主机都会有自己的hostname,在一个特定的网络范围内唯一
    • 每个主机都会有自己的ip地址,在一个特定的网络范围内唯一

    比如xx系统的一个联机服务节点,运行在生产核心区的一台虚机上,该虚机的主机名是dcsapp17,也就是其hostname是dcsapp17。

    2.2 域名

    • 域名是因特网范围内某一个服务器的名字,是用来解决IP地址不可读不好记的问题
    • 每个域名都对应一个具体的主机
    • 域名本身有自己的命名系统和层级规范,但其本质是为了按照一种通用的约定,标识因特网中的一台主机

    例如baidu.com这个域名,就代表了百度的一台服务器,通过ping可以看到其代表主机信息

    ping baidu.com
    PING baidu.com (123.125.114.144): 56 data bytes
    64 bytes from 123.125.114.144: icmp_seq=0 ttl=48 time=25.982 ms
    64 bytes from 123.125.114.144: icmp_seq=1 ttl=48 time=25.990 ms
    

    大型网站在多服务器负载均衡的情况下,一个域名可能会以对应多个ip地址,但是DNS一定会将其解析成某一个IP地址

    2.3 域名解析

    • 当我们希望往某个域名发送请求的时候,需要将域名映射为实际的ip地址,这个过程称之为域名解析
    • 专门用来做域名解析的服务,称之为DNS服务,相应的有DNS服务器
    • 根据DNS服务器服务范围的不同,可以分为互联网DNS服务器和局域网DNS服务器
    • 局域网不一定需要DNS服务器,只要当前主机的/etc/hosts中配置了其他主机的ip地址和hostname的映射,则当前主机就可以通过主机名或者域名来访问对应的机器

    域名解析的过程

    请求某个域名下的一个url,域名解析的过程大致为:

    1. 检查缓存中有没有这个域名对应的解析过的ip地址,如果有则返回。缓存的时间、大小都可以配置
    2. 如果缓存没有,OS检查本地的hosts文件中有没有对应的映射关系,如果有,则以配置的hosts映射为准,返回
    3. 如果hosts里没有,则查找本地DNS服务器缓存,是否有对应的解析过的ip地址,如果有,返回(根据策略配置,可能还会缓存到本机)
    4. 如果没有,则会到配置的首选DNS服务器上去查询解析,查询到,则返回
    5. 如果首选DNS服务器上仍然没有,则根据规则,进行一层层DNS查询转发,直到找到

    解析过程还有很多细节,根据不同的策略配置会有不同的路径,但是整体上类似

    无论请求的url中是域名还是hostname,其本质都是使用别名替代了原有的ip地址,在请求路由的时候,要么是hosts映射文件,要么是各种级别的DNS服务器,总要将别名解析为具体的ip地址,才可以将请求发送到目的地。

    3. 应用中的hostname

    我们发布的每一个网络服务,其底层通讯通常都是基于TCP/IP协议:OS在本地监听某个端口,客户端主机可以通过服务所在的ip和端口请求建立链接并交换数据:

    • 在启动监听的时候,以java网络应用程序为例,无论是原生的ServerSocket还是netty中的的nio,都是指定端口即可
    • 当客户端请求连接的时候,需要提供服务端的ip地址和端口;即使某些API支持通过hostname建立链接,其内部一定还是通过hosts映射或者dns解析出目标ip并发起建链请求

    越来越多的开源通讯组件如dubbo、storm、kafka等在设计时,一般都会考虑如下几个原则:

    • 基于服务注册发现机制,即服务提供者都是将自己的元信息注册到服务注册中心,然后客户端访问注册中心,从而获取服务提供者的主机信息,进而再请求与服务提供者建立链接
    • 在服务注册发现时,推荐使用hostname而不是ip地址,原因是hostname更具有可读性且在云环境下如果需要,可以保证在不更改hostname的情况下替换ip地址,从而客户端应用无需修改配置
    • 客户端从注册中心获取到hostname之后,需要通过dns或者本地hosts映射文件,将hostname解析成ip地址,进而请求建立链接和后续服务

    接下来我们看看kafka、storm在设计的时候是怎么做的。

    3.1 kafka中的hostname

    kafka中的broker的监听配置如下

    # The address the socket server listens on. It will get the value returned from 
    # java.net.InetAddress.getCanonicalHostName() if not configured.
    #   FORMAT:
    #     listeners = listener_name://host_name:port
    #   EXAMPLE:
    #     listeners = PLAINTEXT://your.host.name:9092
    listeners=PLAINTEXT://172.21.132.66:49092
    
    # Hostname and port the broker will advertise to producers and consumers. If not set, 
    # it uses the value for "listeners" if configured.  Otherwise, it will use the value
    # returned from java.net.InetAddress.getCanonicalHostName().
    #advertised.listeners=PLAINTEXT://your.host.name:9092
    
    • listeners:kafka的连接协议名、主机名和端口,如果没有配置,将使用java.net.InetAddress.getCanonicalHostName()的返回值作为主机名
    • advertised.listeners:生产者和消费者使用的主机名和端口,如果没有配置,将使用listeners的配置,如果listeners也没有配置,将使用java.net.InetAddress.getCanonicalHostName()的返回值

    以上的配置,在kafka broker启动的时候,会将配置的信息注册到zookeeper并在集群中维护自己的元信息。当客户端与broker建立链接时,broker会将此处配置的信息返回给客户端,然后客户端会根据需要向某个broker节点发起建链请求。

    因此,如果这里配置的hostname,则返回给客户端的broker信息便是hostname;如果配置的是ip地址,则返回给客户端的broker信息便是ip地址。如果客户端拿到的是hostname,则在建立链接的时候,便要将hostname解析成ip地址,假设此时hosts映射文件或者dns服务器中没有相关的解析配置,则客户端无法建立链接。

    这里需要指出的是,无论是生产者还是消费者,我们配置的bootstrap.servers都是引导地址,而不是客户端真正建立长链接的地址。也就是说,客户端会根据引导地址去broker询问集群的所有broker信息,拿到返回的broker服务信息之后,再向指定的broker发起链接请求。因此bootstrap.servers无论配置的是ip还是hostname,都是只作为引导信息发起查询请求而已。

    3.2 storm中的hostname

    storm是比较流行的流式计算引擎,在storm集群中,每个负责执行计算任务的节点称为Supervisor节点,每个Supervisor启动的时候,将自己注册到zookeeper,当任务分发时,Supervisor从zk中获取到自己的上下游Supervisor节点并与之建立链接,形成流式计算任务序列,一个任务在一个计算节点处理完则被发送到下一个任务处理节点。

    在storm中,Supervisor启动时,也是先读取配置文件,当配置文件不存在时,使用netAddress.getLocalHost().getCanonicalHostName()的返回值作为hostname并将其注册到zookeeper。

    部分hostname解析逻辑如下:

    protected String hostnameImpl () throws UnknownHostException  {
        if (localConf == null) {
            return memoizedLocalHostname();
        }
        Object hostnameString = localConf.get(Config.STORM_LOCAL_HOSTNAME);
        if (hostnameString == null || hostnameString.equals("")) {
            return memoizedLocalHostname();
        }
        return (String)hostnameString;
    }
        
    protected String localHostnameImpl () throws UnknownHostException {
        return InetAddress.getLocalHost().getCanonicalHostName();
    }
    

    因此在storm中,需要在集群的每个节点的hosts中配置彼此的hostname和ip的映射,以在需要的时候完成hostname解析。

    4. 问题解答

    综上,我们可以回答文章开头的问题:

    • hostname和域名的区别是什么?
      • hostname和域名都是为了解决ip地址不方便阅读和记忆的问题,前者在局域网内唯一,后者在因特网内唯一
      • 在发起链接请求时,需要根据hosts配置或者不同层级的dns服务器将hostname或者域名解析为具体的ip地址
    • 为什么kafka服务端server.properties中如果配置了hostname,则客户端的/etc/hosts就必须配置服务端的ip和hostname的映射?
      • kafka broker将配置的hostname作为服务器的元信息存储在zk和broker集群
      • 客户端通过引导地址访问broker之后,broker返回给客户端的是用户配置的服务信息,如果配置的是hostname,则客户端拿到的就是hostname;如果配置的是ip地址,则客户端拿到的就是ip地址
      • 客户端向服务端发起建链请求,需要将hostname解析成具体的ip地址。在没有局域网dns服务其的情况下,hosts映射文件就是一个本地的dns服务
    • 为什么storm集群环境必须配置集群所有机器的ip和hostname的映射?
      • storm的计算节点在启动时候会注册到zk,如果配置文件包含"storm.local.hostname",那么就使用配置的主机名;否则通过调用InetAddress.getLocalHost().getCanonicalHostName()获取主机名
      • storm的计算节点在请求和其他计算节点建立链接的时候,先通过zk获取到主机名,然后发起建立链接的请求。如果拿到的是hostname,则需要hosts映射或者dns服务将其解析成ip地址

    需要说明的是,只要hostname能够被解析成ip地址即可,当前在生产环境有时候必须配置hsotname,是因为没有提供局域网dns服务器。随着后续dns服务器的配套完善,就不一定非要配置hosts映射文件了。

    如有问题欢迎及时指正

    相关文章

      网友评论

          本文标题:为什么必须配置hosts文件映射-hostname问题整理

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