RPC
RPC:Remote Procedure Call(远程过程调用)
一.定义:
是进程间的通信方式(Socket通信),是一种技术的思想,而不是规范。
核心:通讯、序列化
二.流程梳理
client - server
(TestRPCClient)
1 客户端通过socket请求服务端,并通过字符串形式(或者Class)将请求的接口发送给服务端
动态代理:发送接口名、方法名、方法参数、方法参数类型
(TestRPCServer)
2 服务端将可以提供的接口注册到服务中心(通过map保存 key[接口名]——value[接口实现类])
3 服务端接收到客户端的请求后,通过请求的接口名 在服务中心的map中寻找对应的接口实现类
解析Client发送的:接口名、方法名、方法参数、方法参数类型
通过reflect执行该方法,并将返回值传给客户端
Method method = serviceClass.getMethod(methodName, parameterTypes);
Object result = method.invoke(serviceClass.newInstance(), arguements);
4 客户端接收服务器处理的结果
三.代码实现
1 server服务端
(1)声明业务接口
public interface HelloService {
public Object sayHi(String name);
}
(2)实现业务接口
public class HelloServiceImpl implements HelloService {
@Override
public String sayHi(String name) {
String str = "hi:"+name;
System.out.println(str);
return str;
}
}
(3)声明服务接口
//服务中心
public interface MyServer {
public void start() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException;
public void stop();
//注册服务
public void register(Class service,Class serviceImpl);
}
(4)实现服务接口
(一)单线程实现
查看代码
public class ServerCenter_Singlethread implements MyServer {
//map:服务端的所有可供客户端访问的接口,都注册到该map中。接口名字“HelloService” value:HelloService的实现
private static HashMap serverRegister = new HashMap();
private static int port;
public ServerCenter_Singlethread(int port){
this.port = port;
}
//开启服务端服务
@Override
public void start() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
ObjectInputStream input = null;
ObjectOutputStream output = null;
try{
ServerSocket server = new ServerSocket();//ip,port
server.bind(new InetSocketAddress(9999));//绑定端口-
Socket socket = server.accept();//等待客户端连接..
//接收到客户端连接及请求,处理该请求..
input = new ObjectInputStream(socket.getInputStream());
//因为ObjectInputStream对发送数据的顺序严格要求,因此需要参照发送的顺序逐个接收。
//接收:接口名、方法名 | 方法参数、方法参数的类型
String serviceName = input.readUTF();
String methodName = input.readUTF();
Class[] parameterTypes = (Class[])input.readObject();//方法的参数类型
Object[] arguements = (Object[]) input.readObject();//方法参数名
//根据客户请求,到map中找到阈值对应的接口
Class serviceClass = serverRegister.get(serviceName);//HelloService
Method method = serviceClass.getMethod(methodName, parameterTypes);
//执行该方法
Object result = method.invoke(serviceClass.newInstance(), arguements);
//向客户端将方法执行完毕的返回值传给客户端
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();
}
}
}
@Override
public void stop() {
}
@Override
public void register(Class service,Class serviceImpl) {
serverRegister.put(service.getName(),serviceImpl);
}
}
(二)多线程实现(常用)
查看代码
public class ServerCenter implements MyServer {
//map:服务端的所有可供客户端访问的接口,都注册到该map中。接口名字“HelloService” value:HelloService的实现
private static HashMap serverRegister = new HashMap();
private static int port;
//连接池(并发多线程):存在多个连接对象,每个连接对象都可以处理一个客户请求
/*
获取处理器个数(生产连接池对象):Runtime.getRuntime().availableProcessors());
*/
private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private static boolean isRunning;
public ServerCenter(int port){
this.port = port;
}
//开启服务端服务
@Override
public void start() {
ServerSocket server = null;
try {
server = new ServerSocket();//ip,port
server.bind(new InetSocketAddress(port));//绑定端口-
} catch (IOException e) {
e.printStackTrace();
}
isRunning = true;
System.out.println("start server..服务器已启动!");
//一旦开启,执行多次
while(true){
//100: 1 1 1 1 .. 1 -- 如果让多个客户端请求并发执行 -- 多线程
//客户端每请求一次连接(发出一次请求),服务端从连接池中获取一个线程对象去处理
Socket socket = null;
try{
socket = server.accept();//等待客户端连接..
}catch (Exception e){
e.printStackTrace();
}
//启动线程处理客户请求
executor.execute(new ServiceTask(socket));
System.out.println("已接收到新的用户用户请求!");
}
}
//关闭服务
@Override
public void stop() {
isRunning = false;
executor.shutdown();
}
@Override
public void register(Class service,Class serviceImpl) {
serverRegister.put(service.getName(),serviceImpl);
}
//定义内部类实现线程任务
private static class ServiceTask implements Runnable{
private Socket socket;
public ServiceTask(){
}
//线程与外部的对象桥梁
public ServiceTask(Socket socket){
this.socket = socket;
}
//线程所做的事情(并发任务) doThing()
@Override
public void run() {
doThing(socket);
}
}
//具体的服务内容:接收客户端请求-处理请求-返回结果
public static void doThing(Socket socket){
ObjectInputStream input = null;
ObjectOutputStream output = null;
try{
//接收到客户端连接及请求,处理该请求..
input = new ObjectInputStream(socket.getInputStream());
//因为ObjectInputStream对发送数据的顺序严格要求,因此需要参照发送的顺序逐个接收。
//接收:接口名、方法名 | 方法参数、方法参数的类型
String serviceName = input.readUTF();
String methodName = input.readUTF();
Class[] parameterTypes = (Class[])input.readObject();//方法的参数类型
Object[] arguements = (Object[]) input.readObject();//方法参数名
//根据客户请求,到map中找到阈值对应的接口
Class serviceClass = serverRegister.get(serviceName);//HelloService
Method method = serviceClass.getMethod(methodName, parameterTypes);//方法名、方法参数类型
//执行该方法
Object result = method.invoke(serviceClass.newInstance(), arguements);
//向客户端将方法执行完毕的返回值传给客户端
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();
}
}
}
}
2 client客户端
查看代码
public class MyClient {
/*
InetSocketAddress 包括:ip、port
serviceName:请求的接口名
return:返回动态代理对象
*/
//获取代表服务端接口的动态代理对象(HelloService)
public static T getRemoteProxyObj(Class service, InetSocketAddress addr){
/*
三个参数:
1 ClassLoader类加载器:需要代理哪个类(例如HelloService接口),就需要将HelloService的类加载器 传入第一个参数
2 Class<?>[] 需要代理的对象,具备哪些方法 -- 接口
3 InvocationHandler
*/
return (T)Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() {
/*
proxy:代理的对象HelloService
method:哪个方法(sayHello())
args:该方法的参数列表
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ObjectOutputStream output = null;
ObjectInputStream input = null;
try{
//客户端向服务端发送请求:请求某一个具体的接口
Socket socket = new Socket();
//InetSocketAddress:ip、port
socket.connect(addr);
socket.getOutputStream();//发送[序列化流(对象流)](客户端->服务端 OutputStream)
output = new ObjectOutputStream(socket.getOutputStream());
//发送:接口名、方法名 | 方法参数、方法参数的类型
output.writeUTF(service.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 {
try{
if(input != null) input.close();
if(output != null) output.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
});
}
}
3 测试使用
(1)启动服务并注册接口,同时监听客户端连接
public class TestRPCServer {
public static void main(String[] args) {
//服务中心
ServerCenter server = new ServerCenter(9999);
//注册接口
server.register(HelloService.class, HelloServiceImpl.class);
server.start();
}
}
(2)客户端启动,根据接口名的类加载器以及IP地址端口号,获取代表服务端接口的动态代理对象(HelloService)
public class TestRPCClient {
public static void main(String[] args) throws ClassNotFoundException {
HelloService service = MyClient.getRemoteProxyObj(Class.forName("rpc.server.dao.HelloService"),new InetSocketAddress("127.0.0.1",9999));
Object obj = service.sayHi("张三");
System.out.println(obj);
}
}