Dubbo + zookeeper搭建分布式服务入门(带源码)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011350550/article/details/80626172

dubbo + zookeeper 搭建分布式服务入门

dubbo是阿里开源的高性能RPC框架,框架图如下:
<img src="./dubbo-architecture.png" width="550" height="350" /><br>
可以分为4个部分,注册中心,消费者,提供者和监控中心,这也是一般分布式服务的常见架构。
本文作为dubbo入门例子,采用zookeeper作为注册中心,可分为两个部分,如下:
- 搭建zookeeper注册中心
- 利用dubbo搭建分布式服务

搭建zookeeper注册中心

我们学习dubbo,可以在本机上搭建一个zookeeper注册中心,zookeeper在主流操作系统上都可以运行(windows,linux等)。需要注意的是zookeeper是依赖jdk的,在安装zookeeper前先安装java。无论在windows还是linux

在windows上安装zookeeper

1、直接在官网下载zookeeper压缩包zookeeper-x.x.xx.tar.gz(x.x.xx是版本号),解压到本地文件夹。
2、修改conf文件夹下zoo_sample.cfg文件名为zoo.cfg,并将文件内容替换为下面:

tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181

这个也官网介绍的方法,以standalone方式安装zookeeper,官网地址:http://zookeeper.apache.org/doc/current/zookeeperStarted.html
3、启动bin文件下启动zkServer.cmd,可以用命令行启动,也可以直接启动。

在ubuntu中安装zookeeper

在生产环境,很少会把zookeeper安装的windows下,所以为了学习更多linux相关知识,我在本机上装了一个虚拟机。
1、安装virtualBox虚拟机软件。
2、新建虚拟机,并安装ubuntu系统。
3、用xsheel远程连接ubuntu,并把zookeeper压缩包上传至服务器,建议用root用户登录,一般用户远程连接,传输文件时经常没有权限被拒绝,ubuntu系统默认不支持root用户远程连接,需要修改配置,配置教程:https://www.cnblogs.com/TechSnail/p/7695090.html
4、解压zookeeper文件。
5、在/etc/profile文件中设置PATH。
修改profile文件:指令 sudo vim /etc/profile,

export ZOOKEEPER_HOME=/xxx/xx/zookeeper-3.4.3
PATH=$ZOOKEEPER_HOME/bin:$PATH
export PATH

注意,ZOOKEEPER_HOME的值是解压zookeeper的路径。
6、启动zookeeper。
指令:zkServer.sh start
查看状态:zkServer.sh status.
注意:若在虚拟机中安装zookeeper,需要修改虚拟机网络的配置(配置NAT转换和host-only网卡),添加端口映射才能连接到虚拟机的zookeeper。
到此,zookeeper服务注册中心已经搭建完毕,下面讲讲利用dubbo搭建分布式服务。

利用dubbo搭建分布式服务

本利为加深对分布式服务的理解,以一个学生信息(Student)的增删查为例进行讲解。
服务端(提供者)实现对学生信息的插入,查询和删除三项操作。
客户端(消费者)可以远程调用服务端接口,实现信息的操作。
项目地址:https://github.com/xubaodian/dubbo-study ,可下载参考
项目结构如下:
<img src="./project.PNG" width="300" height="250" /><br>
简单介绍下工程结构,这是一个maven工程,包括三个模块(module),分别为consumer(消费者、客户端),demoapi(都依赖的一些接口和实体类)和provider(服务提供者、服务端)。
最外层pom依赖进行依赖管理,把本项目所有的依赖都添加进来.
注意:各项依赖之间存在隐性依赖,可能会产生冲突,利用 标签消除依赖冲突,具体可百度。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xbd</groupId>
    <artifactId>dubbo-study</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>provider</module>
        <module>demoapi</module>
        <module>consumer</module>
    </modules>
    <properties>
        <junit.version>4.11</junit.version>
        <spring.version>4.3.16.RELEASE</spring.version>
        <slf4j.version>1.6.4</slf4j.version>
        <dubbo.version>2.6.2</dubbo.version>
        <zk.version>3.4.10</zk.version>
        <log4j.version>1.2.17</log4j.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>${zk.version}</version>
            </dependency>
            <!--日志依赖-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <!-- spring相关 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jms</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.6.11</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.6.11</version>
            </dependency>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-recipes</artifactId>
                <version>2.4.2</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.zookeeper</groupId>
                        <artifactId>zookeeper</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

按顺序介绍三个模块

demoapi模块

该模块包括两个实体类和一个接口,实体类分别是Student.java学生信息类,RetMessage操作信息类,提示消费者操作是否成功及操作失败原因。
一个接口DemoApi.java,提供学生信息增删改接口。

//学生信息实体类
package com.xbd.demoapi.entity;
import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 0L;
    private String stuId;
    private String name;
    private int age;
    private int grade;

    public String getStuId() {
        return stuId;
    }

    public void setStuId(String stuId) {
        this.stuId = stuId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getGrade() {
        return grade;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }
}
//RetMessage操作信息类
package com.xbd.demoapi.entity;

import java.io.Serializable;

public class RetMessage implements Serializable {
    private static final long serialVersionUID = 1L;
    private boolean success;
    private String message;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

//DemoApi接口
package com.xbd.demoapi.service;

import com.xbd.demoapi.entity.RetMessage;
import com.xbd.demoapi.entity.Student;

public interface DemoApi {
    public Student getInfoById(String Id);
    public RetMessage insertInfo(Student stu);
    public RetMessage deleteById(String Id);
}
provider模块

该模块依赖我就不多说了,主要介绍下spring配置文件,dubbo-provider.xml,该文件内容如下,各个语句都有注释:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo
       http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- provider's application name, used for tracing dependency relationship -->
    <!--服务提供者名称-->
    <dubbo:application name="provider"/>

    <!-- use multicast registry center to export service -->
    <!-- <dubbo:registry protocol="zookeeper" address="localhost:2181"/>-->
    <!--zookeeper注册中心地址-->
    <dubbo:registry protocol="zookeeper" address="zookeeper://192.168.56.10:10000"/>

    <!-- use dubbo protocol to export service on port 20880 -->
    <!--服务名称和端口号-->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- service implementation, as same as regular local bean -->
    <!--接口的执行类-->
    <bean id="demoService" class="com.xbd.provider.DemoApiImpl"/>

    <!-- declare the service interface to be exported -->
    <!--声明接口-->
    <dubbo:service interface="com.xbd.demoapi.service.DemoApi" ref="demoService"/>

</beans>

服务实现类为DemoApiImpl.java,如下,各个接口的意义意义在注释中有解释。

package com.xbd.provider;

import com.xbd.demoapi.entity.RetMessage;
import com.xbd.demoapi.entity.Student;
import com.xbd.demoapi.service.DemoApi;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class DemoApiImpl implements DemoApi {
    /**
     * 学生信息List,存放学生信息,本来打算在数据库中增删,为了简化例子,直接用一个List代替;
     * 若是连接数据库进行增删改查,在此处调用dao层接口即可
     */
    List<Student> stuList = new ArrayList<>();

    /**
     * 根据Id获取学生信息,若是连接数据库,调用dao层接口查询学生信息
     * @param Id String
     * @return Student
     **/
    @Override
    public Student getInfoById(String Id) {
        Student stu = this.getInfo(Id);
        return stu;
    }

    /**
     * 插入学生信息,并判断是否插入成功,返回操作信息
     * @param stu
     */
    @Override
    public RetMessage insertInfo(Student stu) {
        RetMessage ret = new RetMessage();
        ret.setSuccess(false);
        if(stu!=null && !isBlank(stu.getStuId()) && !isBlank(stu.getName())){
            Student stuVo = this.getInfo(stu.getStuId());
            if(stuVo != null) {
                ret.setMessage("该用户已存在!");
            } else{
                stuList.add(stu);
                ret.setSuccess(true);
                ret.setMessage("成功");
            }
        } else {
            ret.setMessage("数据不全,请检查!");
        }
        return ret;
    }

    /**
     * 删除学生信息
     * @param Id
     * @return RetMessage
     */
    @Override
    public RetMessage deleteById(String Id) {
        RetMessage ret = new RetMessage();
        ret.setSuccess(false);
        if(!isBlank(Id)) {
            int len = stuList.size();
            for(int i = 0; i < len; i++) {
                if(Id.equals(stuList.get(i).getStuId())){
                    stuList.remove(i);
                    ret.setSuccess(true);
                    ret.setMessage("成功!");
                    break;
                }
            }
        } else {
            ret.setMessage("Id为空,请检查输入数据");
        }
        return ret;
    }

    private Student getInfo(String Id){
        Iterator<Student> it = stuList.iterator();
        Student stu = null;
        while (it.hasNext()) {
            stu = it.next();
            if(stu.getStuId().equals(Id)){
                break;
            }
        }
        return stu;
    }

    private boolean isBlank(String value){
        return value == null || "".equals(value) || "null".equals(value);
    }
}

启动类provider.java如下,在代码中启动了spring支持。

package com.xbd.provider;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;

public class Provider {
    public static void main(String [] args) throws IOException {
        System.setProperty("java.net.preferIPv4Stack", "true");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-provider.xml"});
        context.start();
        System.out.println("the server start");
        System.in.read(); // press any key to exit
    }
}
消费者模块(consumer)

消费者模块代码如下:

package com.xbd;

import com.xbd.demoapi.entity.RetMessage;
import com.xbd.demoapi.entity.Student;
import com.xbd.demoapi.service.DemoApi;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Consumer {
    public static void main(String [] args){
        System.setProperty("java.net.preferIPv4Stack", "true");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-consumer.xml"});
        context.start();
        DemoApi demoService = (DemoApi) context.getBean("demoService"); // get remote service proxy
        Student student = new Student();
        student.setStuId("111");
        student.setName("xxx");
        RetMessage retMessage1 = demoService.insertInfo(student);
        System.out.println(retMessage1.getMessage());
        RetMessage retMessage2 = demoService.insertInfo(student);
        System.out.println(retMessage2.getMessage());
    }
}

同样的信息插入了两次,打印信息如下:

成功
该用户已存在!

对照DemoServiceImpl中的实现,我们想要实现的功能已经跑通了,赶紧动手试一试吧。

猜你喜欢

转载自blog.csdn.net/u011350550/article/details/80626172