9-Dubbo 客户端测试
Dubbo 项目测试
此次测试将针对上一节中定义好的 Dubbo 业务中心项目,测试方法采用 JUNIT 和 WEB 方式。
1、使用 junit 进行测试
1.1、创建 Maven 项目,使用 webapp 模式,以便接下来进行 web 方式的测试,因此,可先将基本的 web 项目进行属性设置,以免报错。
1.2、父项的 pom.xml 定义 junit 版本
<junit.version>4.12</junit.version>
复制代码
1.3、修改项目 pom.xml 文件,可将上一节中定义的业务中心项目的相关依赖复制过来
1.4、创建 src/main/resources 目录,用于保存 dubbo.properties 配置信息,该文件可以使用上一节配置好的信息
1.5、src/main/resources 目录下,创建 dubbo-consumer.xml 文件
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:dubbo.properties</value> </list> </property> </bean> <dubbo:application name="${dubbo.application.name}" /> <dubbo:registry protocol="zookeeper" address="${dubbo.registry.address}" /> <dubbo:protocol port="${dubbo.protocol.port}"/> <dubbo:reference id="msgService" interface="org.fuys.ownrapi.service.MessageService" version="${dubbo.interface.version}"/></beans>
复制代码
1.6、pom.xml 导入 spring 测试开发包,便于 junit 使用
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope></dependency>
复制代码
1.7、进行 junit 测试,启动业务中心,进行服务调用
package owntest;import javax.annotation.Resource;import org.fuys.ownrapi.service.MessageService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@ContextConfiguration(locations={"classpath:dubbo-consumer.xml"})@RunWith(SpringJUnit4ClassRunner.class)public class TestDubbo { @Resource private MessageService msgService; @Test public void test() { System.out.println(msgService.echo("Hello,DUBBO! ")); }}
复制代码
2、web 基于 springMVC 调用 Dubbo
本次项目,在之前建立好的项目基础上,添加 springMVC 和 web 支持。
2.1、父项目 pom.xml 文件中添加 servlet、jsp、jstl 版本信息
<servlet.version>4.0.0</servlet.version><jsp.version>2.3.2-b02</jsp.version><jstl.version>1.2</jstl.version>
复制代码
2.2、修改本次项目 pom.xml 文件,追加 servlet、jsp、jstl、spring-web、spring-webmvc 开发包
2.3、src/main/resources 目录下添加 spring-common.xml 文件
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- configure AOP annotation support --> <aop:aspectj-autoproxy/> <!-- configure annotation --> <context:annotation-config/> <context:component-scan base-package="org.fuys.owntest"/> <import resource="dubbo-consumer.xml"/></beans>
复制代码
2.4、src/main/resources 目录下添加 spring-mvc.xml 文件
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- configure errors.jsp --> <bean id="mappingException" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException"> errors </prop> <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException"> errors </prop> </props> </property> </bean> <!-- open Spring MVC annotation support --> <!-- 若單獨配置mvc,而將context注解支持配置置於上級文件中 則controller仍然無法起作用,請求訪問不了,同時後臺不報錯不報錯 但會出現一個問題,日志顯示如:Did not find handler method --> <context:annotation-config/> <context:component-scan base-package="org.fuys.owntest"/> <mvc:annotation-driven/> <mvc:default-servlet-handler/> <!-- configure to access safe diretory --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"/> <property name="suffix" value=".jsp"/> </bean> <!-- configure property file --> <!-- id is solid and id must be messageSource --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <array> <value>log4j</value> <value>config</value> </array> </property> </bean></beans>
复制代码
2.5、web.xml 添加 springMVC 支持
<?xml version="1.0" encoding="UTF-8"?><!-- 對於各個節點的理解可根據英文原意進行解釋,這是最好的方式 --><!-- XML文件,版本1.0,編碼UTF8,該説明衹可放置于標記之下 --><!-- web-app:web應用程序根節點 --><!-- xmlns:xsi即xml命名空間的xml規範實例方式 --><!-- xmlns:xml命名空間類型 --><!-- xsi:schemaLocation:xml規範實例的規範位置 --><!-- id:web應用的標記 --><!-- version:web應用服務器的版本 --><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <!-- web項目名稱 --> <display-name>owntest</display-name> <!-- Spring容器启动,并且读取上下文配置文件 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/classes/spring-common.xml </param-value> </context-param> <!-- SpringMVC的web程序支持,即处理用户请求等控制层操作 --> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> <!-- <url-pattern>*.action</url-pattern> --> </servlet-mapping> <!-- Spring过滤器 --> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app>
复制代码
2.6、编写 controller 控制器,并引用服务,创建 WEB-INF/pages 目录,新建 jsp 页面
10-dubbo 項目部署与监控
Dubbo 项目部署
因 Dubbo 项目可以作为独立的进程运行,故可将文件压缩上传,解压之后,执行指令运行程序。执行命令文件可通过引用的 Dubbo 开发包获取,文件处于 dubbo.jar 包中目录(..\META-INF\assembly\bin)下。
1、Dubbo 项目通过 Maven 进行打包,故创建源文件目录 src/main/bin,将可执行命令文件存储于该存储目录之下。
2、项目打包为压缩文件,将压缩文件上传至 Linux 系统运行,压缩文件的形式一般以 tar.gz 为主。
因项目打包为压缩文件,需要对配置文件的目录进行打包配置,故新建目录,用于存储打包配置文件。新建目录为 src/main/assembly。
3、创建 assembly.xml 文件
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> <!-- <id>release</id> --> <formats> <!-- 生成压缩文件 --> <format>tar.gz</format> <!-- 将打包的程序归档到一个目录之中 --> <format>dir</format> </formats> <!-- zip包里直接就是bin等子目录,不包括artifactId这层目录 --> <includeBaseDirectory>false</includeBaseDirectory> <fileSets> <fileSet> <directory>src/main/resources</directory> <outputDirectory>conf</outputDirectory> <fileMode>0644</fileMode> </fileSet> <!-- <fileSet> <directory>${profile.dir}</directory> <outputDirectory>config</outputDirectory> <fileMode>0644</fileMode> </fileSet> --> <fileSet> <directory>${profiles.dir}</directory> <filtered>true</filtered> <includes> <include>logback.xml</include> <include>config/*.properties</include> </includes> <outputDirectory>conf</outputDirectory> <fileMode>0644</fileMode> </fileSet> <fileSet> <!-- 将src/main/bin目录下的文件打包到根目录(/bin)下. --> <directory>src/main/bin</directory> <outputDirectory>/bin</outputDirectory> <fileMode>0755</fileMode> </fileSet> </fileSets> <dependencySets> <dependencySet> <!-- 当前项目构件是否包含在这个依赖集合里 --> <useProjectArtifact>true</useProjectArtifact> <!-- 将scope为runtime的依赖包打包到lib目录下。 --> <outputDirectory>lib</outputDirectory> <scope>runtime</scope> </dependencySet> </dependencySets></assembly>
复制代码
4、pom.xml 追加打包插件
<plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <!--描述文件路径 --> <descriptor>src/main/assembly/assembly.xml</descriptor> </configuration> <executions> <execution> <id>make-assembly</id> <!-- 绑定到package生命周期阶段上 --> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions></plugin>
复制代码
5、默认情况下,项目打包后会包含所有文件,就会在*.jar 中包含重复的配置文件,这个问题当然只有打包运行程序时,才会发现,故对打包进行排除配置,修改 pom.xml 文件。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>${maven.jar.plugin.version}</version> <configuration> <encoding>${project.build.sourceEncoding}</encoding> <excludes> <exclude>META-INF/</exclude> <exclude>config</exclude> <exclude>logback.xml</exclude> </excludes> </configuration></plugin>
复制代码
6、因业务中心涉及三个项目,因此,都需要进行打包。父项和远程接口项目先進行打包,则可使用 Maven 指令:clean install,进行打包。业务中心项目则可使用 clean package 进行打包。打包业务中心时,需要注意 profile 目录,制定好使用配置目录,当然本次默认使用 dev。
7、上传*.tar.gz 文件至 Linux 系统,创建目录(mkdir -p /app/ownservice),将文件解压至该目录下。
8、先启动 zookeeper 服务,再启动 Dubbo 服务,启动成功后,可使用程序调用进行测试。
这里需要注意,使用命令文件 start.sh 时,会无法执行,是因为其文件格式为 doc,将文件格式修改为 unix 即可。编辑文件 vim start.sh,然后使用 set ff=unix,写入:wq 退出即可。
Dubbo 项目监控
为了监控 Dubbo 项目的状态,使用监控的客户端(dubbo-monitor-simple-2.5.3-assembly.tar.gz),该监控程序默认集成了 WEB 容器 jetty。
1、下载文件,创建目录(mkdir -p /app/dubbo-monitor),在该目录下解压文件。
2、编辑 dubbo-monitor 配置文件 dubbo.properties
#Set zookeeper registry center address
dubbo.registry.address=zookeeper://192.168.6.128:2181
#Set dubbo protocol port
dubbo.protocol.port=7070
#Set dubbo access jetty port
dubbo.jetty.port=8080
2.3、启动 dubbo-monitor 进行 dubbo 服务监控
2.4、实际开发项目中,可以在业务中心添加监控配置。
但是实际意义不大,因为,监控程序会从注册中心去自动搜寻业务中心。
修改 dubbo.properties 文件
# dubbo monitor address
dubbo.monitor.address=192.168.6.128:8080
修改 spring-dubbo.xml 文件
总结
在进行开发时,一旦出现找不到文件或者类,那么,只可能是两种情况,一种是真实没有,另一种是具有相同的多个文件或者类,造成项目不知道读取哪个。
11-集群服务
集群容错
单节点调用,当出现宕机或者无法访问的情况时,会导致服务不可用。为了解决这一问题,dubbo 提供了集群容错机制。在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
节点关系
1、Invoker
Invoker 是 Provider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息。
2、Directory
Directory 代表多个 Invoker,可以把它看成 List,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更。
3、Cluster
Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
4、Router
Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等。
5、LoadBalance
LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。
集群容错模式
1、Failover Cluster
失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2"来设置重试次数(不含第一次)。该容错方案是 dubbo 容错机制中的默认方案。如果未设置重试次数,则系统会一直尝试下去。
2、Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
幂等性指的是,使用相同参数对同一资源重复调用某个接口的结果与调用一次的结果相同,幂等性的数学表达:f(f(x)) = f(x)。
3、Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。易出现的问题则是调用信息丢失。
4、Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。最大问题在于不可靠,重启丢失。
5、Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2"来设置最大并行数。
6、Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息,也就是更新提供方本地状态。最大问题在于速度慢,任意一台报错则报错。
12-负载均衡
在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。
负载均衡策略
1、Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
2、RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
3、LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
4、ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
缺省只对第一个参数 Hash,如果要修改,请配置
缺省用 160 份虚拟节点,如果要修改,请配置
13-线程模型
如果事件处理的逻辑能迅速完成,并且不会发起新的 IO 请求,比如只是在内存中记个标识,则直接在 IO 线程上处理更快,因为减少了线程池调度。
但如果事件处理逻辑较慢,或者需要发起新的 IO 请求,比如需要查询数据库,则必须派发到线程池,否则 IO 线程阻塞,将导致不能接收其它请求。
如果用 IO 线程处理事件,又在事件处理过程中发起新的 IO 请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁。
因此,需要通过不同的派发策略和不同的线程池配置的组合来应对不同的场景:
<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
复制代码
Dispatcher
1、all
所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
2、direct
所有消息都不派发到线程池,全部在 IO 线程上直接执行。
3、message
只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
4、execution
只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
5、connection
在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。
ThreadPool
1、fixed
固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
2、cached
缓存线程池,空闲一分钟自动删除,需要时重建。
3、limited
可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题。
4、eager
优先创建 Worker 线程池。在任务数量大于 corePoolSize 但是小于 maximumPoolSize 时,优先创建 Worker 来处理任务。当任务数量大于 maximumPoolSize 时,将任务放入阻塞队列中。阻塞队列充满时抛出 RejectedExecutionException。(相比于 cached:cached 在任务数量超过 maximumPoolSize 时直接抛出异常而不是将任务放入阻塞队列)
14-直连提供者
对于 Dubbo 服务而言,消费者远程请求提供者服务,默认通过注册中心获取服务列表实现。
在一些不需要注册中心的情况下,可以通过以服务接口为单位的点对点直连方式,实现服务调用。A 接口配置了点对点,不影响 B 接口对于注册中心的访问。
通过 XML 配置通过 XML 配置,可在 dubbo:reference 中配置 url 指向提供者,将绕过注册中心,多个地址用分号隔开。
<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" />
复制代码
通过 -D 参数指定在 JVM 启动参数中加入-D 参数映射服务地址,该方式优先级最高。java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890
通过文件映射如果服务比较多,也可以用文件映射,用 -Ddubbo.resolve.file 指定映射文件路径,此配置优先级高于 dubbo:reference 中的配置。java -Ddubbo.resolve.file=xxx.properties 然后在映射文件 xxx.properties 中加入配置,其中 key 为服务名,value 为服务提供者 URL。com.alibaba.xxx.XxxService=dubbo://localhost:20890
基于线上复杂的环境,不建议使用该功能,只适合于测试阶段。
评论