分布式--安装zookeeper、结合RMI实现RPC框架

2022-06-19 14:48:25 浏览数 (1)

对于后端大型项目开发,都会将功能模块拆分并部署在不同服务器上,那么不同模块之间的跨网络通讯是必不可少的,RPC协议就由此诞生,广义上的RPC就是跨网络通讯,具体的框架如Dubbo。从架构来说,RPC一般都带有注册中心,对客户端和服务端进行管理,服务端在注册中心提供对外接口,客户端获取服务端接口调用,实现跨网络通讯

一、zookeeper安装

zookeeper是分布式管理软件,一般作为RPC通讯的注册中心

1. 下载zookeeper

官网下载地址:https://zookeeper.apache.org/releases.html#download

下载稳定版本:

2. 传输到服务器中

我这边利用sftp,上传服务器中:

代码语言:javascript复制
sftp> put ./apache-zookeeper-3.7.1-bin.tar.gz /root
3. 解压
代码语言:javascript复制
tar zxf apache-zookeeper-3.7.1-bin.tar.gz
mv apache-zookeeper-3.7.1-bin zookeeper
4. 创建data文件夹

zookeeper需要一个路径用于存放服务端的注册信息

代码语言:javascript复制
cd zookeeper
mkdir data

在配置文件中指定该路径

代码语言:javascript复制
cd conf
mv zoo-simple.cfg zoo.cfg
vi zoo.cfg

修改配置文件内容:

代码语言:javascript复制
dataDir = /root/zookeeper/data
5. 启动zookeeper

在bin目录下执行shell脚本:

代码语言:javascript复制
cd /root/zookeeper/bin
./zkServer.sh start

查看zookeeper状态可以执行:

代码语言:javascript复制
./zkServer.sh status

关闭防火墙:

代码语言:javascript复制
systemctl stop firewalld

二、使用zookeeper

1. 导入依赖

新建Maven项目,导入依赖:

代码语言:javascript复制
        <!-- 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方法:

代码语言:javascript复制
    @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方法:

代码语言:javascript复制
    @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服务端项目

1.1 定义对外接口

要求:继承至Remote接口、方法需要抛出RemoteException异常

代码语言:javascript复制
public interface ServerService extends Remote {

    String demo(String content) throws RemoteException;

}
1.2 定义实现类

要求:继承至UnicastRemoteObject类,该类实现了序列化接口

代码语言:javascript复制
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方法:

代码语言:javascript复制
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

2.1 将对外接口拷贝一份
2.2 获取远程对象,调用方法
代码语言:javascript复制
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

定义一个实体类,实现序列化接口:

代码语言:javascript复制
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客户端和服务端两者共同使用的对外接口

导入bean模块依赖:

代码语言:javascript复制
        <dependency>
            <groupId>com.aruba</groupId>
            <artifactId>bean</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

定义对外接口,继承至Remote接口:

代码语言:javascript复制
public interface UserService extends Remote {

    public List<User> getUserList() throws RemoteException;

}
3. provider模块

创建Maven模块,作为服务端

导入service模块依赖:

代码语言:javascript复制
        <dependency>
            <artifactId>service</artifactId>
            <groupId>com.aruba</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

实现对外接口:

代码语言:javascript复制
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;
    }

}

启动方法:

代码语言:javascript复制
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模块,作为客户端

导入SpringBoot依赖、service模块依赖:

代码语言:javascript复制
    <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接口:

代码语言:javascript复制
public interface UserService {

    public List<User> getUserList() throws IOException, InterruptedException, KeeperException, NotBoundException;

}

service接口实现类:

代码语言:javascript复制
@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层

处理单元:

代码语言:javascript复制
@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
代码语言:javascript复制
@SpringBootApplication
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}

浏览器访问结果:

项目地址:

https://gitee.com/aruba/rpcdemo.git

0 人点赞