HttpInvoker远程调用原理解析

2018-04-25  本文已影响89人  太大_453b

前言

服务端在开发一般大致分为:

  • controller
  • service
  • dao/mapper(ROM框架处理)

开发完成后用nginx进行部署,nginx支持多服务的负载均衡,在和tomcat进行反向代理后可以完美实现部署

负载

graph LR
客户端-->服务端1
客户端-->服务端2
客户端-->...

服务端暴露的服务实在@Controller实现
通过dispaterServlet拦截请求后找到对应HttpRequestHandler找到对应的controller
通过上面的模式即可实现简单的分布式
到目前为止好像说的跟远程调用没关系


远程调用的场景

远程调用方法 HttpInvoker

  1. 服务端定义接口
public interface UserHttpService {  
   List<User> getUserByAcount(Stringname,String password);  
   void insert(User user);  
}
  1. 服务端接口实现
publicclass UserHttpServiceImpl implements UserHttpService {  
   
   @Autowired  
   private UserMapper userMapper;  
   @Override  
   public List<User>getUserByAcount(String name, String password) {  
            System.err.println("httpInvoker获取用户信息:"+ name + password);  
            return new ArrayList<User>();  
   }  
   @Override  
   public void insert(User user) {  
            System.err.println("httpInvoker开始插入用户信息:"+ user.toString());  
   }  
   
}
  1. 服务端接口暴露 类似与Controller
<bean name="userHttpService"class="com.lm.core.service.impl.UserHttpServiceImpl"/>  
<bean name="userExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">  
    <property name="service" ref="userHttpService"/>  
    <property name="serviceInterface" value="com.lm.core.service.UserHttpService"/>  
</bean>

<bean id="simpleUrlRequestHandler"
        class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="alwaysUseFullPath" value="true" />
    <property name="urlMap">
        <map>
          <entry key="/remoting/userExporter" value-ref="userExporter" />
        </map>
    </property>
</bean>
  1. web.xml
    <servlet>  
        <servlet-name>dispatcherServlet</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>  
            classpath:applicationContext-httpinvoker.xml  
         </param-value>  
        </init-param>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>dispatcherServlet</servlet-name>  
        <url-pattern>*</url-pattern>  
    </servlet-mapping> 
  1. 客户端接口
public interface UserHttpService {  
   List<User> getUserByAcount(Stringname,String password);  
   void insert(User user);  
}
  1. 客户端配置
<bean id="httpInvokerProxy"  
       class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">  
       <property name="serviceUrl"  
           value="http://127.0.0.1:8080/spring_remote_server/remoting/userExporter"/>  
       <property name="serviceInterface"value="com.lm.core.service.UserHttpService"/>  
</bean>
  1. 客户端调用
    @RequestMapping(value = "/httpInvokerTest")  
    @ResponseBody  
    public BaseMapVo httpInvokerTest(String name, String password) {  
       BaseMapVo vo = new BaseMapVo();  
       long startDate = Calendar.getInstance().getTimeInMillis();  
       System.out.println("httpInvoker客户端开始调用" + startDate);  
       UserHttpService rmi = (UserHttpService) ApplicationContextUtil.getInstance().getBean("httpInvokerProxy");  
       rmi.getUserByAcount("张三", ":张三的密码");  
       System.out.println("httpInvoker客户端调用结束" +  (Calendar.getInstance().getTimeInMillis()-startDate));  
       vo.setRslt("sucess");  
       return vo;  
    }

原理解析

通过http请求,封装序列化的对象,通过动态代理的方式进行信息获取

spring 源码解析

public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    // 如果是调用toString()方法则直接本地打印下方法信息
    if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
        return "HTTP invoker proxy for service URL [" + getServiceUrl() + "]";
    }
    // 构建RemoteInvocation对象,服务器和客户端统一使用该类进行通信
    RemoteInvocation invocation = createRemoteInvocation(methodInvocation);
    RemoteInvocationResult result;
    try {
    // 使用JDK自带的HttpURLConnection将序列化后的invocation的发送出去
        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);
    }
}

我们最关心的是当我们调用接口的方法时,HttpInvoker是如何做到调用到远方系统的方法的,其实HttpInvokerProxyFactoryBean最后返回的是一个代理类(Cglib Proxy或者Jdk Proxy),我们调用接口的任何方法时,都会先执行HttpInvokerClientInterceptor的invoke()方法,

result = executeRequest(invocation, methodInvocation);

然后通过HttpUrlClient将序列化的invocation传输到服务端,服务端在返回invocationResult

上一篇下一篇

猜你喜欢

热点阅读