文章目录

  • 1 SOA的理解
  • 1.1 基本理解
  • 1.2 没用SOA
  • 1.3 使用SOA
  • 2 RPC
  • 2.1 定义
  • 2.2 RPC服务注册/发现过程
  • 2.3 设计一个RPC框架需要哪些功能
  • 2.4 实例分析
  • 2.4.1 流程图设计
  • 2.4.2 公共接口部分
  • 2.4.3 服务提供者
  • 2.4.4 服务消费者


1 SOA的理解

1.1 基本理解

SOA英文名称(Service Oriented Ambiguity),中文名称:面向服务架构,有一个专门提供服务单元,其他所有单元都调用这个服务.
SOA定位:如何设计项目,让开发时更有效率.,SOA是一种思想

1.2 没用SOA

之前项目架构设计:在公司项目不允许所有项目都访问数据库,开发时,数据库访问层代码可能出现冗余

sofa rpc原理_SOA

1.3 使用SOA

使用SOA架构:专门访问数据库服务(项目),开发时可以实现,数据访问控制和代码复用

sofa rpc原理_RPC_02


实现SOA架构时,常用服务:

  • Dubbo 做为服务.
  • WebService 做为服务.
  • Dubbox 做为服务.
  • 服务方就是web项目,调用web项目的控制器,使用HttpClient可以调用其他项目的控制器.

2 RPC

2.1 定义

RPC英文名称(Remote Procedure Call Protocol),中文名称:远程过程调用协议

RPC解析:客户端(A)通过互联网调用远程服务器,不知道远程服务器具体实现,只知道远程服务器提供了什么功能.

RPC最大优点:数据安全性.

sofa rpc原理_服务提供者_03

2.2 RPC服务注册/发现过程

简述如下:

  • 服务提供者启动时,会将其服务名称,ip地址注册到配置中心。
  • 服务消费者在第一次调用服务时,会通过注册中心找到相应的服务的IP地址列表,并缓存到本地,以供后续使用。当消费者调用服务时,不会再去请求注册中心,而是直接通过负载均衡算法从IP列表中取一个服务提供者的服务器调用服务。
  • 当服务提供者的某台服务器宕机或下线时,相应的ip会从服务提供者IP列表中移除。同时,注册中心会将新的服务IP地址列表发送给服务消费者机器,缓存在消费者本机。
  • 当某个服务的所有服务器都下线了,那么这个服务也就下线了。
  • 同样,当服务提供者的某台服务器上线时,注册中心会将新的服务IP地址列表发送给服务消费者机器,缓存在消费者本机。
  • 服务提供方可以根据服务消费者的数量来作为服务下线的依据

2.3 设计一个RPC框架需要哪些功能

  • 首先我们得需要一个注册中心,去管理消费者和提供者的节点信息,这样才会有消费者和提供才可以去订阅服务,注册服务。
  • 当有了注册中心后,可能会有很多个provider节点,那么我们肯定会有一个负载均衡模块来负责节点的调用,至于用户指定路由规则可以使一个额外的优化点
  • 具体的调用肯定会需要牵扯到通信协议,所以需要一个模块来对通信协议进行封装,网络传输还要考虑序列化。
  • 当调用失败后怎么去处理?所以我们还需要一个容错模块,来负责失败情况的处理。
  • 其实做完这些一个基础的模型就已经搭建好了,我们还可以有更多的优化点,比如一些请求数据的监控,配置信息的处理,日志信息的处理等等。

2.4 实例分析

手写一个简单RPC实例如下

2.4.1 流程图设计

API定义各种服务标准,provider服务提供者,consumer服务消费者

sofa rpc原理_SOA_04

2.4.2 公共接口部分

业务逻辑部分:

方法
public interface UserService {
    public UserDTO addUser(UserDTO dto);
}
参数对象 必须实现序列化接口,因为是流化后的对象在网络间传输的
@Data
public class UserDTO implements Serializable {

    private static final long serialVersionUID = -7688562483398217382L;
    private String userId;
    private int age;
    private String name;
 }

provider和consumer通讯的公共报文定义部分

public class RPCCommonRequestDTO implements Serializable {

    private static final long serialVersionUID = -3612648405080153759L;
    private String classpath;//类路径
    private String methodName;//方法名字
    private Object[] params;//参数列表
}

2.4.3 服务提供者

刚进到provider以及服务启动部分

public class ServerAcceptor {
    static ExecutorService threadPool= Executors.newFixedThreadPool(10);
    public static void startup(int port) throws IOException {

        ServerSocket ss = new ServerSocket(port);
        while (true) {
            Socket socket = ss.accept();//等待客户端连接
            threadPool.execute(new Processor(socket));
        }
    }
    public static void main(String[] args) throws IOException {
        System.out.println("服务正在启动。。。。。。。。。");
        startup(8888);
    }
}

服务流化对象读取和写入部分

public class Processor implements Runnable{
    private Socket socket;
    public Processor(Socket socket) {
        this.socket=socket;
    }
    @Override
    public void run() {
        try ( ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
              ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());){
            Object object = ois.readObject();
            Object resObject = ServiceDispatch.dispatch(object);
            oos.writeObject(resObject);
            oos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

通过反射处理服务端业务逻辑

public class ServiceDispatch {

    /**
     * 通过反射机制处理 服务提供 方法
     * @param object
     * @return
     */
    public static Object dispatch(Object object){
        RPCCommonRequestDTO dto=(RPCCommonRequestDTO)object;
        String classpath = dto.getClasspath();
        String methodName = dto.getMethodName();
        Object[] params = dto.getParams();
        Class[] types =null;
        if(params!=null && params.length>0){
            types=new Class[params.length];
            for(int i=0;i<params.length;i++){
                types[i]=params[i].getClass();
            }
        }
        Object resObj=null;
        try {
            Class<?> clazz = Class.forName(classpath);
            Method method = clazz.getDeclaredMethod(methodName, types);
            resObj = method.invoke(clazz.newInstance(), params);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return resObj;
    }
}

服务端业务逻辑部分

public class UserServiceimpl implements UserService {
    @Override
    public UserDTO addUser(UserDTO dto) {
        System.out.println(dto);

        dto.setUserId(new Random().nextInt(100000)+"");
        dto.setName("haahhah");
        dto.setAge(10);
        return dto;
    }
}

2.4.4 服务消费者

消费者启动以及调用部分

public class CallTest {
    public static void main(String[] args) {
       UserService userService = InstanceFactory.getUserService(UserService.class);
        UserDTO userDTO = new UserDTO();
        userDTO= userService.addUser(userDTO);
        System.out.println(userDTO);
    }
}

消费者网络部分流化处理

public class InstanceFactory {

    public static <T> T getUserService(Class<T> clazz){
        Object object = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("RPCInvocationHandler===="+method.getName());

                RPCCommonRequestDTO rpc = new RPCCommonRequestDTO();
                rpc.setMethodName(method.getName());
                rpc.setParams(args);
                rpc.setClasspath("cn.jzh.rpc.api.impl.UserServiceimpl");

                Object resObj = NetClient.callRemoteService("localhost", 8888, rpc);
                return resObj;
            }
        });
        return (T)object;
    }
}

消费者网络发送部分

public class NetClient {

    public static Object callRemoteService(String host,int port,Object intObj){

        Object object = null;
        try (Socket socket = new Socket(host, port);
             ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());){
             ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());

            oos.writeObject(intObj);
            oos.flush();

            object = ois.readObject();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return object;
    }
}