Dubbo和Zookeeper
一. 上节回顾
1. 监控的场景,不推荐LoadRunner自带的监控,使用nmon来监控我们的Linux系统
2. nmon + 分析思路,最后定位到性能问题(mysql数据库的用户表没有加索引导致的性能问题)
3. 生成LoadRunner自带的分析报告,生成了数据的监控,关注TPS和响应时间
二. Dubbo简介
1. Dubbo是什么?
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案
其核心部分包括:
(1) 远程通讯:提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,已经"请求--响应"模式的信息交换方式
(2) 集群容错:提供基于接口方法的透明化远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持
(3) 自动发现:基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使服务提供方可以平滑增加或减少机器
2. Dubbo能做什么?
(1) 远程方法调用,只需简单配置,没有任何API接入
(2) 软负载均衡以及容错机制,可以在内网替代F5等硬件负载均衡
(3) 服务自动注册与发现,不需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且平滑增加或减少机器
(4) Dubbo采用全spring配置方式
3. Dubbo模型
Provider:暴露服务的服务提供方
Consumer:调用远程服务的服务消费方
Registry:服务注册于发现的注册中心
Monitor:统计服务的调用次数和调用时间的监控中心
Container:服务运行容器
0:服务容器负责启动,加载,运行服务提供者
1:服务提供者在启动时,向注册中心注册自己提供的服务
2:服务消费者在启动时,向注册中心订阅自己所需的服务
3:注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
4:服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调研,如果调用失败,再选另一台调用
5:服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
三. Zookeeper简介
1. 什么是Zookeeper?
Zookeeper作为Dubbo服务的注册中心,是一个分布式的服务框架,是树型的目录服务的数据存储,能够做到集群管理数据
Dubbo能与Zookeeper做到集群部署
2. Zookeeper的安装
(1) 下载
在/opt目录下,# wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
(2) 解压
# tar -zxvf zookeeper-3.4.14.tar.gz
(3) 配置环境变量
# vim /etc/profile
export ZK_HOME=/opt/zookeeper-3.4.14
export PATH=$$PATH:$ZK_HOME/bin
使环境变量生效:# source /etc/profile
(4) 新建data,logs目录
# cd zookeeper-3.4.14
# mkdir data, logs
(5) 修改编辑配置文件
# cd zookeeper-3.4.14/conf
拷贝zoo_sample.cfg并重命名为zoo.cfg
# cp zoo_sample.cfg zoo.cfg
# vim zoo.cfg
dataLogDir=/opt/zookeeper-3.4.14/logs
dataDir=/opt/zookeeper-3.4.14/data
我们使用的是单点模式, 所以配置server.1=192.168.0.105:3888
server.1=192.168.0.105:3888
(6) 启动Zookeeper服务
# cd bin
# ./zkServer.sh start
查看服务的状态
# ./zkServer.sh status
也可以通过查看Zookeeper端口2181,验证是否启动
# netstat -anp | grep 2181
(7) 连接到Zookeeper服务
# ./zkCli.sh -server localhost
[zk: localhost(CONNECTED) 0] ls /
[dubbo, zookeeper]
四. 开发Dubbo接口
1. 搭建Dubbo服务提供者(这个不需要我们必须掌握,以后开发会有,了解下就可以了)
(1) 新建服务提供者的maven工程:dubbo_pertest_provider
(2) 新建包com.pertest.server
(3) 在server包下新建一个接口类ServerToClient
package com.pertest.server; public interface ServiceToClient { /* 定义一个发短信的接口 @param mobile:手机号 @param content:内容 @param platform:平台,分别对应LIANTONG, YIDONG, DIANXIN @return 正常返回发送成功、失败。这里为了显示发送的手机号和平台内容,直接 */ String sendSMS(String mobile, String content, String platform); }
(4) 新建包com.pertest.server.impl,在impl包下新建一个接口实现类SMSServerImpl
package com.pertest.server.impl; import com.pertest.server.ServiceToClient; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; @Service //这个注释是暴露给消费者使用 @Component public class SMSServiceImpl implements ServiceToClient { public String sendSMS (String mobile, String content, String platform) { try { Thread.sleep(3000); return String.format("发送结果: %s, 手机号码: %s, 内容: %s, 平台%s", "SUCCESS", mobile, content, platform); } catch (InterruptedException e) { e.printStackTrace(); } return null; } }
(5) 在resources下创建配置文件applicationProvider.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 "> <!--应用名称--> <dubbo:application name="hello-world" /> <!-- 注册地址 --> <dubbo:registry address="192.168.0.105:2181" protocol="zookeeper"/> <!-- 用dubbo协议在28080端口暴露服务 --> <dubbo:protocol name="dubbo" port="28080"/> <!-- 接口的位置 --> <dubbo:service interface="com.pertest.server.ServiceToClient" ref="demoService" executes="10" /> <!-- 实现bean,客户端应用的bean就以这个id名称为主 --> <bean id="demoService" class="com.pertest.server.impl.SMSServiceImpl" /> </beans>
创建日志配置文件log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console name="STDOUT" target="SYSTEM_OUT"> <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> </Console> </Appenders> <Loggers> <Logger name="com.opensymphony.xwork2" level="info"/> <Logger name="org.apache.struts2" level="info"/> <Logger name="org.demo.rest" level="debug"/> <Root level="info"> <AppenderRef ref="STDOUT"/> </Root> </Loggers> </Configuration>
(6) 新建com.pertest.main包,在此包下新建执行类MyMainPertest
package com.pertest.main; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyMainPertest { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationProvider.xml"}); context.start(); System.out.println("按任意键退出"); System.in.read(); } }
运行main方法,测试一下,如果能看到控制台打印:按任意键退出,说明服务已经运行成功了
2. 搭建客户端(这个是必须要掌握的)
(1) 为什么需要java request?
不是所有接口都是HTTP协议(比如RPC底层调用,thrift协议),Dubbo协议或者thrift协议都有需要我们自己定制化开发jar包,jmeter只是一个封装好的工具
(2) java request是什么?
a. 纯java程序,实现了jmeter中提供接口Javasamplerclient
b. 将java程序集成到jmeter中,通过java request实现调用
c. java程序实现与压测目标的交互
d. jmeter控制java程序的生命周期、并发调度、收集结果报告等处理
(3) java request优势VS我们自己的短板
优势:
a. 自己写程序,流程控制比较灵活
b. 只要java程序可以实现的,就能够支持
(4) 编写客户端的步骤
a. 创建客户端工程dubbo_pertest_customer(这里新建的工程是和dubbo_pertest_provider并列的,在同一个window打开)
b. 在客户端代码里需要使用到服务端的jar包,在pom.xl里面需要这样引入
<!--继承提供者的jar包--> <dependencies> <dependency> <groupId>com.lemon.org</groupId> <artifactId>dubbo_pertest_provider</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
c. 我们需要和jmeter交互,因此要继承jmeter里面的接口方法,需要jmeter的一些jar包。由于客户端pom.xml依赖了服务端的jar包,所以我们将jmeter的jar包的依赖写在服务端的pom.xml中
<!-- https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_java --> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_java</artifactId> <version>5.0</version> <exclusions> <exclusion> <artifactId>quartz</artifactId> <groupId>org.quartz-scheduler</groupId> </exclusion> </exclusions> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_core --> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_core</artifactId> <version>5.0</version> </dependency>
d. 需要一个测试方法,看能不能调用服务端,进行信息返回:在src.main.java包下新建一个客户端类CustomerApplicationTests,在里面加入测试方法GetStringTest
//写一个测试方法 public static String GetStringTest(String mobile, String content, String platform) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"customer.xml"}); context.start(); ServiceToClient serviceToClient = (ServiceToClient) context.getBean("demoService"); String result = serviceToClient.sendSMS(mobile, content, platform); return result; }
调用这个测试方法,运行查看结果
public static void main(String[] args) { String mobile = "13524015547"; String content = "test dubbo"; String platform = "phone"; String result = GetStringTest(mobile, content, platform); System.out.println(result); }
e. 通过上面的调用,说明服务端已经可以使用了,怎么来改造jmeter的jar?
我们需要继承jmeter提供的抽象类AbstractJavaSamplerClient,这个类里面有setupTest,teardownTest,getDefaultParameters方法,而它实现了JavaSamplerClient类,这个类里runTest方法
f. getDefaultParameters方法为参数化内容,可以在jmeter的界面上显示出来要输入哪些参数,如果返回为null,则不显示
//此方法为参数化内容,可以在GUI模式下显示,如果返回为null,则不显示 @Override public Arguments getDefaultParameters() { Arguments params = new Arguments(); //参数化名字,自定义 params.addArgument("mobile", ""); params.addArgument("content", ""); params.addArgument("platform", ""); return params; }
g. 执行初始化内容,每个线程执行一次,通常建议再该方法中获取参数值,而不是在RunTest方法中使用,以便尽可能减少测试开销
// 执行初始化内容,每个线程执行一次,通常建议在该方法中获取参数值,而不是RunTest方法中使用,以便尽可能减少测试开销 public void setupTest(JavaSamplerContext context){ mobile = context.getParameter("mobile"); content = context.getParameter("content"); platform = context.getParameter("platform"); }
h. 每次执行的内容部分
//每次需要执行的内容部分 public SampleResult runTest(JavaSamplerContext context) { SampleResult sr = new SampleResult(); //在查看结果树里面显示的名称 sr.setSampleLabel("JmeterDubboTest"); //事务开始,开始计算时间 sr.sampleStart(); try { //调用java接口 String result = GetStringTest(mobile, content, platform); //输入方法的结果 sr.setResponseData("From dubbo provider back result: "+ result,null); //输入结果保存内容 sr.setDataType(SampleResult.TEXT); //在jmeter控制台终端显示 System.out.println(result); //设置测试结果为true sr.setSuccessful(true); } catch (Throwable e) { sr.setSuccessful(false); } finally { //事务的结束 sr.sampleEnd(); } return sr; }
i. 在测试运行结束时进行本次测试所需的清理工作,也是一个线程执行一次
// 在测试运行结束时进行本次测试所需的清理工作,也是一个线程执行一次 public void teardownTest(JavaSamplerContext context) { }
j. 在resources下编写客户端的配置文件customer.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 "> <!--应用名称--> <dubbo:application name="hello-world" /> <!-- 注册地址 --> <dubbo:registry address="192.168.0.105:2181" protocol="zookeeper"/> <!-- 用dubbo协议在28080端口暴露服务 --> <dubbo:protocol name="dubbo" port="28080"/> <!-- 接口的位置 --> <dubbo:service interface="com.pertest.server.ServiceToClient" ref="demoService" executes="10" /> <!-- 实现bean,客户端应用的bean就以这个id名称为主 --> <bean id="demoService" class="com.pertest.server.impl.SMSServiceImpl" /> </beans>
客户端的log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console name="STDOUT" target="SYSTEM_OUT"> <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> </Console> </Appenders> <Loggers> <Logger name="com.opensymphony.xwork2" level="info"/> <Logger name="org.apache.struts2" level="info"/> <Logger name="org.demo.rest" level="debug"/> <Root level="info"> <AppenderRef ref="STDOUT"/> </Root> </Loggers> </Configuration>
3. 将客户端打包成jar包
注意:客户端的pom.xml中还需要加入bulid相关的依赖,否则构建出来的jar包会有问题
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>1.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.handlers</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.fxc.rpc.impl.member.MemberProvider</mainClass> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.schemas</resource> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build>
点击右侧Maven,在对应的工程名下的Lifecycle中,点击install可以打成jar包
4. 在jmeter中调用dubbo接口
将jar包拷贝到jmeter的lib/ext目录下,重启jmeter
新建一个Java Request取样器,输入参数后运行
查看结果树