dubbo组成原理-http服务消费端如何调用

dubbo协议已经用的很多了,这里来稍微介绍一下http协议,官方对http协议的说明简直少的让人发指。哈哈

百度大部分都只是讲了http服务端的配置

那就先从服务端的配置说起

dubbo需要的jar包这里就不说明了,网上找些maven的pom就可以

web.xml配置servlet,注意url-pattern 是需要拦截哪些请求


	    dubbo
	    com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet
	    1
	
	
	    dubbo
	    /api/*
	
接着配置dubbo配置文件


	
	
	 
	
	
	
	
	

因为dubbo支持多协议,所以这里配置了dubbo与http二种

http配置需要注意contextpath这个参数,引用一下官方说明

  • 协议的上下文路径必须与servlet应用的上下文路径相同

什么看不懂?跑起来打个断点看看

用java模拟一个psot的http请求http://localhost:8080/imp/api/httpService/com.hellowin.imp.common.service.IDeviceService

服务端接收到http请求,因为/api/。所以会被DispatcherServlet拦截执行service

public class DispatcherServlet extends HttpServlet {

	private static final long serialVersionUID = 5766349180380479888L;
	
	private static DispatcherServlet INSTANCE;

    private static final Map handlers = new ConcurrentHashMap();

    public static void addHttpHandler(int port, HttpHandler processor) {
        handlers.put(port, processor);
    }

    public static void removeHttpHandler(int port) {
        handlers.remove(port);
    }
    
    public static DispatcherServlet getInstance() {
    	return INSTANCE;
    }
    
    public DispatcherServlet() {
    	DispatcherServlet.INSTANCE = this;
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) 
    		throws ServletException, IOException {
        HttpHandler handler = handlers.get(request.getLocalPort());
        if( handler == null ) {// service not found.
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Service not found.");
        } else {
            handler.handle(request, response);
        }
    }

}

进入handler.handle(request, response);

dubbo组成原理-http服务消费端如何调用_第1张图片

handle有hession、http、webservice 这里我们只看http的

点击进入HttpProtocol 这个类

 private class InternalHandler implements HttpHandler {
        
        public void handle(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
            String uri = request.getRequestURI();
            HttpInvokerServiceExporter skeleton = skeletonMap.get(uri);
            if (! request.getMethod().equalsIgnoreCase("POST")) {
                response.setStatus(500);
            } else {
                RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
                try {
                    skeleton.handleRequest(request, response);
                } catch (Throwable e) {
                    throw new ServletException(e);
                }
            }
        }
        
    }

同志们看到了吗,skeletonMap.get(uri);。是不是很像springmvc中根据url定位handlermapping

dubbo组成原理-http服务消费端如何调用_第2张图片

所以http暴露服务的路径是contextpath +暴露接口类名称

最开始我以为可以模拟http请求调用dubbo接口,因为网上始终找不到demo,都是各种复制黏贴的内容,所以打算通过源码来研究一下是否可以通过模拟http请求来实现调用。

当跟进到HttpProtocol 中看到HttpInvokerServiceExporter 感觉不妙

HttpInvokerServiceExporter 是spring-web.jar包下的一个类,以下是介绍

Spring HTTP Invoker一种JAVA远程方法调用框架实现,原理与JDK的RMI基本一致,所以我们先跟其它JAVA远程方法调用实现做下简单比较。

  • RMI:使用JRMP协议(基于TCP/IP),不允许穿透防火墙,使用JAVA系列化方式,使用于任何JAVA应用之间相互调用。

  • Hessian:使用HTTP协议,允许穿透防火墙,使用自己的系列化方式,支持JAVA、C++、.Net等跨语言使用。

  • Burlap: 与Hessian相同,只是Hessian使用二进制传输,而Burlap使用XML格式传输(两个产品均属于caucho公司的开源产品)。

  • Spring HTTP Invoker: 使用HTTP协议,允许穿透防火墙,使用JAVA系列化方式,但仅限于Spring应用之间使用,即调用者与被调用者都必须是使用Spring框架的应用。


Spring一定希望大家尽量使用它的产品,但实际项目中我们还是要根据需求来决定选择哪个框架,下面我们来看看Spring HTTP Invoker的使用。


既然通过是HTTP请求调用,那么客户端肯定需要一个代理用于帮忙发送HTTP请求,帮忙做对象系列化和反系列化等,Spring框架中的HttpInvokerServiceExporter类处理这些杂事;而服务器端需要一个HTTP请求处理器,帮忙处理HTTP请求已经对象系列化和反系列化工作,Spring框架中的HttpInvokerServiceExporter类就是干这活的,对于Sun JRE 6 的HTTP Server,Spring还提供了SimpleHttpInvokerServiceExporter类供选择。


网上有一些SimpleHttpInvokerServiceExporter的服务端和客户端配置说明,这里就不说明了

之前介绍dubbo说过,服务方就是doExport,消费方就是doRefer。继续看HttpProtocol 中这二个方法

protected  Runnable doExport(final T impl, Class type, URL url) throws RpcException {
        String addr = url.getIp() + ":" + url.getPort();
        HttpServer server = serverMap.get(addr);
        if (server == null) {
            server = httpBinder.bind(url, new InternalHandler());
            serverMap.put(addr, server);
        }
        final HttpInvokerServiceExporter httpServiceExporter = new HttpInvokerServiceExporter();
        httpServiceExporter.setServiceInterface(type);
        httpServiceExporter.setService(impl);
        try {
            httpServiceExporter.afterPropertiesSet();
        } catch (Exception e) {
            throw new RpcException(e.getMessage(), e);
        }
        final String path = url.getAbsolutePath();
        skeletonMap.put(path, httpServiceExporter);
        return new Runnable() {
            public void run() {
                skeletonMap.remove(path);
            }
        };
    }

httpServiceExporter设置的二个参数

Service实现类,一般引用其它bean

ServiceInterface服务类型

这样就达到了暴露服务


 protected  T doRefer(final Class serviceType, final URL url) throws RpcException {
        final HttpInvokerProxyFactoryBean httpProxyFactoryBean = new HttpInvokerProxyFactoryBean();
        httpProxyFactoryBean.setServiceUrl(url.toIdentityString());
        httpProxyFactoryBean.setServiceInterface(serviceType);
        String client = url.getParameter(Constants.CLIENT_KEY);
        if (client == null || client.length() == 0 || "simple".equals(client)) {
        	SimpleHttpInvokerRequestExecutor httpInvokerRequestExecutor = new SimpleHttpInvokerRequestExecutor() {
				protected void prepareConnection(HttpURLConnection con,
						int contentLength) throws IOException {
					super.prepareConnection(con, contentLength);
					con.setReadTimeout(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
					con.setConnectTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
				}
        	};
        	httpProxyFactoryBean.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
        } else if ("commons".equals(client)) {
        	CommonsHttpInvokerRequestExecutor httpInvokerRequestExecutor = new CommonsHttpInvokerRequestExecutor();
        	httpInvokerRequestExecutor.setReadTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
        	httpProxyFactoryBean.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
        } else if (client != null && client.length() > 0) {
        	throw new IllegalStateException("Unsupported http protocol client " + client + ", only supported: simple, commons");
        }
        httpProxyFactoryBean.afterPropertiesSet();
        return (T) httpProxyFactoryBean.getObject();
    }
ServiceUrl路径

ServiceInterface服务类型

这样,消费方在调用服务FactoryBean的getObject()的时候,获取到的代理对象就是httpProxyFactoryBean的对象


所以最后得出结论,dubbo的http协议,只是最后远程调用走的http,消费方通过服务调用还是需要类似dubbo协议的调用方式。如果需要传统http访问方式则需要看dubbox


不过有兴趣的可以看看当当的dubbox

http://dangdangdotcom.github.io/dubbox/rest.html

在Dubbo中开发REST风格的远程调用







你可能感兴趣的:(java,dubbo)