一、RPC
RPC(Remote Procedure Call Protocol)远程过程调用协议;是一种进程间通信方式,该协议允许运行于一台计 算机的程序调用另一台计算机的程序
RPC主要功能是让构建分布式计算(应用)更容易
RPC基本原理
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基本架构
服务提供者(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接口
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仓库配置路径
这时去本机相应路径下查看jar包
2、创建一个 maven web 消费者(userservice-provider)工程 ;创建数据模型和service接口
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启动 服务提供者
再启动消费者
这时浏览器打开访问
Dubbo的配置请参考
本文主要使用的xml标签配置
(二)服务接口尽可能大粒度
建议将服务接口、服务模型、服务异常等均放在公共包中。 服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤
1、我们创建一个maven Java 接口模块
将其打成 jar 包
服务接口项目的类文件打包为 jar, 安装到 maven 仓库,仓库中的提供者 jar 可以被消费者使用。使用 idea 的 maven 窗口执行 install
2、创建 maven web模块 服务提供者
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 消费者模块
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>