RPC(Remote Procedure Call)远程过程调用,一种计算机之间的远程调用技术,客户端能够在不知道服务器底层的通信架构的情况下调用服务器端的方法,就像调用自身的方法一样。
举个例子:
老婆自己去超市买瓶酱油,这是本地调用
老婆发微信要我去买瓶酱油回来,不管我是开车、打车、骑车、坐地铁去超市,这就是远程过程调用
现在的软件系统规模越来越大,很多采用了微服务架构,就是将系统拆分成了一个个独立的服务,部署在不同的服务器上,如电商系统一般会有商品服务、库存服务、订单服务、支付服务、物流服务、优惠券服务、售后服务等等。
服务和服务之间需要相互通信,如订单服务就需要商品服务提供订单商品的数据,这时就可以通过RPC进行网络通信。
进行RPC通信的两台计算机,提供服务的称为服务提供者,调用服务的称为服务消费者,消费者需要知道服务提供者的地址(IP和端口)才能进行调用,这时我们需要一台服务器来保存服务提供者的地址提供给服务消费者调用,这台服务器就是服务注册中心Registry。
这就像你要跟一家公司打电话,但是不知道对方的号码,可以去114查询,因为这些公司都在114上注册了自己的电话号码,114告诉你,就可以联系对方了。
Dubbo是阿里的一款高性能、轻量级的开源Java RPC框架
它提供了三大核心能力:
透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
服务自动注册与发现,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。
Dubbo的架构
此案例中的注册中心是Zookeeper,首先需要安装和启动Zookeeper
创建父项目,下面三个子项目:
父项目的pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.3
org.example
zookeeper_dubbo_demo
pom
1.0-SNAPSHOT
common_api
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.apache.dubbo
dubbo-spring-boot-starter
2.7.3
org.apache.dubbo
dubbo
2.7.3
org.apache.curator
curator-framework
4.0.1
org.apache.curator
curator-recipes
2.8.0
org.apache.zookeeper
zookeeper
3.4.14
com.101tec
zkclient
0.10
通用项目添加接口
消费者和服务者都继承父项目并引入通用项目
org.example
zookeeper_dubbo_demo
1.0-SNAPSHOT
com.blb
common_api
1.0-SNAPSHOT
服务提供者配置文件
server.port=6606
spring.application.name=provider-service
#Dubbo
dubbo.application.name=provider-service
# dubbo的注册中心协议类型
dubbo.registry.protocol=zookeeper
# zookeeper的地址
dubbo.registry.address=zookeeper://192.168.223.223:2181
# dubbo的协议配置
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
# dubbo扫描接口的包
dubbo.scan.base-packages=com.blb.provider_service.service
服务提供者实现接口
package com.blb.provider_service.service;
import com.blb.api.HelloService;
import org.apache.dubbo.config.annotation.Service;
/**
* 接口实现
* @Service是Dubbo的注解
*/
@Service(version = "1.0.0",interfaceClass = HelloService.class)
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
return "Hello,我是服务提供者!" + name;
}
}
服务消费者的配置文件
server.port=7707
dubbo.application.name=consumer-service
# 服务消费者注册同一个Zookeeper
dubbo.registry.protocol=zookeeper
dubbo.registry.address=zookeeper://192.168.223.223:2181
服务消费者调用服务
@RestController
public class HelloController {
//实现远程方法调用 RPC
@Reference(version = "1.0.0")
private HelloService helloService;
@RequestMapping("/hello")
public String hello(String name){
//调用远程方法
return helloService.hello(name);
}
}
在前面的案例中使用了Dubbo实现RPC,如果想要更加深入的掌握Dubbo实现RPC底层原理,可以自己完成一个简单的RPC框架
案例使用的技术栈:
执行流程:
** RPC就是将3~11步骤封装起来,对用户透明,减少网络程序调用的复杂性 **
依赖坐标
org.slf4j
slf4j-log4j12
1.7.7
io.netty
netty-all
5.0.0.Alpha1
io.protostuff
protostuff-core
1.6.0
io.protostuff
protostuff-runtime
1.6.0
org.apache.zookeeper
zookeeper
3.4.6
org.apache.commons
commons-collections4
4.0
org.objenesis
objenesis
2.1
cglib
cglib
3.1
common/service/IDiff
package com.xray.rpc.common.service;
/**
* 用于测试的减法接口
*/
public interface IDiff {
double diff(double a, double b);
}
common/service/ISum
package com.xray.rpc.common.service;
/**
* 用于测试的加法接口
*/
public interface ISum {
public int sum(int a, int b);
}
common/registry/Constant
package com.xray.rpc.common.registry;
/**
* 常量接口
*/
public interface Constant {
//Zookeeper连接超时
int ZK_SESSION_TIMEOUT = 10000;
//zk地址
String ZK_CONNECT = "127.0.0.1:2181";
//zk保存路径
String ZK_REGISTRY_PATH = "/registry";
//zk保存节点
String ZK_DATA_PATH = ZK_REGISTRY_PATH + "/data";
//ip和端口的分隔符号
String ZK_IP_SPLIT = ":";
}
common/registry/ServiceDiscovery
package com.xray.rpc.common.registry;
import io.netty.util.internal.ThreadLocalRandom;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* 服务发现:连接ZK,添加watch事件
*/
public class ServiceDiscovery {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceDiscovery.class);
private final CountDownLatch latch = new CountDownLatch(1);
private volatile List dataList = new ArrayList<>();
private final String registryAddress;
public ServiceDiscovery(String registryAddress) {
this.registryAddress = registryAddress;
//连接Zookeeper
ZooKeeper zk = connectServer();
if (zk != null) {
//监听registry/data节点
watchNode(zk);
}
}
/**
* 读取Zookeeper中的服务端地址
* @return
*/
public String discover() {
String data = null;
int size = dataList.size();
if (size > 0) {
if (size == 1) {
data = dataList.get(0);
LOGGER.debug("using only data: {}", data);
} else {
data = dataList.get(ThreadLocalRandom.current().nextInt(size));
LOGGER.debug("using random data: {}", data);
}
}
return data;
}
/**
* 连接Zookeeper服务
* @return
*/
private ZooKeeper connectServer() {
ZooKeeper zk = null;
try {
zk = new ZooKeeper(registryAddress, Constant.ZK_SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
latch.countDown();
}
}
});
latch.await();
} catch (IOException | InterruptedException e) {
LOGGER.error("", e);
}
return zk;
}
/**
* 监听节点修改
* @param zk
*/
private void watchNode(final ZooKeeper zk) {
try {
//给注册节点添加监听器
List nodeList = zk.getChildren(Constant.ZK_REGISTRY_PATH, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeChildrenChanged) {
watchNode(zk);
}
}
});
//一旦节点发生改变,就读取地址到dataList中
List dataList = new ArrayList<>();
for (String node : nodeList) {
byte[] bytes = zk.getData(Constant.ZK_REGISTRY_PATH + "/" + node, false, null);
dataList.add(new String(bytes));
}
LOGGER.debug("node data: {}", dataList);
this.dataList = dataList;
} catch (KeeperException | InterruptedException e) {
LOGGER.error("", e);
}
}
}
common/registry/ServiceRegistry
package com.xray.rpc.common.registry;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
* 连接ZK注册中心,创建服务注册目录
*/
public class ServiceRegistry {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistry.class);
private final CountDownLatch latch = new CountDownLatch(1);
private ZooKeeper zk;
public ServiceRegistry() {
}
/**
* 注册服务端地址到Zookeeper中
* @param data
*/
public void register(String data) {
if (data != null) {
zk = connectServer();
if (zk != null) {
try {
Stat stat = zk.exists(Constant.ZK_REGISTRY_PATH, false);
if (stat == null) {
zk.create(Constant.ZK_REGISTRY_PATH, new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
stat = zk.exists(Constant.ZK_DATA_PATH, false);
if (stat == null) {
zk.create(Constant.ZK_DATA_PATH, new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
//保存服务端地址
zk.setData(Constant.ZK_DATA_PATH, data.getBytes(), -1);
LOGGER.info("create zookeeper node ({} => {})", Constant.ZK_DATA_PATH, data);
} catch (InterruptedException | KeeperException e) {
LOGGER.error("Connect Zookeeper Error {}", e);
}
}
}
}
/**
* 连接Zookeeper
* @return
*/
private ZooKeeper connectServer() {
ZooKeeper zk = null;
try {
zk = new ZooKeeper(Constant.ZK_CONNECT, Constant.ZK_SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 判断是否已连接ZK,连接后计数器递减.
if (event.getState() == Event.KeeperState.SyncConnected) {
latch.countDown();
}
}
});
// 若计数器不为0,则等待.
latch.await();
} catch (IOException | InterruptedException e) {
LOGGER.error("Connect Zookeeper Error {}", e);
}
return zk;
}
}
common/codec/RPCRequest
package com.xray.rpc.common.codec;
/**
* 请求协议
*/
public class RPCRequest {
//请求id
private String requestId;
//接口类型
private String className;
//方法名
private String methodName;
//方法参数
private Class[] parameterTypes;
//参数值
private Object[] parameters;
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class[] getParameterTypes() {
return parameterTypes;
}
public void setParameterTypes(Class[] parameterTypes) {
this.parameterTypes = parameterTypes;
}
public Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}
common/codec/RPCResponse
package com.xray.rpc.common.codec;
/**
* 请求响应
*/
public class RPCResponse {
//请求id
private String requestId;
//错误对象
private Throwable error;
//返回值
private Object result;
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public Throwable getError() {
return error;
}
public void setError(Throwable error) {
this.error = error;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}
common/codec/SerializationUtil
package com.xray.rpc.common.codec;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 基于Protobuff的序列化工具类
*/
public class SerializationUtil {
private static Map, Schema> cachedSchema = new ConcurrentHashMap<>();
private static Objenesis objenesis = new ObjenesisStd(true);
private SerializationUtil() {
}
/**
* 返回协议类型
*/
@SuppressWarnings("unchecked")
private static Schema getSchema(Class cls) {
Schema schema = (Schema) cachedSchema.get(cls);
if (schema == null) {
schema = RuntimeSchema.createFrom(cls);
if (schema != null) {
cachedSchema.put(cls, schema);
}
}
return schema;
}
/**
* 序列化方法
*/
@SuppressWarnings("unchecked")
public static byte[] serialize(T obj) {
Class cls = (Class) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema schema = getSchema(cls);
return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
/**
* 反序列化方法
*/
public static T deserialize(byte[] data, Class cls) {
try {
T message = (T) objenesis.newInstance(cls);
Schema schema = getSchema(cls);
ProtostuffIOUtil.mergeFrom(data, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
common/codec/RPCDecoder
package com.xray.rpc.common.codec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
* 请求解码器
*/
public class RPCDecoder extends ByteToMessageDecoder {
//泛型类型
private Class genericClass;
public RPCDecoder(Class genericClass) {
this.genericClass = genericClass;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List
common/codec/RPCEncoder
package com.xray.rpc.common.codec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* 请求编码器
*/
public class RPCEncoder extends MessageToByteEncoder
server/impl/DiffImpl
package com.xray.rpc.server.impl;
import com.xray.rpc.common.service.IDiff;
/**
* 接口实现 减法
*/
public class DiffImpl implements IDiff {
@Override
public double diff(double a, double b) {
return a - b;
}
}
server/impl/SumImpl
package com.xray.rpc.server.impl;
import com.xray.rpc.common.service.ISum;
/**
* 加法实现
*/
public class SumImpl implements ISum {
@Override
public int sum(int a, int b) {
return a + b;
}
}
rpc/server/RPCServer
package com.xray.rpc.server;
/**
* RPC服务端
*/
public class RPCServer {
/**
* 将可调用的接口名和实现类包装到Map集合中
* @return
*/
private Map getServices() {
Map services = new HashMap();
services.put(ISum.class.getName(), new SumImpl());
services.put(IDiff.class.getName(), new DiffImpl());
return services;
}
/**
* 绑定监听某个端口
* @param port
*/
private void bind(int port) {
//创建线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//创建启动对象
ServerBootstrap b = new ServerBootstrap();
//配置参数
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2))
//添加RPC解码器
.addLast(new RPCDecoder(RPCRequest.class))
.addLast(new LengthFieldPrepender(2))
//添加RPC编码器
.addLast(new RPCEncoder(RPCResponse.class))
//添加RPC处理器
.addLast(new RPCServerHandler(getServices()));
}
});
//绑定端口
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
/**
* 获取本机ip
* @return
*/
public static String getAddress(){
InetAddress host = null;
try {
host = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
String address = host.getHostAddress();
return address;
}
/**
* 初始化服务器
* @param port
*/
public void initService(int port) {
ServiceRegistry serviceRegistry = new ServiceRegistry();
String ip = getAddress();
//向zookeeper注册服务地址
serviceRegistry.register(ip + Constant.ZK_IP_SPLIT+port);
bind(port);
}
}
rpc/server/RPCServerHandler
package com.xray.rpc.server;
/**
* RPC处理器
*/
public class RPCServerHandler extends ChannelHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(RPCServerHandler.class);
//本地接口Map
private final Map handlerMap;
public RPCServerHandler(Map handlerMap) {
this.handlerMap = handlerMap;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
RPCResponse response = new RPCResponse();
//解码得到RPC协议
RPCRequest request = (RPCRequest) msg;
response.setRequestId(request.getRequestId());
try {
//调用本地方法
Object result = handle(request);
response.setResult(result);
} catch (Throwable t) {
response.setError(t);
}
//返回响应结果
ctx.writeAndFlush(response);
}
/**
* 调用本地方法
*/
private Object handle(RPCRequest request) throws Throwable {
//获得接口类型名
String className = request.getClassName();
//获得接口类型对应的实现对象
Object serviceBean = handlerMap.get(className);
//获得接口类型、方法名、参数、参数值
Class serviceClass = serviceBean.getClass();
String methodName = request.getMethodName();
Class[] parameterTypes = request.getParameterTypes();
Object[] parameters = request.getParameters();
FastClass serviceFastClass = FastClass.create(serviceClass);
//获得方法
FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);
//调用本地方法
return serviceFastMethod.invoke(serviceBean, parameters);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
LOGGER.error("server caught exception", cause);
ctx.close();
}
}
rpc/server/RPCServiceTest
package com.xray.rpc.server;
public class RPCServiceTest {
public static void main(String[] args) {
//启动服务器测试
int port = 9090;
new RPCServer().initService(port);
}
}
rpc/client/RPCProxy
package com.xray.rpc.client;
import com.xray.rpc.common.codec.RPCRequest;
import com.xray.rpc.common.codec.RPCResponse;
import com.xray.rpc.common.registry.Constant;
import com.xray.rpc.common.registry.ServiceDiscovery;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.Proxy;
import java.lang.reflect.Method;
import java.util.UUID;
/**
* RPC动态代理类
*/
public class RPCProxy {
private String serverAddress;
private ServiceDiscovery serviceDiscovery;
public RPCProxy(ServiceDiscovery serviceDiscovery) {
this.serviceDiscovery = serviceDiscovery;
}
/**
* 创建RPC代理对象
*/
@SuppressWarnings("unchecked")
public T create(Class interfaceClass) {
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] { interfaceClass },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 创建并初始化 RPC 请求
RPCRequest request = new RPCRequest();
request.setRequestId(UUID.randomUUID().toString());
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameterTypes(method.getParameterTypes());
request.setParameters(args);
// 通过Zookeeper查询服务端地址
if (serviceDiscovery != null) {
serverAddress = serviceDiscovery.discover();
}
// 分割出IP和端口 "123.23.213.23:9090"
String[] array = serverAddress.split(Constant.ZK_IP_SPLIT);
String host = array[0];
int port = Integer.parseInt(array[1]);
// 初始化 RPC客户端
RPCClient client = new RPCClient(host, port);
// 发送网络请求
RPCResponse response = client.send(request);
// 返回结果或错误信息
if (response.getError() != null) {
throw response.getError();
} else {
return response.getResult();
}
}
});
}
}
rpc/client/RPCClient
package com.xray.rpc.client;
/**
* RPC客户端
*/
public class RPCClient {
private final String host;
private final int port;
private final CountDownLatch latch;
public RPCClient(String host,int port) {
this.host = host;
this.port = port;
this.latch = new CountDownLatch(1);
}
/**
* 发出RPC请求
* @param request
* @return
*/
public RPCResponse send(RPCRequest request){
//自定义RPC客户端处理器
RPCClientHandler handler = new RPCClientHandler(request,latch);
//创建netty启动对象
EventLoopGroup group = new NioEventLoopGroup();
RPCResponse response = null;
try {
Bootstrap b = new Bootstrap().group(group)
//配置参数
.channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
//设置处理器
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2))
//设置RPC解码器
.addLast(new RPCDecoder(RPCResponse.class))
.addLast(new LengthFieldPrepender(2))
//设置RPC编码器
.addLast(new RPCEncoder(RPCRequest.class))
//设置RPC请求处理器
.addLast(handler);
}
});
//连接服务端
ChannelFuture f = b.connect(host, port).sync();
//阻塞线程,直到处理器读取到服务端数据为止
latch.await();
//获得服务端响应
response = handler.getResponse();
if(response != null) {
f.channel().close();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
group.shutdownGracefully();
}
return response;
}
}
rpc/client/RPCClientHandler
package com.xray.rpc.client;
import com.xray.rpc.common.codec.RPCRequest;
import com.xray.rpc.common.codec.RPCResponse;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import java.util.concurrent.CountDownLatch;
/**
* 客户端处理器
*/
public class RPCClientHandler extends ChannelHandlerAdapter {
//请求对象
private RPCRequest request;
//响应对象
private RPCResponse response;
//RPCClient传来的latch
private final CountDownLatch latch;
public RPCClientHandler(RPCRequest request, CountDownLatch latch) {
this.request = request;
this.latch = latch;
}
public RPCResponse getResponse() {
return response;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(request);
}
//读取服务端消息
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//获得消息,并解码为响应结果
response = (RPCResponse) msg;
//让client阻塞的线程继续执行
latch.countDown();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.getCause().printStackTrace();
ctx.close();
}
}
rpc/client/RPCTest
package com.xray.rpc.client;
import com.xray.rpc.common.service.IDiff;
import com.xray.rpc.common.service.ISum;
import com.xray.rpc.common.registry.Constant;
import com.xray.rpc.common.registry.ServiceDiscovery;
public class RPCTest {
public static void main(String[] args) {
//创建动态代理对象
RPCProxy rpcProxy = new RPCProxy(new ServiceDiscovery(Constant.ZK_CONNECT));
//创建接口的动态代理
IDiff diff = rpcProxy.create(IDiff.class);
//通过动态代理调用远程方法
double result = diff.diff(1321, 32.2);
//同上
ISum sum = rpcProxy.create(ISum.class);
int result2 = sum.sum(1000, 1000);
System.out.println(result+":"+result2);
}
}
通过文章我们了解到RPC是一种计算机的远程调用技术,能够像调用本地方法那样,简单的调用远程服务器上的方法,大大减少了服务器之间数据通信的复杂性。
但是RPC的内部实现是比较复杂的,需要掌握Netty、Zookeeper、Protobuff、动态代理等技术,总而言之是“把简单留给用户,把复杂留给自己”。通过本文最后的案例,我们也大致了解了RPC框架底层的总体逻辑,不过该案例只实现了基本的RPC功能,如果需要像Dubbo那样通过配置注解实现,还需要在后面的版本中进行完善。