一、RPC

RPC(Remote Procedure Call Protocol)远程过程调用协议;是一种进程间通信方式,该协议允许运行于一台计 算机的程序调用另一台计算机的程序

RPC主要功能是让构建分布式计算(应用)更容易

RPC基本原理 

rpcbind 状态如何查询_dubbo

1、客户端Client通过以本地调用的方式,调用远程接口服务;

2、客户端存根Client Stub接收到调用后,将调用信息对象进行序列化,组装成网络传输的二进制消息体;

3、客户端Client通过Sockets将消息发送到远程服务端;

4、服务端存根Server Stub收到消息后,对网络信息对象进行反序列化解码;

5、服务端存根Server Stub根据解码结果,调用服务端本地的接口服务;

6、本地接口服务执行,并将处理结果返回给服务端存根Server Stub;

7、服务端存根Server Stub将返回结果对象进行序列化,组装成消息体;

8、服务端Server再通过Sockets将消息发送到客户端;

9、客户端存根Client Stub收到结果消息后,对网络信息对象进行序列化解码;

10、客户端Client拿到最终接口处理结果

分布式系统

1、分布式系统是若干个独立计算机的集合,这些计算机对于用户来说就像单个相关系统

2、分布式系统是一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统

3、分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据

二、Dubbo简介

Dubbo 是一款高性能、轻量级的开源 Java RPC服务框架

Apache Dubbo |ˈdʌbəʊ| 提供了六大核心能力:

1、面向接口代理的高性能RPC调用

2、智能容错和负载均衡

3、服务自动注册和发现

4、高度可扩展能力

5、运行期流量调度

6、可视化的服务治理与运维

Dubbo官网 https://dubbo.apache.org/zh/

Dubbo主要核心部件

Remoting: 网络通信框架,实现了 sync-over-async 和 request-response 消息机制.

RPC: 一个远程过程调用的抽象,支持负载均衡容灾集群功能

Registry: 服务目录框架用于服务的注册和服务事件发布和订阅

Dubbo基本架构

rpcbind 状态如何查询_ide_02

服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务

服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用

注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者

监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

Dubbo支持多种协议:dubbo , hessian , rmi , http, webservice , thrift , memcached , redis

dubbo 官方推荐使用 dubbo 协议;dubbo 协议默认端口 20880

使用 dubbo 协议,spring 配置文件加入:

<dubbo:protocol name="dubbo" port="20880" />



三、Dubbo示例

(一)直连方式

点对点的直连项目:消费者直接访问服务提供者,没有注册中心。消费者必须指定服务提供者的访问地址(url)

消费者直接通过 url 地址访问固定的服务提供者

1、创建一个 maven web 服务提供者(userservice-provider)工程 ;创建数据模型和service接口

rpcbind 状态如何查询_spring_03

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.dubbo</groupId>
  <artifactId>dubbo-1-link-userservice-provider</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>

  <dependencies>
    <!--Spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>

    <!--dubbo依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.2</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <!--JDK1.8编译插件-->
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

User.java

注:

Dubbo传输的对象必须序列化(Serializable)

不然报错:cause: java.lang.IllegalStateException: Serialized class 

/**
 * 必须序列化;实现 Serializable 接口
 * 不然报如下错误
 * cause: java.lang.IllegalStateException: Serialized class com.mycompany.dubbo.model.User must implement java.io.Serializable
 */
public class User implements Serializable {
    private Integer id;

    private String username;

    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

UserService UserServiceImpl

public interface UserService {
    User queryUserById(Integer id);
}

public class UserServiceImpl implements UserService {
    @Override
    public User queryUserById(Integer id) {
        User user = new User();
        user.setId(id);
        user.setUsername("admin");
        user.setAge(18);

        return user;
    }
}

spring.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">

    <!-- 服务提供者声明名称:必须保证服务名称的唯一性(dubbo内部使用的唯一标识符)-->
    <dubbo:application name="dubbo-1-link-userservice-provider" />

    <!-- 访问服务协议的名称以及端口号,dubbo官方推荐使用dubbo协议,端口号默认20880
        name:指定协议名称
        port:指定协议的端口号(默认为20880)
     -->
    <dubbo:protocol name="dubbo" port="20880" />

    <!-- 表示不用注册中心 -->
    <dubbo:registry address="N/A"/>

    <!-- 暴露服务接口
        dubbo:service
        interface:暴露服务接口的全限定类名
        ref:接口引用的实现类在spring容器中的标识
        registry:如果不使用注册中心,值为:N/A
     -->
    <dubbo:service interface="com.mycompany.dubbo.service.UserService" ref="userService"/>
<!--                   registry="N/A"/>-->

    <!-- 将接口实现类加载到spring容器 -->
    <bean id="userService" class="com.mycompany.dubbo.service.impl.UserServiceImpl"/>

    <!-- 服务提供者全局超时时间配置优先级最低 -->
<!--    <dubbo:provider timeout="10000" />-->

</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 注册监听器 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:dubbo-userservice-provider.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

将其打成 jar 包

服务接口项目的类文件打包为 jar, 安装到 maven 仓库,仓库中的提供者 jar 可以被消费者使用。使用 idea 的 maven 窗口执行 install

我的本地maven仓库配置路径

rpcbind 状态如何查询_xml_04

这时去本机相应路径下查看jar包

rpcbind 状态如何查询_spring_05

2、创建一个 maven web 消费者(userservice-provider)工程 ;创建数据模型和service接口




rpcbind 状态如何查询_spring_06


pom.xml

<groupId>com.mycompany</groupId>
  <artifactId>dubbo-1-link-consumer</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>

  <dependencies>
    <!--Spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>

    <!--dubbo依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.2</version>
    </dependency>

    <!-- 依赖服务提供者
        先将服务提供者 dubbo-1-link-userservice-provider 打成一个 jar包,然后引入依赖
    -->
    <dependency>
      <groupId>com.mycompany.dubbo</groupId>
      <artifactId>dubbo-1-link-userservice-provider</artifactId>
      <version>1.0.0</version>
    </dependency>

  </dependencies>

UserController.java

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/user")
    public String userDetail(Model model, Integer id){
        User user = userService.queryUserById(id);
        model.addAttribute("user",user);
        return "userDetail";
    }
}

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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 扫描组件 -->
    <context:component-scan base-package="com.mycompany.controller" />

    <!-- 配置注解驱动 -->
    <mvc:annotation-driven />

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>


dubbo-consumer.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://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="dubbo-1-link-consumer" />

    <!-- 表示不用注册中心 -->
    <dubbo:registry address="N/A"/>

    <!-- 引用远程服务接口 dubbo:reference
        id:远程服务接口对象名称
        interface:调用远程接口的全限定类名
        url:访问远程服务接口的url地址
        registry:不使用注册中心,值为N/A
    -->
    <dubbo:reference id="userService"
                     interface="com.mycompany.dubbo.service.UserService"
                     url="dubbo://localhost:20880"/>
<!--                     registry="N/A" />-->

    <!--
        provider:
          系统向外提供的 facade 请求超时时间,默认1000 ms
          provider 接受到请求时,会把整个处理逻辑执行完,不管你是否设置了时间;dubbo 只会在方法执行完,判断是否超时,如果超时,记一个 warn 日志
          <dubbo:provider timeout="" >

        consumer:
          调用外部系统接口的超时时间,默认1000 ms
          请求发出后,线程处于阻塞状态,线程的唤醒条件是超时和收到 provider 返回
          <dubbo:consumer timeout="" >

        provider 和 consumer 都设置了超时时间,Dubbo 会默认优先使用 consumer 的配置
    -->
    <dubbo:consumer timeout="10000" />
</beans>

 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

  <!-- 中央调度器 -->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:application.xml,classpath:dubbo-consumer.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

 .jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户详情</title>
</head>
<body>
<h1>用户详情</h1>
<div>用户标识:${user.id}</div>
<div>用户名称:${user.username}</div>
</body>
</html>

此时我们配置Tomcat启动 服务提供者

rpcbind 状态如何查询_spring_07

再启动消费者

rpcbind 状态如何查询_xml_08

这时浏览器打开访问

rpcbind 状态如何查询_spring_09

Dubbo的配置请参考

配置管理 | Apache Dubbo

本文主要使用的xml标签配置

rpcbind 状态如何查询_ide_10

(二)服务接口尽可能大粒度

建议将服务接口、服务模型、服务异常等均放在公共包中。 服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤

1、我们创建一个maven Java 接口模块

rpcbind 状态如何查询_ide_11

将其打成 jar 包

服务接口项目的类文件打包为 jar, 安装到 maven 仓库,仓库中的提供者 jar 可以被消费者使用。使用 idea 的 maven 窗口执行 install

2、创建 maven web模块 服务提供者

rpcbind 状态如何查询_dubbo_12

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.dubbo</groupId>
  <artifactId>dubbo-2-link-userservice-provider</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>

  <dependencies>
    <!--Spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>

    <!--dubbo依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.2</version>
    </dependency>

    <!--接口工程依赖-->
    <dependency>
      <groupId>com.mycompany.dubbo</groupId>
      <artifactId>dubbo-2-link-interface</artifactId>
      <version>1.0.0</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <!--JDK1.8编译插件-->
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

dubbo-userservice-provider.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">

    <!-- 服务提供者声明名称:必须保证服务名称的唯一性(dubbo内部使用的唯一标识符)-->
    <dubbo:application name="dubbo-2-link-userservice-provider" />

    <!-- 访问服务协议的名称以及端口号,dubbo官方推荐使用dubbo协议,端口号默认20880
        name:指定协议名称
        port:指定协议的端口号(默认为20880)
     -->
    <dubbo:protocol name="dubbo" port="20880" />

    <!-- 表示不用注册中心 -->
    <dubbo:registry address="N/A"/>

    <!-- 暴露服务接口
        dubbo:service
        interface:暴露服务接口的全限定类名
        ref:接口引用的实现类在spring容器中的标识
        registry:如果不使用注册中心,值为:N/A
     -->
    <dubbo:service interface="com.mycompany.dubbo.service.UserService" ref="userService"/>
<!--                   registry="N/A"/>-->

    <!-- 将接口实现类加载到spring容器 -->
    <bean id="userService" class="com.mycompany.dubbo.service.impl.UserServiceImpl"/>

    <!-- 服务提供者全局超时时间配置优先级最低 -->
<!--    <dubbo:provider timeout="10000" />-->

</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd">

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:dubbo-userservice-provider.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

</web-app>

3、创建maven web 消费者模块

rpcbind 状态如何查询_spring_13

 pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.dubbo</groupId>
  <artifactId>dubbo-2-link-consumer</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>

  <dependencies>
    <!--Spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>

    <!--dubbo依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.2</version>
    </dependency>

    <!--接口工程依赖-->
    <dependency>
      <groupId>com.mycompany.dubbo</groupId>
      <artifactId>dubbo-2-link-interface</artifactId>
      <version>1.0.0</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <!--JDK1.8编译插件-->
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

applicationContext.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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--扫描组件-->
    <context:component-scan base-package="com.mycompany.dubbo.controller"/>

    <!--配置注解驱动-->
    <mvc:annotation-driven/>

    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

dubbo-consumer.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">

    <!-- 生命服务消费者的名称:保证唯一性 -->
    <dubbo:application name="dubbo-2-link-consumer" />

    <!-- 表示不用注册中心 -->
    <dubbo:registry address="N/A"/>

    <!-- 引用远程服务接口 dubbo:reference
        id:远程服务接口对象名称
        interface:调用远程接口的全限定类名
        url:访问远程服务接口的url地址
        registry:不使用注册中心,值为N/A
    -->
    <dubbo:reference id="userService"
                     interface="com.mycompany.dubbo.service.UserService"
                     url="dubbo://localhost:20880"/>
    <!--                     registry="N/A" />-->
</beans>

web.xml

<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:applicationContext.xml,classpath:dubbo-consumer.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>