dubbo是阿里开源的高性能RPC框架,框架图如下:
可以分为4个部分,注册中心,消费者,提供者和监控中心,这也是一般分布式服务的常见架构。
本文作为dubbo入门例子,采用zookeeper作为注册中心,可分为两个部分,如下:
- 搭建zookeeper注册中心
- 利用dubbo搭建分布式服务
我们学习dubbo,可以在本机上搭建一个zookeeper注册中心,zookeeper在主流操作系统上都可以运行(windows,linux等)。需要注意的是zookeeper是依赖jdk的,在安装zookeeper前先安装java。无论在windows还是linux。
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,可以用命令行启动,也可以直接启动。
在生产环境,很少会把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搭建分布式服务。
本利为加深对分布式服务的理解,以一个学生信息(Student)的增删查为例进行讲解。
服务端(提供者)实现对学生信息的插入,查询和删除三项操作。
客户端(消费者)可以远程调用服务端接口,实现信息的操作。
项目地址:https://github.com/xubaodian/dubbo-study ,可下载参考
项目结构如下:
简单介绍下工程结构,这是一个maven工程,包括三个模块(module),分别为consumer(消费者、客户端),demoapi(都依赖的一些接口和实体类)和provider(服务提供者、服务端)。
最外层pom依赖进行依赖管理,把本项目所有的依赖都添加进来.
注意:各项依赖之间存在隐性依赖,可能会产生冲突,利用 标签消除依赖冲突,具体可百度。
<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.0modelVersion>
<groupId>com.xbdgroupId>
<artifactId>dubbo-studyartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<modules>
<module>providermodule>
<module>demoapimodule>
<module>consumermodule>
modules>
<properties>
<junit.version>4.11junit.version>
<spring.version>4.3.16.RELEASEspring.version>
<slf4j.version>1.6.4slf4j.version>
<dubbo.version>2.6.2dubbo.version>
<zk.version>3.4.10zk.version>
<log4j.version>1.2.17log4j.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>dubboartifactId>
<version>${dubbo.version}version>
<exclusions>
<exclusion>
<groupId>org.springframeworkgroupId>
<artifactId>springartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>${zk.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jmsartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.6.11version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.6.11version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>2.4.2version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
exclusion>
exclusions>
dependency>
dependencies>
dependencyManagement>
project>
按顺序介绍三个模块
该模块包括两个实体类和一个接口,实体类分别是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);
}
该模块依赖我就不多说了,主要介绍下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">
<dubbo:application name="provider"/>
<dubbo:registry protocol="zookeeper" address="zookeeper://192.168.56.10:10000"/>
<dubbo:protocol name="dubbo" port="20880"/>
<bean id="demoService" class="com.xbd.provider.DemoApiImpl"/>
<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 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 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
}
}
消费者模块代码如下:
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中的实现,我们想要实现的功能已经跑通了,赶紧动手试一试吧。