Soap方式调试webservice接口实例 中主要记录了用webservice soap方式去实现服务远程调用,远程调用是个很宽的概念,RPC(远程过程调用)和HTTP(网络传输协议)是两种最常见的远程调用方式。

一、RPC的概念

RPC(Remote ProcedureCall,远程过程调用)是一个很大的概念。它是一种通过网络从远程计算机程序上跨语言跨平台的请求服务。RPC能省略部分接口代码的开发,可以跨机器之间访问对象(JavaRMI),可以有更方便的加密和更高效的数据传输性能,而不需要了解底层网络技术的协议。RPC不仅可以走HTTP/HTTPS,也可以自定义TCP协议,从而省略HTTP繁杂的规则和冗余信息。

RPC的主要作用是解决分布式系统中服务之间的调用问题,能够在远程调用时,像调用本地方法一样方便,让调用者感知不到远程调用的逻辑。RPC主要是用在大型企业里面,因为大型企业里面系统繁多,业务线复杂,而且效率优势非常重要。而由于小型企业一般业务简单,不需要进行分布式架构,小型企业主要还是使用WebService中的RESTful WebService,部分特殊业务会使用Soap WebService。

二、使用soap、restful、rpc进行服务远程调用
1、soap

有例子:Soap方式调试webservice接口实例

2、restful

一般使用RestTemplate来实现调用,在SpringCloud一般会把Ribbon+restTemplate组合使用,不过一般用openfeign;在SprongBoot单应用使用较多(而我还在用httpclient)

HttpClient和RestTemplate的使用:微服务(2):服务的调用方式:RPC和HTTP

3、rpc

单独展开

三、RPC的原理及基于JAVA的RPC基本实现

比如我们日常从本地开发程序,写完一个service,然后本地执行main作为client来调用一下,过程非常的流畅,毕竟都在一个项目里。

1.PNG

而一旦踏入公司尤其是大型互联网公司就会发现,公司的系统都由成千上万大大小小的服务组成,各服务部署在不同的机器上,由不同的团队负责。这时就会遇到两个问题:1)要搭建一个新服务,免不了需要依赖他人的服务,而现在他人的服务都在远端,怎么调用?2)其它团队要使用我们的新服务,我们的服务该怎么发布以便他人调用?

问题2服务发布就不在这里写了,以前有分析过,这里就写(抄)一个最简单的基于TCP协议的RPC远程调用例子

注: RPC跟HTTP不是对立面,RPC中可以使用HTTP作为通讯协议。RPC是一种设计、实现框架 ,通讯协议只是其中一部分。

首先,基于TCP协议的话,就使用Socket做通讯,Socket 是对 TCP/IP 协议的封装,是针对TCP或UDP的具体接口实现 ,提供了在传输层进行网络编程的方法。

声明接口和实现类:

package com.jin;

/**
 * @author jinyunlong
 * @date 2022/1/11 22:00
 * @profession ICBC锅炉房保安
 */
public interface HelloRpc {
    String hello(String name);
}
package com.jin;

/**
 * @author jinyunlong
 * @date 2022/1/11 22:00
 * @profession ICBC锅炉房保安
 */
public class HelloRpcImpl implements HelloRpc {

    @Override
    public String hello(String name) {
        return "hello "+name;
    }

}

编写RPCClient的代码,这里要用到动态代理,因为用接口实现的方式,所以这里用JDK动态代理,通过如下代码,通过Socket发起网络请求,向RPCServer发送类信息,方法信息及方法参数信息,经RPCServer解析调用反射方法后返回代理对象给RPCClient。动态代理细节可以看Spring(5)

package com.jin;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

/**
 * @author jinyunlong
 * @date 2022/1/11 21:38
 * @profession ICBC锅炉房保安
 */
public class RPCProxy {

    @SuppressWarnings("unchecked")
    public static <T> T create(Object target){

        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new InvocationHandler(){

            @SuppressWarnings("resource")
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                Socket socket = new Socket("192.168.6.1", 8899);
                ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                try {
                    output.writeUTF(target.getClass().getName());
                    output.writeUTF(method.getName());
                    output.writeObject(method.getParameterTypes());
                    output.writeObject(args);
                    ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                    try {
                        Object result = input.readObject();
                        if (result instanceof Throwable) {
                            throw (Throwable) result;
                        }
                        return result;
                    } finally {
                        input.close();
                    }
                } finally {
                    output.close();
                    socket.close();
                }
            }

        });
    }
}

package com.jin;

/**
 * @author jinyunlong
 * @date 2022/1/11 22:23
 * @profession ICBC锅炉房保安
 */
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;

public class RPCServer {

    public static ConcurrentHashMap<String, Object> classMap = new ConcurrentHashMap<String,Object>();

    public static void main(String [] args) throws Exception{
        System.out.println("server start");
        RPCServer.invoker(8899);
    }
    public static void invoker(int port) throws Exception{

        ServerSocket server = new ServerSocket(port);
        for(;;){
            try{
                final Socket socket = server.accept();
                new Thread(new Runnable() {
                    ObjectOutputStream output =  null;
                    @Override
                    public void run() {
                        try{
                            try {
                                output = new ObjectOutputStream(socket.getOutputStream());
                                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                                String className = input.readUTF();
                                String methodName = input.readUTF();
                                Class<?>[] parameterTypes = (Class<?>[])input.readObject();
                                Object[] arguments = (Object[])input.readObject();
                                Object claszz = null;
                                if(!classMap.containsKey(className)){
                                    try {
                                        claszz = Class.forName(className).newInstance();
                                        classMap.put(className, claszz);
                                    } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                                        e.printStackTrace();
                                    }
                                }else {
                                    claszz = classMap.get(className);
                                }
                                Method method = claszz.getClass().getMethod(methodName, parameterTypes);
                                Object result = method.invoke(claszz, arguments);
                                output.writeObject(result);
                            } catch (IOException | ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
                                output.writeObject(e);
                            }finally {
                                output.close();
                            }
                        }catch(Exception e){
                            e.printStackTrace();
                        }finally {
                            try {
                                socket.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

测试main方法:

package com.jin;

/**
 * @author jinyunlong
 * @date 2022/1/11 22:25
 * @profession ICBC锅炉房保安
 */
public class Main {

    public static void main(String [] args){
        HelloRpc helloRpc = new HelloRpcImpl(); //创建原始对象(接口实现类)
        System.out.println(helloRpc.hello("opopopopopopopo")); //这里为原始对象调用hello()方法
        helloRpc = RPCProxy.create(helloRpc);         //用JDK动态代理方式,通过原始对象生成了代理对象
        System.out.println(helloRpc.hello("jyl"));   //代理对象和原始对象都实现同一接口HelloRpc,这里为代理对象调用hello()方法
    }
}

3.PNG

为了方便看再调试一下,前一条是接口实现类原始对象调用方法,后一条则是代理对象调用方法。

4.PNG

5.PNG

一个简单的RPC之Socket实现,注册中心、负载均衡啥的都没有做。总之一定要用动态代理就完事了。

比较多的场景就是服务端开发完了将接口丢给注册中心管理,然后客户端代理服务接口生成代理对象,这个过程像是在本地调用方法一样。

参考文章:

1、javaRPC原理 这篇文章写的很好啊

2、简单RPC之Socket实现

3、微服务(2):服务的调用方式:RPC和HTTP

4、rpc和webservice的关系简述

5、rpc、socket、tcp/udp简要梳理

6、RPC 原理,Demo 演示,为什么 RPC 用动态代理

7、RPC与Zookeeper注册中心的简单实现

8、Spring(5)


标题:RPC原理和基于Java的基本RPC实现
作者:jyl
地址:http://114.116.238.43/articles/2022/01/11/1641891975600.html