Dubbo源码学习--Rest协议(五)

61人阅读 评论(0) 收藏 举报
分类:

Dubbo的rest协议是采用的org.jboss.resteasy框架实现的,dubbo项目重新被维护之后才添加的功能,之前当当的dubbox对dubbo进行扩展支持Rest

在dubbo中开发一个REST风格的服务会比较简单,下面以一个注册用户的简单服务为例说明。

这个服务要实现的功能是提供如下URL(注:这个URL不是完全符合REST的风格,但是更简单实用):

http://localhost:8080/users/register

而任何客户端都可以将包含用户信息的JSON字符串POST到以上URL来完成用户注册。

首先,开发服务的接口:

public class UserService {    
   void registerUser(User user);
}

然后,开发服务的实现:

@Path("users")
public class UserServiceImpl implements UserService {

    @POST
    @Path("register")
    @Consumes({MediaType.APPLICATION_JSON})
    public void registerUser(User user) {
        // save the user...
    }
}

上面的服务实现代码非常简单,但是由于REST服务是要被发布到特定HTTP URL,供任意语言客户端甚至浏览器来访问,所以这里要额外添加了几个JAX-RS的标准annotation来做相关的配置:

@Path("users"):指定访问UserService的URL相对路径是/users,即http://localhost:8080/users

@Path("register"):指定访问registerUser()方法的URL相对路径是/register,再结合上一个@Path为UserService指定的路径,则调用UserService.register()的完整路径为http://localhost:8080/users/register

@POST:指定访问registerUser()用HTTP POST方法

@Consumes({MediaType.APPLICATION_JSON}):指定registerUser()接收JSON格式的数据。REST框架会自动将JSON数据反序列化为User对象

最后,在spring配置文件中添加此服务,即完成所有服务开发工作:

<!-- 用rest协议在8080端口暴露服务 -->
<dubbo:protocol name="rest" port="8080"/>

<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="xxx.UserService" ref="userService"/>

<!-- 和本地bean一样实现服务 -->
<bean id="userService" class="xxx.UserServiceImpl" />

接下来我们看看RestProtocol中的实现:

RestProtocol也是提供了两个接口doExport和doRefer对外暴露服务和引用服务。

(1)doExport:RestProtocol即可以通过servlet暴露服务,也可以通过netty等暴露服务

 protected <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException {
        String addr = getAddr(url);
        Class implClass = ServiceClassHolder.getInstance().popServiceClass();
        RestServer server = servers.get(addr);
		//创建服务提供者,服务提供者即可以是servlet容器,或者直接是netty等tcp服务
        if (server == null) {
            server = serverFactory.createServer(url.getParameter(Constants.SERVER_KEY, "jetty"));
            server.start(url);
            servers.put(addr, server);
        }
		//如果是serlvet提供服务就会有向下文
        String contextPath = getContextPath(url);
        if ("servlet".equalsIgnoreCase(url.getParameter(Constants.SERVER_KEY, "jetty"))) {
            ServletContext servletContext = ServletManager.getInstance().getServletContext(ServletManager.EXTERNAL_SERVER_PORT);
            if (servletContext == null) {
                throw new RpcException("No servlet context found. Since you are using server='servlet', " +
                        "make sure that you've configured " + BootstrapListener.class.getName() + " in web.xml");
            }
            String webappPath = servletContext.getContextPath();
            if (StringUtils.isNotEmpty(webappPath)) {
                webappPath = webappPath.substring(1);
                if (!contextPath.startsWith(webappPath)) {
                    throw new RpcException("Since you are using server='servlet', " +
                            "make sure that the 'contextpath' property starts with the path of external webapp");
                }
                contextPath = contextPath.substring(webappPath.length());
                if (contextPath.startsWith("/")) {
                    contextPath = contextPath.substring(1);
                }
            }
        }

        final Class resourceDef = GetRestful.getRootResourceClass(implClass) != null ? implClass : type;
		//暴露服务
        server.deploy(resourceDef, impl, contextPath);

        final RestServer s = server;
		//创建线程,服务销毁时卸载服务
        return new Runnable() {
            public void run() {
                // TODO due to dubbo's current architecture,
                // it will be called from registry protocol in the shutdown process and won't appear in logs
                s.undeploy(resourceDef);
            }
        };
    }

(2)doRefer:简单来说就是创建http请求进行远程调用。

//通过http请求进行接口调用
    protected <T> T doRefer(Class<T> serviceType, URL url) throws RpcException {
        if (connectionMonitor == null) {
            connectionMonitor = new ConnectionMonitor();
        }

        // TODO more configs to add
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        // 20 is the default maxTotal of current PoolingClientConnectionManager
        connectionManager.setMaxTotal(url.getParameter(Constants.CONNECTIONS_KEY, 20));
        connectionManager.setDefaultMaxPerRoute(url.getParameter(Constants.CONNECTIONS_KEY, 20));

        connectionMonitor.addConnectionManager(connectionManager);
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT))
                .setSocketTimeout(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT))
                .build();

        SocketConfig socketConfig = SocketConfig.custom()
                .setSoKeepAlive(true)
                .setTcpNoDelay(true)
                .build();
		//创建http连接
        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
                    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                        HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                        while (it.hasNext()) {
                            HeaderElement he = it.nextElement();
                            String param = he.getName();
                            String value = he.getValue();
                            if (value != null && param.equalsIgnoreCase("timeout")) {
                                return Long.parseLong(value) * 1000;
                            }
                        }
                        // TODO constant
                        return 30 * 1000;
                    }
                })
                .setDefaultRequestConfig(requestConfig)
                .setDefaultSocketConfig(socketConfig)
                .build();

        ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient/*, localContext*/);
		//创建rest客户端
        ResteasyClient client = new ResteasyClientBuilder().httpEngine(engine).build();
        clients.add(client);

        client.register(RpcContextFilter.class);
        for (String clazz : Constants.COMMA_SPLIT_PATTERN.split(url.getParameter(Constants.EXTENSION_KEY, ""))) {
            if (!StringUtils.isEmpty(clazz)) {
                try {
                    client.register(Thread.currentThread().getContextClassLoader().loadClass(clazz.trim()));
                } catch (ClassNotFoundException e) {
                    throw new RpcException("Error loading JAX-RS extension class: " + clazz.trim(), e);
                }
            }
        }

        // TODO protocol
		//创建服务代理类
        ResteasyWebTarget target = client.target("http://" + url.getHost() + ":" + url.getPort() + "/" + getContextPath(url));
        return target.proxy(serviceType);
    }

查看评论

Web Application 開 發 利 器 - WebSnap(六)

Web Application 開 發 利 器 - WebSnap!第 六 章 、 執 行 者 : TAdapterDispatcher 及 AdapterAction  6-1 Action 的 運...
  • rh
  • rh
  • 2001-12-09 15:33:00
  • 1036

dubbo消费者没有指定protocol,服务器端支持dubbo和rest,结果一会走dubbo、一会走rest

在使用dubbo时,消费者调用服务者,居然走了http请求!大吃一惊。 项目的配置是consumer(消费者)没有指定protocol,provider(服务者) 同时支持rest和dubb...
  • wangjun5159
  • wangjun5159
  • 2016-10-01 21:05:43
  • 2478

dubbo 集成restful协议

Maven工程请导入以下配置: javax.ws.rs javax.ws.rs-api 接口实现,可忽略: org.j...
  • icoudsoft_saas
  • icoudsoft_saas
  • 2015-12-24 15:26:25
  • 6088

Dubbo+Zookeeper(rest)

什么是dubbo? DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架. winows环境下: 先下载:zookeepe...
  • CSDN3436
  • CSDN3436
  • 2017-06-22 18:13:55
  • 810

dubbox+rest详细介绍

完善中…… 本文篇幅较长,因为REST本身涉及面较多。另外,本文参照Spring等的文档风格,不仅仅局限于框架用法的阐述,同时也努力呈现框架的设计理念和优良应用的架构思想。 对于想粗略...
  • Airenone
  • Airenone
  • 2017-03-13 17:52:01
  • 1430

Dubbo/Dubbox的服务消费(二)- 服务发现

上文书整理了dubbo是如何生成服务代理的,并且留了个尾巴,这一文主要介绍dubbo是如何实现服务发现的,继续前文的脚步,看一下dubbo如何完成传说中的服务自动发现 打开com.alibaba.d...
  • weiythi
  • weiythi
  • 2017-11-23 15:15:33
  • 290

dubboX提供rest服务

上一章,我们详细介绍了如何搭建dubbo服务,本章节我们将在dubbo服务的基础上,利用dubbox,对外提供rest服务。 1. 修改maven 依赖 目前的最新版是 dubbox-2.8.4 ,...
  • z_xiaocun
  • z_xiaocun
  • 2017-01-10 09:51:07
  • 3237

Dubbo源码分析系列-服务的发布

Dubbo源码分析系列-服务的发布
  • qq418517226
  • qq418517226
  • 2016-07-06 09:04:11
  • 8120

dubbox rest学习

dubbo支持多种远程调用方式,例如:dubbo RPC(二进制序列化+tcp协议)、http调用 (二进制序列化+http协议)、hessian(二进制序列化+http协议)、webService(...
  • CHS007chs
  • CHS007chs
  • 2017-07-26 17:29:24
  • 406

Dubbo——各协议暴露和引用服务的逻辑

各协议暴露和引用服务的逻辑    目前Dubbox版本支持的协议有dubbo、injvm、rmi、hessian、thrift、memcached、redis、rest等九种,其中memcached和...
  • meilong_whpu
  • meilong_whpu
  • 2017-05-15 16:34:55
  • 1861
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 69万+
    积分: 1万+
    排名: 2131
    Github
    访问:https://github.com/IAMTJW
    博客专栏
    最新评论