基于JAVA的RPC简单实现。
一、RPC是什么?
RPC即Remote Procedure Call 是一种进程间通信方式。RPC是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议,是分布式系统常见的一种通信方法。RPC采用客户机/服务器模式。
二、RPC框架结构图
二、RPC框架流程图
四、代码实现
1.rpc-framework(父项目)
pom.xml文件,引入依赖。
<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.wc</groupId>
<artifactId>rpcProject-framework</artifactId>
<packaging>pom</packaging>
<version>1.0.0-SNAPSHOT</version>
<modules>
<module>rpc-common</module>
<module>rpc-proto</module>
<module>rpc-codec</module>
<module>rpc-transport</module>
<module>rpc-server</module>
<module>rpc-client</module>
<module>rpc-example</module>
</modules>
<!--版本管理-->
<properties>
<junit.version>4.13</junit.version>
<lombok.version>1.18.8</lombok.version>
<slf4j-api.version>1.7.26</slf4j-api.version>
<logback-classic.version>1.2.3</logback-classic.version>
<commons-io.version>2.5</commons-io.version>
<jetty-servlet.version>9.4.28.v20200408</jetty-servlet.version>
<fastjson.version>1.2.62</fastjson.version>
<java.version>1.8</java.version>
</properties>
<!--通用的依赖-->
<dependencies>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!--lombok插件是为了方便实体类bean快速生成get set方法,从而不用手动添加-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback-classic.version}</version>
</dependency>
</dependencies>
<!--子项目中可能用到的依赖-->
<dependencyManagement>
<dependencies>
<!-- dubbo相关依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.4</version>
</dependency>
<!--处理io流的工具-->
<dependency>
<groupId>common-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
<!-- <version>${commons-io.version}</version>-->
</dependency>
<!--服务器jetty-->
<dependency>
<groupId>org.eclipose.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty-servlet.version}</version>
</dependency>
<!--序列化-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!---->
<build>
<plugins>
<!--通过 maven-compiler-plugin 插件来对 Java 代码编译的-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>${java.version}</source> <!-- 源代码使用的JDK版本 -->
<target>${java.version}</target> <!-- 需要生成的目标class文件的编译版本 -->
<encoding>UTF-8</encoding><!-- 字符集编码 -->
</configuration>
</plugin>
</plugins>
</build>
</project>
2.rpc-common(公共模块)
ReflectionUtils(反射工具类)
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
/**
* 反射工具类
*/
@Slf4j
public class ReflectionUtils {
/**
* 根据传入的Class创建相应的对象
* @param clazz 待创建对象的类
* @param <T> 对象类型
* @return 创建的对象
*/
public static <T> T newInstance(Class<T> clazz) throws Exception{
try{
return clazz.newInstance();
}catch (Exception e){
log.error(e.getMessage(),e);
throw new IllegalStateException(e);
}
}
/**
* 获取传入类自有的公共方法
* @param clazz 传入类
* @return 传入类自有的公共方法
* @throws Exception
*/
public static Method[] getPublicMethods(Class clazz)throws Exception{
try{
//调用getMethods方法输出的是自身的public方法和父类Object的public方法
//调用getDeclaredMethods方法输出的是自身的public、protected、private方法
Method[] methods=clazz.getDeclaredMethods();
ArrayList<Method> list=new ArrayList<>();
for(Method m:methods){
//用Modifier类的函数来判断它是否属被某个修饰符修饰
//用getModifiers()得到了类(或者字段、或者方法)的访问修饰符int值
if(Modifier.isPublic(m.getModifiers())){
list.add(m);
}
}
return list.toArray(new Method[0]);
}catch (Exception e){
log.error(e.getMessage(),e);
throw new IllegalStateException(e);
}
}
/**
* 调用传入对象的传入方法(静态方法obj传null)
* @param obj 传入对象
* @param method 待调用方法
* @param args 传入参数
* @return
* @throws Exception
*/
public static Object invoke(Object obj,Method method,Object... args)throws Exception{
try{
return method.invoke(obj,args);
}catch (Exception e){
log.error(e.getMessage(),e);
throw new IllegalStateException(e);
}
}
}
3.rpc-codec(序列化模块)
对网络传输的数据进行序列化和反序列化,示例中是基于fastjson实现的序列化和反序列,也可以根据其它的依赖或自己定义来实现序列化和反序列化。注:如果是使用IO流来实现序列化和反序列,则可以不需要该模块。
反序列化接口
/**
* 反序列化接口
*/
public interface Decoder {
/**
* 将序列化的字节数组转为对象
* @param bytes
* @param clazz
* @param <T>
* @return
*/
<T>T decode(byte[] bytes,Class<T> clazz);
}
基于fastjson的反序列化实现
import com.alibaba.fastjson.JSON;
/**
* 基于JSON的反序列化实现
*/
public class JSONDecoder implements Decoder{
/**
* 将序列化的字节数组转为对象
* @param bytes
* @param clazz
* @param <T>
* @return
*/
@Override
public <T> T decode(byte[] bytes, Class<T> clazz) {
return JSON.parseObject(bytes,clazz);
}
}
序列化接口
/**
* 序列化接口
*/
public interface Encoder {
/**
* 将对象转为字节数组
* @param obj
* @return
*/
byte[] encode(Object obj);
}
基于fastjson的序列化实现
import com.alibaba.fastjson.JSON;
/**
* 基于Json的序列化实现
*/
public class JSONEncoder implements Encoder {
/**
* 将对象转为字节数组
* @param obj
* @return
*/
@Override
public byte[] encode(Object obj) {
return JSON.toJSONBytes(obj);
}
}
4.rpc-proto(协议模块)
对网络传输中请求和响应里的信息进行封装,示例中是自定义的。注:可以根据需求自定义,也可以继承原生HttpRequest和HttpResponse来实现或者使用第三方的。该模块如不需要,可去掉。
网络传输的一个端点
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 表示网络传输的一个端点
*/
@Data//属性getset方法,ToString方法
@AllArgsConstructor//创建一个带所有字段的构造方法
@NoArgsConstructor//创建一个无参构造方法
public class Peer {
private String host;//网络传输地址
private int port;//网络传输端口
}
请求服务类
/**
* 表示请求服务
*/
@Data//属性getset方法,ToString方法
@AllArgsConstructor//创建一个带所有字段的构造方法
@NoArgsConstructor//创建一个无参构造方法
public class ServiceDescriptor {
private String clazz;//类
private String method;//方法
private String returnType;//返回方式
private String[] parameterTypes;//返回参数
public static ServiceDescriptor from(Class clazz, Method method){
ServiceDescriptor sd=new ServiceDescriptor();
sd.setClazz(clazz.getName());
sd.setMethod(method.getName());
sd.setReturnType(method.getReturnType().getName());
Class[] parameterClass=method.getParameterTypes();
String[] parameterArr=new String[parameterClass.length];
for(int i=0;i<parameterClass.length;i++){
parameterArr[i]=parameterClass[i].getName();
}
sd.setParameterTypes(parameterArr);
return sd;
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
if(this==obj){
return true;
}else if(null==obj||getClass()!=obj.getClass()){
return false;
}else{
ServiceDescriptor that= (ServiceDescriptor) obj;
return this.toString().equals(that.toString());
}
}
@Override
public String toString() {
return "clazz="+clazz+",method="+method+",returnType="+returnType+",parameterTypes="+Arrays.toString(parameterTypes);
}
}
请求封装类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 表示RPC的一个请求
*/
@Data//属性getset方法,ToString方法
@AllArgsConstructor//创建一个带所有字段的构造方法
@NoArgsConstructor//创建一个无参构造方法
public class Request {
private ServiceDescriptor service;//请求服务
private Object[] parameters;//请求参数
}
响应封装类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 表示RPC的一个响应
*/
@Data//属性getset方法,ToString方法
@AllArgsConstructor//创建一个带所有字段的构造方法
@NoArgsConstructor//创建一个无参构造方法
public class Response {
private int code=-1;//响应码,0-成功,非0失败
private String message="请求失败";//响应信息
private Object data;//响应数据
//响应集
public static final int FAIL=-1;//失败
public static final int SUCC=0;//成功
public static final int ERROR=-999;//系统错误
}
5.rpc-transport(网络通信模块)
该模块是处理服务器和客户端的网络通信方式,本案例使用的是Http协议的网络通信方式,如有需要也可以使用别的通信方式。服务器使用了jetty,如有需要
pom.xml
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty-servlet.version}</version>
</dependency>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-proto</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
网络请求服务端接口
/**
* 网络请求服务端接口
*/
public interface TransportServer {
/**
* 初始化
* @param port
* @param handler
*/
void init(int port,RequestHandler handler);
/**
* 启动服务并监听
*/
void satrt();
/**
* 结束服务
*/
void stop();
}
网络请求处理器接口
import java.io.InputStream;
import java.io.OutputStream;
/**
* 网络请求处理器:处理网络请求的Handler
*/
public interface RequestHandler {
/**
*
* @param inputStream
* @param outputStream
*/
void onRequest(InputStream inputStream, OutputStream outputStream);
}
Http网络服务器实现
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* http协议的网络服务端(http是短连接)
*/
@Slf4j
public class HttpTransportServer implements TransportServer {
private RequestHandler handler;//请求处理器
private Server server;//jetty服务器
@Override
public void init(int port, RequestHandler handler) {
this.handler=handler;
this.server=new Server(port);
//servlet接收请求
ServletContextHandler ctx=new ServletContextHandler();
server.setHandler(ctx);
//服务台
ServletHolder holder=new ServletHolder(new RequestServlet());
ctx.addServlet(holder,"/*");
}
@Override
public void satrt() {
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage(),e);
}
}
@Override
public void stop() {
try {
server.stop();
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage(),e);
}
}
/**
*
*/
class RequestServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log. info("client connect") ;
ServletInputStream input = req.getInputStream();
ServletOutputStream output = resp.getOutputStream();
if(handler!=null){
handler.onRequest(input,output);
}
output.flush();
}
}
}
网络请求客户端接口
import com.wc.rpc.proto.Peer;
import java.io.InputStream;
/**
* 网络请求客户端接口
*/
public interface TransportClient {
/**
* 创建连接
* @param peer
*/
void connect(Peer peer);
/**
* 发送数据,处理响应
* @param inputStream
* @return
*/
InputStream write(InputStream inputStream);
/**
* 关闭连接
*/
void close();
}
http协议的网络客户端实现
import com.wc.rpc.proto.Peer;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* http协议的网络客户端(http是短连接)
*/
public class HttpTransportClient implements TransportClient{
private String url;//请求地址+端口
@Override
public void connect(Peer peer) {
this.url="http://"+peer.getHost()+":"+peer.getPort();
}
@Override
public InputStream write(InputStream inputStream) {
try {
HttpURLConnection httpConn = (HttpURLConnection) new URL(url).openConnection();
httpConn.setDoOutput(true);//开启输出
httpConn.setDoInput(true);//开启读取
httpConn.setUseCaches(false);//关闭缓存
httpConn.setRequestMethod("POST");//设置请求方法:POST
httpConn.connect();//连接
IOUtils.copy(inputStream,httpConn.getOutputStream());//发送数据
int resultCode=httpConn.getResponseCode();//获取响应码
if(HttpURLConnection.HTTP_OK==resultCode){
return httpConn.getInputStream();
}else{
return httpConn.getErrorStream();
}
} catch (IOException e) {
//e.printStackTrace();
throw new IllegalStateException(e);
}
}
@Override
public void close() {
}
}
6.rpc-server(服务器模块)
该模块负责注册生产者,接收消费者发送过来的请求,对请求进行识别处理并返回组装响应的结果。如果序列化与反序列化未使用自定义的,那该模块的中的相关代码可以去除。
pom.xml
<dependencies>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-codec</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-proto</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-transport</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
</dependencies>
server配置
import com.wc.rpc.codec.Decoder;
import com.wc.rpc.codec.Encoder;
import com.wc.rpc.codec.JSONDecoder;
import com.wc.rpc.codec.JSONEncoder;
import com.wc.rpc.transport.HttpTransportServer;
import com.wc.rpc.transport.TransportServer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* server配置
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RpcServerConfig {
private Class<? extends TransportServer> transportClass= HttpTransportServer.class;
private Class<? extends Encoder> encodeClass= JSONEncoder.class;
private Class<? extends Decoder> decodeClass= JSONDecoder.class;
private int port=3000;//服务器端口
}
生产者对象类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
/**
* 表示具体服务
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ServiceInstance {
private Object target;//对象
private Method method;//方法
}
对象管理类,负责注册生产者和查询对应的对象方法。
import com.wc.rpc.common.utils.ReflectionUtils;
import com.wc.rpc.proto.Request;
import com.wc.rpc.proto.ServiceDescriptor;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 管理rpc暴露的服务
*/
@Slf4j
public class ServiceManager {
private Map<ServiceDescriptor,ServiceInstance> service;
public ServiceManager(){
this.service=new ConcurrentHashMap<>();
}
/**
* 将接口和对应的对象绑定到一起,并放入service中
* @param interfaceClass 接口的class
* @param bean 接口具体实现的子类的对象
*/
public <T> void register(Class<T> interfaceClass,T bean) {
try {
Method[] methods = ReflectionUtils.getPublicMethods(interfaceClass);
for(Method method:methods){
ServiceInstance sis = new ServiceInstance(bean, method);
ServiceDescriptor sd = ServiceDescriptor.from(interfaceClass, method);
service.put(sd,sis);
log.info("register service:{} {}",sd.getClazz(),sd.getMethod());
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage(),e);
}
}
public ServiceInstance lookup(Request request){
ServiceDescriptor sd=request.getService();
return service.get(sd);
}
}
Rpc服务器类
import com.wc.rpc.codec.Decoder;
import com.wc.rpc.codec.Encoder;
import com.wc.rpc.common.utils.ReflectionUtils;
import com.wc.rpc.proto.Request;
import com.wc.rpc.proto.Response;
import com.wc.rpc.transport.RequestHandler;
import com.wc.rpc.transport.TransportServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.io.OutputStream;
@Slf4j
public class RpcServer {
private RpcServerConfig config;
private TransportServer netServer;
private Encoder encoder;
private Decoder decoder;
private ServiceManager manager;
private RequestHandler handler=new RequestHandler(){
@Override
public void onRequest(InputStream inputStream, OutputStream outputStream) {
//生成响应对象
Response response=new Response();
try {
//获取传入数据
byte[] inBytes = IOUtils.readFully(inputStream, inputStream.available());
//反序列化生成请求对象
Request request=decoder.decode(inBytes,Request.class);
log.info("get Request:{}",request);
ServiceInstance instance = manager.lookup(request);
if(instance!=null){
//调用方法
Object obj =ReflectionUtils.invoke(instance.getTarget(),instance.getMethod(),request.getParameters())
response.setData(obj);
response.setCode(Response.SUCC);
response.setMessage("成功");
}else{
response.setCode(Response.FAIL);
response.setMessage("未找到该方法");
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage(),e);
response.setCode(Response.ERROR);
response.setMessage("error:"+e.getClass().getName()+":"+e.getMessage());
}finally{
try{
byte[] btyes = encoder.encode(response);
outputStream.write(btyes);
}catch (Exception e){
e.printStackTrace();
log.error(e.getMessage(),e);
}
}
}
};
public RpcServer(){
this(new RpcServerConfig());
}
public RpcServer(RpcServerConfig config) {
try{
this.config = config;
//网络服务
this.netServer= ReflectionUtils.newInstance(config.getTransportClass());
this.netServer.init(config.getPort(),this.handler);
//序列化
this.encoder=ReflectionUtils.newInstance(config.getEncodeClass());
this.decoder=ReflectionUtils.newInstance(config.getDecodeClass());
//服务管理
this.manager=new ServiceManager();
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage(),e);
}
}
/**
* 注册方法
* @param interfaceClass
* @param interfaceImplClass
* @param <T>
*/
public <T> void register(Class<T> interfaceClass,Class<?> interfaceImplClass) {
try {
T bean= (T) interfaceImplClass.newInstance();
manager.register(interfaceClass,bean);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 启动方法
*/
public void start(){
netServer.satrt();
}
/**
* 停止方法
*/
public void stop(){
netServer.stop();
}
}
7.rpc-client(客户端模块)
该模块负责连接服务器,将请求方法发送给服务器,对返回的结果进行解析并返给消费者。如果序列化与反序列化未使用自定义的,那该模块的中的相关代码可以去除。
pom.xml
<dependencies>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-codec</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-proto</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-transport</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
</dependencies>
客户端配置
import com.wc.rpc.codec.Decoder;
import com.wc.rpc.codec.Encoder;
import com.wc.rpc.codec.JSONDecoder;
import com.wc.rpc.codec.JSONEncoder;
import com.wc.rpc.proto.Peer;
import com.wc.rpc.transport.HttpTransportClient;
import com.wc.rpc.transport.TransportClient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Arrays;
import java.util.List;
/**
* client配置
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RpcClientConfig {
private Class<? extends TransportClient> transportClass= HttpTransportClient.class;
private Class<? extends Encoder> encodeClass= JSONEncoder.class;
private Class<? extends Decoder> decodeClass= JSONDecoder.class;
private Class<? extends TransportSelector> selectorClass=RandomTransportSelector.class;
private int connectCount=1;
private List<Peer> servers= Arrays.asList(new Peer("127.0.0.1",3000));
}
连接服务器的接口
import com.wc.rpc.proto.Peer;
import com.wc.rpc.transport.TransportClient;
import java.util.List;
/**
* 表示选择哪个server去连接
*/
public interface TransportSelector {
/**
* 初始化selector
* @param peers 可以连接的server端点信息
* @param count client与server可以建立多少个连接
* @param clazz client实现类的class
*/
void init(List<Peer> peers,int count,Class<? extends TransportClient> clazz);
/**
* 关闭selector
*/
void close();
/**
* 选择一个transport与server交互
* @return
*/
TransportClient select();
/**
* 释放用完的client
* @param client
*/
void relase(TransportClient client);
}
TransportSelector的实现类,用于存储已经连接好的client
import com.wc.rpc.common.utils.ReflectionUtils;
import com.wc.rpc.proto.Peer;
import com.wc.rpc.transport.TransportClient;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* TransportSelector的实现类
*/
@Slf4j
public class RandomTransportSelector implements TransportSelector {
/**
* 用于存储已经连接好的client
*/
private List<TransportClient> clients;
@Override
public synchronized void init(List<Peer> peers, int count, Class<? extends TransportClient> clazz) {
count=Math.max(count,1);//两者取最大
try {
for(Peer peer:peers){
for(int i=0;i<count;i++){
TransportClient client= ReflectionUtils.newInstance(clazz);
client.connect(peer);
clients.add(client);
}
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage(),e);
}
}
@Override
public synchronized void close() {
for(TransportClient client:clients){
client.close();
}
clients.clear();
}
@Override
public synchronized TransportClient select() {
int i=new Random().nextInt(clients.size());//随机
return clients.remove(i);
}
@Override
public synchronized void relase(TransportClient client) {
clients.add(client);
}
public RandomTransportSelector() {
super();
clients=new ArrayList<>();
}
}
rpc客户端类
import com.wc.rpc.codec.Decoder;
import com.wc.rpc.codec.Encoder;
import com.wc.rpc.common.utils.ReflectionUtils;
import com.wc.rpc.proto.Peer;
import com.wc.rpc.proto.Request;
import com.wc.rpc.proto.Response;
import com.wc.rpc.proto.ServiceDescriptor;
import com.wc.rpc.transport.TransportClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@Slf4j
public class RpcClient {
private RpcClientConfig config;
private Encoder encoder;
private Decoder decoder;
private TransportSelector selector;
/**
* 无参构造方法
*/
public RpcClient(){
this(new RpcClientConfig());
}
/**
* 有参构造方法
*/
public RpcClient(RpcClientConfig rpcClientConfig){
try{
this.config=rpcClientConfig;
this.decoder=ReflectionUtils.newInstance(this.config.getDecodeClass());
this.encoder=ReflectionUtils.newInstance(this.config.getEncodeClass());
this.selector=ReflectionUtils.newInstance(this.config.getSelectorClass());
this.selector.init(this.config.getServers(),this.config.getConnectCount(),this.config.getTransportClass());
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage(),e);
}
}
/**
* 获取接口代理对象
* @param clazz
* @return
*/
public <T> T getProxy(Class<T> clazz){
return (T) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//构造请求
Request request=new Request();
request.setService(ServiceDescriptor.from(clazz,method));
request.setParameters(args);
//通过网络把请求发送给server,等待server的响应
Response response=invokeRemote(request);
//获取数据
if(response==null||response.getCode()!=0){
throw new IllegalStateException("fail:"+response);
}
return response.getData();
}
/**
* 网络传输
* @param request
* @return
*/
private Response invokeRemote(Request request) {
TransportClient client=null;
Response response=null;
try{
client=selector.select();
byte[] bytes = encoder.encode(request);
//把请求发送给server,等待server的响应
Peer peer = config.getServers().get(0);
client.connect(peer);
InputStream revice = client.write(new ByteArrayInputStream(bytes));
//返回结果
byte[] resBytes = IOUtils.readFully(revice, revice.available());
response = decoder.decode(resBytes, Response.class);
}catch (Exception e){
e.printStackTrace();
log.error(e.getMessage(),e);
response=new Response();
response.setCode(Response.ERROR);
response.setMessage("invokeRemote Error:"+e.getClass()+":"+e.getMessage());
return response;
}finally {
if(client!=null){
selector.relase(client);
}
}
return response;
}
});
}
}
8.rpc-demo(示例模块)
模拟外部调用
pom.xml
<dependencies>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.wc</groupId>
<artifactId>rpc-server</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
示例接口
public interface DemoService {
/**
* 示例方法01
* @param a
* @param b
* @return
*/
String method01(int a, int b);
/**
* 示例方法02
* @param c
* @param d
* @return
*/
String method02(int c, int d);
}
示例接口实现类
public class DemoServiceImpl implements DemoService {
@Override
public String method01(int a, int b) {
return "a="+a+",b="+b;
}
@Override
public String method02(int c, int d) {
return "c="+c+",d="+d;
}
}
示例中的生产者
import com.wc.rpc.server.RpcServer;
import com.wc.rpc.server.RpcServerConfig;
public class Provider{
public static void main(String[] args){
try{
//配置服务器
RpcServerConfig config=new RpcServerConfig();
config.setDecodeClass(JSONDecoder.class);
config.setEncodeClass(JSONEncoder.class);
config.setTransportClass(HttpTransportServer.class);
config.setPort(3000);
//生成带配置的服务器对象
RpcServer server=new RpcServer(config);
//将接口与对应的实现放入服务注册中心
server.register(DemoService.class,DemoServiceImpl.class);
//启动
server.start();
}catch (Exception e){
e.printStackTrace();
}
}
}
示例中的消费者
import com.wc.rpc.client.RandomTransportSelector;
import com.wc.rpc.client.RpcClient;
import com.wc.rpc.client.RpcClientConfig;
import com.wc.rpc.codec.JSONDecoder;
import com.wc.rpc.codec.JSONEncoder;
import com.wc.rpc.proto.Peer;
import com.wc.rpc.transport.TransportClient;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class Consumer{
public static void main(String[] args) throws Exception {
//配置客户端,和连接的服务器
RpcClientConfig config=new RpcClientConfig();
config.setDecodeClass(JSONDecoder.class);
config.setEncodeClass(JSONEncoder.class);
config.setTransportClass(HttpTransportClient.class);
config.setSelectorClass(RandomTransportSelector.class);
List<Peer> list=new ArrayList<Peer>();
list.add(new Peer("127.0.0.1",3000));
config.setServers(list);
//生成带配置的客户端对象
RpcClient client = new RpcClient(config);
//通过动态代理调用服务注册中心里相应的方法实现
CalcService service =client.getProxy(DemoService.class);
String method01= service.method01(1, 2);
String method02= service.method02(10, 5);
log.info("method01:"+method01);
log.info("method02:"+method02);
}
}
总结
难点:动态代理。