• RPC实现原理(HSF、dubbo) 从头开始(一)


    前言

    阔别了很久博客园,虽然看了以前写的很多东西感觉好幼稚,但是还是觉得应该把一些自己觉得有用的东西和大家分享。废话不多说,现在开始进入正题。

    之前的六年工作经验,呆过了一些大公司,每个在大公司呆过的人应该知道,在一个大型应用中不断的增加业务和功能,还有基于性能的考虑,使得很多基础服务必须进行模块化,从而让各子系统方便使用而不是每个系统重新再实现一套,也可以使可能成为瓶颈的基础功能可以单独进行扩展,比如(以电商系统举例)用户信息管理、交易管理中心、商品管理中心等等。  在rpc发展最初,服务进行模块块以后,各个子系统、模块实现的技术五花八门,如:hessian、WebService、Socket、http等进行互相调用,各个子系统之间的交互方式和方法不统一,使得各系统间很难很好的整合。并且这些方式还涉及超时、加密解密、参数的传递等各种问题。  在这种情况下,hsf、dubbo这种高性能rpc中间件出现了。  现在我就已最简单的方式从头开始讲起其中的原理。

    我将分为一个系列为大家进行解剖

    一、RPC实现原理(HSF、dubbo) 从头开始(一)

    二、RPC实现原理(HSF、dubbo)发布一个服务与订阅一个服务(三)

    三、RPC实现原理(HSF、dubbo)zookeeper进行集群配置管理(二)

    四、RPC实现原理(HSF、dubbo)netty替换java socket(四)

    五、待补充

    NO.1  TCP传输协议

    为什么选择TCP作为传输协议?HTTP在TCP的上一层,位于应用层,TCP位于网络层,以越往底层越快的原理,我就不过多解释为什么选择tcp作为传输协议了。 那么在项目中我们怎么使用tcp进行调用呢?直接上个例子代码:

    socket服务端:

     

    import java.net.*;
    import java.io.*;
    
    /**
     * socket编程之:简单socket server
     * 
     * @author chengwei.lcw 2016-11-27
     */
    public class SocketServer {
        private ServerSocket serverSocket;
        private Socket socket;
        private BufferedReader in;
        private PrintWriter out;
    
        public SocketServer() {
            try {
                serverSocket = new ServerSocket(9999);
                while (true) {
                    // 此处会阻塞,后面会讲到nio的作用 
                    socket = serverSocket.accept();
                    in = new BufferedReader(new InputStreamReader(
                            socket.getInputStream()));
                    out = new PrintWriter(socket.getOutputStream(), true);
                    String line = in.readLine();
                    // 打印出来看看结果
                    System.out.println("line:" + line);
                    
                    // 返回给client端,通知我已收到数据
                    out.println("you input is :" + line);
                    out.close();
                    in.close();
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            new SocketServer();
        }
    }

    scoket客户端:

    import java.io.*;
    import java.net.*;
    
    /**
     * socket编程之:简单socket client
     * 
     * @author chengwei.lcw 2016-11-27
     */
    public class SocketClient {
        private Socket socket;
        private BufferedReader in;
        private PrintWriter out;
    
        public SocketClient() {
            try {
                socket = new Socket("127.0.0.1", 9999);
                in = new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));
                out = new PrintWriter(socket.getOutputStream(), true);
                // 向服务端写数据
                BufferedReader line = new BufferedReader(new InputStreamReader(
                        System.in));
    
                out.println(line.readLine());
                line.close();
                // 打印出来服务端发回来的回执
                System.out.println(in.readLine());
                
                in.close();
                out.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            new SocketClient();
        }
    }

    先启动server,再启动client,输入参数,回车,两者第一次会话完成。

    小结总结:

    目前例子中我们使用了标准io socket,这里的很多时候会阻塞,如accept()、read()时都会阻塞。测试的时候可以让客户端睡眠几秒,在这期间启动第二个客户端,这个时候第一个客户端未完成前,第二个客户端是被阻塞在accept()中的。  这种情况可以给每个客户端都单独分配一个线程,但是这样创建过多的线程,可能会严重影响服务器的性能。 第二种解决方案就是使用NIO 非阻塞的通信方式,jdk1.4之后已经引入了这个功能,这样可以使得服务器只要启动一个线程就能处理所有的客户端socket请求。netty就是基于NIO的高性能框架,相比jdk nio做了很多改进,修复了一些缺陷。  (这里不对netty与jdk nio做过多赘述,这不在我们讨论原理细节里,如果大家对这方面有兴趣,我会单独写篇随笔进行深度讲解)

    NO.2 序列化方式

    在真正的项目中,很多时候我们传的都是自己定义的类。在远程通讯中,类的传输我们需要对类进行序列化和反序列化。序列化的方式有多种,如二进制、xml、soap。我们就以用的最多的二进制进行举例:

    socket服务端:

    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * socket编程之:传输对象server
     * 
     * @author chengwei.lcw 2016-11-27
     */
    public class SocketObjectSever {
    
        private ServerSocket serverSocket;
        private ObjectInputStream in;
        private ObjectOutputStream out;
    
        public SocketObjectSever() {
            try {
                serverSocket = new ServerSocket(9999);
    
                while (true) {
                    // 此处会阻塞,后面会讲到nio的作用
                    Socket socket = serverSocket.accept();
    
                    in = new ObjectInputStream(socket.getInputStream());
                    out = new ObjectOutputStream(socket.getOutputStream());
    
                    // 接收server端传来的数据,并转为Student
                    Student student = (Student) in.readObject();
                    // 重写了toString()方法,打印出来看看
                    System.out.println("Server: " + student);
    
                    // 返回给client端,通知我已收到数据
                    out.writeObject("yes client, I receive");
                    out.flush();
    
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            new SocketObjectSever();
        }
    
    }

    socket客户端:

    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
     
    /**
     * socket编程之:传输对象client
     * 
     * @author chengwei.lcw 2016-11-27
     */
    public class SocketObjectClient {
        private Socket socket;
        private ObjectInputStream in;
        private ObjectOutputStream out;
         
        public SocketObjectClient() {
            try {
                socket = new Socket("127.0.0.1",9999);
                out = new ObjectOutputStream(socket.getOutputStream());
                in = new ObjectInputStream(socket.getInputStream());
                 
                /*
                 * 建一个student对象,用于传输
                 */
                Student s = new Student("chengwei.lcw", 28);
                
                // 把对象写到管道中,client端进行接收
                out.writeObject(s);
                out.flush();
                 
                String receive = (String) in.readObject();
                System.out.println("Client Receive :"+receive);
                 
                in.close();
                out.close();
                socket.close();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
         
        public static void main(String[] args) {
            new SocketObjectClient();
        }
     
    }

    另外定义一个要传输的类:

    import java.io.Serializable;
    
    /**
     * socket编程之:要进行传输的类,需要继承Serializable接口
     * 
     * @author chengwei.lcw 2016-11-27
     * 
     */
    public class Student implements Serializable {
        
        private static final long serialVersionUID = 1L;
        private String name;
        private int age;
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        public String toString() {
            return "name=" + this.name + ", age=" + this.age; 
        }
    }

    依然先启动server,再启动client,server端控制台输出:

    Server: name=chengwei.lcw, age=28

    这样为止,我们的socket可以传输对象了。

    这里我们使用的序列化方式为java直接进行序列化,而hessian序列化比Java序列化高效很多,生成的字节流也要短很多,因为hessian在序列化时会把字节流进行压缩。在后面的升级版中我会使用hessian序列化的方式进行序列化。

    公司里还有事,而且我不知道这些是不是各位朋友想看到的内容,忙完今天我会继续进行补充。 哪里有讲的不对的希望大家来矫正。

  • 相关阅读:
    【转】Linux逻辑卷管理
    oracle range分区表已经有了MAXVALUE 分区,如何添加分区?要不能删除MAXVALUE分区里的数据,不影响在线应用。
    切换用户后whoami打印用户的问题
    tomcat版本号修改已dwr配置错误安全漏洞整改
    [LeetCode]: 64: Minimum Path Sum
    [LeetCode]: 292: Nim Game
    [LeetCode]: 22 : Generate Parentheses
    [LeetCode]: 121: Best Time to Buy and Sell Stock
    [LeetCode]: 62: Unique Paths
    [LeetCode]: 145: Binary Tree Postorder Traversal
  • 原文地址:https://www.cnblogs.com/lichengwei/p/5529492.html
Copyright © 2020-2023  润新知