/**
*
* @ClassName: EchoService
* @Description: 通过Java原生的序列化,socket通信,动态代理,和反射机制实现最简单的rpc框架
* 1服务提供者:它运行在服务端,负责提供服务接口定义和实现类
* 2服务发布者:它运行在RPC服务端,负责将本地服务发布成远程服务,供其他消费者调用
* 3本地服务代理:它运行在RPC客户端,通过代理调用远程服务提供者,然后将结果封装后返回给本地消费者
*
* @author: jkson
* @date: 2016年10月9日 下午1:58:20
*
*/
public interface EchoService {
String echo(String ping);
}
/**
*
* @ClassName: EchoServiceImpl
* @Description: 通过Java原生的序列化,socket通信,动态代理,和反射机制实现最简单的rpc框架
* 1服务提供者:它运行在服务端,负责提供服务接口定义和实现类
* 2服务发布者:它运行在RPC服务端,负责将本地服务发布成远程服务,供其他消费者调用
* 3本地服务代理:它运行在RPC客户端,通过代理调用远程服务提供者,然后将结果封装后返回给本地消费者
* @author: jkson
* @date: 2016年10月9日 下午1:58:10
*
*/
public class EchoServiceImpl implements EchoService {
@Override
public String echo(String ping) {
return ping !=null?ping+"--> Hi i get it.":"i get it.";
}
}
package jk.rpc;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
*
* @ClassName: RpcExporter
* @Description: 服务发布者的主要职责如下
* 1作为服务端,监听客户端的TCP链接,接收到新的TCP链接之后将其封装成TASk,由线程池执行
* 2将客户端发送的码流转换成对象,反射调用服务实现者,获取执行结果
* 3将执行结果对象反序列化,通过socket发送给客户端
* 4远程服务调用完成之后,释放socket等链接资源,防止句柄泄漏
* @author: jkson
* @date: 2016年10月9日 下午1:57:56
*
*/
public class RpcExporter {
static Executor executor =Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public static void exporter(String hostName,int port)throws Exception{
ServerSocket server =new ServerSocket();
server.bind(new InetSocketAddress(hostName,port));
try{
while(true){
System.out.println("等待访问");
executor.execute(new ExporterTask(server.accept()));//此处是阻塞方法,调用一次阻塞到这里,有访问的时候走下,然后再次走到这里等待下次调用
}
}finally{
server.close();
}
}
private static class ExporterTask implements Runnable{
Socket client=null;
public ExporterTask(Socket client){
this.client=client;
}
@Override
public void run() {
ObjectInputStream intput=null;
ObjectOutputStream output=null;
try{
System.out.println("准备接受数据");
intput =new ObjectInputStream(client.getInputStream());
String interfaceName = intput.readUTF();
Class<?> service = Class.forName(interfaceName);
String methodName = intput.readUTF();
Class<?>[] parameterTypes = (Class<?>[])intput.readObject();
Object[] arguments= (Object[])intput.readObject();
Method method =service.getMethod(methodName, parameterTypes);
Object result = method.invoke(service.newInstance(), arguments);
output =new ObjectOutputStream(client.getOutputStream());
output.writeObject(result);
System.out.println("接收并执行完毕");
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("释放服务器资源");
if(output !=null){
try{
output.close();
}catch(IOException e){
e.printStackTrace();
}
}
if(intput !=null){
try{
intput.close();
}catch(IOException e){
e.printStackTrace();
}
}
if(client !=null){
try{
client.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
}
package jk.rpc;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
*
* @ClassName: RpcImporter
* @Description:本地服务代理的主要功能如下:
* 1将本地接口调用转换成JDk的动态代理,在动态代理中实现接口的远程调用
* 2创建Socket客户端,根据指定地址链接远程服务提供者
* 3将远程接口服务调用所需要的接口类,方法名,参数列表等编码后发给服务提供者、
* 4同步阻塞,等待服务端返回应答,获取应答之后返回
* @author: jkson
* @date: 2016年10月9日 下午1:57:40
*
* @param <S>
*/
public class RpcImporter<S> {
public S importer(final Class<?> serviceClass,final InetSocketAddress addr){
return (S)Proxy.newProxyInstance(serviceClass.getClassLoader(),
new Class<?>[]{serviceClass.getInterfaces()[0]},
new InvocationHandler(){
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
Socket socket =null;
ObjectOutputStream output=null;
ObjectInputStream intput=null;
try{
System.out.println("准备向服务器发送数据");
socket=new Socket();
socket.connect(addr);
output=new ObjectOutputStream(socket.getOutputStream());
output.writeUTF(serviceClass.getName());
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(args);
intput=new ObjectInputStream(socket.getInputStream());
System.out.println("发送完毕");
return intput.readObject();
}finally{
System.out.println("释放客户端资源");
if(socket !=null){
socket.close();
}
if(output !=null){
output.close();
}
if(intput !=null){
intput.close();
}
}
}
});
}
}
package jk.rpc;
import java.net.InetSocketAddress;
/**
*
* @ClassName: RpcTest
* @Description: 1首先创建一个异步发布服务端的线程并启动,用于接收RPC客户端的请求,根据请求参数调用服务实现类,返回结果给客户端
* 2创建客户端服务代理类,构造RPC请求参数,发起RPC调用,将调用结构输出到控制台上
* @author: jkson
* @date: 2016年10月9日 下午1:57:14
*
*/
public class RpcTest {
public static void main(String[] args) {
new Thread(new Runnable(){
public void run(){
try{
RpcExporter.exporter("localhost",8088);
}catch(Exception e){
e.printStackTrace();
}
}
}).start();
RpcImporter<EchoService> importer =new RpcImporter<EchoService>();
EchoService echo=importer.importer(EchoServiceImpl.class, new InetSocketAddress("localhost",8088));
System.out.println("begin");
System.out.println(echo.echo("Do you get it?"));
System.out.println("end");
}
}
下面是执行结果
等待访问
begin
准备向服务器发送数据
等待访问
准备接受数据
发送完毕
接收并执行完毕
释放服务器资源
释放客户端资源
Do you get it?--> Hi i get it.
end
简单的RPC实现
最新推荐文章于 2023-07-09 10:58:41 发布