自己实现一个Dubbo

Author Avatar
Rico 12月 20, 2018

Dubbo

Dubbo是一款高性能轻量级的开源Java RPC框架

什么是RPC?

远程过程调用。

HTTP也是一种RPC协议 TCP也是

传统的HTTP调用

需要关心地址,地址变了消费端也要跟着变 ,需要字符串转换等缺点

RPC式的调用

consumer配置

provider配置

特别简单,作为消费者不需要关心地址

dubbo可扩展,它支持多种协议

自己实现

本项目就不采用模块的形式了(可以采用模块的形式) ,采用包代替模块的方式

我们注册中心一般用zookeeper或者eureka 或者redis ,注册中心也是可以扩展的

我们自己实现一个注册中心

注册中心要实现的是保存服务配置,需要保存: 服务名, url, 服务的实现类

形如

{
 服务名://因为可能有多个提供者
   {URL:服务实现类},
   {URL:服务实现类}
}

我们用hashmap来存

一个Java对象要在网络中进行传输,需要序列化

部分源码

注册中心

采用一个hashmap来充当注册中心. 注册中心是独立于服务提供方和服务消费方的,但是这里为了省事,放在同一个工程中了

package org.rico.learnDubbo.register;
import org.rico.learnDubbo.framework.URL;
import java.util.HashMap;
import java.util.Map;
/**
 * 自己实现的一个注册中心,采用map
 * Created by Rico on 2018/12/20.
 */
public class RegisterServer {
    private static Map<String,Map<URL,Class>>   REGISTER =new HashMap<String,Map<URL,Class>>();

    //注册服务
    public static  void register(URL url,String interfaceName,Class implClass){//需要传入URL,服务名,接口名(interface),实现类
        Map<URL,Class> map=new HashMap<URL,Class>();
        map.put(url,implClass);
        System.out.println("服务注册成功!注册的服务接口名为:"+interfaceName+",所在接口类为:"+implClass.getName());
        REGISTER.put(interfaceName,map);
    }

    public static Class get(URL url,String interfaceName){
        return REGISTER.get(interfaceName).get(url);
    }
    //获取可用服务
    public static  URL get(String interfaceName){
      return null;
    }
}

传输协议

http

package org.rico.learnDubbo.protocol.http;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * Created by Rico on 2018/12/20.
 */
public class DispatcherServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.service(req, resp);
        new HttpServerHandler().handler(req,resp);
    }
}
package org.rico.learnDubbo.protocol.http;

import org.apache.commons.io.IOUtils;
import org.rico.learnDubbo.framework.TransferModel;
import org.rico.learnDubbo.framework.URL;
import org.rico.learnDubbo.register.RegisterServer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Created by Rico on 2018/12/20.
 */
public class HttpServerHandler{
    //处理请求和响应
    public void handler(HttpServletRequest req, HttpServletResponse resp){
        //对象通过网络传输
        InputStream inputStream= null;
        try {
            inputStream = req.getInputStream();
            ObjectInputStream objectInputStream=new ObjectInputStream(inputStream);
            TransferModel  transferModel =(TransferModel)objectInputStream.readObject();//二进制数据反序列化成一个对象
            //根据transferModel中的信息去注册中心找接口的实现类
            String interfaceName=transferModel.getInterfaceName();
            URL url=new URL("localhost",8080);
            Class implementClass=RegisterServer.get(url,interfaceName);//根据url和接口名找一个实现类
            //然后new出这个实现类,然后执行一下需要调用的这个方法
            Method method=implementClass.getMethod(transferModel.getMethodName(),transferModel.getParamTypes());//这里Class的getMethod方法的第一个参数:方法的名字,第二个参数,方法的参数类型
            //执行,并保存返回值
            String result=(String)method.invoke(implementClass.newInstance(),transferModel.getParams());//第一个参数:new出来的这个实现类,第二个参数:参数 我们这里因为知道sayHello方法返回的是String,所以强转成String了,实际dubbo框架肯定是Object
            //然后把结果返回,通过outputstream返回给resp
            OutputStream outputStream=resp.getOutputStream();
            IOUtils.write(result,outputStream);//把字符串给output了 这样就完成了一个结果的返回
            //server端就写完了
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            System.out.println("找不到这个类");
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            System.out.println("没有这个方法");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
package org.rico.learnDubbo.protocol.http;

import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;

/**
 * Created by Rico on 2018/12/20.
 */
public class HttpServer {
    public void start(String hostname,Integer port){
        Tomcat tomcat=new Tomcat();

        Server server=tomcat.getServer();
        Service service=server.findService("Tomcat");//默认 叫tomcat

        Connector connector=new Connector();
        connector.setPort(port);

        Engine engine=new StandardEngine();
        engine.setDefaultHost(hostname);

        Host host=new StandardHost();
        host.setName(hostname);

        String contextPath="";
        Context context=new StandardContext();
        context.setPath(contextPath);
        context.addLifecycleListener(new Tomcat.FixContextListener());//加一个tomcat生命周期的监听器

        host.addChild(context);
        engine.addChild(host);

        service.setContainer(engine);
        service.addConnector(connector);
        //tomcat是servlet的容器 所以要加入servlet,servlet用来处理请求的
        tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet());//指定servlet,第二个参数是servlet的名字,第三个参数是指定其实现类
        context.addServletMappingDecoded("/*","dispatcher");//mapping
        try {
            tomcat.start();//只是初始化
            tomcat.getServer().await();//接收请求
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
    }
}
package org.rico.learnDubbo.protocol.http;
import org.apache.commons.io.IOUtils;
import org.rico.learnDubbo.framework.TransferModel;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
/**
 * Created by Rico on 2018/12/21.
 */
public class HttpClient {
    public String post(String hostname, Integer port, TransferModel transferModel) {
        try {
            //发送请求
            URL url = new URL("http", hostname, port, "/");
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("POST");
            httpURLConnection.setDoOutput(true);
            OutputStream outputStream = httpURLConnection.getOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(transferModel);
            objectOutputStream.flush();
            objectOutputStream.close();
            //发送完请求,然后拿到结果
            InputStream inputStream=httpURLConnection.getInputStream();
            return IOUtils.toString(inputStream);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return "";
        } catch (ProtocolException e) {
            e.printStackTrace();
            return "";
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

未完,待续….

目录结构

下面2张图帮助理解源码

源码下载

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://hogwartsrico.github.io/2018/12/20/write-a-dubbo/