基于JAVA的RPC简单实现

1 篇文章 0 订阅

基于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);
    }
}

 

总结

难点:动态代理。
 

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值