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);


}
ServiceToClient.java

(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;
    }
}
SMSServiceImpl.java

(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>
applicationProvider.xml

创建日志配置文件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>
log4j2.xml

(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();
    }

}
View Code

运行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>
View Code

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>
pom.xml

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;
    }
View Code

调用这个测试方法,运行查看结果

 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);
    }
View Code

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;
    }
View Code

g. 执行初始化内容,每个线程执行一次,通常建议再该方法中获取参数值,而不是在RunTest方法中使用,以便尽可能减少测试开销

    // 执行初始化内容,每个线程执行一次,通常建议在该方法中获取参数值,而不是RunTest方法中使用,以便尽可能减少测试开销
    public void setupTest(JavaSamplerContext context){
        mobile = context.getParameter("mobile");
        content = context.getParameter("content");
        platform = context.getParameter("platform");
    }
View Code

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;
    }
View Code

i. 在测试运行结束时进行本次测试所需的清理工作,也是一个线程执行一次

    // 在测试运行结束时进行本次测试所需的清理工作,也是一个线程执行一次
    public void teardownTest(JavaSamplerContext context) {

    }
View Code

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>
customer.xml

客户端的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>
log4j2.xml

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>
pom.xml

点击右侧Maven,在对应的工程名下的Lifecycle中,点击install可以打成jar包

4. 在jmeter中调用dubbo接口

将jar包拷贝到jmeter的lib/ext目录下,重启jmeter

新建一个Java Request取样器,输入参数后运行

查看结果树

 

posted @ 2020-04-08 18:58  cnhkzyy  阅读(382)  评论(0编辑  收藏  举报