网页右边,向下滑有目录索引,可以根据标题跳转到你想看的内容 |
---|
如果右边没有就找找左边 |
介绍RPC之前需要先了解RFC |
---|
- 由互联网工程任务组(IETF)发布的文件集,文件集中每个文件都有自己的唯一编号,例如:rfc1831
- 目前RFC文件由互联网协会(ISOC)赞助发行
- 而
RPC(注意这里不是RFC)
会收集这些编号,可通过指定网址查看
RPC |
---|
- 在rfc 1831中收录,RPC(Remote Procedure Call)远程过程调用协议
- RPC协议规定允许互联网中一台主机程序调用另一台主机程序,而程序员无需对这个交互过程进行编程,在RPC协议中强调当A程序调用B程序中功能或方法时,A是不知道B中方法具体实现的。
- RPC是上层协议,底层可以基于TCP协议,也可以基于HTTP协议。一般我们说的RPC都是基于RPC的具体实现,如Dubbo框架
- 广义上讲,只要满足网络中进行通讯调用都统称为RPC,甚至HTTP协议都可以说是RPC的具体实现,但是具体分析看来RPC协议要比HTTP协议更加高效,基于RPC的框架功能更多。
- RPC协议是基于分布式框架而出现的,所以RPC在分布式项目中有着得天独厚的优势
总之:RPC就是让我们可以不通过http协议,比如浏览器发请求等方式,就可以远程调用其它程序,比如两个不同服务器的程序,可以互相调用,而且不会暴露代码
RPC和HTTP对比 |
---|
不同点 | RPC | HTTP |
---|---|---|
具体实现 | 可以基于TCP协议,也可基于HTTP协议 | 基于HTTP协议 |
效率 | 自定义具体实现可以减少很多无用的报文内容,是报文体积更小 | HTTP1.1报文中很多内容是无用的,HTTP2.0以后和RPC相差不大,比RPC少的可能就是一些服务治理等功能 |
连接方式 | 长链接支持 | 每次连接都是3次握手 |
性能 | 可以基于很多序列化方法,如:thrift | 主要通过JSON,序列化和反序列效率更低 |
注册中心 | 一般RPC框架都带有注册中心 | 都是直连 |
负载均衡 | 绝大多数RPC框架都带有负载均衡测量 | 一般都需要借助第三方工具,例如:nginx |
一、通过HttpClient实现RPC(了解即可)
HttpClient简介 |
---|
- 在JDK中java.net包下提供了用户Http访问的基本功能,但它缺少灵活性和许多应用所需要的功能
- HttpClient起初是Apache Jakarta Common的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本。2007年成为了顶级项目
- HttpClient可以实现使用Java代码完成标准Http请求与响应
1、服务端编写
只需要编写一个简单的spring boot工程作为服务端返回点东西就行 |
---|
- 创建maven引入依赖
- 创建启动类
- 创建一个controller
2、客户端通过HttpClient请求
通过get方式请求 |
---|
- 创建maven,引入依赖
- 编写代码
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class HttpClientGetDome {
public static void main(String[] args) {
//1.创建一个工具(相当于浏览器工具),发送请求,解析响应
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
//2.请求路径
URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/demoController/demo?param=zhanagsan");
//3.创建httpGet请求对象
HttpGet httpGet = new HttpGet(uriBuilder.build());
//4.创建响应对象
CloseableHttpResponse response = httpClient.execute(httpGet);
//5.因为响应体响应的是字符串,所以通过httpEntity类型转换为字符串类型,并设置字符集编码,接受响应数据
String s = EntityUtils.toString(response.getEntity(), "utf-8");
//输出响应结果
System.out.println(s);
//6.释放资源
response.close();
httpClient.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过post方式请求 |
---|
- 服务器端创建处理Post请求的方法
- 编写客户端代码
二、RMI实现RPC
RMI |
---|
- RMI(Remote Method Invocation) 远程方法调用
- 从JDK1.2推出的功能,
可以实现在一个java应用中可以像调用本地方法一样调用另一个服务器中Java应用(JVM)中的内容
- RMI是Java语言的远程调用,无法实现跨语言
RMI的执行流程 |
---|
- 首先RMI提供一个RMI Registry(注册表),是一个放置所有服务器对象的命名空间,每次服务端创建一个对象,它会使用bind()或rebind()方法注册该对象,使用称为绑定名称的唯一名称注册。
也就是说,RMI的注册表,记录了这些服务器对象,客户端就可以通过注册表实现远程调用
- 要调用远程对象,客户端需要该对象的引用,既通过服务端绑定的名称从注册表中获取对象(通过lookup()方法)
常用API |
---|
- Remote
java.rmi.Remote 定义了此接口为远程调用接口。如果接口被外部调用,需要继承此接口(也就是说,如果我们想要写需要被远程调用的方法,那么需要先创建一个接口,然后这个接口继承这个Remote接口,然后通过实现类,实现你要被远程调用的方法,只有你的接口里面定义的方法,才能被远程调用,比如你接口定义了a,实现类定义了a,b,c,那么只有a方法可以被远程调用
)public interface Remote{ }
- RemoteException
java.rmi.RemoteException 上面我们说,你想被远程调用需要继承Remote接口,那么继承之后,如果你的方法是允许被远程调用的,需要抛出此异常 - UnicastRemoteObject
java.rmi.server.UnicastRemoteObject 此类实现了Remote接口和Serializable接口,你的自定义接口的实现类,除了实现自定义接口的方法,还需要继承此类 - LocateRegistry
java.rmi.registry.LocateRegistry 可以通过LocateRegistry在本机上创建Registry,通过特定的端口就可以访问这个Registry(也就是内个注册表,客户端相应通过RMI远程调用方法,需要到这里拿对象) - Naming
java.rmi.Naming 定义了发布内容可访问RMI名称,也是通过Naming获取到指定的远程方法
编写服务端 |
---|
- 自定义接口,继承Remote接口,声明允许远程调用的方法,并抛出RemoteException异常
import java.rmi.Remote;
import java.rmi.RemoteException;
//1. 自定义接口继承Remote接口
public interface RmiDomeService extends Remote {
//2. 允许被调用方法定义,并抛出RemoteException异常
public String dome(String param) throws RemoteException;
}
- 编写实现类,继承UnicastRemoteObject类,实现接口中定义的方法
import com.yzpnb.service.RmiDomeService;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
//3. 继承UnicastRemoteObject类,此时会报错,因为UnicastRemoteObject类中构造方法只允许同包下访问,需要重写构造方法
public class RmiDomeServiceImpl extends UnicastRemoteObject implements RmiDomeService{//4. 实现自定义接口
//因为UnicastRemoteObject类中的构造方法都是protected修饰,只允许同包下访问,所以,我们需要重新定义构造方法
//因为默认构造方法会用super()调用父类构造方法
public RmiDomeServiceImpl() throws RemoteException{
}
@Override
public String dome(String param) throws RemoteException {
return param + " abc";
}
}
- 编写注册表并启动
import com.yzpnb.service.RmiDomeService;
import com.yzpnb.service.impl.RmiDomeServiceImpl;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class DomeRegistry {
public static void main(String[] args) {
try {
//1. 创建接口实例
RmiDomeService rmiDomeService = new RmiDomeServiceImpl();
//2. 创建注册表,指定端口号
LocateRegistry.createRegistry(8989);
//3. 绑定服务,注意,这里使用的是rmi协议的URI地址
Naming.bind("rmi://localhost:8989/rmiDome",rmiDomeService);
System.out.println("服务器启动成功");
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
}
}
}
客户端远程调用 |
---|
- 定义与服务端相同的接口
- 编写一个方法,远程调用服务端的方法
import com.yzpnb.service.RmiDomeService;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class ClientDome {
public static void main(String[] args) {
try {
//通过Naming.lookup请求指定服务器对象
RmiDomeService rmiDome = (RmiDomeService) Naming.lookup("rmi://localhost:8989/rmiDome");
//远程调用方法,获取返回值
String result = rmiDome.dome("i'm client");
System.out.println(result);
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
三、Zookeeper
为什么我们不用HttpClient和RMI,而借助Zookeeper实现RPC |
---|
HttpClient和RMI,代码繁杂,麻烦,两个方法调用不方便 |
所以我们需要一个注册中心,将这些可以远程调用的方法发布到注册中心,此时谁想调用,和注册中心要就行 |
而不需要专门在自己的方法中,写远程调用的逻辑,直接要过来用就行了 |
Zookeeper |
---|
- 分布式管理软件,常用它作为注册中心(依赖zookeeper的发布/订阅功能)、配置文件中心、分布式锁配置、集群管理等。
- zookeeper一共就有两个版本。主要使用的是java语言编写
Zookeeper的内容我都放到了这里https://blog.csdn.net/grd_java/article/details/119299318 |
---|