分类
代码

Dubbo 入门示例

Dubbo 是一个分布式服务架构的工具,而且与 Spring 很好的集成,就把他理解为 RMI(Remote Method Invoke 远程方法调用)的框架吧……

上学期的《软构建与中间件》课程中 EJB 相关内容就涉及到 RMI 的内容,因此还是可以接受这个概念的,客户端明明调用的是接口,那到底是谁实现的呢?这个就不用客户端来操心了,客户端只需要获取到接口实例,调用它的方法达到自己的目的就行了。

下面是根据官方文档写的 Dubbo 的一个 Hello World 示例。

项目结构

使用 Maven 作为构建工具,项目结构如下:

Dubbo\
|
|----pom.xml    # 使用 dependencyManagement 管理依赖
|
|----Dubbo-api\
|    |----pom.xml  # 依赖 dubbo logback-classic 
|    \----com.youthlin.demo.dubbo.api.service.HelloService # 接口
|
|----Dubbo-Consumer\
|    |----pom.xml  #依赖 Dubbo-api spring-mvc(我是为了省事不写一大堆spring-context/beans直接写的mvc)
|    |----com.youthlin.demo.dubbo.consumer.main.Consumer # 客户端
|    \----consumer.xml   # 客户端配置文件
|
\----Dubbo-Provider\
     |----pom.xml # 同客户端
     |----com.youthlin.demo.dubbo.provider\
     |    |----service.HelloServiceImpl   # 具体实现
     |    \----main.Provider   # 服务端
     \----provider.xml  # 服务端配置文件

我分为了三个模块,一个 api, 一个 provider, 一个 consumer. 其中 api 是公用的模块,服务端为它提供实现,客户端只依赖 api 编程。Provider 与 Consumer 一定要在不同的 Moddule 中,否则就不过经过 Dubbo 这个 RPC 框架而是调用的本地接口实现了。

服务接口

比如我们的 api 中有一个 String sayHello(String name) 的接口:

package com.youthlin.demo.dubbo.api.service;

/**
 * Created by lin on 2016-11-19-019.
 * Hello Service
 */
public interface HelloService {
    String sayHello(String name);
}

服务端

接口实现

然后在服务端是具体的实现:

package com.youthlin.demo.dubbo.provider.service;

import com.youthlin.demo.dubbo.api.service.HelloService;

/**
 * Created by lin on 2016-11-19-019.
 * Impl
 */
public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

服务端配置

服务端用配置文件暴露服务接口:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="hello-world-app"/>

    <!-- 使用multicast广播注册中心暴露服务地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234"/>

    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.youthlin.demo.dubbo.api.service.HelloService" ref="helloService"/>

    <!-- 和本地bean一样实现服务 -->
    <bean id="helloService" class="com.youthlin.demo.dubbo.provider.service.HelloServiceImpl"/>

</beans>

启动服务端

最后启动服务端,就可以使这个服务可用了:

package com.youthlin.demo.dubbo.provider.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

/**
 * Created by lin on 2016-11-19-019.
 * Main
 */
public class Provider {
    private static final Logger log = LoggerFactory.getLogger(Provider.class);

    public static void main(String[] args) throws IOException {
        log.debug("Provider starting...");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"provider.xml"});
        log.debug("context loaded.");
        context.start();
        log.debug("context started.");
        System.out.println("Please input any key to exit.");
        System.in.read(); // 按任意键退出
    }
}

客户端

客户端配置

客户端只需要使用相应的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
    <dubbo:application name="consumer-of-helloworld-app"/>

    <!-- 使用multicast广播注册中心暴露发现服务地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234"/>

    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <dubbo:reference id="helloService" interface="com.youthlin.demo.dubbo.api.service.HelloService"/>

</beans>

启动客户端

然后就可以获取到相应的接口实例,并调用需要的方法了:

package com.youthlin.demo.dubbo.consumer.main;

import com.youthlin.demo.dubbo.api.service.HelloService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by lin on 2016-11-19-019.
 * consumer
 */
public class Consumer {
    private static final Logger log = LoggerFactory.getLogger(Consumer.class);

    public static void main(String[] args) {
        log.debug("Consumer is starting...");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"consumer.xml"});
        log.debug("context is loaded.");
        context.start();
        log.debug("context started.");

        // Caused by: java.lang.IllegalStateException:
        // Failed to check the status of the service com.youthlin.demo.dubbo.api.service.HelloService.
        // No provider available for the service com.youthlin.demo.dubbo.api.service.HelloService
        // from the url multicast://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService
        // ?anyhost=true&application=consumer-of-helloworld-app&check=false&dubbo=2.5.3
        // &interface=com.youthlin.demo.dubbo.api.service.HelloService&methods=sayHello
        // &pid=1536&side=consumer&timestamp=1479564027023
        // to the consumer 192.168.191.1 use dubbo version 2.5.3

        // telnet 一次就好了?……
        // telnet 192.168.31.197 20800
        // 其中 地址是 provider log 中显示的地址

        HelloService helloService = (HelloService) context.getBean("helloService"); // 获取远程服务代理
        log.debug("Service loaded.");
        String hello = helloService.sayHello("world"); // 执行远程方法
        log.debug("invoke result = {}", hello);
        System.out.println(hello); // 显示调用结果
    }
}

执行结果:

Dubbo示例-运行
Dubbo示例-运行

可能遇到的问题及解决

No provider available for the service xxx

按照示例写法,但是启动服务端后一切正常,启动客户端却报错没有可用的服务提供者。
这个错误我刚刚启动客户端就遇到了,也不知道为啥,网上搜的有说 zookeeper 相关问题的,可是我们这里根本就还没引入 zookeeper 嘛,也有说配置出错的,端口不通之类的,还有一个说要关掉给手机开的热点才能用的。吓得我赶紧关掉 WiFi 共享,然而并没有什么用。最后试着用 telnet 登陆一下服务端 log 中写的那个地址(192.168.31.197:20800),也是可以登上去的,退出后再试一下,就好使了!!我断点跟踪了这么久也没找到为啥获取不到接口实例,telnet 一下就好了?黑人问号+一脸懵逼.jpg 嗯,反正我就是这么解决的。

然后看官网,发现 telnet 支持好多命令呢http://dubbo.io/User+Guide-zh.htm#UserGuide-zh-Telnet%E5%91%BD%E4%BB%A4%E5%8F%82%E8%80%83%E6%89%8B%E5%86%8C
顺便附上 telnet 的一个截图

dubbo-telnet
dubbo-telnet

Update:其实这里主要是因为我使用的是最简单的广播模式(multicast),但是网络配置中有多个(用过Hyper-V,有虚拟机的网络),因此导致找不到服务提供者。有空的话再记录一下使用 zookeeper 作为注册中心的用法或者 Dubbo 的异步、回调相关内容~
Update:https://github.com/apache/dubbo/issues/3561#issuecomment-498574806
multicast 注册时,No provider available 需要配置 unicast=false


        

or

        

or
Map parameters = application.getParameters();
if (parameters == null) {
    parameters = new HashMap<>(4);
    application.setParameters(parameters);
}
 parameters.put("unicast", "false");

日志不显示

项目用的 slf4j 日志接口,实现用的是 logback, 但是 Dubbo 里貌似是用的 log4j. 因此可能会有冲突,只需要引入 log4j-over-slf4j 就可以了。这个包会接管 log4j 的日志,并统一使用 slf4j 进行管理,这样项目中的 logback 配置文件就能对他起作用了。


“Dubbo 入门示例”上的4条回复

根本原因是源码中会判断发送给consumer的服务消息是采用单播还是组播,因为虚拟网卡对ip地址的影响导致判断逻辑出错,误认为consumer可以接收到单播,但其实consumer在收到单播消息之前provider自己就把这个消息给抢着处理了(因为provider和consumer在同一台机器上,相当于provider自发自收),导致consumer无法获取服务消息。此问题在linux系统上实验时又无法复现,因为linux和windows的接口对待是否回环是不同的,linux下的consumer可以在provider之先获取到单播消息。以下是源码判断逻辑

作者:树心图物
链接:https://www.jianshu.com/p/3646263199a1
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

大佬……!
这貌似都是负载均衡相关的了吧
PS.啥时有空麻烦再帮我改下友链?抱歉又换了个域名,这回不出意外不会变了……

还没有负载均衡啊,只是远程调用。
——————
又换域名…… 另外 你干嘛不在评论表单里写链接啊

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

[/鼓掌] [/难过] [/调皮] [/白眼] [/疑问] [/流泪] [/流汗] [/撇嘴] [/抠鼻] [/惊讶] [/微笑] [/得意] [/大兵] [/坏笑] [/呲牙] [/吓到] [/可爱] [/发怒] [/发呆] [/偷笑] [/亲亲]