1:Spring Http设计思想

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

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

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

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

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

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

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

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

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

先来说说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进行封装处理的 。

从图可以知道,当服务启动,系统在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);
    }

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

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);
}

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

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

Spring远端调用的实现-Spring Http调用的实现的更多相关文章

  1. spring cloud 声明式rest客户端feign调用远程http服务

    在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.Feign就是Spring Cloud提供的一种声明式R ...

  2. Spring AOP不拦截从对象内部调用的方法原因

    拦截器的实现原理很简单,就是动态代理,实现AOP机制.当外部调用被拦截bean的拦截方法时,可以选择在拦截之前或者之后等条件执行拦截方法之外的逻辑,比如特殊权限验证,参数修正等操作. 但是最近在项目中 ...

  3. Spring Cloud 服务端注册与客户端调用

    Spring Cloud 服务端注册与客户端调用 上一篇中,我们已经把Spring Cloud的服务注册中心Eureka搭建起来了,这一章,我们讲解如何将服务注册到Eureka,以及客户端如何调用服务 ...

  4. spring boot中使用@Async实现异步调用任务

    本篇文章主要介绍了spring boot中使用@Async实现异步调用任务,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 什么是“异步调用”? “异步调用”对应的是“同步 ...

  5. Spring普通类/工具类获取并调用Spring service对象的方法

    参考<Spring普通类获取并调用Spring service方法>,网址:https://blog.csdn.net/jiayi_0803/article/details/6892455 ...

  6. spring boot 2.0.3+spring cloud (Finchley)3、声明式调用Feign

    Feign受Retrofix.JAXRS-2.0和WebSocket影响,采用了声明式API接口的风格,将Java Http客户端绑定到他的内部.Feign的首要目标是将Java Http客户端调用过 ...

  7. Spring AOP深入理解之拦截器调用

    Spring AOP深入理解之拦截器调用 Spring AOP代理对象生成回想 上一篇博客中:深入理解Spring AOP之二代理对象生成介绍了Spring代理对象是怎样生成的,当中重点介绍了JDK动 ...

  8. Spring Cloud Eureka 注册,发布,调用服务

    Spring Cloud为服务治理做了一层抽象接口,所以在Spring Cloud应用中可以支持多种不同的服务治理框架,如Netflix Eureka.Consul和Zookeeper.在Spring ...

  9. 使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。

    使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务.

随机推荐

  1. 关于路由、AP、交换机的小总结

    整理自关于路由.AP.交换机的小总结 要将各种设备连成网络,一般运用网络中的两层,即第二层的数据链路层和第三层的网络层.而设备之间需要通信就需要各自的网络地址. 第二层设备有物理地址即MAC地址,这对 ...

  2. Learning WCF Chapter1 Generating a Service and Client Proxy

    In the previous lab,you created a service and client from scratch without leveraging the tools avail ...

  3. MySQL源码 数据结构array

    MySQL源码中自己定义了许多数据结构,放在mysys的目录下,源码中通常都使用这些数据结构来组织存放数据,也更容易实现跨平台.   下面先来看下MySQL定义的动态数组: [源代码include/a ...

  4. 转自 x_x_的百度空间 搞ACM的你伤不起

    来源:http://roba.rushcj.com/?p=548 劳资六年前开始搞ACM啊!!!!!!!!!! 从此踏上了尼玛不归路啊!!!!!!!!!!!! 谁特么跟劳资讲算法是程序设计的核心啊!! ...

  5. BZOJ2038小Z的袜子(hose)

    2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 2343  Solved: 1077[Subm ...

  6. 【转】Valid signing identity not found解决办法(原有IDP私钥丢失)及Certificate、App ID、Devices、Provisioning Profiles之间区别--不错

    原文网址:http://blog.csdn.net/mad1989/article/details/8699147 前言: 刚刚把mini换成了macbookair,之前一直在mini上进行开发,到换 ...

  7. (转载)最黑的黑客米特尼克:多次耍FBI 终被高手擒

    (转载)http://bbs.chinabyte.com/thread-816847-1-1.html 凯文·米特尼克 50岁 第一个被FBI通缉的黑客,被称为“头号电脑骇客”,曾入侵北美防空指挥系统 ...

  8. HDU 1162 Eddy's picture

    坐标之间的距离的方法,prim算法模板. Eddy's picture Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32 ...

  9. linux 给用户添加进新的组

    给用户user1添加一个新的组group1 usermod -G group1 #给当前登录用户所在组设置为 group1 注意:上面的命令有个问题需要知道,这个操作是重置用户所在组,也就是会让当前用 ...

  10. 搭建Git远程仓库(也就是Git服务器,不用再连Github了)

    github上一般托管的代码都是公开的,任何人都可以查看.复制下载等,而私有的项目则需要付费.所以可以自己搭建一个git服务,自己人用. 首先安装git: sudo apt-get install g ...