RPC起步
2018-03-19 本文已影响26人
叫我宫城大人
一、RPC?
RPC(Remote Procedure Call),远程过程调用协议,采用C/S模型,请求程序就是一个client,服务提供程序就是一个server。client请求调用server的某个方法,由于不在同一个内存空间,不能直接调用,需要解决下列三个问题:
- 网络传输
客户端和服务端之间建立TCP连接; - 寻址问题
调用的服务器的主机或IP地址,端口; - 序列化和反序列化
网络协议基于二进制;
二、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的架构:
image.png1. 简单起步,首先引入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框架作为分布式的核心,具有以下优点:
- 独立服务,分布式响应,减小服务端压力;
- 统一接口监控,贴合SOA;
- 各个服务隔离,系统解耦;