对于后端大型项目开发,都会将功能模块拆分并部署在不同服务器上,那么不同模块之间的跨网络通讯是必不可少的,RPC协议就由此诞生,广义上的RPC就是跨网络通讯,具体的框架如Dubbo。从架构来说,RPC一般都带有注册中心,对客户端和服务端进行管理,服务端在注册中心提供对外接口,客户端获取服务端接口调用,实现跨网络通讯
![](https://i-blog.csdnimg.cn/blog_migrate/2afb2351e0a692105befc4ba17c38bf0.png)
一、zookeeper安装
zookeeper是分布式管理软件,一般作为RPC通讯的注册中心
1. 下载zookeeper
官网下载地址:https://zookeeper.apache.org/releases.html#download
下载稳定版本:
![](https://i-blog.csdnimg.cn/blog_migrate/367fb43ddd889392c7a3e6974ac33caa.png)
2. 传输到服务器中
我这边利用sftp,上传服务器中:
sftp> put ./apache-zookeeper-3.7.1-bin.tar.gz /root
3. 解压
tar zxf apache-zookeeper-3.7.1-bin.tar.gz
mv apache-zookeeper-3.7.1-bin zookeeper
4. 创建data文件夹
zookeeper需要一个路径用于存放服务端的注册信息
cd zookeeper
mkdir data
在配置文件中指定该路径
cd conf
mv zoo-simple.cfg zoo.cfg
vi zoo.cfg
修改配置文件内容:
dataDir = /root/zookeeper/data
5. 启动zookeeper
在bin目录下执行shell脚本:
cd /root/zookeeper/bin
./zkServer.sh start
查看zookeeper状态可以执行:
./zkServer.sh status
关闭防火墙:
systemctl stop firewalld
二、使用zookeeper
1. 导入依赖
新建Maven项目,导入依赖:
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.1</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
2. 使用zookeeper注册内容
编写一个test方法:
@Test
public void register() throws IOException, InterruptedException, KeeperException {
// 获取zooKeeper对象
ZooKeeper zooKeeper = new ZooKeeper("192.168.42.4:2181", 10000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("连接成功");
}
});
//ZooDefs.Ids.OPEN_ACL_UNSAFE 表示权限。
//CreateMode.PERSISTENT_SEQUENTIAL 永久存储,文件内容编号递增。
String info = zooKeeper.create("/demos", "hello".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println("info:" + info);
}
执行结果:
连接成功
info:/demos0000000001
3. 获取zookeeper中的内容
编写test方法:
@Test
public void read() throws IOException, InterruptedException, KeeperException {
// 获取zooKeeper对象
ZooKeeper zooKeeper = new ZooKeeper("192.168.42.4:2181", 10000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("连接成功");
}
});
byte[] data = zooKeeper.getData("/demos0000000001", false, null);
System.out.println(new String(data));
}
结果:
连接成功
hello
三、RMI使用
RMI是JDK提供的远程方法调用工具,一个Java程序可以像调用本地方法一样调用另一个Java程序的内容,不支持跨语言
RMI使用流程大致分为两步,第一,它拥有注册表,用于存放服务端对象,服务端通过bind方法注册该对象;第二,客户端通过lookup方法从注册表中获取服务端对象。和安卓Binder机制挺像,客户端也要有一份服务端对象的类
1. 编写服务端
创建一个maven的Module,作为RMI服务端项目
![](https://i-blog.csdnimg.cn/blog_migrate/d43ea8780c3f6f99d4b9fcd8eee3c62b.png)
1.1 定义对外接口
要求:继承至Remote接口、方法需要抛出RemoteException异常
public interface ServerService extends Remote {
String demo(String content) throws RemoteException;
}
1.2 定义实现类
要求:继承至UnicastRemoteObject类,该类实现了序列化接口
public class ServerServiceImpl extends UnicastRemoteObject implements ServerService {
public ServerServiceImpl() throws RemoteException {
}
@Override
public String demo(String content) throws RemoteException {
return content + "hello";
}
}
1.3 启动
执行下面的main方法:
public class ServerTest {
public static void main(String[] args) throws RemoteException, MalformedURLException, AlreadyBoundException {
ServerService serverService = new ServerServiceImpl();
//创建注册表
LocateRegistry.createRegistry(8000);
//以一个别名绑定该对象
Naming.bind("rmi://localhost:8000/serverService", serverService);
System.out.println("启动成功");
}
}
2. 编写客户端
新建一个客户端maven的module
![](https://i-blog.csdnimg.cn/blog_migrate/c1b017fb710c45fcae7fdab9d72cbc95.png)
2.1 将对外接口拷贝一份
![](https://i-blog.csdnimg.cn/blog_migrate/9f778f57973c5e39ba7c7fb9eee591d5.png)
2.2 获取远程对象,调用方法
public class ClientTest {
@Test
public void get() throws RemoteException, MalformedURLException, NotBoundException {
//获取远程对象
ServerService service = (ServerService) Naming.lookup("rmi://localhost:8000/serverService");
System.out.println("调用远程方法结果:" + service.demo("rmi"));
}
}
结果:
调用远程方法结果:rmihello
四、RMI结合zookeeper实现RPC框架
接下来使用RMI作为传输协议,zookeeper作为注册中心,实现RPC框架
1. bean模块
创建一个maven模块,作为公共的JavaBean
![](https://i-blog.csdnimg.cn/blog_migrate/fc51edbc588e711d6a2eb703393a1062.png)
定义一个实体类,实现序列化接口:
public class User implements Serializable {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
2. service模块
创建Maven模块,作为rmi客户端和服务端两者共同使用的对外接口
![](https://i-blog.csdnimg.cn/blog_migrate/e72889b35986c01aa37479f04aaa3697.png)
导入bean模块依赖:
<dependency>
<groupId>com.aruba</groupId>
<artifactId>bean</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
定义对外接口,继承至Remote接口:
public interface UserService extends Remote {
public List<User> getUserList() throws RemoteException;
}
3. provider模块
创建Maven模块,作为服务端
![](https://i-blog.csdnimg.cn/blog_migrate/122732d82c946b1e8517fa480c513323.png)
导入service模块依赖:
<dependency>
<artifactId>service</artifactId>
<groupId>com.aruba</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
实现对外接口:
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
public UserServiceImpl() throws RemoteException {
}
@Override
public List<User> getUserList() throws RemoteException {
List<User> list = Arrays.asList(new User("张三", "123"),
new User("李四", "456"));
return list;
}
}
启动方法:
public class Run {
public static void main(String[] args) throws IOException, AlreadyBoundException, InterruptedException, KeeperException {
UserService serverService = new UserServiceImpl();
//创建注册表
try {
LocateRegistry.createRegistry(8000);
} catch (RemoteException e) {
e.printStackTrace();
}
String url = "rmi://localhost:8000/userService";
//以一个别名绑定该对象
try {
Naming.unbind(url);
} catch (NotBoundException e) {
e.printStackTrace();
}
Naming.bind(url, serverService);
// 获取zooKeeper对象
ZooKeeper zooKeeper = new ZooKeeper("192.168.42.4:2181", 10000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("连接成功");
}
});
//ZooDefs.Ids.OPEN_ACL_UNSAFE 表示权限。
//CreateMode.PERSISTENT 永久存储。
zooKeeper.create("/demos/userService", url.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("服务发布成功");
}
}
4. consumer模块
创建一个Maven模块,作为客户端
![](https://i-blog.csdnimg.cn/blog_migrate/2148daabf13de6277671d4a6421419b4.png)
导入SpringBoot依赖、service模块依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.aruba</groupId>
<artifactId>service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
4.1 service层
定义service接口:
public interface UserService {
public List<User> getUserList() throws IOException, InterruptedException, KeeperException, NotBoundException;
}
service接口实现类:
@Service
public class UserServiceImpl implements UserService {
@Override
public List<User> getUserList() throws IOException, InterruptedException, KeeperException, NotBoundException {
ZooKeeper zooKeeper = new ZooKeeper("192.168.42.4:2181", 10000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
}
});
// 通过zookeeper获取rmi的url
byte[] url = zooKeeper.getData("/demos/userService", false, null);
// 获取rmi的远程对象
com.aruba.rmi.service.UserService userService = (com.aruba.rmi.service.UserService) Naming.lookup(new String(url));
return userService.getUserList();
}
}
4.2 controller层
处理单元:
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/getUserList")
public List<User> getUserList() throws NotBoundException, IOException, InterruptedException, KeeperException {
return userService.getUserList();
}
}
4.3 启动springboot
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
浏览器访问结果:
![](https://i-blog.csdnimg.cn/blog_migrate/55f0fb09f0018dba24da884fe6aaf6e6.png)