SpringBoot dubbo 整合
最近公司在考虑公司的java项目的分布式架构的技术选型问题,初步讨论之后选择了 SpringCloud,但是在对 SpringCloud 进行一些的组件测试之后发现 一个问题,就是 SpringCloud 的eureka分布式服务在调用的时候还是有些不方便的地方,她主要是采用 http 协议实现 restful API,这样在调用的时候 一个是效率不高,第二是参数的序列化是个问题,经测试发现她目前对远程服务的调用只能接收一个复杂类型的参数,也就是第一个参数可以是复杂类型 的参数,其他参数都要是基本类型的,那就意味着,如果想要传入多个复杂类型参数,比如传入一个 User 类型的和一个 Order 类型,就必须自己手动将 参数打包成一个复杂的参数。这样无端为编码带来了很多额外的工作量。因此架构组在再三考虑之下决定用 dubbo 替换 SpringCloud 自带的分布式服务框架。
# 为什么用 dubbo
dubbo 是阿里巴巴开源的一款非常优秀的分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。 我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,而且她是 API 零入侵的。 最关键是她经过天猫商城等多个在线的大型项目的实战,每天30亿次的 API 调用,证明她的性能是可靠的,这一点对于技术选型尤其重要,至少我们知道 她一定能够解决我们的问题,因为有实战案例摆在那呢。 想对 dubbo 的架构和使用有进一步的了解,不妨去阅读官方的文档 dubbo-user-book (opens new window)
# SpringBoot 整合 dubbo
既然技术选型已经确定,那么接下来就是如何把 dubbo 整合到 SpringBoot中了。 dubbo 官方也开发了一个 spring-boot-starter-dubbo (opens new window) 但是经过我们验证之后发现至少目前都是不可行的。Provider 都可以正常运行,服务可以正常暴露出去,在dubbo-admin 后台上面也是可以发现服务的。 但是在运行 Consumer 之后都出现问题了,以官方的为例,它是通过在Spring Boot Application的上添加 @EnableDubboConfiguration, 表示要开启dubbo功能. 然后通过 @DubboConsumer 注入需要使用的 interface. 我们在 Consuomer 模块的控制器中 使用了 @DubboConsumer 注解来 创建服务实例,结果发现启动 SpringBoot 之后,发现 Controller 的服务无法访问。在调试源码之后我们发现原来是 spring-boot-starter-dubbo 在 扫描 @DubboConsumer 创建实例的时候导致 SpringBoot 无法创建 Controller 的实例。
接下来我们就分别说说这 spring-boot-starter-dubbo 和 xml 这两种接入方式
# spring-boot-starter-dubbo 接入方式
# 1. 安装 spring-boot-start-dubbo 到本地仓库
## 克隆代码
git clone git@github.com:teaey/spring-boot-starter-dubbo.git
## 编译安装
cd spring-boot-starter-dubbo
mvn clean install
# 2. 修改 pom.xml 文件,加入依赖
<!-- spring boot dubbo starter -->
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
<version>1.0.0</version>
</dependency>
# 3. 发布服务
先修改 application.properties 文件,设置服务暴露参数
spring.dubbo.module=provider
spring.dubbo.application.name=provider
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20884
spring.dubbo.scan=com.springboot.dubbo.provider.service
在 Spring Application 的 application.properties 中添加 spring.dubbo.scan 即可支持Dubbo服务发布,其中scan表示要扫描的package目录。
通过注解暴露服务
import com.alibaba.dubbo.config.annotation.Service;
import com.springboot.dubbo.service.DemoService;
@Service(version = "1.0.0")
public class DemoServiceImpl implements DemoService {
@Override
public String hello(String name) {
return "From Spring-Boot-Starter Provider, Hello "+name+", Fuck it whatever!";
}
}
启动 Provider 项目的 springBoot 应用可以成功将服务发布了。
# 4. 消费服务
首先同样要在消费项目的 pom.xml 引入 spring-boot-dubbo-starter
然后在需要调用服务的类(Controller) 中使用 @Reference 注解来创建 Bean
package com.springboot.dubbo.consumer.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.springboot.dubbo.service.DemoService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Reference(version = "1.0.0")
private DemoService demoService;
@GetMapping(value = "/hello")
public String index() {
return demoService.hello("Xiao Ming");
}
}
启动 Consumer 项目的 springBoot 访问 http://localhost:8001/hello 看看服务是否能正常访问
# 使用原生的 xml 方式接入
分别创建2个 SpringBoot 项目 XmlProvider, XmlConsumer. 分别加入 dubbo 和 zookeeper 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.2</version>
</dependency>
# 1. 发布服务
在 XmlProvider 项目的 resource 目录下新建 dubbo-demo-provider.xml, 配置服务暴露
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="demo-provider-xml"/>
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20885"/>
<!-- demo service -->
<dubbo:service version="1.0.1" interface="com.springboot.dubbo.service.DemoService" ref="demoService"/>
<!-- user service -->
<dubbo:service version="1.0.1" interface="com.springboot.dubbo.service.UserService" ref="userService"/>
<!-- order service -->
<dubbo:service version="1.0.1" interface="com.springboot.dubbo.service.OrderService" ref="orderService"/>
</beans>
这里尤其需要注意,服务的暴露需要一个一个暴露,不要使用扫描服务包路径的形式一次性暴露
<dubbo:annotation package="com.springboot.dubbo.service" />
这样在暴露多个服务的时候,会导致 @Transactional 注解失效
而且比较坑的是,启动的时候并不会报错,但是你在 dubbo 的后台查看服务的 的时候会发现服务的暴露地址有点奇怪,正常的暴露地址应该是这样的:
而使用扫描包路径暴露的服务却是这样的:
而且所有的服务都是一样的,没有什么区分,返回的都是一个 SpringProxy 代理对象,在调用的时候就出问题的了。
在 SpringBoot 应用入口程序 XmlProviderApplication 导入 dubbo-demo-provider.xml
package com.springboot.dubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
@ImportResource(value = "classpath:dubbo-demo-provider.xml")
public class XmlproviderApplication {
public static void main(String[] args) {
SpringApplication.run(XmlproviderApplication.class, args);
}
}
在服务暴露上也是不相同的,使用的是 Spring 的 @Service 注解,而不是 Dubbo 的 @Service.
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author yangjian
* @since 17-10-20.
*/
@Service("userService")
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {}
启动 SpringBoot 应用,服务就可以成功发布了。
# 2. 消费服务
在 XmlConsumer 项目的 resource 目录下新建 dubbo-demo-consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="demo-consumer-xml"/>
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 要扫描的包路径 使用注解方式创建服务 -->
<dubbo:annotation package="com.springboot.dubbo.consumer.controller" />
</beans>
在应用启动的时候加载 dubbo-demo-consumer.xml
package com.springboot.dubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
@ImportResource(value = "classpath:dubbo-demo-consumer.xml")
public class XmlconsumerApplication {
public static void main(String[] args) {
SpringApplication.run(XmlconsumerApplication.class, args);
}
}
使用也跟第一种方法一样,在需要调用远程的服务的地方使用 @Reference 注解创建服务就好了
总结:
第一种方式比较简单,方便,可以完全摆脱 xml 文档,但是兼容性有待考证,可能跟其他 SpringBoot 组件有冲突。
第二种方式稍微麻烦一些,需要配置 xml 文档,但是由于是采用原生的方式接入,兼容性会比较好些,可以放心使用。
两种方式的服务发布和消费都是相当方便的,通过注解就可以轻松解决,而且效率要比 SpringCloud 官方提供的分布式服务组件高的多。
# 项目源码
https://gitee.com/blackfox/springboot-dubbo (opens new window)
《完》
本站博文如非注明转载则均属作者原创文章,引用或转载无需申请版权或者注明出处,如需联系作者请加微信: geekmaster01