美文网首页
HttpInvoker远程调用原理解析

HttpInvoker远程调用原理解析

作者: 太大_453b | 来源:发表于2018-04-25 16:31 被阅读89次

    前言

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

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

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

    负载

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

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


    远程调用的场景

    • 服务和客户端不在一个机器上
    • 为了实现客户端和服务端分布式
    • rpc框架:dubbo , spring-cloud 其中dubbo算是一个spring-cloud的一个功能分支

    远程调用方法 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

    相关文章

      网友评论

          本文标题:HttpInvoker远程调用原理解析

          本文链接:https://www.haomeiwen.com/subject/tkeylftx.html