分享

利用Docker实现Dubbo跨主机通讯

fc013 2018-7-26 13:43:33 发表于 原理型 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 2 12694
问题导读:

1.怎样利用Docker实现Dubbo跨主机通讯?
2.怎样解决Not found exported service问题?
3.怎样获取Dubbo暴露端口?





最近在对现有业务系统进行Dubbo服务化重构,部署方式采用Docker部署,在部署过程中发现Dubbo服务注册的一些问题(因为现在团队中缺少容器化的大神支撑),在这里和大家进行分享;

Dubbo在Docker中部署时往注册中心注册的是Docker实例的IP地址,通常为:172.18.0.*。这种IP外网是服务访问的。


针对这种方式,在Google上查了很多解决方案,本人觉得比较合理的解决方案


[mw_shl_code=text,true]version: '3'
services:
  server1:
    image: uc-server:1.0
    restart: always
    ports:
      - "8081:8080"
      # 前面为注册到注册中心的端口,后面为docker监听的端口
      - "20881:20881"
    environment:
      # 注册到注册中心的IP,这里我们选择宿主机的IP
      DUBBO_IP_TO_REGISTRY: 116.62.139.15
      # 注册到注册中心的端口
      DUBBO_PORT_TO_REGISTRY: 20881
  server2:
    image: uc-server:1.0
    restart: always
    ports:
      - "8082:8080"
      # 前面为注册到注册中心的端口,后面为docker监听的端口
      - "20882:20882"
    environment:
      # 注册到注册中心的IP,这里我们选择宿主机的IP
      DUBBO_IP_TO_REGISTRY: 116.62.139.15
      # 注册到注册中心的端口
      DUBBO_PORT_TO_REGISTRY: 20882
  server3:
    image: uc-server:1.0
    restart: always
    ports:
      - "8083:8080"
      # 前面为注册到注册中心的端口,后面为docker监听的端口
      - "20883:20883"
    environment:
      # 注册到注册中心的IP,这里我们选择宿主机的IP
      DUBBO_IP_TO_REGISTRY: 116.62.139.15
      # 注册到注册中心的端口
      DUBBO_PORT_TO_REGISTRY: 20883[/mw_shl_code]


使用docker-compose进行编排,最主要的两个参数:DUBBO_IP_TO_REGISTRY,DUBBO_PORT_TO_REGISTRY,用于指定注册到注册中心的IP和端口。这样就解决了注册IP的问题。但是在后面测试过程中发现可以通讯,但是一直报错:


[mw_shl_code=text,true]com.alibaba.dubbo.remoting.RemotingException: Not found exported service:[/mw_shl_code]


通过报错信息查看到Dubbo源码:


[mw_shl_code=java,true]boolean isCallBackServiceInvoke = false;
        boolean isStubServiceInvoke = false;
        int port = channel.getLocalAddress().getPort();
        String path = inv.getAttachments().get(Constants.PATH_KEY);
        // if it's callback service on client side
        isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(Constants.STUB_EVENT_KEY));
        if (isStubServiceInvoke) {
            port = channel.getRemoteAddress().getPort();
        }
        //callback
        isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke;
        if (isCallBackServiceInvoke) {
            path = inv.getAttachments().get(Constants.PATH_KEY) + "." + inv.getAttachments().get(Constants.CALLBACK_SERVICE_KEY);
            inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString());
        }
        String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY));

        DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);

        if (exporter == null)
            throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv);

        return exporter.getInvoker();[/mw_shl_code]


问题就在获取端口的时候产生的,例如:消费者消费的端口是宿主机对外暴露的20881端口,但是在Docker实例中Dubbo使用的是20880端口,两个端口不一致就造成服务没有暴露的问题。解决这个问题的思路就在与指定Dubbo服务启动时候的暴露端口,但是考虑到扩展性,端口配置不能配置到应用的配置文件中,需要在启动Docker的时候指定暴露的端口,查看Dubbo源码:


[mw_shl_code=java,true]com.alibaba.dubbo.config.ServiceConfig.doExportUrlsFor1Protocol[/mw_shl_code]

[mw_shl_code=java,true]String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
        Integer port = this.findConfigedPorts(protocolConfig, name, map);
        URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);[/mw_shl_code]


在源码中可以看到获取暴露端口的方法:


[mw_shl_code=java,true]/**
     * Register port and bind port for the provider, can be configured separately
     * Configuration priority: environment variable -> java system properties -> port property in protocol config file
     * -> protocol default port
     *
     * @param protocolConfig
     * @param name
     * @return
     */
    private Integer findConfigedPorts(ProtocolConfig protocolConfig, String name, Map<String, String> map) {
        Integer portToBind = null;

        // parse bind port from environment
        String port = getValueFromConfig(protocolConfig, Constants.DUBBO_PORT_TO_BIND);
        portToBind = parsePort(port);

        // if there's no bind port found from environment, keep looking up.
        if (portToBind == null) {
            portToBind = protocolConfig.getPort();
            if (provider != null && (portToBind == null || portToBind == 0)) {
                portToBind = provider.getPort();
            }
            final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();
            if (portToBind == null || portToBind == 0) {
                portToBind = defaultPort;
            }
            if (portToBind == null || portToBind <= 0) {
                portToBind = getRandomPort(name);
                if (portToBind == null || portToBind < 0) {
                    portToBind = getAvailablePort(defaultPort);
                    putRandomPort(name, portToBind);
                }
                logger.warn("Use random available port(" + portToBind + ") for protocol " + name);
            }
        }

        // save bind port, used as url's key later
        map.put(Constants.BIND_PORT_KEY, String.valueOf(portToBind));

        // registry port, not used as bind port by default
        String portToRegistryStr = getValueFromConfig(protocolConfig, Constants.DUBBO_PORT_TO_REGISTRY);
        Integer portToRegistry = parsePort(portToRegistryStr);
        if (portToRegistry == null) {
            portToRegistry = portToBind;
        }

        return portToRegistry;
    }[/mw_shl_code]


整个方法最核心的代码:


[mw_shl_code=java,true]String port = getValueFromConfig(protocolConfig, Constants.DUBBO_PORT_TO_BIND);[/mw_shl_code]


通过读取系统环境变量:DUBBO_PORT_TO_BIND获取暴露端口。找到了配置,只需要修改下docker-compose.yml就可以指定暴露的端口,所以最终的docker-compose.yml文件是:


[mw_shl_code=text,true]version: '3'
services:
  server1:
    image: user-server:1.0
    restart: always
    ports:
      - "8081:8080"
      # 前面为注册到注册中心的端口,后面为docker监听的端口
      - "20881:20881"
    environment:
      # 注册到注册中心的IP,这里我们选择宿主机的IP
      DUBBO_IP_TO_REGISTRY: 116.62.139.15
      # 注册到注册中心的端口
      DUBBO_PORT_TO_REGISTRY: 20881
      DUBBO_PORT_TO_BIND: 20881
  server2:
    image: user-server:1.0
    restart: always
    ports:
      - "8082:8080"
      # 前面为注册到注册中心的端口,后面为docker监听的端口
      - "20882:20882"
    environment:
      # 注册到注册中心的IP,这里我们选择宿主机的IP
      DUBBO_IP_TO_REGISTRY: 116.62.139.15
      # 注册到注册中心的端口
      DUBBO_PORT_TO_REGISTRY: 20882
      DUBBO_PORT_TO_BIND: 20882
  server3:
    image: user-server:1.0
    restart: always
    ports:
      - "8083:8080"
      # 前面为注册到注册中心的端口,后面为docker监听的端口
      - "20883:20883"
    environment:
      # 注册到注册中心的IP,这里我们选择宿主机的IP
      DUBBO_IP_TO_REGISTRY: 116.62.139.15
      # 注册到注册中心的端口
      DUBBO_PORT_TO_REGISTRY: 20883
      DUBBO_PORT_TO_BIND: 20883[/mw_shl_code]



来源: csdn
作者: 我的执着
原文链接:Docker部署Dubbo跨主机IP访问解决方案


已有(2)人评论

跳转到指定楼层
jiangzi 发表于 2018-8-1 13:20:39
很实际, 正要搞 为服务话
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条