《面向服务的软件系统》实验二:Dubbo环境搭建与服务部署
《面向服务的软件系统》实验报告
——实验二:Dubbo环境搭建与服务部署
姓名: 石卓凡 学号: 120L021011
Dubbo框架与Zookeeper框架之间的关系是什么?如何将Zookeeper框架与Dubbo框架进行集成?
关系:
关系概括:Dubbo框架中需要的服务注册中心可以由zookeeper来完成
为了解决微服务遇到的各种问题,产生主流微服务框架Dubbo和Spring Cloud,比如说dubbo是一个远程调用服务的分布式框架,可以实现远程通讯、动态配置、地址路由等等功能
功能 | Dubbo | SpringCloud |
---|---|---|
服务注册中心 | Zookeeper | Eureka(主流)、Consul、zookeeper |
服务调用方式 | RPC基于Dubbo协议 | REST API 基于Http协议 |
服务监控 | Dubbo-Monitor | Spring Boot Admin |
… | … | … |
关于dubbo的众多功能中的服务注册中心功能,Dubbo目前支持4种注册中心,其中包括Zookeeper注册中心,并且Dubbo建议使用Zookeeper作为服务的注册中心,实现服务注册和发现
Dubbo使用zookeeper具体来说:
dubbo有很多服务的提供者和消费者,提供者和消费者需要一个管理中心来管理,这个时候用zookeeper来管理
如果没有zookeeper作为注册中心,具体流程如下图:
图1.1
Provider | 暴露服务的服务提供方 |
---|---|
Consumer | 调用远程服务的服务消费方 |
如果使用zookeeper作为注册中心,具体流程
- 服务容器负责启动加载,运行Provider。
- Provider在启动时,向注册中心注册自己提供的服务。
- Consumer在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回Provider地址列表给Consumer,如果有变更,注册中心将基于长连接推送变更数据给Consumer。
- Consumer,从Provider地址列表中,基于软负载均衡算法,选一台Provider进行调用,如果调用失败,再选另一台调用。
如下图:
图1.2
Provider | 暴露服务的服务提供方 |
---|---|
Consumer | 调用远程服务的服务消费方 |
Zookeeper | 注册中心 |
集成过程:
下载zookeeper并且解压
图1.3
目录中创建 data、logs 两个目录作为 zookeeper的数据文件夹和日志文件夹
图1.4
下载dubbo-master,使用 maven 命令“mvn install -Dmaven.test.skip=true”编译 dubbo-master 项目,使得每一个模块都可以在target文件夹下获取war文件
图1.5
将对应的.war文件放入tomcat的webapps文件夹对应位置
编辑 D:\apache-tomcat-7.0.91\conf\server.xml,在<Host>节点下添加节点 <Context docBase=”D:/dubbox-master/dubbo-admin/target/dubboadmin-2.8.4” path=”” reloadable=”false”/>
图1.6
重新启动tomcat,等待tomcat的重新执行,此时就可以允许tomcat可以处理dubbo并且发布
在对应的provider和consumer的xml配置文件中找到dubbo:regegisry标签,将address属性设置为想要设定的注册中心地址
<dubbo:registry address=”zookeeper://127.0.0.1:2181”/>
- 启动zookeeper,tomcat,可以直接访问dubbo 内置管理界面
集成完毕
Dubbo框架中的Dubbo-master的作用是什么?
Dubbo-master的主要作用是,作为dubbo的稳定版本,里面封装了dubbo的文件内容,可以做到mvn编译之后即可开箱即用,供客户简单方便将dubbo与tomcat或者spring集成使用
Dubbo-master是dubbo在git仓库中的master分支的内容,一般是目前的最稳定的代码版本,master为主分支,也是用于部署生产环境的分支。
其他分支比如dubbo-2.4.11-dev则是dubbo的2.4.11的dev版本
Dubbo-master内容,包含了以下几个模块
公共逻辑模块 | dubbo-common | 包括Util类和通用模型 |
---|---|---|
远程通信模块 | dubbo-remoting | 相当于dubbo协议的实现,如果RPC使用RRRMI协议则不需要使用此包 |
远程调用模块 | dubbo-rpc | 抽象各种协议,以及动态代理,包含一对一的调用,不关心集群的原理。 |
集群模块 | dubbo-cluster | 将多个服务提供方伪装成一个提供方,包括负载均衡,容错,路由等,集群的地址列表可以是静态配置的,也可以是注册中心下发的. |
注册中心模块 | dubbo-registry | 基于注册中心下发的集群方式,以及对各种注册中心的抽象 |
监控模块 | dubbo-monitor | 统计服务调用次数,调用时间,调用链跟踪的服务 |
配置模块 | dubbo-config | 是dubbo对外的api,用户通过config使用dubbo,隐藏dubbo所有细节 |
容器模块 | dubbo-container | 是一个standlone的容器,以简单的main加载spring启动,因为服务通常不需要Tomcat/Jboss等web容器的特性,没必要用web容器去加载服务. |
Dubbo框架下Service Provider发布服务的过程,及过程中的关键点。
Provider:
服务提供者,在注册中心注册作为服务提供的一方,发布服务到服务注册中心。本次实验provider模块作为提供者
发布服务过程:
服务容器负责启动加载,运行Provider。
BootStrap中的ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(“META-INF/spring/provider.xml”);
ctx.start();启动了spring的IOC容器,准备读取”META-INF/spring/provider.xml”文件作为应用程序上下文
Provider在启动时,向注册中心注册自己提供的服务。
- 读取provider模块中的”META-INF/spring/provider.xml”
- dubbo:application给应用配置基本信息
- dubbo:registry给dubbo配置注册中心zookeeper
- dubbo:protocol定义发布服务用到的dubbo和rest协议
- bean注册spring bean给容器管理
- Dubbo:service以dubbo或者rest协议发布服务去注册中心
- 运行后完成向注册中心注册自己提供的服务
- 读取provider模块中的”META-INF/spring/provider.xml”
本次实验,服务提供者代码分析:
- 在provider中的java目录下的service中存放实现多个Impl类(实现了api中的接口),比如UserRestServiceImpl,UserServiceImpl等,
这些类就是后续将通过dubbo的注册中心发布并给消费者订阅
2.在provider中的resources/META-INF/spring中的provider.xml文件中,实现了spring和dubbo的衔接,是xml config配置文件,配置提供者相关的spring和dubbo的相关内容
provider.xml提供者配置具体内容分析如下:
Dubbo:application标签:本application的信息
适用于向dubbo控制中心提供本application的信息
name | 当前应用名称,用于注册中心计算应用间依赖关系 |
---|---|
owner | 应用负责人,用于服务治理 |
organization | 组织名称(BU或部门),用于注册中心区分服务来源 |
并且可以在该标签下设置paramter属性比如qos
<**dubbo:application name=”provider” owner=”programmer” organization=”dubbox”**>
<**dubbo:parameter key=”qos.enable” value=”true”**/>
<**dubbo:parameter key=”qos.accept.foreign.ip” value=”false”**/>
<**dubbo:parameter key=”qos.port” value=”33333”**/>
</**dubbo:application**>
dubbo:registry标签:注册中心配置
其中的address为Provider指定注册中心的地址,使得provider去注册。
利用多个 <dubbo:registry> 标签,声明同时有多个不同的注册中心并在 <dubbo:service> 或 <dubbo:reference> 的 registry 属性指定使用的注册中心。
<!–zookeeper注册中心, 多地址间用”,”隔开 –>
<**dubbo:registry address=”zookeeper://127.0.0.1:2181”**/>
dubbo:protocol标签:服务提供者协议配置
为本次的provider后续的service发布提供了可选协议属性,比如这里定义了可以使用dubbo和rest协议
Dubbo协议采用单一长连接和 NIO 异步通讯,在消费者直接利用注册中心引入并使用ctx.getBean(XXXXX.class)即可使用
Rest协议需要消费者利用URL来调用,而本实验中的TestDubboRequest中不支持对于userRestService的getBean
<**dubbo:protocol name=”dubbo” serialization=”kryo”optimizer=”com.hit.lyx.dubbo.demo.api.common.SerializationOptimizerImpl”**/>
<dubbo:protocol name=”rest” port=”8888” threads=”500”
contextpath=”services” server=”tomcat” accepts=”500”
extension=”com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter”/>
<bean>
spring的bean标签,在XML文件进行配置,让spring容器来生产和管理这些bean,类似于工厂模式管理类的实例
这里定义的bean被dubbo:service标签可以发布到注册中心
<!–普通的spring bean–>
<**bean id=”userService” class=”com.hit.lyx.dubbo.demo.service.UserServiceImpl”**/>
<**bean id=”userRestService” class=”com.hit.lyx.dubbo.demo.service.UserRestServiceImpl”**>
<**property name=”userService” ref=”userService”**/>
</**bean**>
dubbo:service标签: 服务提供者暴露服务配置
在这个标签中provider向注册中心发布以指定的协议想发布的服务,
interface | 服务接口名 |
---|---|
ref | 服务对象实现引用 |
protocol | 使用指定的协议暴露服务 |
以本实验代码为例:
第一个dubbo:service标签,让id为userService,实现接口为com.hit.lyx.dubbo.demo.api.service.UserService的bean,以dubbo协议发布到注册中心.在消费者TestDubboRequest中,可以利用cxf.getBean(userService.class)获取到bean
<!–服务提供者以dubbo协议发布服务–>
<dubbo:service interface=”com.hit.lyx.dubbo.demo.api.service.UserService”
ref=”userService” protocol=”dubbo”/>
<!–服务提供者发布rest服务–>
<dubbo:service interface=”com.hit.lyx.dubbo.demo.api.service.UserRestService”
ref=”userRestService” protocol=”rest”
validation=”true”/>
第二个dubbo:service标签,让id为userRestService,实现接口为com.hit.lyx.dubbo.demo.api.service.userRestService的bean,以rest协议发布到注册中心.
启动类src/test/java/Bootstrap.java分析:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(“META-INF/spring/provider.xml”);
ctx.start();
创建 Spring 的 IOC 容器,生产管理了对应的bean实例以便于后续交给注册中心,被消费者所使用
关键点:
Q1:控制台报错信息Qos started failed,经过推断猜测qos端口与其他端口发生了冲突,导致无法正常启动Qos的服务
QoS,全称为Quality of Service,在Dubbo中,QoS这个概念被用于动态的对服务进行查询和控制在这类可以通过
<**dubbo:parameter key=”qos.port” value=”33333”**/>的命令修改dubbo中的qos默认配置从而解决问题
Q2:userRestService显示没有消费者
协议问题:
本次试验中除了userRestService以外其他采取的都是dubbo协议,userRestService是rest协议
Dubbo协议
在消费者直接利用注册中心引入
并使用ctx.getBean(XXXXX.class)即可使用
Rest协议
需要消费者利用URL来调用,而本实验中的TestDubboRequest中没有写入对于userRestService的rest调用
所以dubbo中显示userRestServic没有消费者
如果强行添加了ctx.getBean(UserRestService.class)会提示报错
Q3.userRestServiceImpl代码风格问题:
如果按照dubbo官方文档的推荐写法应该要加入“/”,本实验指导书给出的示例代码中缺少斜杠号/
@path(“users”)–应该为–>@path(“/users”)
@path(“{id: \\d+}”)–应该为–>@path(“/{id: \\d+}”)
图3.1
但是无论是否修改,都可以利用http://localhost:8080/users/load?id=1001来调用userRestService
Dubbo框架下Service Consumer消费服务的过程,及过程中的关键点。
Consumer:
服务消费者,通过注册中心协调,订阅可用的已注册的服务。
订阅服务过程:
Consumer向注册中心订阅自己所需的服务。
读取consumer模块中的”META-INF/spring/consumer.xml”
- dubbo:application给应用配置基本信息
- dubbo:registry给dubbo配置注册中心zookeeper
- Dubbo:refernce生成远程服务代理,声明消费者订阅了哪些被提供者发布的服务,允许消费者像使用本地bean一样使用远程服务
- dubbo:application给应用配置基本信息
Consumer启动,注册中心返回Provider地址列表给Consumer,如果有变更,注册中心将基于长连接推送变更数据给Consumer。
TestDubboRequest启动,通过ClassPathXmlApplicationContext ctx=newClassPathXmlApplicationContext(“META-INF/spring/provider.xml”);ctx.start();启动了spring的IOC容器,读取provider.xml的应用程序上下文
Consumer,从Provider地址列表中,基于软负载均衡算法,选一台Provider进行调用,如果调用失败,再选另一台调用。
TestDubboRequest利用ctx对注册中心中被provider发布的服务进行订阅调用,可以直接ctx.getBean(UserService.class)来获取到userService的bean实例
本次实验,消费提供者代码分析:
- 在java目录下有个TestDubooRequest的启动类,负责启动消费者
- 在provider中的resources/META-INF/spring中的consumer.xml文件中,实现了spring和dubbo的衔接,是xml config配置文件,配置提供者相关的spring和dubbo的相关内容
Consumer.xml消费者配置具体内容分析如下:
同provider的标签
dubbo:application
dubbo:registry
不同标签:
dubbo:reference标签: 服务消费者引用服务配置
生成远程服务代理,可以像使用本地bean一样使用demoService
消费者订阅的服务, 默认情况下需要先发布服务才能订阅, 通过添加check=”false”可以直接订阅任意服务,只是调用时需要注意判断是否可用
id | 服务引用BeanId |
---|---|
interface | 服务接口名 |
比如第一个标签,指定了BeanId=”userRestService” 实现的接口是”com.hit.lyx.dubbo.demo.api.service.UserRestService”的bean被消费者引入
<**dubbo:reference id=”userRestService” interface=”com.hit.lyx.dubbo.demo.api.service.UserRestService”**/>
TestDubboRequest消费者启动类具体内容分析如下:
1.根据路径找到配置上下文,启动spring的IOC容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(“META-INF/spring/consumer.xml”);
ctx.start();
2.通过IOC容器和注册中心的共同作用下,利用getBean()方法调用提供者的bean来给消费者进行使用
比如UserService service = ctx.getBean(UserService.class);调用了userSerivce的bean
关键点:
指导书展示效果原理分析
“b) 分别在启动 provider.Bootstrap、consumer. TestDubboRequest 后重新查看服务列表: ”
现象:出现了UserRestService提示没有消费者
原因:在消费者启动类中TestDubboRequest中,没有ctx.getBean(UserRestService)因此没有直接向注册中心调用
图4.1
现象:如果在TestDubboRequest中加入UserRestService userRestService = ctx.getBean(UserRestService.class);
提示报错:Error creating bean with name ‘userRestService’: FactoryBean threw exception on object creation;
图4.2
原因分析:在provider中我们发布userRestService是利用的rest协议,想要通过dubbo使用rest协议的服务还需要配置setter等,因此在本试验中不利用getBean直接获取,而得是通过URL获取。
其他的类由于是通过Dubbo协议发布,所以其他类可以利用getBean
“c) 在 consumer. TestDubboRequest 控制台中输入任意数字并回车,查看输出;”
现象:
控制台就得到
图4.3
原因分析:
在TestDubboRequest中获取System.in作为id参数,然后调用service.getUser(id)
图4.4
图4.5
方法将返回一个new User的toString(),然后这份String就输出在控制台中
“d) 在 浏 览 器 地 址 栏 输 入
127.0.0.1:8888/services/users/1.json 、
127.0.0.1:8888/services/users/1.xml
查 看 浏 览 器 及 provider.Bootstap 的输出:”
现象:
浏览器:
图4.6浏览器输出json
图4.7浏览器输出xml
控制台provider的输出:
图4.8控制台输出报错
图4.9控制台输出信息
问题:提示没有找到favicon.ico
原因分析:
比如说百度的
。
在本demo中的resources资源目录中没有存放favicon图标,spring在执行过程中发布网页之后自然无法找到。在springboot中可以通过在resources中提供favicon.ico图标即可将图标发布到Web上
问题:控制台信息是为什么输出这段内容
原因分析:
127.0.0.1:8888/services/users/1.json和127.0.0.1:8888/services/users/1.xml都对应的是provider在dubbo中以rest风格发布的userRestService
Rest协议就是将这个userRestService服务要实现的功能,提供如下URL,而任何客户端都可以将包含用户信息的JSON字符串POST到以上URL来完成用户注册
在userRestServiceImpl中:
图4.9代码
@Path(“/users”)指定访问UserRestService的URL相对路径是/users
@GET指定访问用HTTP GET方法
@Path(“/{id : \\d+}”)根据上面结合分析,访问的URL应当是“http://localhost:端口/users/ + 任意数字”,其中{id : \\d+}使用了正则表达式,要求是id为数字数字
代表路径为/users/{id},method为GET方式
@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
指定接收JSON格式和xml格式的数据。REST框架会自动将JSON数据和xml数据,反序列化为User对象
@Produces({ContentType.APPLICATION_JSON_UTF_8,ContentType.TEXT_XML_UTF_8})
指定输出JSON格式或者xml格式的数据。框架会自动将User对象序列化为JSON数据或者xml格式。
因此:访问127.0.0.1:8888/services/users/1.json将传送json格式的数据给后端userRestServiceImpl然后,后端返回json格式数据展示在前端网页
详细描述你的服务的发布过程和被消费方式。
服务容器负责启动加载,通过BootStrap运行Provider。
BootStrap中的ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(“META-INF/spring/provider.xml”);
ctx.start();启动了spring的IOC容器,准备读取”META-INF/spring/provider.xml”文件作为应用程序上下文
Provider在启动时,向注册中心注册自己提供的服务。
- 读取provider模块中的”META-INF/spring/provider.xml”
- dubbo:application给应用配置基本信息
- dubbo:registry给dubbo配置注册中心zookeeper
- dubbo:protocol定义发布服务用到的dubbo和rest协议
- bean注册spring bean给容器管理
- Dubbo:service以dubbo或者rest协议发布服务去注册中心
- 向注册中心注册自己提供的服务:userRestSertvice,userService,hsuse,houseManager,userManager,utilData这几个服务
- 读取provider模块中的”META-INF/spring/provider.xml”
Consumer向注册中心订阅自己所需的服务。
- 读取consumer模块中的”META-INF/spring/consumer.xml”
- dubbo:application给应用配置基本信息
- dubbo:registry给dubbo配置注册中心zookeeper
- Dubbo:refernce生成远程服务代理,声明消费者订阅了哪些被提供者发布的服务,允许消费者像使用本地bean一样使用远程服务
- 读取到userRestSertvice,userService,hsuse,houseManager,userManager,utilData这几个服务
- 读取consumer模块中的”META-INF/spring/consumer.xml”
Consumer启动,注册中心返回Provider地址列表给Consumer,如果有变更,注册中心将基于长连接推送变更数据给Consumer。
- TestDubboRequest启动,通过ClassPathXmlApplicationContext ctx=newClassPathXmlApplicationContext(“META-INF/spring/provider.xml”);ctx.start();启动了spring的IOC容器,读取provider.xml的应用程序上下文
Consumer,从Provider地址列表中,基于软负载均衡算法,选一台Provider进行调用,如果调用失败,再选另一台调用。
- TestDubboRequest利用ctx对注册中心中被provider发布的服务进行订阅调用,可以直接ctx.getBean(UserService.class)来获取到userService的bean实例
关键点:
如果在provider中注释掉某个dubbo:service标签比如UserService,然后启动消费者TestDubboRequest会提示对应的服务没有提供者
图5.1
如果只启动提供者的BootStrap而不启动消费者的TestDubboRequest,会提示所有服务都没有消费者
图5.2
如果在provider中注释掉某个dubbo:service标签比如UserService,然后不启动消费者TestDubboRequest会在dubbo中,刚才注释的服务不会出现
图5.3
如果启动provider的BootStrap,然后仅注释掉消费者中的consumer.xml中的dubbo:reference标签比如userService,启动消费者TestDubboRequest
会提示找不到注释掉的UserService找不到对应的bean
图5.4
如果启动provider的BootStrap,然后仅注释掉消费者中的TestDubboRequest
中的ctx.getBean()标签比如UtilData,启动消费者TestDubboRequest
dubbo会提示没有消费者