手写RPC之集成Spring(仿mybatis集成spring)

本文详细介绍了一个手写的RPC框架,包括服务端与客户端的实现过程,集成了Zookeeper进行服务发现与负载均衡,提供了完整的测试步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、服务端

1.修改服务提供方

package com.MyRpc.Server;

import com.MyRpc.Common.CloseUtils;
import com.MyRpc.Common.RpcServiceRunnable;
import org.I0Itec.zkclient.ZkClient;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RpcServer  extends Thread{
    // 端口号 利用Spring注入
    private Integer port;
    // IP地址 利用Spring注入
    private String Address;
    public void startServerSocket(){
        ServerSocket serverSocket=null;
        ThreadPoolExecutor executor=null;
        try{
        // 创建ServerSocket链接
            serverSocket = new ServerSocket(port);
            // 注册服务
            register();
            // 创建线程池
            executor=new ThreadPoolExecutor(5,10,60, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(10));
            while (true){
                // 获取客户端链接
                Socket accept = serverSocket.accept();
                RpcServiceRunnable server = new RpcServiceRunnable(accept);
                // 使用线程池执行任务
                executor.execute(server);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            CloseUtils.close(serverSocket,executor,null,null, null,null);
        }

    }

    private void register(){
        ZkClient client=new ZkClient(Address,30000,1000);
        String s = "/dubbo";
        if (!client.exists(s))
            // 创建持久化节点
            client.createPersistent(s);
        InetAddress localHost=null;
        try {
            // 获取本机主机地址
            localHost = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            throw new RuntimeException("获取本机ip地址失败");
        }
        String ip = localHost.getHostAddress();
        String service = ip + ":" + this.port;
        System.out.println("service = " + service);
        // 创建临时节点
        String path = s + "/" + service;
        // 如果存在过此临时节点删除
        if (client.exists(path))
            client.delete(path);
        // 创建临时节点 节点名称ip+端口号 值ip+端口号
        client.createEphemeral(path,service);
    }

    public Integer getPort() {
        return port;
    }

    public RpcServer(Integer port, String address) {
        this.port = port;
        Address = address;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public void run() {
        startServerSocket();
    }

}

2.具体执行的业务逻辑

package com.MyRpc.Common;

import com.MyRpc.Common.bean.RpcRequest;
import com.MyRpc.Common.bean.RpcResponse;
import com.MyRpc.Server.RpcServerFactory;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.Socket;


public class RpcServiceRunnable implements Runnable{
    private Socket client;
    public RpcServiceRunnable(Socket client) {
        this.client=client;
    }

    public void run() {
        InputStream in=null;
        OutputStream out=null;
        HessianInput hessianInput=null;
        HessianOutput hessianOutput=null;
        RpcResponse response=new RpcResponse();
        try {
        // 获取请求的流信息
        in=client.getInputStream();
        hessianInput=new HessianInput(in);
         // 准备响应相关的流和序列化技术
        out=client.getOutputStream();
        hessianOutput=new HessianOutput(out);
        // 获取请求的具体信息,转换成RPCRequest对象
        Object object = hessianInput.readObject();
        if (!(object instanceof RpcRequest)){
           setError(response,hessianOutput,"非法参数");
            return;
        }
        // 请求信息对象
        RpcRequest request= (RpcRequest) object;
            String className = request.getClassName();
            Class<?> aClass = Class.forName(className);
            // 找到接口的实现类
        Object impl = RpcServerFactory.getBean(aClass);
        if (impl==null){
            setError(response,hessianOutput,"没有对应的实体类");
            return;
        }
        //  从动态代理获取真实对象
            Object target = AopTargetUtils.getTarget(impl);
        if (target!=null){
            impl=target;
        }
            Class<?> implClass = impl.getClass();
        // 如果类上没有RPC注解
            if (!implClass.isAnnotationPresent(Rpc.class)) {
                throw new RuntimeException("该没有服务提供");
            }
                Method method = implClass.getMethod(request.getMethodName(), request.getParamsType());
        if (method==null){
            setError(response,hessianOutput,"没有找到对应的方法");
            return;
        }
        // 返回结果
        Object result = method.invoke(impl, request.getParams());
        response.setResult(result);
            hessianOutput.writeObject(response);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            CloseUtils.close(null,null,in,out,hessianInput,hessianOutput);
        }

    }
    private void setError(RpcResponse response,HessianOutput out,String error){
        response.setError(new Exception(error));
        try {
            out.writeObject(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


3.实现ApplicationContextAware接口

package com.MyRpc.Server;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.List;


public class RpcServerFactory implements ApplicationContextAware {
        private Integer port;
        private String address;
        private List<String> classNames;
        private static ApplicationContext applicationContext;
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;

    }
    public static <T> T getBean(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }
    public void init(){
        RpcServer server=new RpcServer(port,address);
        // 由于Server会一直阻塞,需要重新开启一个线程来启动serverSocket
        server.start();
    }
    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<String> getClassNames() {
        return classNames;
    }

    public void setClassNames(List<String> classNames) {
        this.classNames = classNames;
    }
}

4.判断是否是代理对象工具类

package com.MyRpc.Common;

import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;

import java.lang.reflect.Field;


public class AopTargetUtils {


    /**
     * 获取 目标对象
     * @param proxy 代理对象
     * @return
     * @throws Exception
     */
    public static Object getTarget(Object proxy) throws Exception {

        if(!AopUtils.isAopProxy(proxy)) {
            return proxy;//不是代理对象
        }

        if(AopUtils.isJdkDynamicProxy(proxy)) {
            return getJdkDynamicProxyTargetObject(proxy);
        } else { //cglib
            return getCglibProxyTargetObject(proxy);
        }



    }


    private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        h.setAccessible(true);
        Object dynamicAdvisedInterceptor = h.get(proxy);

        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advised.setAccessible(true);

        Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();

        return target;
    }


    private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
        h.setAccessible(true);
        AopProxy aopProxy = (AopProxy) h.get(proxy);

        Field advised = aopProxy.getClass().getDeclaredField("advised");
        advised.setAccessible(true);

        Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();

        return target;
    }

}

二、客户端

1.客户端业务逻辑

package com.MyRpc.client;

import com.MyRpc.Common.CloseUtils;
import com.MyRpc.Common.bean.RpcRequest;
import com.MyRpc.Common.bean.RpcResponse;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;


public class RpcClient  {

    public Object start(RpcRequest request,String host,int port) throws Exception {
        InputStream in=null;
        OutputStream out=null;
        HessianInput hessianInput=null;
        HessianOutput hessianOutput=null;
        try{
        Socket socket = new Socket(host, port);
        // 封装请求信息
        out=socket.getOutputStream();
        hessianOutput=new HessianOutput(out);
            // 发起请求
            hessianOutput.writeObject(request);
            // 获取响应信息
        in=socket.getInputStream();
        hessianInput=new HessianInput(in);
            System.out.println("获取响应信息");
        Object object = hessianInput.readObject();
        if (!(object instanceof RpcResponse)){
            throw new RuntimeException("服务器参数格式不正确");
        }
        RpcResponse response= (RpcResponse) object;
        // 判断服务器是否存在异常
        if (response.getError()!=null)
                throw new RuntimeException("服务器繁忙");
        return response.getResult();

    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException("客户端发生异常");
    }finally {
            CloseUtils.close(null,null,in,out,hessianInput,hessianOutput);
        }

    }


}

2.生成客户端代理对象

package com.MyRpc.Common;

import com.MyRpc.Common.bean.RpcRequest;
import com.MyRpc.client.RpcClient;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;


public class RpcClientProxy implements InvocationHandler {
    private List<String> list = new LinkedList<String>();
    private String address;


    public RpcClientProxy() {

    }

    /**
     * @Description: 创建接口的代理对象
     * @Params [clazz]
     * @Return T
     * @Author 
     * @Date 2020/4/26 21:20
     */
    public <T> T getProxyObject(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
    }

    private void initServer() {
        final ZkClient client = new ZkClient(address, 30000, 1000);
        final String path = "/dubbo";
        if (!client.exists(path))
            throw new RuntimeException("没有对应的服务器提供服务");
        // 清空服务器列表
        list.clear();
        final List<String> children = client.getChildren(path);
        for (String child : children) {
            // 将服务器列表放入集合
            list.add(client.readData(path + "/" + child).toString());
        }
        // 订阅节点监听 子节点改变后刷新集合
        client.subscribeChildChanges(path, new IZkChildListener() {
            public void handleChildChange(String s, List<String> ChildrenList) throws Exception {
                // 清空列表
                list.clear();
                for (String child : ChildrenList) {
                    // 将服务器列表放入集合
                    list.add(client.readData(path + "/" + child).toString());
                }
                System.out.println("-----------------server change---------------" + list);
            }
        });

    }

    private int count = 0;

    public String getServer() {
        initServer();
        System.out.println(list.size());
        //负载均衡策略 轮询
        int index = count % list.size();
        count++;
        System.out.println("index = " + index);
        // 负载均衡策略 随机
//    int index= new Random().nextInt(list.size());
        return list.get(index);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RpcRequest request = new RpcRequest();
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setParamsType(method.getParameterTypes());
        request.setParams(args);
        String server = getServer();
        String[] split = server.split(":");
        // 使用客户端发起请求
        RpcClient rpc = new RpcClient();
        return rpc.start(request, split[0], Integer.valueOf(split[1]));
    }

    public RpcClientProxy(String address) {
        this.address = address;
    }
}

3.实现FactoryBean接口

package com.MyRpc.client;

import com.MyRpc.Common.RpcClientProxy;
import org.springframework.beans.factory.FactoryBean;


public class RpcClientFactory<T> implements FactoryBean<T> {
    private String address;
    private Class<T> interfaceClass;
    public T getObject() throws Exception {
        return new RpcClientProxy(address).getProxyObject(interfaceClass);
    }

    public Class<?> getObjectType() {
        return interfaceClass;
    }

    public boolean isSingleton() {
        return true;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Class<T> getInterfaceClass() {
        return interfaceClass;
    }

    public void setInterfaceClass(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }
}

4.实现BeanDefinitionRegistryPostProcessor, ApplicationContextAware接口

package com.MyRpc.client;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.List;


public class RpcClientManager implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
    private ApplicationContext applicationContext;
    private List<String> classNames;
    private String address;
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        for (String className : classNames) {
            Class clazz=null;
            try {
               clazz= Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            //  根据接口创建BeanDefinitionBuilder
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            // 通过构建对象获取GenericBeanDefinition
            GenericBeanDefinition definition = (GenericBeanDefinition) beanDefinitionBuilder.getRawBeanDefinition();
            // 注入接口
            definition.getPropertyValues().add("interfaceClass",definition.getBeanClassName());
            // 注入zookeeper的服务器地址
            definition.getPropertyValues().add("address",address);
            // 注入工厂的bean
            definition.setBeanClass(RpcClientFactory.class);
            // 设置类型注入
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            // 注册bean
            beanDefinitionRegistry.registerBeanDefinition(clazz.getSimpleName(),definition);


        }
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }

    public List<String> getClassNames() {
        return classNames;
    }

    public void setClassNames(List<String> classNames) {
        this.classNames = classNames;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

三、测试

1.使用mvn install安装框架到maven仓库

2.引入依赖

    <dependency>
        <groupId>com.myRpc</groupId>
        <artifactId>RPC</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

3.配置服务端applicationContext-rpc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean  class="com.MyRpc.Server.RpcServerFactory" init-method="init">
    <property name="address" value="127.0.0.1:2181"/>
    <property name="port" value="10001"/>
</bean>
<context:component-scan base-package="com.travel.background.test"/>
</beans>

4.测试类接口

package com.travel.background.test;


public interface IHello {
    void sayhello();
}

5.接口实现类

package com.travel.background.test;
@Rpc
@Service
public class HelloService implements IHello {

    @Override
    public void sayhello() {
        System.out.println("执行结束");
    }

}

6.配置客户端applicationContext-rpc.xml

    <bean class="com.MyRpc.client.RpcClientManager">
        <property name="address" value="127.0.0.1:2181"/>
        <property name="classNames">
            <list>
                <value>com.travel.background.test.IHello</value>
            </list>
        </property>
    </bean>

7.测试controller

package com.travel.background.controller;


import com.travel.background.test.IHello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@RequestMapping("hello")
public class HelloController {
    @Autowired
    IHello iHello;
    @RequestMapping("hello")
    @ResponseBody
    public String hello(){
        iHello.sayhello();
        return "hello";
    }
}

手写RPC1
手写RPC2集成Zookeeper

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值