Dubbo3 配置rest和dubbo两种协议发布应用服务

本文介绍了如何在 Dubbo 中发布 RESTful 服务

什么是 REST

REST 是 Roy Thomas Fielding 在 2000 年他的博士论文 “架构风格以及基于网络的软件架构设计” 中提出来的一个概念。REST 是 REpresentational State Transfer 的缩写,翻译过来就是 “表现层状态转化”。REST 就是 Roy 在这篇论文中提出的面向互联网的软件所应当具备的架构风格。

按照 REpresentational State Transfer 的字面意思,可以把应用看成是一个虚拟的状态机,软件提供的不是服务而是一系列的资源,对这些资源的访问通过统一的操作来访问,而返回的结果代表了资源状态的一次跃迁。REST 是一种架构风格,如果一个软件架构符合 REST 风格,就可以称之为 RESTful 架构。这个架构应当具备以下一些设计上的约束:资源具有唯一标示、资源之间有关联关系、使用标准的方式来访问、资源有多种表现形式、无状态交互。

举例来说,一个简单的静态 HTML 页面的网站就很好的符合了 RESTful 架构风格。访问 http://example.com/accounts 返回一个包含所有账号的页面,选取其中一个链接 http://example.com/accounts/1 又会返回包含用户 1 的详细信息。爬虫软件在这种场景下工作的很好,当知道了某个网站的首页地址后,可以自举发现这个网站上所有关联的网页。更重要的是,这种访问形式不依赖网站提供的任何客户端,而是仅仅通过 HTTP 标准的访问方式完成的。可以说,HTML 这种超媒体文档的组织形式就是资源的表现层状态迁移的一种形式。

对于一个提供服务的动态网站来说,可以按照类似的思路将其 RESTful 化:

  • GET http://example.com/accounts 返回所有账号信息

  • POST http://example.com/accounts 创建一个新的账号

  • GET http://example.com/accounts/1 返回账号 1 的信息

  • DELETE http://example.com/accounts/1 删除账号 1

  • PUT http://example.com/accounts/1 更新账号 1 信息

 其中的思路是利用 HTTP 协议的标准方法 POST、DELETE、PUT、GET 来表达对于一个资源的增删改查 (CRUD) 操作,利用 URL 来表示一个资源的唯一标识。对资源访问的错误码也复用 HTTP 协议的状态码。返回结果通常由 json 或 XML 来表示,如果其中包换了对关联资源的访问方式 (所谓的表现层状态迁移) ,这种类型的 RESTful 应用可以进一步的称之为 hypermedia as the engine of application state (HATEOAS) 应用 。

在 Dubbo 中使用 REST

背景

随着微服务的流行以及多语言互操作诉求日益增多,在 Dubbo 中暴露 REST 服务变成了一个不容忽视的诉求。为了在 Dubbo 中暴露 REST 服务,通常有两种做法,一种是直接依赖 Sprng REST 或者其他 REST 框架来直接暴露,另一种是通过 Dubbo 框架内置的 REST 能力暴露。两种做法各有优缺点,主要体现在前者与微服务体系中的服务发现组件能够更好的工作,而后者可以无缝的享受到 Dubbo 体系中的服务发现以及服务治理的能力。本文关注的是如何使用后者来暴露 REST 服务。

自 2.6.0 开始,Dubbo 合并了当当网捐献的 DubboX 4 中的主要特性,其中就包括了基于 RESTeasy 3.0.19.Final 的 REST 支持,具备 JAXRS 2.0 规范中所有的能力。

基本用法

在以下的例子中,展示了如何通过注解的形式暴露和调用一个 REST 服务。其中底层的 server 使用的是 netty,服务注册发现基于 Zookeeper。

注:本章讨论的示例可以通过 Dubbo3.0新特性介绍&&配置dubbo和rest协议--github 来获得(含有注解形式和XML形式的两种配置方式)

1. Maven 依赖

生产者provider的相关依赖如下,REST support dependencies下面的依赖是rest应用必须的。

目前 REST 协议在 Dubbo 中可以跑在五种不同的 server 上,分别是:

  • "netty": 直接基于 netty 框架的 rest server,通过 <dubbo:protocol name="rest" server="netty"/> 对应本项目配置文件 dubbo.protocols.dubbo.server=netty4来配置
  • "tomcat": 基于嵌入式 tomcat 的 rest server,通过 <dubbo:protocol name="rest" server="tomcat"/>来配置
  • "jetty": 默认选项,基于嵌入式 jetty 的 rest server,通过 <dubbo:protocol name="rest" server="jetty"/> 来配置
  • "sunhttp": 使用 JDK 内置的 Sun HTTP server 作为 rest server,通过 <dubbo:protocol name="rest" server="sunhttp"/> 来配置,仅推荐在开发环境中使用
  • "servlet”: 采用外部应用服务器的 servlet 容器来做 rest server,这个时候,除了配置<dubbo:protocol name="rest" server="servlet"/> 之外,还需要在 web.xml 中做额外的配置
<dependencies>
        <dependency>
            <groupId>com.yimint</groupId>
            <artifactId>annotation-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <!-- dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <type>pom</type>
        </dependency>

        <!-- dubbo starter -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>

        <!-- spring starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <!-- REST support dependencies -->
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-netty4</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson-provider</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>

    </dependencies>

消费者consumer的相关依赖如下 

    <dependencies>
        <dependency>
            <groupId>com.yimint</groupId>
            <artifactId>annotation-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <!-- dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <type>pom</type>
        </dependency>
        <!-- dubbo starter -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>

        <!-- spring starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- REST support dependencies -->
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
        </dependency>
    </dependencies>

 注意:如果消费端不添加Rest supprot依赖,会找不到生产者发布的rest服务。控制台会报No provider available for the service 错误以及启动失败。

 2. 定义服务接口

在Dubbo中开发REST服务主要都是通过JAX-RS的annotation来完成配置的,在本此示例中,我是将annotation放在服务的接口类中。但其实也可以将annotation放到服务的实现类上,这两种方式是完全等价的,例如定义一个服务接口 TestService,提供一个获取用户的功能。

package com.yimint.common.interfaces;

import com.yimint.common.domain.User;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;


@Path("test")
@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) // #2
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
public interface TestService {
    @GET // #3
    @Path("{id: \\d+}")
    public User getUser(@PathParam("id") String uId);
}

通过在接口上用 JaxRS 标准的 annotation 来修饰,我们规定了该服务在 REST 下的访问形式:

  1. @Path("test") 定义了 TestService 通过 ‘/test’ 来访问
  2. 在类级别上定义 @Consumers 和 @Produces 来规定参数以及返回值的类型为 XML 和 JSON。在类级别上定义之后,就可以不用在方法级别上进一步定义了
  3. getUser 方法上通过 @GET 定义了接受的 HTTP 方法为 GET,通过 @Path 来规定参数是来自于 URL 中的 path。‘GET /test/1’ 等同于调用 ‘getUser(1)’

注意:@Path需要添加以下依赖

<dependency>
   <groupId>org.jboss.resteasy</groupId>
   <artifactId>resteasy-jaxrs</artifactId>
</dependency>

在 Dubbo 中,将 REST 相关的 annotation 定义在接口或者实现上都是可以的。这个在设计上是个权衡问题。Annotation 定义在实现类上可以保证接口的纯净,否则对于不需要通过 REST 方式调用的 Dubbo 调用方来说将需要强制依赖 JaxRS 的库,但是同时,对于需要通过 REST 方式调用的 Dubbo 调用方来说,就需要自己来处理 REST 调用相关的细节了。Annotation 定义在接口上,框架会自动处理掉 REST 调用相关的细节,并和 Dubbo 的服务发现以及服务治理功能能够很好的结合起来。

3. 实现生产端接口和装配服务

为了简洁,这里给出的接口的实现只是简单的返回了接口需要的类型的示例,在真实的系统中,逻辑可能会比较复杂。

本例展示的是如何通过注解的形式来装配并暴露 Dubbo 服务。需要指出的是,这里展示了如何同时暴露两种不同的协议,一种是 REST,另一种是原生的 Dubbo 协议

package com.yimint.provider.service;

import com.yimint.common.domain.User;
import com.yimint.common.interfaces.TestService;
import org.apache.dubbo.config.annotation.DubboService;

@DubboService(group = "testService", protocol = {"rest"})
public class TestServiceImpl implements TestService {
    @Override
    public User getUser(String uId) {
        return User.builder().id(uId).name("Dubbo").build();
    }

}

生产端 application.properties 配置文件 

server.port=8081

dubbo.application.name=provider-application
dubbo.registry.register-mode=instance
dubbo.registry.address=zookeeper://127.0.0.1:2181?registry-type=service

dubbo.protocols.dubbo.name=dubbo
dubbo.protocols.dubbo.port=20880
dubbo.protocols.dubbo.server=netty4

dubbo.protocols.rest.name=rest
dubbo.protocols.rest.port=8082
dubbo.protocols.rest.server=netty

# 注意protocols,在dubbo对应的配置类中,protocols字段是一个Map<String, ProtocolConfig>类型,
# 所以这里的 dubbo 和 rest 是Map集合中的key,是可以随意取的,只需要和@DubboService注解中的protocol的值保持一致就行,这里为了让服务协议一目了然。
# @DubboService(group = "testService", protocol = {"rest","dubbo"})
# 如果只有一个协议的,一般我们直接按以下的配置

# dubbo.protocol.name=dubbo
# dubbo.protocol.port=20880
  1. 定义了该应用的名字为 provider-provider
  2. 定义了服务注册通过 Zookeeper,并且 URL 为 “zookeeper://127.0.0.1:2181”
  3. 在端口 8082 上以 REST 方式暴露服务,底层的传输使用的是 netty
  4. 在默认端口 20880 上以原生 Dubbo 方式暴露服务,底层的传输方式是 netty

4. 服务提供方的启动类 

package com.yimint.provider;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.yimint.provider.service")
public class ProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }

}

 5.启动服务端

由于本例依赖 Zookeeper 做服务注册发现,在启动 RestProvider 之前,需要先启动一个 Zookeeper 服务器。之后就可以之间运行 RestProvider 了。

6. 装配消费端

Dubbo 调用方只需要依赖服务的接口,通过以下方式装配好 Dubbo Consumer,即可发起调用。

package com.yimint.consumer.service;

import com.yimint.common.domain.User;
import com.yimint.common.interfaces.TestService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @DubboReference(group = "testService")
    private TestService testService;

    public String testOrder(){
        User user = testService.getUser("2");
        System.out.println("测试订单");
        return user.toString() + " succeeded in testing the order";
    }
}

消费端application.properties配置文件,注意端口不要和rest服务的端口冲突。

server.port=8083

dubbo.application.name=consumer-application
dubbo.registry.address=zookeeper://127.0.0.1:2181

7.发起调用

直接通过访问rest协议定义的端口号进行调用,localhost:8082/test/1,结果如下

通过消费端进行访问,这里贴一下消费端的controller接口

package com.yimint.consumer.controller;

import com.yimint.consumer.service.OrderService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class OrderController {

    @Resource
    private OrderService orderService;

    @GetMapping("/testOrder")
    public String testOrder() {
        return orderService.testOrder();
    }
}

调用localhost:8083/testOrder,请求访问testIOrder方法,通过OrderService #testOrder方法调用 testService调用我们发布的rest服务,结果和上面一致就不贴了,以上为注解形式,同时我也配置了xml的形式在我的项目地址上,最后再附上项目地址。

Dubbo3.0新特性介绍&&配置dubbo和rest协议--gitee

Dubbo3.0新特性介绍&&配置dubbo和rest协议--github

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值