RPC 基本概念
RPC 协议(Remote Procedure Call Protocol)
RPC 远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。在 OSI 网络通信模型中,RPC 跨越了传输层和应用层,使得开发包括网络分布式多程序在内的应用程序更加容易。
客户机(客户端)-服务器模式:当请求没有到达服务端,服务端(全天候)处于休眠状态,当请求到达时,服务端程序会被唤醒,处理客户端请求(计算,图形处理 等),将结果响应给客户端(RPC Socket)。
OSI 七层网络模型(从上至下):
应用层:Http (Https),ftp,smtp,pop3
表示层
会话层
传输层:TCP|UDP
网络层
数据链路层
物理层
RPC 框架
IPC:单机中运行的进程之间的相互通信。
RPC:可以在同一台电脑上不同进程进行,也可以在不同电脑上进行。
LPC:在windows 里面同一台电脑上不同进程间的通讯还可以采用 LPC(本地访问)。
综上:RPC 或 LPC是上层建筑,IPC 是底层基础。
RPC 与 HTTP、TCP、UDP、Socket 的区别
TCP/UDP:都是传输协议,主要区别是 TCP 协议连接需要 3 次握手,断开需要四次挥手,
是通过流来传输的,就是确定连接后,一直发送信息,传完后断开。udp 不需要进行连接,
直接把信息封装成多个报文,直接发送。所以 UDP 的速度更快写,但是不保证数据的完整
性。
HTTP:超文本传输协议,是一种应用层协议,建立在 TCP 协议之上。
Socket:是在应用程序层面上对 TCP/IP 协议的封装和应用。其实是一个调用接口,方便
程序员使用 TCP/IP 协议栈而已。程序员通过 Socket 来使用 TCP/IP 协议。但是 Socket 并不是
一定要使用 TCP/IP 协议,Socket 编程接口在设计的时候,就希望也能适应其他的网络协议。
RPC: 是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
所以 RPC 的实现可以通过不同的协议去实现,比如可以使 HTTP、RMI 等。
RPC 的内部运行
**(1)网络通讯问题:**Socket
**(2)网络寻址问题:**本地存根(字典)
**(3)数据序列化:**A 服务器通过寻址和传输将序列化的二进制发送给 B 服务器。
**(4)数据反序列化:**B 服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。
**(5)再次序列化和反序列化:**返回值还要发送回服务器 A 上的应用,也要经过序列化的方式发送,服务器 A 接到后,再反序列化,恢复为内存中的表达方式,交给 A 服务器上的应用。
RPC 基于 RMI 的简单实现
第一步:定义一个接口,必须继承 remote
public interface IUserService extends Remote{ public User queryUserByUserId(Integer userId) throws Exception; }
|
第二步:编写一个实现类即服务端,继承 UnicastRemoteObject 实现 IUserService接口
public class UserServiceImpl extends UnicastRemoteObject implements IUserService{ private Map<Integer, User> users; public UserServiceImpl() throws Exception{ users = new HashMap<>(); users.put(1, new User(1, "admin", "上海大厦")); } @Override public User queryUserByUserId(Integer userId) throws Exception { return users.get(userId); } }
|
第三步:发布服务
public class Publisher { public static void main(String[] args) throws Exception{ LocateRegistry.createRegistry(8989); Naming.bind("rmi://127.0.0.1:8989/userService",new UserServiceImpl()); System.out.println("服务发布成功!"); } }
|
第四步:客户端调用
public class Test { public static void main(String[] args) throws Exception{ IUserService userService = (IUserService) Naming.lookup("rmi://127.0.0.1:8989/userService"); System.out.println(userService.queryUserByUserId(1)); } }
|
实现效果:
-
服务端:
-
客户端
Dubbo
Dubbo 框架基本概念
Dubbo 是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
结构流程
1. 服务提供方(Provider):
环境:Spring 环境
应用启动时:服务初始化,服务注册(将服务信息写入到远程注册中心:ip:port、项目名/uri?属性参数配置&)
2. 注册中心(Registry):
第三方软件实现(zookeeper) 一种树形的目录服务。(练习中可使用广播address=“multicast://224.5.6.7:1234”)
2.1: 保存服务提供方提供的服务信息
2.2: 支持服务变更推送
**3. 服务消费方(Consumer) **
环境:Spring 环境
应用启动时:订阅注册中西提供的服务列表信息,执行远程服务调用(RPC)。
4. 监控中心(Monitor)
负责项目运行信息的监控操作(消费方运行信息 提供方运行信息)。
Dubbo 入门环境配置
dubbo_par 父工程(pom)
dubbo_api 服务api定义模块(普通工程)
dubbo_provider 服务提供方(普通)
dubbo_consumer 服务消费方(普通)
Dubbo 常用标签配置
dubbo:application :应用名称配置(与项目名一致) (提供方与消费方)
<dubbo:application name="dubbo_provider"/>
|
dubbo:registry 注册中心配置 可以配置多个 一般情况配置一个 通常使用zookeeper 必须配置(提供方和消费方)
<dubbo:registry address="multicast://224.5.6.7:1234"/>
|
dubbo:protocol 服务注册使用协议 官方建议 dubbo 默认端口:20880 (提供方)
<dubbo:protocol name="dubbo" port="20880"/>
|
dubbo:service 注册服务 配置 (提供方)
<dubbo:service interface="com.shsxt.service.IUserService" ref="userServiceImpl"/>
|
dubbo:reference 服务订阅 (消费方)
<dubbo:reference interface="com.shsxt.service.IUserService" id="userService"/>
|
RPC 基于 Dubbo 的简单实现
一、配置结构
二、导包 Dubbo 坐标
<dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.5.6</version> </dependency>
|
三、定义服务接口
public interface IUserService { public User queryUserByUserId(Integer userId); }
|
四、服务实现
@Service public class UserServiceImpl implements IUserService{ private Map<Integer,User> users;
public UserServiceImpl() { users = new HashMap<>(); users.put(1,new User(1,"admin","绿地伯顿大厦")); }
@Override public User queryUserByUserId(Integer userId) { System.out.println("客户端调用参数-->"+userId); return users.get(userId); } }
|
五、配置生产者
添加 dubbo_provider.xml 文件到 resources 文件下
<?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" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.shsxt.service"/>
<dubbo:application name="dubbo_provider"/>
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="com.shsxt.service.IUserService" ref="userServiceImpl"/>
</beans>
|
六、启动服务提供程序
public class Publisher { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo_provider.xml"); context.start(); System.in.read(); } }
|
七、配置服务的消费端
添加 dubbo_consumer.xml 文件到 resources 文件下
<?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" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.shsxt.controller"/>
<dubbo:application name="dubbo_consumer"/>
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<dubbo:reference interface="com.shsxt.service.IUserService" id="userService"/>
</beans>
|
八、调用服务的方法
@Controller public class UserController { @Autowired private IUserService userService; public User queryUserByUserId(Integer userId){ return userService.queryUserByUserId(userId); } }
|
九、启动服务消费端进行消费
public class Test { public static void main(String[] args){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo_consumer.xml"); UserController userController = (UserController) context.getBean("userController"); System.out.println(userController.queryUserByUserId(1)); } }
|
实现效果
-
服务端
-
客户端
-
因为启用客户端调用了controller层的方法,传入的id客户端就可以拿到了