Dubbo服务暴露之注册地址和端口

 
 
 
 

今天在SIT环境部署dubbo容器的时候遇到一个问题, 明明我的dubbo容器已经注册到zk了, 而且在dubbo控制台也能看到提供者和消费者的信息, 可就是死活调用不了, 仔细一看, 发现提供者的地址不对啊, 这明明不是我的dubbo容器对应的那天服务器地址.这是为什么呢?下面我们来分析一波dubbo服务暴露注册地址和端口那一段代码.

由于我公司是做海外业务, 服务器的部署都在国外, 由于一些政策原因, 我们是访问不了的, 这时候就需要服务器端口地址进行转发, 我们的做法是在一台国内机器上面再用一层nginx做中间转发, 那么这时候问题就来了, 如果我们部署的项目地址是本机的话, 服务之间是调用不通的, 首先说明项目的服务器也是通过跳板机的形式登陆的. 我们回到文章刚开始那个问题, 其实是没错的, 确实得注册到国内作跳板机服务器的地址上, 原因是因为提供转发的那台机器没有做对我的项目地址进行nginx映射. 

我们直接就定位到dubbo服务暴露url组装的方法com.alibaba.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol这个方法源码太多了, 其实查找Host和Port就两行调用代码:

 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {     // ...     // 查找host     String host = this.findConfigedHosts(protocolConfig, registryURLs, map);     // 查找port     Integer port = this.findConfigedPorts(protocolConfig, name, map);     // ... }
     // ...
     // 查找host
     String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
     // 查找port
     Integer port = this.findConfigedPorts(protocolConfig, name, map);
     // ...
 }

继续往下撸:

private String findConfigedHosts(ProtocolConfig protocolConfig, List<URL> registryURLs, Map<String, String> map) {    boolean anyhost = false;    String hostToBind = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_BIND);    if (hostToBind != null && hostToBind.length() > 0 && isInvalidLocalHost(hostToBind)) {        throw new IllegalArgumentException("Specified invalid bind ip from property:" + Constants.DUBBO_IP_TO_BIND + ", value:" + hostToBind);    }    // if bind ip is not found in environment, keep looking up    if (hostToBind == null || hostToBind.length() == 0) {        hostToBind = protocolConfig.getHost();        if (provider != null && (hostToBind == null || hostToBind.length() == 0)) {            hostToBind = provider.getHost();        }        if (isInvalidLocalHost(hostToBind)) {            anyhost = true;            try {                hostToBind = InetAddress.getLocalHost().getHostAddress();            } catch (UnknownHostException e) {                logger.warn(e.getMessage(), e);            }            if (isInvalidLocalHost(hostToBind)) {                if (registryURLs != null && !registryURLs.isEmpty()) {                    for (URL registryURL : registryURLs) {                        if (Constants.MULTICAST.equalsIgnoreCase(registryURL.getParameter("registry"))) {                            // skip multicast registry since we cannot connect to it via Socket                            continue;                        }                        try {                            Socket socket = new Socket();                            try {                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());                                socket.connect(addr, 1000);                                hostToBind = socket.getLocalAddress().getHostAddress();                                break;                            } finally {                                try {                                    socket.close();                                } catch (Throwable e) {                                }                            }                        } catch (Exception e) {                            logger.warn(e.getMessage(), e);                        }                    }                }                if (isInvalidLocalHost(hostToBind)) {                    hostToBind = getLocalHost();                }            }        }    }    map.put(Constants.BIND_IP_KEY, hostToBind);    // registry ip is not used for bind ip by default    String hostToRegistry = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_REGISTRY);    if (hostToRegistry != null && hostToRegistry.length() > 0 && isInvalidLocalHost(hostToRegistry)) {        throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);    } else if (hostToRegistry == null || hostToRegistry.length() == 0) {        // bind ip is used as registry ip by default        hostToRegistry = hostToBind;    }    map.put(Constants.ANYHOST_KEY, String.valueOf(anyhost));    return hostToRegistry;}
    boolean anyhost = false;

    String hostToBind = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_BIND);
    if (hostToBind != null && hostToBind.length() > 0 && isInvalidLocalHost(hostToBind)) {
        throw new IllegalArgumentException("Specified invalid bind ip from property:" + Constants.DUBBO_IP_TO_BIND + ", value:" + hostToBind);
    }

    // if bind ip is not found in environment, keep looking up
    if (hostToBind == null || hostToBind.length() == 0) {
        hostToBind = protocolConfig.getHost();
        if (provider != null && (hostToBind == null || hostToBind.length() == 0)) {
            hostToBind = provider.getHost();
        }
        if (isInvalidLocalHost(hostToBind)) {
            anyhost = true;
            try {
                hostToBind = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                logger.warn(e.getMessage(), e);
            }
            if (isInvalidLocalHost(hostToBind)) {
                if (registryURLs != null && !registryURLs.isEmpty()) {
                    for (URL registryURL : registryURLs) {
                        if (Constants.MULTICAST.equalsIgnoreCase(registryURL.getParameter("registry"))) {
                            // skip multicast registry since we cannot connect to it via Socket
                            continue;
                        }
                        try {
                            Socket socket = new Socket();
                            try {
                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                socket.connect(addr, 1000);
                                hostToBind = socket.getLocalAddress().getHostAddress();
                                break;
                            } finally {
                                try {
                                    socket.close();
                                } catch (Throwable e) {
                                }
                            }
                        } catch (Exception e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
                }
                if (isInvalidLocalHost(hostToBind)) {
                    hostToBind = getLocalHost();
                }
            }
        }
    }

    map.put(Constants.BIND_IP_KEY, hostToBind);

    // registry ip is not used for bind ip by default
    String hostToRegistry = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_REGISTRY);
    if (hostToRegistry != null && hostToRegistry.length() > 0 && isInvalidLocalHost(hostToRegistry)) {
        throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
    } else if (hostToRegistry == null || hostToRegistry.length() == 0) {
        // bind ip is used as registry ip by default
        hostToRegistry = hostToBind;
    }

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

    return hostToRegistry;
}

这个方法是获取地址的方法, 从源码我们也看到了这个地址分为两个, 分别是hostToBind和hostToRegistry, 这里很巧妙, 从名称看这是绑定地址和注册地址, 也就是绑定地址用于本地netty监听, 而注册地址是注册到zk上面提供给消费者调用的, 一般来说, 如果没有特殊要求, dubbo是拿本地服务器网卡0的内网ip作为地址, 绑定地址和注册地址都一样, 到是我们公司就遇到了这么个坑爹问题, 服务器在国外,如果绑定地址和监听地址都一样, 那么服务之间就无法通讯了,我们必须在注册地址上面做点手脚才行, 因此我继续往下看.

private String getValueFromConfig(ProtocolConfig protocolConfig, String key) {    String protocolPrefix = protocolConfig.getName().toUpperCase() + "_";    String port = ConfigUtils.getSystemProperty(protocolPrefix + key);    if (port == null || port.length() == 0) {        port = ConfigUtils.getSystemProperty(key);    }    return port;}
    String protocolPrefix = protocolConfig.getName().toUpperCase() + "_";
    String port = ConfigUtils.getSystemProperty(protocolPrefix + key);
    if (port == null || port.length() == 0) {
        port = ConfigUtils.getSystemProperty(key);
    }
    return port;
}

该方法的作用是从系统环境变量里面获取地址信息:

/** * System environment -> System properties * * @param key key * @return value */public static String getSystemProperty(String key) {  String value = System.getenv(key);  if (value == null || value.length() == 0) {    value = System.getProperty(key);  }  return value;}
public static String getSystemProperty(String key) {
  String value = System.getenv(key);
  if (value == null || value.length() == 0) {
    value = System.getProperty(key);
  }
  return value;
}

这里System.getenv(key);方法作用是获取环境变量key中的地址值,如果环境变量没有配置对应key的值,那么就会调用System.getProperty(key);方法自动获取本地网卡的内网地址。

public class Constants {  public static final String DUBBO_IP_TO_REGISTRY = "DUBBO_IP_TO_REGISTRY";  public static final String DUBBO_PORT_TO_REGISTRY = "DUBBO_PORT_TO_REGISTRY";  public static final String DUBBO_IP_TO_BIND = "DUBBO_IP_TO_BIND";  public static final String DUBBO_PORT_TO_BIND = "DUBBO_PORT_TO_BIND";}class Constants {
  public static final String DUBBO_IP_TO_REGISTRY = "DUBBO_IP_TO_REGISTRY";
  public static final String DUBBO_PORT_TO_REGISTRY = "DUBBO_PORT_TO_REGISTRY";
  public static final String DUBBO_IP_TO_BIND = "DUBBO_IP_TO_BIND";
  public static final String DUBBO_PORT_TO_BIND = "DUBBO_PORT_TO_BIND";
}

以上就是我们地址和端口的绑定与注册的key值,如果我们想要将指定的地址和端口注册到zk,我们可以在本地配置环境变量:

export DUBBO_DUBBO_IP_TO_REGISTRY=指定ipexport DUBBO_PORT_TO_REGISTRY=指定端口
export DUBBO_PORT_TO_REGISTRY=指定端口

我将本地服务器这两个环境变量配置成跳板机的ip和端口,下面我还要将跳板机配置nginx映射。

upstream service-172.17.10.121-20040 {    server 172.17.10.121:20040 weight=1 max_fails=2 fail_timeout=30s;}server {    listen 20040;    access_log /log/nginx/access_service-172.17.10.121-20040.log str_basic;    error_log /log/nginx/error_service-172.17.10.121-20040.log;    proxy_connect_timeout 2s;    proxy_pass service-172.17.10.121-20040;}172.17.10.121-20040 {
    server 172.17.10.121:20040 weight=1 max_fails=2 fail_timeout=30s;
}

server {
    listen 20040;
    access_log /log/nginx/access_service-172.17.10.121-20040.log str_basic;
    error_log /log/nginx/error_service-172.17.10.121-20040.log;
    proxy_connect_timeout 2s;
    proxy_pass service-172.17.10.121-20040;
}

由于我的服务注册到zk的地址是跳板机的地址,那么其他消费者调用我提供的服务,就会访问跳板机,然后跳板机再做一层转发,就完美解决了这个问题.



长按可以订阅

640?wx_fmt=jpeg

http://objcoding.com

Dubbo是一款分布式服务框架,可以帮助我们快速构建高性能、高可用性的分布式服务应用。要将服务暴露出去,需要进行以下步骤: 1. 定义服务接口:定义服务接口,包括方法名、参数列表和返回值等。 2. 实现服务接口:实现服务接口,编写业务逻辑。 3. 配置服务:在Dubbo配置文件中配置服务提供者的相关信息,包括服务接口、实现类、协议、端口等。 4. 启动服务:在应用启动时启动Dubbo服务,将服务暴露出去。 以下是一个示例: 1. 定义服务接口: ```java public interface UserService { String sayHello(String name); } ``` 2. 实现服务接口: ```java public class UserServiceImpl implements UserService { public String sayHello(String name) { return "Hello, " + name; } } ``` 3. 配置服务: 在Dubbo配置文件中添加以下配置: ```xml <!-- 服务提供者配置 --> <dubbo:service interface="com.example.UserService" ref="userService" /> <!-- 注册中心配置 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 协议配置 --> <dubbo:protocol name="dubbo" port="20880" /> ``` 其中,`interface`指定服务接口,`ref`指定服务实现类,`registry`指定注册中心的地址,`protocol`指定协议和端口。 4. 启动服务: 在应用启动时通过以下代码启动Dubbo服务: ```java public class Application { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo.xml"); context.start(); System.in.read(); } } ``` 其中,`dubbo.xml`为Dubbo配置文件的名称。启动后,服务提供者的服务就会被注册注册中心并暴露出去,供消费者调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值