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

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映射文件了。

 

 

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值