• (转载)spring 之间的远程调用-Spring Http调用的实现


    原文:https://www.cnblogs.com/lewisat/p/6132082.html

    1:Spring Http设计思想

    最近在研究公司自己的一套rpc远程调用框架,看到其内部实现的设计思想依赖于spring的远端调用的思想,所以闲来无事,就想学习下,并记录下。

    作为spring远端调用的实现,最为简单的应该是通过http调用的实现,在这种依赖中不会依赖第三方等相关组件,调用者只需要配置相关http的协议

    就可以实现,简单的配置,就可以使用spring的 IOC  容器的bean的定义等等思想去调用,简单,方便,无需写更多的http相关的代码,

    比较适合内部通信系统之间的调用。

    在日常开发中,经常会遇到各种内部系统之间的通讯调用,其实可以使用如下几种设计方式。不过,最简单的应该是spring自带的http模式,然后自己封装

    打包成客户端jar等等,共客户端调用

    image

    其实在日常调用实现中,可以通过若干种设计 都可以完成客户端与服务端之间的调用,如

    阿里的db中间件就是使用的是类似的通讯模式,不过没有研究过

    image

    公司内部使用的是如下的方式进行通讯,可以通过监控软件来进行对Q的跟踪,在处理大数据高并发的时候,良好的Q中间件及Java中封装异步处理,解决了数据库以及各方面的瓶颈压力,不过,也有缺点,各个应用之间的事务是无法控制,只能通过事后补偿处理。综合来看,还是很好的设计

    image

    先来说说springHttp协议吧.

    2:客户端

    在客户端中设计了HttpInvokerProxyFactoryBean ,这是一个代理工厂bean(ProxyFactoryBean),它会使用spring aop来对http调用器的客户端进行封装,既然使用了aop,就会使用到代理对象,并为代理方法设置相应的拦截器。HttpInvokerProxyFactoryBean 中,通过afterPropertiesSet来启动远端调用基础设施的建立等,代理拦截器HttpInvokerClientInterceptor,HttpInvokerClientInterceptor中会封装客户端的基本实现,如http请求链接,请求对象序列化,请求传送到服务端,在收到拦截器的时候,同样也会将服务器端发送过来http响应反序列化,并且把远端调用的服务器端返回的对象交给应用使用,完成一个http请求的基本实现。

    Http调用器是基于http协议提供的远端调用方案,使用http调用器和使用Java rmi一样的基础配置模块

    HttpInvokerProxyFactoryBean 具体实现如下图 继承了FactoryBean,使用HttpInvokerProxyFactoryBean 需要设置相应的bean对代理工厂的配置
    设置远程服务的url地址,设置远端服务的的接口,然后把这个代理工厂设置到客户端应用的bean的remoteService属性中,有了这个设置,客户端应用配置就已经配置好了,就可以像调用本地一样,享用远端服务了

    复制代码
    <bean id="proxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
       <property name="serviceUrl">http://localhost:8080/xxxx/xxx</property><!--请求的服务端url-->
       <property name="serviceInterface">com.abc.cc.ddd.ee</property><!--具体的服务端调用接口路径-->
    </bean>
    <bean id="test" class="abc.sss.ssss">
       <property name="remoteService" ref="proxy"/> <!--引入到代理工厂中-->
    </bean>
    复制代码

    HttpInvokerProxyFactoryBean中封装对应的远端服务信息,域名,端口,服务所在URL,这些都是远端服务调用所需要的信息,同时,由于使用httpInvoker,所以在URL指定的协议中是http协议,对访问远端服务调用的客户端而言,只要持有HttpInvokerProxyFactoryBean提供的代理对象,就可以方便的使用远端调用,使用起来如本地一样,远端调用发送的数据通信及远端服务交互,都被proxy代理类进行了封装,对客户端是透明的,这些封装主要都是在HttpInvokerProxyFactoryBean进行封装处理的 。

    image

    从图可以知道,当服务启动,系统在spring容器里面自动初始化该代理对象,自动执行afterPropertiesSet相关方法
    HttpInvokerProxyFactoryBean 是如何封装并将数据传输到服务端的?

    复制代码
    public class HttpInvokerProxyFactoryBean extends HttpInvokerClientInterceptor implements FactoryBean<Object> {
    
        private Object serviceProxy;
    
        //代码省去若干  代码省去若干  代码省去若干  代码省去若干  
        @Override
        public void afterPropertiesSet() {
            //初始化执行 http配置加载等,为后续发送到服务端做准备工作
            super.afterPropertiesSet();
            //获取在客户端配置的接口,判断接口是否配置,此处的父类进行了类的加载配置等,
            //在此处,不是一个配置,而是一个具体的接口类
            if (getServiceInterface() == null) {
                throw new IllegalArgumentException("Property 'serviceInterface' is required");
            }
            //将相关参数,相应的类加载的信息,以及指定具体的客户端实现类代理
            this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
        }
    
    
        public Object getObject() {
            return this.serviceProxy;
        }
    
        public Class<?> getObjectType() {
            return getServiceInterface();
        }
    
        public boolean isSingleton() {
            return true;
        }
    }
    复制代码

    ProxyFactory所指定的HttpInvokerClientInterceptor 拦截器中执行的invoke方法

    复制代码
    //具体调用执行的方法
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
                return "HTTP invoker proxy for service URL [" + getServiceUrl() + "]";
            }
            //创建远端服务所需要的参数信息等 ,格式是spring自己实现的 ,创建工厂等
            RemoteInvocation invocation = createRemoteInvocation(methodInvocation);
            RemoteInvocationResult result;
            try {
                //调用服务端,发送请求
                result = executeRequest(invocation, methodInvocation);
            }
            catch (Throwable ex) {
                throw convertHttpInvokerAccessException(ex);
            }
            try {
                //将服务端的返回信息所需要的对象等
                return recreateRemoteInvocationResult(result);
            }
            catch (Throwable ex) {
                if (result.hasInvocationTargetException()) {
                    throw ex;
                }
                else {
                    throw new RemoteInvocationFailureException("Invocation of method [" + methodInvocation.getMethod() +
                            "] failed in HTTP invoker remote service at [" + getServiceUrl() + "]", ex);
                }
            }
    }
    复制代码

    上述代码中主要操作:封装发送的接口等
    RemoteInvocationFactory  >> createRemoteInvocation(MethodInvocation methodInvocation);
    RemoteInvocation    >>(RemoteInvocation(String methodName, Class[] parameterTypes, Object[] arguments))
    DefaultRemoteInvocationFactory   >> createRemoteInvocation(MethodInvocation methodInvocation)

    复制代码
        /**
         * Create a new RemoteInvocation for the given AOP method invocation.
         * @param methodInvocation the AOP invocation to convert
         */
         RemoteInvocation    >>(RemoteInvocation(String methodName, Class[] parameterTypes, Object[] arguments)) 具体实现
       public RemoteInvocation(MethodInvocation methodInvocation) {
            this.methodName = methodInvocation.getMethod().getName();
            this.parameterTypes = methodInvocation.getMethod().getParameterTypes();
            this.arguments = methodInvocation.getArguments();
       }
       执行发送
       HttpInvokerRequestExecutor -->>  AbstractHttpInvokerRequestExecutor类 具体实现
       public final RemoteInvocationResult executeRequest(
                HttpInvokerClientConfiguration config, RemoteInvocation invocation) throws Exception {

            ByteArrayOutputStream baos = getByteArrayOutputStream(invocation);
            if (logger.isDebugEnabled()) {
                logger.debug("Sending HTTP invoker request for service at [" + config.getServiceUrl() +
                        "], with size " + baos.size());
            }
            return doExecuteRequest(config, baos);
        }
    复制代码

    具体流程图如下:
    客户端请求配置发送请求

    image

    3:服务端

    在服务端中设计了HttpInvokerServiceExporter,通过ServiceExporter来导出远端的服务对象,由于需要处理http请求,所以其需要依赖springMVC模块来实现,会封装mvc框架的dispatchServlet,并设置相应的控制器。这个控制器执行相应的http请求处理,比如接收服务请求,将服务请求对象反序列化,交给服务端对象完成请求,最后把生成的结果通过序列化通过http协议返回到客户端。

    服务端配置:

    复制代码
    <bean id="helloService" class="com.logcd.server.service.impl.HelloService"/>  
          
     <bean name="/xxxService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" lazy-init="false">  
            <property name="service">  
                <ref bean="xxxService"/>  
            </property>  
            <property name="serviceInterface">  
                <value>com.abc.cc.ddd.ee</value>  
            </property>  
      </bean>
    复制代码

    spring http的服务端接收是如何响应的?主要是使用HttpInvokerServiceExporter

    复制代码
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException { 
    
            try {
                //封装请求对象,将其转换成远程调用对象
                RemoteInvocation invocation = readRemoteInvocation(request);
                //执行请求并创建返回对象
                RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy());
                //返回到客户端
                writeRemoteInvocationResult(request, response, result);
            }
            catch (ClassNotFoundException ex) {
                throw new NestedServletException("Class not found during deserialization", ex);
            }
        }
    复制代码

    执行代码的逻辑 并返回,此处代码如下

    复制代码
    RemoteInvocation 
    
     public Object invoke(Object targetObject)
                throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 
    
            Method method = targetObject.getClass().getMethod(this.methodName, this.parameterTypes);
            return method.invoke(targetObject, this.arguments);
        }
    复制代码

    写入返回到客户端

    复制代码
    HttpInvokerServiceExporter
        protected void writeRemoteInvocationResult(
                HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result, OutputStream os)
                throws IOException { 
    
            ObjectOutputStream oos = createObjectOutputStream(decorateOutputStream(request, response, os));
            try {
                doWriteRemoteInvocationResult(result, oos);//调用父类RemoteInvocationSerializingExporter
            }
            finally {
                oos.close();
            }
        } 
    
    RemoteInvocationSerializingExporter 
    
        protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos)
                throws IOException { 
    
            oos.writeObject(result);
        }
    复制代码

    服务端具体的执行代码的流程图如下:

    image

    具体整个spring http远端调用就结束了,其实开发中,如果要封装类似的远端调用,可以仿照springhttp模式进行开发,将其中的http换成其他的协议,即可实现

  • 相关阅读:
    测试产品
    三年回顾
    测试服务输出业务价值
    慎用重载_2
    慎用重载_1
    ByteBuffer和String的互相转换
    线程较为深的度剖析1
    线程同步的故事描述
    Java线程同步
    TCP关闭过程
  • 原文地址:https://www.cnblogs.com/JoeyWong/p/9298021.html
Copyright © 2020-2023  润新知