RPC起步

一、RPC?

RPC(Remote Procedure Call),远程过程调用协议,采用C/S模型,请求程序就是一个client,服务提供程序就是一个server。client请求调用server的某个方法,由于不在同一个内存空间,不能直接调用,需要解决下列三个问题:

  1. 网络传输
    客户端和服务端之间建立TCP连接;
  2. 寻址问题
    调用的服务器的主机或IP地址,端口;
  3. 序列化和反序列化
    网络协议基于二进制;

二、RMI?

RMI(Remote Method Invoke),是RPC协议的Java具体实现。创建一个RMI程序需要以下五个基本步骤;

1. 创建远程接口,继承Remote接口,每个方法必须抛出RemoteException异常;

package com.cjt.rpc;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Service extends Remote {

    void say(String message) throws RemoteException;
}

2. 创建实现类,继承UnicastRemoteObject类,实现远程接口;

package com.cjt.rpc;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class ServiceImpl extends UnicastRemoteObject implements Service {

    public ServiceImpl() throws RemoteException {
    }

    @Override
    public void say(String message) throws RemoteException {
        System.out.println(message);
    }
}

3. 创建服务器程序,在rmi registry注册表中注册远程对象;

package com.cjt.rpc;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.IOException;
import java.rmi.registry.LocateRegistry;

public class Server {

    public static void main(String[] args) {
        try {
            LocateRegistry.createRegistry(6666);
            Context context = new InitialContext();
            Service service = new ServiceImpl();
            context.bind("rmi://localhost:6666/service", service);
            // 防止程序结束
            System.in.read();
        } catch (NamingException | IOException e) {
            e.printStackTrace();
        }
    }
}

4. 创建客户端程序,负责定位远程对象,并且调用远程方法;

package com.cjt.rpc;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.RemoteException;

public class Client {

    public static void main(String[] args) {
        try {
            Context context = new InitialContext();
            Service service = (Service) context.lookup("rmi://localhost:6666/service");
            service.say("我来了~");
        } catch (NamingException | RemoteException e) {
            e.printStackTrace();
        }
    }
}

先运行server端后注册RMI接口,然后启动client端调用,可以发现输出打在了server端的console中,说明调用成功。

三、Dubbo?

Dubbo是阿里的一款优秀的RPC服务框架,与Spring无缝集成。

下图很好说明了Dubbo的架构:

《RPC起步》 image.png

1. 简单起步,首先引入dubbo的pom;

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.5.3</version>
</dependency>

2. 编写远程服务接口;

package com.cjt;

public interface DemoService {

    void say(String message);
}

3. 编写实现类;

package com.cjt;

public class DemoServiceImpl implements DemoService {

    @Override
    public void say(String message) {
        System.out.println(message);
    }
}

4. 编写provider.xml和server端;

服务端配置文件:

<?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"
       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">
    <dubbo:application name="hello-world-app"/>
    <dubbo:registry address="multicast://224.5.6.7:1234"/>
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:service interface="com.cjt.DemoService" ref="demoService"/>

    <bean id="demoService" class="com.cjt.DemoServiceImpl"/>
</beans>

server端测试代码:

package com.cjt;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

public class Server {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:provider.xml");
        try {
            // 防止程序结束
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. 编写consumer.xml和client端;

消费端配置文件:

<?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"
       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">
    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="hello-world-app"  />
    <!-- 使用multicast广播注册中心暴露服务地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234" />
    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <dubbo:reference id="demoService" interface="com.cjt.DemoService" />
</beans>

client端测试代码:

package com.cjt;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml");
        DemoService service = (DemoService) context.getBean("demoService");
        service.say("我来了~");
    }
}

可以先运行server端注册接口服务后,然后再运行client端调用接口方法,测试正常。

四、RPC VS HTTP?

严格来讲,RPC与HTTP并不是一个层级的概念。HTTP本身也可以作为RPC的传输层协议。

RPC框架作为分布式的核心,具有以下优点:

  1. 独立服务,分布式响应,减小服务端压力;
  2. 统一接口监控,贴合SOA;
  3. 各个服务隔离,系统解耦;

五、参考文章

  1. 谁能用通俗的语言解释一下什么是 RPC 框架?
  2. dubbo入门手册
点赞