在本系列文章中,我将利用netty实现一个简单的RPC。
首先,在本节中,主要介绍的是使用spring的FactoryBean和Proxy,进行客户端Bean的创建。
在RPC中,我们需要远程调用一些方法,但是具体能调用哪些方法,以及参数是什么,这个最方便的,就是我们能够和调用本地方法一下去调用远程方法。
我们尝试一下我们日常调用本地方法的方式,通过接口的方式,和实际的实现进行解耦,这个是我们在调用本地方法所熟悉的方法,那么我们是不是可以也使用这种方式来实现RPC呢?当然也是可以的。
通过上面这种方式,客户端只需要拿到对应的方法签名,这里我们先通过依赖二方库中接口,后面我们将介绍通过泛化的方式进行调用。所以客户端能通过接口的定义,可以感知对应远程调用的方法签名,说白了,就是在本地可以有一个接口类,里面定义了调用的方法。
如下,我们先定义了一个接口
/**
* @author admin
*/
public interface InterfaceC {
void process(int a , int b);
}
在客户端,将通过二房库的形式依赖了这个接口。我们的目标方式应该是下面这种
/**
* @author admin
*/
public class RpcTest {
private InterfaceC interfaceC;
public void test(int a , int b) {
interfaceC.process(a , b);
}
public InterfaceC getInterfaceC() {
return interfaceC;
}
public void setInterfaceC(InterfaceC interfaceC) {
this.interfaceC = interfaceC;
}
}
直接定义一个field是InterfaceC类型的,然后直接调用这个接口对应的方法就可以了,这个就非常类似我们本地的调用方式。那为了实现这个目标,spring的FactoryBean和Java的Proxy就可以为我们提供帮助。
首先,我们先实现一个FactoryBean,如下
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author admin
*/
public class RpcConsumerBean implements FactoryBean {
private String interfaceName;
private Object target;
private Object proxyObj;
public void init() throws ClassNotFoundException {
proxyObj = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {Class.forName(interfaceName)},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("call rpc before");
System.out.println("method : " + method.getName() + " , args : " + args);
System.out.println("rpc call");
System.out.println("call rpc after");
return null;
}
});
}
@Override
public Object getObject() throws Exception {
return proxyObj;
}
@Override
public Class<?> getObjectType() {
return proxyObj == null ? Object.class : proxyObj.getClass();
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public void setTarget(Object target) {
this.target = target;
}
}
在这个FactoryBean中,我们可以在bean的初始化过程中,通过代理的方式,代理掉全部的方法,进行远程的调用,具体的远程调用实现,我们会在后面继续介绍。
另外,下面是我们具体的xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"
default-autowire="byName">
<bean id = "interfaceC" class="RpcConsumerBean" init-method="init">
<property name="interfaceName" value="InterfaceC" />
</bean>
<bean id = "rpcTest" class="RpcTest">
<property name="interfaceC" ref="interfaceC" />
</bean>
</beans>
通过上面的这种方式,我们就实现了类似本地调用的方式,进行远程的rpc调用。后面将继续介绍更多的内容,慢慢实现一个简易的rpc。