淘先锋技术网

首页 1 2 3 4 5 6 7

内容主旨

本篇文章主要介绍了如何进行自定义Dubbo服务对外暴露的主机地址的实战技术方案,其中我们需要针对于服务提供者侧的host主机暴漏的目的以及如何进行定制化处理

特性说明

在Dubbo中,Provider启动时主要做两个事情

  1. 启动服务提供者的server端实例。
  2. 服务自身向注册中心注册服务基本信息(服务IP地址、端口以及相关的一些服务实例级别的信息)。

启动server时需要绑定socket套接字以及监听对应的port端口,向注册中心注册自身服务时也是需要发送socket唯一标识服务地址

开发Dubbo的阔扩展暴漏主机地址前需要的问题

  1. Dubbo中不设置host时默认host是什么?

  2. Dubbo中如何指定服务的host,我们是否可以用hostname或domain代替IP地址作为host?

  3. 使用docker时,有时需要设置端口映射,此时,启动server时绑定的socket和向注册中心注册的 socket 使用不同的端口号,此时又该如何设置?

使用场景

不设置host时,使用默认host,一般的dubbo协议配置如下:

xml配置方式

yaml配置方式

dubbo:
  protocol:
	name: dubbo
	port: 20890  	

可以看到,按照上面的配置方式,我们只配置了端口号,没有配置host,此时设置的host是什么呢?
查看源码得知,在 org.apache.dubbo.config.ServiceConfig#findConfigedHosts() 方法获取host值。

    /**
     * Register & bind IP address for service provider, can be configured separately.
     * Configuration priority: environment variables -> java system properties -> host property in config file ->
     * /etc/hosts -> default network address -> first available network address
     *
     * @param protocolConfig
     * @param registryURLs
     * @param map
     * @return
     */
    private String findConfigedHosts(ProtocolConfig protocolConfig,
                                     List<URL> registryURLs,
                                     Map<String, String> map) {
        boolean anyhost = false;
        String hostToBind = getValueFromConfig(protocolConfig, DUBBO_IP_TO_BIND);
        if (hostToBind != null && hostToBind.length() > 0 && isInvalidLocalHost(hostToBind)) {
            throw new IllegalArgumentException("Specified invalid bind ip from property:" + DUBBO_IP_TO_BIND + ", value:" + hostToBind);
        }
        // if bind ip is not found in environment, keep looking up
        if (StringUtils.isEmpty(hostToBind)) {
            hostToBind = protocolConfig.getHost();
            if (provider != null && StringUtils.isEmpty(hostToBind)) {
                hostToBind = provider.getHost();
            }
            if (isInvalidLocalHost(hostToBind)) {
                anyhost = true;
                try {
                    if (logger.isDebugEnabled()) {
                        logger.info("No valid ip found from environment, try to find valid host from DNS.");
                    }
                    hostToBind = InetAddress.getLocalHost().getHostAddress();
                } catch (UnknownHostException e) {
                    logger.warn(e.getMessage(), e);
                }
                if (isInvalidLocalHost(hostToBind)) {
                    if (CollectionUtils.isNotEmpty(registryURLs)) {
                        for (URL registryURL : registryURLs) {
                            if (MULTICAST.equalsIgnoreCase(registryURL.getParameter("registry"))) {
                                // skip multicast registry since we cannot connect to it via Socket
                                continue;
                            }
                            try (Socket socket = new Socket()) {
                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                socket.connect(addr, 1000);
                                hostToBind = socket.getLocalAddress().getHostAddress();
                                break;
                            } catch (Exception e) {
                                logger.warn(e.getMessage(), e);
                            }
                        }
                    }
                    if (isInvalidLocalHost(hostToBind)) {
                        hostToBind = getLocalHost();
                    }
                }
            }
        }
        map.put(BIND_IP_KEY, hostToBind);
        // registry ip is not used for bind ip by default
        String hostToRegistry = getValueFromConfig(protocolConfig, DUBBO_IP_TO_REGISTRY);
        if (hostToRegistry != null && hostToRegistry.length() > 0 && isInvalidLocalHost(hostToRegistry)) {
            throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
        } else if (StringUtils.isEmpty(hostToRegistry)) {
            // bind ip is used as registry ip by default
            hostToRegistry = hostToBind;
        }

        map.put(ANYHOST_KEY, String.valueOf(anyhost));

        return hostToRegistry;
    }

可以从源码看出来像关于读取host值的优先级,通过注释可以得知它的加载顺序如下。

Configuration priority: environment variables -> java system properties -> host property in config file ->
     * /etc/hosts -> default network address -> first available network address

其中最主要还是依赖通过hostToBind = InetAddress.getLocalHost().getHostAddress();获取默认host。

查找host的顺序

缺省主机IP查找顺序:
  1. 通过 ​LocalHost.getLocalHost() ​获取本机地址。
  2. 如果是 ​127.* ​等 loopback 地址,则扫描各网卡,获取网卡 IP。
主机配置

注册的地址如果获取不正确,比如需要注册公网地址,可以在​ /etc/hosts​ 中加入:机器名 公网IP,比如:

www.xxx.com  205.182.23.114

在 ​dubbo.xml ​中加入主机地址的配置:

或在 ​dubbo.properties​ 中加入主机地址的配置:端口配置

dubbo.protocol.host=205.182.23.201

application.yml文件对应的配置信息

dubbo:
  protocol:
	host: 205.182.23.201
其返回值如下:
  1. 未联网时,返回 127.0.0.1
  2. 在阿里云服务器中,返回私有地址,如: 172.18.46.234
  3. 在本机测试时,返回公有地址,如: 30.5.10.11

那在dubbo中如何指定服务的socket?

除此之外,可以通过dubbo.protocol或dubbo.provider的host属性对host进行配置,支持IP地址和域名,如下:

xml配置方式

yaml配置方式

dubbo:
  protocol:
	name: dubbo
	port: 20890  	
	host: www.xxx.com

开发Dubbo的阔扩展暴漏主机地址前需要的问题

在使用docker时,有时需要设置端口映射,此时,启动 server 时绑定的 socket 和向注册中心注册的 socket 使用不同的端口号,此时又该如何设置?

dubbo通过环境变量设置host

有些部署场景需要动态指定服务注册的地址,如docker bridge网络模式下要指定注册宿主机 ip 以实现外网通信。dubbo提供了两对启动阶段的系统属性,用于设置对外通信的ip、port地址。

  • DUBBO_IP_TO_REGISTRY — 注册到注册中心的ip地址
  • DUBBO_PORT_TO_REGISTRY — 注册到注册中心的port端口
  • DUBBO_IP_TO_BIND — 监听ip地址
  • DUBBO_PORT_TO_BIND — 监听port端口

以上四个配置项均为可选项,如不配置 dubbo会自动获取ip与端口,请根据具体的部署场景灵活选择配置。 dubbo支持多协议,如果一个应用同时暴露多个不同协议服务,且需要为每个服务单独指定ip或port,请分别在以上属性前加协议前缀。 如:

HESSIAN协议的定制化绑定

  • HESSIAN_DUBBO_IP_TO_REGISTRY hessian协议注册的ip
  • HESSIAN_DUBBO_PORT_TO_BIND hessian协议绑定的port

Dubbo协议的定制化绑定

  • DUBBO_DUBBO_PORT_TO_BIND dubbo协议注册的ip
  • DUBBO_DUBBO_PORT_TO_BIND dubbo协议绑定的port

PORT_TO_REGISTRY及IP_TO_REGISTRY与PORT_TO_BIND及IP_TO_BIND的关系

  • PORT_TO_REGISTRY或IP_TO_REGISTRY不会用作默认 PORT_TO_BIND 或 IP_TO_BIND,但是反过来是成立的
    如设置 PORT_TO_REGISTRY=20881 IP_TO_REGISTRY=30.5.97.6,则 PORT_TO_BIND IP_TO_BIND 不受影响。

  • 如果设置 PORT_TO_BIND=20881 IP_TO_BIND=30.5.97.6,则默认 PORT_TO_REGISTRY=20881 IP_TO_REGISTRY=30.5.97.6。

此外对应的缺省主机端口与协议相关:
在这里插入图片描述

总结

可以通过dubbo.protocol或dubbo.provider的host属性对host进行配置,支持IP地址和域名。但此时注册到注册中心的IP地址和监听IP地址是同一个值。

为了解决在虚拟环境或局域网内consumer无法与provider通信的问题,可以通过环境变量分别设置注册到注册中心的IP地址和监听IP地址,其优先级高于dubbo.protocol或dubbo.provider的host配置

Docker的实战案例分析

Dockerfile文件

FROM openjdk:8-jdk-alpine
ADD target/dubbo-app.jar app.jar
ENV JAVA_OPTS=""
ENTRYPOINT exec java $JAVA_OPTS -jar /app.jar
docker build --no-cache -t app. 

Create and run containers from mirroring

启动Zookeeper注册中心服务

docker run --name zkserver --restart always -d zookeeper:3.4.9

采用Docker的方式加入DUBBO_IP_TO_REGISTRY以及DUBBO_PORT_TO_REGISTRY的环境变量

docker run -e DUBBO_IP_TO_REGISTRY=30.5.97.6 -e DUBBO_PORT_TO_REGISTRY=20881 -p 30.5.97.6:20881:20880 --link zkserver:zkserver -it --rm dubbo-app

通过环境变量DUBBO_IP_TO_REGISTRY=30.5.97.6 和 DUBBO_PORT_TO_REGISTRY=20881设置提供程序以注册注册中心的IP地址和端口通过-p 30.5.97.6:20881:20880实现端口映射,其中20880是DUBBO自动选择的侦听端口。

没有监控IP配置,因此它将侦听0.0.0.0(所有IP)。启动后,提供程序的注册地址为30.5.97.6:20881,容器的侦听地址为:0.0.0.0:20880。

配置对应对比图(修改之前)

在这里插入图片描述
我们修改了对应的参数
在这里插入图片描述

配置对应对比图(修改之后)

在这里插入图片描述