服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)
我们到底能走多远系列(40)
扯淡:
判断是否加可以效力于这家公司,一个很好的判断是,接触下这公司工作几年的员工,了解下生活工作状态,这就是你几年后的状态,如果满意就可以考虑加入了。
主题:
场景:项目A作为主项目,业务实现完整,项目B需要调用项目A中的部分服务,那么项目A就需要提供出服务出来。实现分布式调用的方法有很多,这里介绍一下利用Spring Http Invoker 来实现的服务提供和调用。
demo地址:摸我
如果你对快速用springmvc搭建web应用感兴趣:摸我
阅读Spring的源码基本的流程实现是这样的:
下面就详细分析一下这个流程的实现:
<bean id="remoteDemoService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> <property name="serviceUrl"> <value>http://localhost:7777/remote/demoService</value> </property> <property name="serviceInterface"> <value>com.witown.open.demo.remote.service.DemoService</value> </property> </bean>
首先看的是HttpInvokerProxyFactoryBean类,它继承FactoryBean,所以先来看一看这个FactoryBean。
public class HttpInvokerProxyFactoryBean extends HttpInvokerClientInterceptor implements FactoryBean<Object> { // 代理对象 private Object serviceProxy; // 初始化后执行方法 @Override public void afterPropertiesSet() { super.afterPropertiesSet(); if (getServiceInterface() == null) { throw new IllegalArgumentException("Property 'serviceInterface' is required"); } // 使用AOP代理工厂创建代理对象,对这个代理对象的所有方法调用最后都会被拦截 // 调用接口的任何一个方法时都会被拦截去变成http请求 this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader()); } // 获得bean方法 public Object getObject() { return this.serviceProxy; } public Class<?> getObjectType() { return getServiceInterface(); } public boolean isSingleton() { return true; } }
这样DemoService 的调用都会被HttpInvokerClientInterceptor拦截,拦截的方法调用会去执行HttpInvokerClientInterceptor中的invoke方法,这个方法明了的体现了客户端的执行流程的三个步骤:
public Object invoke(MethodInvocation methodInvocation) throws Throwable { if (AopUtils.isToStringMethod(methodInvocation.getMethod())) { return "HTTP invoker proxy for service URL [" + getServiceUrl() + "]"; } // 1,把方法执行信息封装成RemoteInvocation RemoteInvocation invocation = createRemoteInvocation(methodInvocation); RemoteInvocationResult result = null; try { // 2,发起http请求,把方法执行信息传过去,正常的话,服务器会返回结果 result = executeRequest(invocation, methodInvocation); } catch (Throwable ex) { throw convertHttpInvokerAccessException(ex); } try { // 3,解析返回的结果,转化成java对象 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); } } }
public class RemoteInvocation implements Serializable { //方法名 private String methodName; // 类 private Class[] parameterTypes; // 参数 private Object[] arguments; // 步骤1中实际就是调用了这个构造函数而已 public RemoteInvocation(MethodInvocation methodInvocation) { this.methodName = methodInvocation.getMethod().getName(); this.parameterTypes = methodInvocation.getMethod().getParameterTypes(); this.arguments = methodInvocation.getArguments(); } // ... }
public final RemoteInvocationResult executeRequest(HttpInvokerClientConfiguration config, RemoteInvocation invocation) throws Exception { // RemoteInvocation(远程调用对象),转成可以传输的OutputStream,以便写入http请求中 ByteArrayOutputStream baos = getByteArrayOutputStream(invocation); if (logger.isDebugEnabled()) { logger.debug("Sending HTTP invoker request for service at [" + config.getServiceUrl() + "], with size " + baos.size()); } // 将流写入http请求,发送请求,并接收响应 return doExecuteRequest(config, baos); }
继续追踪getByteArrayOutputStream方法,发现使用了ObjectOutputStream最终调用了writeObject方法来将对象转化成流,下面是一段ObjectOutputStream的描述:
protected RemoteInvocationResult doExecuteRequest( HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws IOException, ClassNotFoundException { // 打开远程的HTTP连接 HttpURLConnection con = openConnection(config); // 设置HTTP连接信息 prepareConnection(con, baos.size()); // 把准备好的序列化的远程方法调用对象的字节流写入到HTTP请求体中 writeRequestBody(config, con, baos); // 校验HTTP响应 validateResponse(config, con); // 获得HTTP相应体的流对象 InputStream responseBody = readResponseBody(config, con); // 读取远程调用结果对象并返回 return readRemoteInvocationResult(responseBody, config.getCodebaseUrl()); }
最后的读取远程调用结果对象并返回:
protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois) throws IOException, ClassNotFoundException { // 获得Object Object obj = ois.readObject(); if (!(obj instanceof RemoteInvocationResult)) { throw new RemoteException("Deserialized object needs to be assignable to type [" + RemoteInvocationResult.class.getName() + "]: " + obj); } // 服务端对返回的对象封装成RemoteInvocationResult! return (RemoteInvocationResult) obj; }
到这里基本完成了,客户端请求远程对象方法的流程。
<servlet> <servlet-name>remote</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/config/remote-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>remote</servlet-name> <url-pattern>/remote/*</url-pattern> </servlet-mapping>
remote-servlet.xml文件中配置了处理请求的service:
< bean name= "/demoService" class ="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" > < property name= "service" ref = "demoServiceImpl"/> < property name= "serviceInterface" value ="com.witown.open.demo.service.DemoService" /> </bean >
HttpInvokerServiceExporter继承了HttpRequestHandler,所以请求从handleRequest方法开始。
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 解析请求,获得RemoteInvocation RemoteInvocation invocation = readRemoteInvocation(request); // 根据RemoteInvocation里的信息:方法,参数,执行,把返回的结果封装成RemoteInvocationResult RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy()); // 把结果写入响应 writeRemoteInvocationResult(request, response, result); } catch (ClassNotFoundException ex) { throw new NestedServletException("Class not found during deserialization", ex); } }
内部调用的代码经过前面请求调用发起的流程学习,就会很好理解了。
至此,整个流程就完整了。
让我们继续前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。