RPC(远程过程调用)简单实现

原理:

    利用JAVA NIO Socket,CGLIB代理,反射,以及Spring容器技术等完成简单实现。
    
注意:以下代码异常处理都是直接抛出,真实项目开发请try...cathc捕获异常,并在fiannly中关闭通道(Channel)。


第一步:定义一个接口:

package com.stock.test.jdk8.proxy;
public interface HelloInterface {

    String say(String content);
}

远传调用服务端的实现:

package com.stock.test.jdk8.proxy;
import org.springframework.stereotype.Service;
@Service
public class Hello implements HelloInterface{
    @Override
    public String say(String content) {
        String returnContent = "say:"+content;
        return returnContent;
    }
}

第二步:封装定义远程过程调用客户端与服务端传递的参数类(注意:此处的get和set方法省略)

package com.stock.test.jdk8.proxy;
import com.stock.common.json.JsonUtils;
import com.stock.common.list.CollectionUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class ProxyParam  implements Serializable {
    private static final long serialVersionUID = -1L;
    private String target;//目标接口
    private String returnType;//返回值类型
    private List paramTypes;//目标方法参数类型
    private Object[] params;//目标方法参数列表
    private String methodName;//目标方法名称

    /**
     * 增加被调用方法的参数类型,按顺序放
     * @param clazz Class
     */
    public void addParamType(String clazz){
        if(CollectionUtils.isEmpty(paramTypes)){
            paramTypes = new ArrayList<>();
        }
        paramTypes.add(clazz);
    }

    public static void main(String[] args){
        System.out.println(ProxyParam.class.getCanonicalName());

    }
}

第三步:代理客户端

package com.stock.test.jdk8.proxy.dynamicProxy;
import com.stock.test.jdk8.proxy.ProxyParam;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 *
 * 远程代理调用客户端
 *
 */
public class ProxyClient {

    /**
     *  执行代理
     * @param param ProxyParam
     * @return Object 接口返回对象
     * @throws IOException
     */
    public static Object proxy(ProxyParam param) throws IOException {

        // 1、获取网络通信通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
        socketChannel.configureBlocking(false);//2、切换成非阻塞模式
        ByteBuffer buffer = ByteBuffer.allocate(10240);//3、分配指定大小的缓冲区
        buffer.put(param.toString().getBytes("utf-8"));  //4、发送数据给服务端(ByteBuffer 默认是写模式)
        buffer.flip();//切换读模式
        socketChannel.write(buffer);//发送数据
        buffer.clear();//清理缓冲区数据

        socketChannel.shutdownOutput();

        /**接收服务端的反馈*/
        Selector selector = Selector.open();
        socketChannel.register(selector, SelectionKey.OP_READ);
        String receiveMsg = null;
        while (selector.select() > 0){
            Iterator iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey sk = iterator.next();
                if(sk.isReadable()){
                    buffer.clear();
                    SocketChannel sc = (SocketChannel)sk.channel();
                    sc.read(buffer);
                    buffer.flip();
                    receiveMsg =  new String(buffer.array(),0,buffer.limit(),"utf-8");
                    buffer.clear();
                }
                iterator.remove();
            }
            if(StringUtils.isNotBlank(receiveMsg)){
                break;
            }
        }
        // 5 、关闭通道
        socketChannel.close();
        return receiveMsg;
    }
}

第四步:服务端

package com.stock.test.jdk8.proxy.dynamicProxy;

import com.alibaba.fastjson.JSONObject;
import com.stock.common.json.JsonUtils;
import com.stock.common.list.CollectionUtils;
import com.stock.core.utils.ApplicationContextUtil;
import com.stock.test.BaseJunit4Test;
import com.stock.test.jdk8.proxy.ProxyParam;
import org.junit.Test;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.List;


/**
 *
 * 测试代理服务端
 *
 */
public class ProxyServer extends BaseJunit4Test {

    @Test
    public  void server() throws IOException {

        // 1、获取通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 2、切换成非阻塞模式
        serverSocketChannel.configureBlocking(false);
        // 3、绑定连接
        serverSocketChannel.bind(new InetSocketAddress(9898));

        // 4 、获取选择器
        Selector selector = Selector.open();

        // 5、将通道注册到选择器上,并指定:监听接收事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 6、轮询式的获取选择器上已经“准备就绪”的时间
        while(selector.select() > 0){

            // 7、获取所有注册的选择器(也就是监听的所有的已就绪的监听事件(网络连接状态))
            Iterator iterator =  selector.selectedKeys().iterator();

            //循环处理:已就绪的事件
            while (iterator.hasNext()){

                SelectionKey key = iterator.next();
                // 8、如果是:访问已就绪
                if(key.isAcceptable()){

                    //9、如果是:访问事件已就绪,则获取客户端连接
                    SocketChannel socketChannel = serverSocketChannel.accept();

                    //10、切换客户端通道连接为非阻塞
                    socketChannel.configureBlocking(false);

                    //11、将该客户端通道注册到选择器上
                    socketChannel.register(selector,SelectionKey.OP_READ);

                }else if (key.isReadable()){
                    // 12、获取当前选择器上“读就绪”状态的通道
                    SocketChannel socketChannel = (SocketChannel)key.channel();
                    try {
                        // 13、读取数据
                        ByteBuffer buffer = ByteBuffer.allocate(10240);
                        StringBuffer paramStr = new StringBuffer();
                        int len;
                        while((len = socketChannel.read(buffer)) != -1){
                            buffer.flip();
                            paramStr.append(new String(buffer.array(),0,len,"utf-8"));
                            buffer.clear();
                        }

                        ProxyParam proxyParam = JSONObject.toJavaObject(JSONObject.parseObject(paramStr.toString()), ProxyParam.class);

                       //ystem.out.println(JsonUtils.format(proxyParam));

                        Class target = Class.forName(proxyParam.getTarget());

                        Object targetInstance = ApplicationContextUtil.get().getBean(target);

                        Method targetMethod = target.getMethod(proxyParam.getMethodName(),transformParamTyps(proxyParam.getParamTypes()));

                        Object result = targetMethod.invoke(targetInstance,proxyParam.getParams());
                        buffer.put(result.toString().getBytes("utf-8"));
                        buffer.flip();
                        socketChannel.write(buffer);
                        buffer.clear();
                    }catch (Exception e){
                        e.printStackTrace();
                        ByteBuffer buffer = ByteBuffer.allocate(10240);
                        buffer.put("已收到,发生异常".getBytes("utf-8"));
                        buffer.flip();
                        socketChannel.write(buffer);
                        buffer.clear();
                    }finally {
                        socketChannel.close();
                    }
                }
                iterator.remove();
            }

        }
    }


    /**
     *
     * @param paramType List
     * @return  Class[] 目标方法参数类型数组
     * @throws ClassNotFoundException
     *
     */
    public Class[] transformParamTyps(List paramType) throws ClassNotFoundException {

        if(CollectionUtils.isEmpty(paramType)){
            return null;
        }
        Class[] classes = new Class[paramType.size()];
        for( int i=0;i

第五步:代理简单实现

package com.stock.test.jdk8.proxy.dynamicProxy;

import com.stock.common.json.JsonUtils;
import com.stock.test.jdk8.proxy.ProxyParam;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {

    private Class target;


    public Object getInstance(Class target){
        this.target = target;
        Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
        enhancer.setSuperclass(target);  //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);
        // 创建动态代理类对象并返回
        return enhancer.create();
    }



    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("预处理——————");
        ProxyParam param = new ProxyParam();
        param.setTarget(this.target.getCanonicalName());
        param.setParams(objects);
        param.setMethodName(method.getName());
        param.setReturnType(method.getReturnType().getCanonicalName());
        Class[] paramTypes = method.getParameterTypes();
        if(null != paramTypes && paramTypes.length > 0){
            for(Class paramType : paramTypes){
                System.out.println("method.getParameterTypes()-->"+paramType.getCanonicalName());
                param.addParamType(paramType.getCanonicalName());
            }
        }
        Object result = ProxyClient.proxy(param);
        System.out.println("调用后操作——————");
        return result;
    }

}

 

第六步:测试

package com.stock.test.jdk8.proxy.dynamicProxy;

import com.stock.test.jdk8.proxy.HelloInterface;

public class ProxyMain {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        HelloInterface helloInterface = (HelloInterface)proxy.getInstance(HelloInterface.class);
        String result = helloInterface.say("测试RPC远程过程调用");
        System.err.println(result);
    }
}

 

 

你可能感兴趣的:(RPC,远程过程调用,CGLIB代理,NIO,Socket编程,Channle)