【Dubbo】Dubbo 实现服务远程通信(无注册中心)

上一篇我们介绍了 Dubbo 有什么用 – 分布式背景下的服务间远程通信以及服务治理。那么,本篇我们就来看看如何使用 Dubbo 框架实现服务间通信。

Dubbo 是基于 rpc 协议的服务调用,且一般以 tcp 协议实现,因此在实际开发中只用写完 service 层就能发布服务;若以 http 为基础暴露的服务,则需要写完 controller 层。下面来看一个订单服务调用支付服务的简单示例…

PS:Dubbo 能进行远程调用的实质封装了 netty,提供方相当于 netty 服务端,而消费方相当于 netty 的客户端。

1.Provider:支付服务

首先就是服务提供方 Provider,负责提供(发布)支付服务。

在这里插入图片描述
这里我们创建的是一个聚合工程,其中 pay-api 是要提供的服务,也可以说是对外暴露的接口,即服务。这里把它单独抽象成一个模块也很好理解,因为 Provider 和 Consumer 需要做的事情都是面向接口编程,提供方和调用方都要依赖。

  • Provider 实现接口,发布服务(可以有多个 Consumer 去消费)
  • Consumer 使用接口,通过 url 远程调用服务

在这里插入图片描述

然后,将 pay-api 服务 install 到本地,以便给提供方和调用方依赖。

注:聚合工中,父项目(pay-service)也要 install ,其子模块(pay-api)才能被外调。


下面就到真正需要用到 Dubbo 的核心模块了,pay-provider:

在这里插入图片描述
1)引入依赖

<dependencies>
    <!-- 服务接口信息-->
    <dependency>
        <groupId>com.xupt.yzh</groupId>
        <artifactId>pay-api</artifactId>
    </dependency>
	
	<!-- 通过 Dubbo 实现服务远程通信-->
	<!-- 对于 Provider 而言,通过 Dubbo 实现服务发布 -->
    <!-- 注:里面还包含了Spring相关jar包 -->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo</artifactId>
        <version>2.7.2</version>
    </dependency>
    
</dependencies>

注:引入的 dubbo 依赖还包含了 Spring 相关jar包,所以使用 Dubbo 默认都是基于 Spring 的

在这里插入图片描述

2)实现上面的服务(接口)

/**
*服务提供方
*/
public class PayServiceImpl implements IPayService{
    @Override
    public String pay(String info) {
        System.out.println("execute pay: " + info);
        return "Hello Dubbo : " + info;
    }
}

3)创建配置文件发布服务

注:这里配置文件一定要是 META-INF/spring.application.xml(约定)

<!-- 服务发布 -->
<?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://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd 
                           http://dubbo.apache.org/schema/dubbo 
                           http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <!-- xmlns用于声明当前spring的xml的命名空间 -->
    <!-- 注:不同类型的xml有不同的文件头-->


    <!-- dubbo:application 提供方应用信息,是服务跟踪监控依据 -->
    <!-- 该标签下提供了name owner organization等-->
    <dubbo:application name="pay-service"/>

    <!-- dubbo:registry 注册中心的相关信息-->
    <!-- 注:若不配置注册中心直接调用address=N/A-->
    <!--     若用注册中心则要引入其相应客户端jar包(如curator),并且url前要加上zookeeper://-->
    <!-- 这里我们先不使用注册中心 -->
    <dubbo:registry address="N/A"/>

    <!-- dubbo:protocol 以什么协议发布服务-->
    <!-- name具体协议(一般采用dubbo协议),port服务发布端口-->
    <!-- 注:只有要发布的服务才需要定义协议,即 Consumer 不需要该配置-->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- dubbo:service 服务相关信息 -->
    <!-- interface是服务名(接口),ref是具体的服务Bean-->
    <!-- 注:只要服务发布方,才需要定义服务名与服务Bean-->
    <dubbo:service interface="com.xupt.yzh.IPayService" ref="payServic"/>
    <bean id="payServic" class="com.xupt.yzh.PayServiceImpl"/>

</beans>

4)启动服务

/**
*启动入口
*/
public class App {
    
    public static void main(String[] args) throws IOException {
        // 加载配置文件,即将相应服务 Bean实例化
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext(
            		new String[]{"META-INF.spring/application.xml"});
        classPathXmlApplicationContext.start();
        
        System.in.read(); // 主线程等待,持续服务
    }
}

=> 启动后会返回对应服务的url,会将所有配置信息都包含进去:

在这里插入图片描述

dubbo://192.168.56.1:20880/com.xupt.yzh.IPayService?anyhost=true  // 该服务的url,调用方就通过这个URL去获取服务

url 后面 & 拼接的是一些配置信息(因为 dubbo 是url驱动):

&application=payservice
&bean.name=com.xupt.yzh.IPayService
&bind.ip=192.168.56.1
&bind.port=20880&deprecated=false
&dubbo=2.0.2
&dynamic=true
&generic=false
&interface=com.xupt.yzh.IPayService
&methods=pay
&pid=17924
&register=true
&release=2.7.2
&side=provider
&timestamp=1596016532558, dubbo version: 2.7.2, current host: 192.168.56.1

PS:所谓 url 驱动,简单来说就是服务的具体配置信息都在 URL 中,通过&拼接;当 Consumer 要远程调用服务时,可以根据实际情况在 URL 后面拼接(修改)配置参数;当请求到达 Provider 时,会根据 URL 中的配置参数提供服务。-- 原理是 SPI 自适应扩展点

2.Consumer:订单服务

服务调用方是订单服务,调用支付服务提供结算支持,结构如下:

在这里插入图片描述

1) 引入依赖

<dependencies>
	<!-- 引入服务接口 -->
    <dependency>
        <groupId>com.xupt.yzh</groupId>
        <artifactId>pay-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
	
	<!-- 通过 Dubbo 实现服务远程通信-->
	<!-- 对于 Consumer 而言,通过 Dubbo 实现服务消费 -->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo</artifactId>
        <version>2.7.2</version>
    </dependency>

</dependencies>

2)创建配置文件引入服务

<?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://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd 
                           http://dubbo.apache.org/schema/dubbo 
                           http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <dubbo:application name="order-service"/>

    <dubbo:registry address="N/A"/>

    <!-- dubbo:reference被引入的服务-->
    <!-- interface是服务名,url是服务路径,id是对找到的服务Bean重新赋予的名-->
    <!-- 注:当有注册中心时,就不需要url了-->
    <dubbo:reference interface="com.xupt.yzh.IPayService" id="PayService"
        url="dubbo://192.168.56.1:20880/com.xupt.yzh.IPayService"/>

</beans>

3)调用 Provider 提供的服务

/**
*服务调用方
*/
public class App {

    public static void main(String[] args) {
        // 加载配置文件,即将相应服务 Bean实例化
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext(
            		new String[]{"META-INF.spring/application.xml"});
        // 拿到服务Bean
        IPayService payService = (IPayService)classPathXmlApplicationContext.getBean("PayService");
        String res = payService.pay("Test");
        System.out.println(res);

    }
}

在这里插入图片描述
同时 Provider 也收了调用信息:

在这里插入图片描述

3.DubboMain启动

Dubbo 也可以直接通过 Main.main(args) 启动服务,比如我们通过该方式启动 Provider

public class App {

    public static void main(String[] args) {
        Main.main(args); // org.springframework.context.support.ClassPathXmlApplicationContext
    }
}

结果如下:
在这里插入图片描述
1.三种容器

正常情况下,我们会认为服务的发布,需要 tomcat、或者 jetty 这类的容器支持,但是只用 Dubbo 以后,我们并不需要这样重的服务器去支持,同时也会增加复杂性和浪费资源。Dubbo 提供了几种容器让我们去启动和发布服务

  • Spring Container:自动加载 META-INF/spring 目录下的所有 Spring 配置。
  • logback Container:自动装配 logback 日志
  • Log4j Container:自动配置 log4j 的配置

Dubbo 提供了一个 Main.main 快速启动相应的容器,默认情况下,只会启动 spring 容器。

2.原理分析

启动 spring 容器本质上就是加载 IOC 容器,然后启动 一个 netty 服务实现服务的发布,所以并没有特别多的黑科技,下面是 spring容器启动的代码:

// 配置文件默认路径,写死
// 所以,前面我才特别强调配置文件要放在 resource/META-INF/ 目录下
public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml";

public void start() {
    String configPath = ConfigUtils.getProperty(SPRING_CONFIG);
    if (StringUtils.isEmpty(configPath)) {
        configPath = DEFAULT_SPRING_CONFIG;
    }
    // 加载Spring容器
    context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"), false);
    context.refresh();
    context.start();
}

本篇相关代码我放到 GitHub 上了,有需要参考的同学点击这里跳转…

已标记关键词 清除标记
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页