通过socket实现通信,通过动态代理实现socket发送请求,接收结果的统一处理。
服务端 rpc-server,包含了2个模块rpc-server-api(暴露接口和实体信息)和rpc-server-provider(提供服务)。反射实现方法的调用。
rpc-server的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rpc</groupId>
<artifactId>rpc-server</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>rpc-server-api</module>
<module>rpc-server-provider</module>
</modules>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
</project>
rpc-server-api的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>rpc-server</artifactId>
<groupId>com.rpc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>rpc-server-api</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
</project>
package com.rpc.server.api;
import com.rpc.server.model.User;
/**
* 暴露接口
*/
public interface UserApi {
/**
* 根据user条件查询user
* @param queryUser
* @return
*/
User queryUser(User queryUser);
}
package com.rpc.server.model;
import lombok.Data;
import java.io.Serializable;
/**
* 请求信息
*/
@Data
public class RequestInfo implements Serializable {
/**
* 接口全路径地址
*/
private String className;
/**
* 请求的方法名
*/
private String methodName;
/**
* 请求方法的参数类型
*/
private Class[] paramTypes;
/**
* 请求方法的参数值
*/
private Object[] params;
}
package com.rpc.server.model;
import lombok.Data;
import java.io.Serializable;
/**
* 资源实体
*/
@Data
public class User implements Serializable {
private long id;
private String name;
}
rpc-server-provider的代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>rpc-server</artifactId>
<groupId>com.rpc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>rpc-server-provider</artifactId>
<dependencies>
<dependency>
<groupId>com.rpc</groupId>
<artifactId>rpc-server-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
package com.rpc.server.impl;
import com.rpc.server.api.UserApi;
import com.rpc.server.model.User;
/**
*接口的简单实现
*/
public class UserApiImpl implements UserApi {
@Override
public User queryUser(User queryUser) {
if(queryUser == null) {
return null;
} else if(queryUser.getId() == 1) {
queryUser.setName("kobe");
} else if(queryUser.getId() == 2) {
queryUser.setName("lebron");
} else if(queryUser.getId() == 3) {
queryUser.setName("Mac");
}
return queryUser;
}
}
package com.rpc.server.main;
import com.rpc.server.api.UserApi;
import com.rpc.server.impl.UserApiImpl;
import com.rpc.server.model.RequestInfo;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 发布RPC服务
*/
public class RpcPublisher {
public static void main(String[] args) throws Exception {
//此处可以从IOC容器获取
UserApi userApi = new UserApiImpl();
ServerSocket serverSocket = new ServerSocket(8889);
System.out.println("开启服务端...");
//死循环,可以一直处理socket连接
while (true) {
System.out.println("等待连接...");
//如果获取不到客户端的连接请求,此处是阻塞的
Socket socket = serverSocket.accept();
System.out.println("获取到连接...");
//可以用线程池替换
new Thread(() -> {
try (InputStream inputStream = socket.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
OutputStream outputStream = socket.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
//获取客户端连接发送的请求信息
RequestInfo requestInfo = (RequestInfo) objectInputStream.readObject();
System.out.println("获取到连接信息: " + requestInfo);
String className = requestInfo.getClassName();
String methodName = requestInfo.getMethodName();
Object[] params = requestInfo.getParams();
Class[] paramTypes = requestInfo.getParamTypes();
//通过反射调用具体方法
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod(methodName, paramTypes);
//此处的userApi可以根据clazz从IOC容器获取,Object userApi = applicationContext.get(clazz);
Object resultObj = method.invoke(userApi, params);
System.out.println("返回结果:" + resultObj);
//将调用结果返回
objectOutputStream.writeObject(resultObj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}).start();
}
}
}
接下来是客户端的实现
rpc-client项目下面有一个模块rpc-client-consumer
rpc-client的pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rpc</groupId>
<artifactId>rpc-client</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>rpc-client-consumer</module>
</modules>
</project>
rpc-client-consumer是调用rpc服务的客户端,
ConsumerInvocationHandler是代理对象的逻辑处理,实现socket调用的服务端的方法
package com.rpc.client.proxy;
import com.rpc.server.model.RequestInfo;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
* 代理对象的逻辑处理,主要是实现通过socket发送消息调用服务端的方法
*/
public class ConsumerInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//定义请求信息
RequestInfo requestInfo = new RequestInfo();
//设置要请求的接口,即com.rpc.server.api.UserApi,proxy是实现了UserApi接口的代理对象,因为获取代理对象的时候是只设置了一个接口,所以这里就可以直接获取对应的接口
requestInfo.setClassName(proxy.getClass().getInterfaces()[0].getName());
//被调用的方法名,暴露的接口本身可以加些标志,标记哪些是接口方法,否则调用Object.toString()方法也会请求到服务端去
requestInfo.setMethodName(method.getName());
//设置调用方法的实际参数值
requestInfo.setParams(args);
//设置调用方法的参数类型,服务端才可以根据参数类型获取直接的方法
requestInfo.setParamTypes(method.getParameterTypes());
Object resultObj = null;
//连接服务端socket
Socket socket = new Socket();
socket.connect(new InetSocketAddress("localhost", 8889));
System.out.println("开启客户端连接...");
try (
OutputStream outputStream = socket.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
System.out.println("发送请求信息..." + requestInfo);
objectOutputStream.writeObject(requestInfo);
//获取请求结果
try (
InputStream inputStream = socket.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
) {
resultObj = objectInputStream.readObject();
}
}
return resultObj;
}
}
package com.rpc.client.consumer;
import com.rpc.client.proxy.ConsumerInvocationHandler;
import com.rpc.server.api.UserApi;
import com.rpc.server.model.User;
import java.lang.reflect.Proxy;
/**
* 客户端的具体业务处理
*/
public class UserConsumer {
public static void main(String[] args) {
//通过jdk动态代理获取一个代理对象,代理对象的方法调用都会走到ConsumerInvocationHandler的invoke
//jdk动态代理是通过按接口UserApi直接生成字节码得到的实例对象,而这个实例对象实现了UserApi的所有方法,这些方法的调用实际就是调用ConsumerInvocationHandler的invoke方法
//可以通过自定义Annotation,如@RefProxy,Annotation处理器可以通过Proxy.newProxyInstance获取代理对象,注入给userApi,
UserApi userApi = (UserApi) Proxy.newProxyInstance(ConsumerInvocationHandler.class.getClassLoader(),
new Class[]{UserApi.class}, new ConsumerInvocationHandler());
//定义查询参数
User queryUser = new User();
queryUser.setId(2);
//接口调用,会走到ConsumerInvocationHandler的invoke方法,然后通过socket调用服务端的方法,并得到结果
User resultUser = userApi.queryUser(queryUser);
//打印结果
System.out.println(resultUser);
}
}
测试
1. 服务端运行RpcPublisher,如图
2. 客户端运行 UserConsumer,如图