signed

QiShunwang

“诚信为本、客户至上”

RPC框架的展示RMI+zk

2021/3/21 1:10:08   来源:

手写RPC框架

使用zookeeper作为注册中心,RMI为连接技术,手写RPC框架。(关于zk的搭建,我在前面已经搭建好了,所以这里不再叙述)

1.创建项目ParentDemo

1.首先创建父项目ParentDemo,包含3个聚合子项目

2.pojo:service中需要的实体类

3.service:包含被serviceimpl和consumer依赖的接口

4.serviceimpl:provider提供的服务内容

5.consumer:消费者调用的内容

(我这里,没有按照课程中创建父目录整合,因为在实际开发中每个project都是独立的,然后在pom.xml中添加相关工程的依赖)

所以单独创建工程,然后整合

1.创建pojo工程

建立maven项目,后面用到springboot在pom.xml中直接添加依赖即可

创建好项目后,创建Person实体类,代码过于简单,只写了两个属性,剩下的就是常见的getter and setter

private Integer id;
private String name;

2.创建service工程

创建调用接口

public interface MyPersonService extends Remote {

    List<Person> findAllPerson() throws RemoteException;
}

3.创建provider工程

前面提到过要使用zk作为注册中心,所以我后面使用docker搭建了一个zk,docker确实比较爽

进入容器后,创建相关节点

#在命令终端创建节点
create /rpc

provider工程实现service接口方法:

@Service
public class MyPersonServiceImpl extends UnicastRemoteObject implements MyPersonService {

    public MyPersonServiceImpl() throws RemoteException {}

    @Override
    public List<Person> findAllPerson() throws RemoteException {
        //不做连接的过程,直接构造数据
        List<Person> personList = new ArrayList<>();
        personList.add(new Person(1, "zhangsan"));
        personList.add(new Person(2, "sansi"));
        return personList;
    }
}

代码中加入CountDownLatch是因为连接zookeeper时间较长,导致还没有连接到zk就开始创建节点,所以抛异常。所以是使用锁阻塞,保证其连接以后再释放锁。这个我记得可以修改,让连接变得快些。但是连接不上就死等了,使用sleep延时,等待连接

创建启动类,代码实现如下:

public class ProviderRun {
    public static void main(String[] args) throws Exception{
        CountDownLatch countDownLatch = new CountDownLatch(1);
        //1.发布信息
        MyPersonService myPersonService = new MyPersonServiceImpl();
        //2.绑定端口
        LocateRegistry.createRegistry(8989);
        //3.绑定地址
        String urL = "rmi://localhost:8989/myPersonService";
        Naming.bind(urL, myPersonService);

        System.out.println("RMI 服务启动成功");

        //4.创建zk服务器连接并发布信息
        ZooKeeper zooKeeper = new ZooKeeper("192.168.1.20:2181", 10, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("服务连接获取成功");
                countDownLatch.countDown();
            }
        });


        if(ZooKeeper.States.CONNECTING == zooKeeper.getState()){
            System.out.println("正在连接中,阻塞等待");
            countDownLatch.await();

        }

        //5.将链接存入zk中,ACL用来控制权限,比如只读获取数据等,因为连接是不断请求,所以使用持久节点,预防id一直增加
        System.out.println("开始创建节点");
        zooKeeper.create("/rpc/provider", urL.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("注册成功");

    }
}

4.创建对外的consumer工程

创建展示数据的接口

public interface PersonService {
    List<Person> personShow();
}

创建实现接口的PersonServiceImpl方法

@Service
public class PersonServiceImpl implements PersonService {
    @Override
    public List<Person> personShow() {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        try {
            //1.发布信息
            MyPersonService myPersonService = new MyPersonServiceImpl();
            //2.绑定端口
            LocateRegistry.createRegistry(8989);
            //3.绑定地址
            String urL = "rmi://localhost:8989/myPersonService";
            Naming.bind(urL, myPersonService);

            System.out.println("RMI 服务启动成功");

            //4.创建zk服务器连接并发布信息
            ZooKeeper zooKeeper = new ZooKeeper("192.168.1.20:2181", 10, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                    System.out.println("服务连接获取成功");
                    countDownLatch.countDown();
                }
            });


            if (ZooKeeper.States.CONNECTING == zooKeeper.getState()) {
                System.out.println("正在连接中,阻塞等待");
                countDownLatch.await();

            }

            //5.获取节点内容
            System.out.println("开始创建节点");
            byte[] bytes = zooKeeper.getData("/rpc/provider", false, null);

            //6.强转结果,发起请求
            MyPersonService myPersonServiceList = (MyPersonService) Naming.lookup(new String(bytes));
            return myPersonServiceList.findAllPerson();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

与provider相同,唯一不同点是getDate获取数据

创建对应访问的controller层

@Controller
public class PersonController {

    @Autowired
    private PersonService personService;

    @RequestMapping("/show")
    @ResponseBody
    public List<Person> show(){
        return personService.personShow();
    }
}

创建对应的ConsumerApplication类启动springBoot,先添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.1.11.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.1.11.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.9</version>
    </dependency>
    <dependency>
        <groupId>com.anzhi</groupId>
        <artifactId>pojo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.anzhi</groupId>
        <artifactId>service</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.anzhi</groupId>
        <artifactId>provider</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

包含了其他的工程依赖,因为要使用到相关的方法,以及类

代码实现:

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

然后启动工程,在浏览器中访问http://localhost:8080/show,虽然可以响应,但是展示的Person并没有出现在页面上

运行终端提示报错:

java.io.NotSerializableException: com.anzhi.pojo.Person

pojo中的Person类没有序列化,在实现序列化后,在zk的客户终端删除provider节点,重新运行ProviderRun,运行之后记得关闭,因为在Consumer获取节点数据也是通过同样的端口,会导致端口冲突,所以可以修改其中的一个端口。不想修改就关掉。

然后重启Consumer服务的ConsumerApplication,再次访问,页面展示了相应的person类的JSON信息。

至此一个RPC大致的框架流程完成

TODO存留问题:因为zk的问题,连接及其缓慢,然后导致页面的相应很慢,没有找到原因,我更改了zk的版本没有得到很好的改善)