rpc详解及实现

定义

一开始AB应用在同一台机器上,是本地调用
在这里插入图片描述
现在分到两个地方上了
在这里插入图片描述
server这边有很多服务A,B,HelloService
在这里插入图片描述
现在要把这些服务放到服务注册中心去,客户端调用的时候直接去跟服务的注册中心去就可以直接调用
注册中心用map来实现
在这里插入图片描述
客户端和服务端通过socket链接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJqHqHRQ-1580367610642)(/Users/dylan/Library/Application Support/typora-user-images/image-20200129144921068.png)]

  1. 客户端如果要调用helloService,,而服务端需要通过该字符串"HelloService"解析出该字符串代表的接口的一切信息(通过反射技术)
  2. socket
  3. 服务端需要根据客户端的不同请求,返回不同的借口类型
    客户端就要接受到不同的接口类型(通过动态代理)

按照流程来写代码

1.服务端start()开启服务端服务

服务端首先需要一些内部成员:

public class ServerCenter implements Sever{
  //这个用来存放服务名和对应的接口
  private static HashMap<String,Class> serviceRegister = new HashMap<>();
  private static int port;
  public ServerCenter(int port){
    this.port = port;
	}
}

然后开启

public void start() throws IOException{
  ServerSocket server = new ServerSocket();
  //绑定端口
  server.bind(new InetSocketAddress(port));
  //接受客户端的请求 
  server.accept();
}

2.Client获取代表服务端接口的动态代理对象

服务端要告诉客户端接口的名字和 IP地址端口号()

//参数是:1.客户端请求的服务的接口,socket的地址(即ip+port)
public static <T> T getRemoteProxyObj(Class serviceInterface,InetSocketAddress addr){
  /*
  返回一个动态代理即可
  newProxyInstance需要三个参数:
  1.类加载器:需要代理哪个类,比如helloService
  2.接口:需要代理的对象具备哪些功能
  3.一个invocationHandler,采用匿名内部类实现
  */
  return (T)Proxy.newProxyInstance(
    serviceInterface.getClassLoader(),//服务类接口的类加载器
  	new Class<?>[] {serviceInterface},//服务类的接口(可能有很多个接口所以使用数组)
    new InvocationHandler(){//匿名内部类
      
      @Override
      //只需要实现这一个方法即可
      /*
        	三个参数:
        	1.proxy是代理的对象
        	2.method是需要代理的方法
       		3.args是方法参数列表
      */
      public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        //1.客户端向服务端发送请求,请求某一个具体的接口
        Socket socket = new Socket();
        socket.connect(addr);//addr = ip + port
        ObjectOutputStream output = newObjectOutputStream(socket.getOutputStream());
        //发送:接口名,方法名,方法参数,参数类型
        output.writeUTF(serviceInterface.getName());
        output.writeUTP(method.getName());
        output.writeObject(method.getParameterTypes());
        output.writeObject(args);
			}
		}
  )
}

服务端接收到客户端连接及请求,处理该请求

public void start() throws IOException{
  ServerSocket server = new ServerSocket();
  server.bind(new InetSocketAddress(port));
  Socket socket = server.accept();
  /*----------从这里开始-----------*/
  ObjectInputStream input = socket.getInputStream();
  //因为ObjectInputStream对发送数据的顺序严格要求,因此需要参照发送的顺序逐个接受
  String serviceName = input.readUTF();//服务名
  String methodName = input.readUTF();//方法名
  Class[] parameterTypes = (Class[])input.readObject();//参数类型
  Object[] arguments = (Object[])input.readObject();//参数值
  //根据客户请求,在map(serviceRegister)找到具体接口
  Class ServiceClass = serviceRegister.get(serviceName);
  //根据方法名和参数类型找到方法
  Method method = ServiceClass.getMethod(methodName,parameterTypes);
  //执行该方法,需要类,和参数列表(反射技术)
  Object result = method.invoke(ServiceClass.newInstance(),arguments);
  //将方法执行完毕的返回值,传给客户端
  ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
  output.writeObject(result);
}

客户端接受服务端处理后的返回值

public static <T> T getRemoteProxyObj(Class serviceInterface,InetSocketAddress addr){
  /*
  1.类加载器:需要代理哪个类,比如helloService
  2.接口:需要代理的对象具备哪些功能
  */
  return (T)Proxy.newProxyInstance(
    serviceInterface.getClassLoader(),
  	new Class<?>[] {serviceInterface},
    new InvocationHandler(){
      @Override
      public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        /*proxy是代理的对象
        	method是需要代理的方法
       		args是方法参数列表
         */
        //客户端向服务端发送请求,请求某一个具体的接口
        Socket socket = new Socket();
        socket.connect(addr);//addr = ip + port
        ObjectOutputStream output = newObjectOutputStream(socket.getOutputStream());
        //发送:接口名,方法名,方法参数,参数类型
        output.writeUTF(serviceInterface.getName());
        output.writeUTP(method.getName());
        output.writeObject(method.getParameterTypes());
        output.writeObject(args);
        /*----从这里开始-----------*/
        //等待服务器处理
        //接受服务器处理后的返回值
        ObjectInputStream input = new ObjectInputStream(socket.getInputStream);
        return input.readObject();
			}
		}
  )
}

最后加个try…catch…finally关掉就可以了

最后的改进

这里的server只能处理一个请求,需要使用多线程来处理多个客户端请求
用线程池,连接池中存在多个连接对象,每一个连接对象都可以处理一个客户请求

代码

可以直接看github,有具体测试步骤

server:

package src;
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.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {
	private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
	//这个用来存放服务名和对应的接口
	private static HashMap<String,Class> serviceRegister = new HashMap<>();
	private static int port;
	private static boolean isRunning = false;
	public Server(int port){
	    this.port = port;
	}	
	public void start() {//while(true){start();}
		
		//start ->线程对象  
		ServerSocket server = null ;
		try {
			server = new ServerSocket();
			server.bind(new InetSocketAddress(port));
		
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		
		isRunning = true ; //服务已经启动
		while(true) {
			//具体的服务内容:接收客户端请求,处理请求,并返回结果
				//100 :1  1   1 ...1  -->如果想让多个 客户端请求并发执行 
				//-> 多线程
				System.out.println("start  server....");
				//客户端每次请求一次连接(发出一次请求),则服务端 从连接池中 
				//获取一个线程对象去处理
				Socket socket = null ;
				try {
					 socket = server.accept();// 等待客户端连接
				} catch (IOException e) {
					e.printStackTrace();
				}
				//启动线程 去处理客户请求
				executor.execute(new ServiceTask(socket) );
			
		}
	}
	
	public void stop() {
		isRunning = false;
		executor.shutdown();
	}
	
	public void register(Class service,Class serviceImpl) {
		serviceRegister.put(service.getName(), serviceImpl);
	}
	
	private static class ServiceTask implements Runnable{
		private Socket socket ; 
		public ServiceTask() {};
		public ServiceTask(Socket socket) {
			this.socket = socket ;
		}
		@Override
		public void run() {
			ObjectOutputStream output = null;
			ObjectInputStream input = null;
			try {
				  input = new ObjectInputStream( socket.getInputStream());
				  //因为ObjectInputStream对发送数据的顺序严格要求,因此需要参照发送的顺序逐个接受
				  String serviceName = input.readUTF();
				  String methodName = input.readUTF();
				  Class[] parameterTypes = (Class[])input.readObject();
				  Object[] arguments = (Object[])input.readObject();
				  //根据客户请求,在map(serviceRegister)找到具体接口
				  Class ServiceClass = serviceRegister.get(serviceName);
				  Method method = ServiceClass.getMethod(methodName,parameterTypes);
				  //执行该方法,需要类,和参数列表
				  Object result = method.invoke(ServiceClass.newInstance(),arguments);
				  //将方法执行完毕的返回值,传给客户端
				  output = new ObjectOutputStream(socket.getOutputStream());
				  output.writeObject(result);
				
			}catch (Exception e) {
				e.printStackTrace();
			}finally {
				try {
					if (output != null) output.close();
					if (input != null) input.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}

client

package src;
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;

public class Client {
	@SuppressWarnings("unchecked")
	public static <T> T getRemoteProxyObj(Class serviceInterface,InetSocketAddress addr) {
		return (T)Proxy.newProxyInstance(
				serviceInterface.getClassLoader(),
				new Class<?>[]{serviceInterface},
				new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Socket socket = new Socket();
				ObjectOutputStream output = null;
				ObjectInputStream input = null;
				try {
					socket.connect(addr);
					output = new ObjectOutputStream(socket.getOutputStream());
					output.writeUTF(serviceInterface.getName());
					output.writeUTF(method.getName());
					output.writeObject(method.getParameterTypes());
					output.writeObject(args);
					/*---等待---*/
					input = new ObjectInputStream(socket.getInputStream());
					return input.readObject();
				}catch (Exception e) {
					e.printStackTrace();
					return null;
				}finally {
					if(input!=null) input.close();
					if(output!=null) output.close();
				}
			}
		});
	}
}

helloServiceInterface

package src;

public interface HelloServiceInterface {
	public String sayHi(String name) ;
}

helloService

package src;

public class HelloService implements HelloServiceInterface{
	public String sayHi(String name) {
		return "hi,"+name ;
	}
}

serverTest

package src;

public class ServerTest {
	public static void main(String[] args) {
		//开启一个线程
		new Thread(new Runnable() {
			@Override
			public void run() {
				//服务中心
				Server server = new Server(9999);
				//将HelloService接口及实现类 注册到 服务中心
				server.register(HelloServiceInterface.class, HelloService.class);
				server.start(); 
			}
		}).start();//start()
	}
}

clientTest

package src;

import java.net.InetSocketAddress;

public class ClientTest {
	public static void main(String[] args) throws ClassNotFoundException {
		HelloServiceInterface service = Client.getRemoteProxyObj(
				Class.forName("src.HelloServiceInterface") , 
				new InetSocketAddress("127.0.0.1", 9999)) ;	
		System.out.println( service.sayHi("zs")  ) ;
	}
}

发布了84 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Mercuriooo/article/details/104115237
今日推荐