简单的RPC实现

/**
 * 
 * @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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值