微服务阿里开源技术个人学习

dubbo及zookeeper结合springboot学习

2019-11-20  本文已影响0人  强某某

传统互联网架构到分布式架构的架构演变

命令记忆:
netstat -tlun  :查看当前启动的应用占用的端口
systemctl status firewalld:查看防火墙状态

pc时代-->移动互联网时代-->物联网时代

传统单体应用

优点:易开发、理解、维护独立部署和启动

不足:

需要管理多个服务、在架构的层面变得复杂

服务切分之后、服务之间的调用可能需要去解决分布式事务的问题


传统单体架构.jpg
传统单体架构的扩展.jpg
单体架构解耦.jpg
异步架构.jpg
soa架构.jpg

soa架构和微服务最大不同就是,微服务没有企业服务总线,微服务是再soa基础上演进出来的


微服务架构.jpg

微服务核心基础知识讲解

讲解微服务核心模块 :网关、服务发现注册、配置中心、链路追踪、负载均衡器、熔断

例子:下单-》查询商品服务获取商品价格-》查询用户信息-》保存数据库

dubbo的发展历程

dubbo与spring-cloud比较

简介:对目前主流微服务框架进行对比,给技术选型提供相应的参考

dubbo: zookeeper + dubbo + springmvc/springboot

官方地址:http://dubbo.apache.org/#!/?lang=zh-cn

springcloud: 全家桶+轻松嵌入第三方组件(Netflix 奈飞)

官网:http://projects.spring.io/spring-cloud/

补充:环境搭建参考VMWare设置静态IP以及CentOS7 JDK环境配置

zookeeper讲解

什么是微服务的注册中心

为什么要用

微服务应用和机器越来越多,调用方需要知道接口的网络地址,如果靠配置文件的方式去控制网络地址,对于动态新增机器,维护带来很大问题

主流的注册中心:zookeeper、Eureka、consul、etcd 等

CAP理论知识

指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可同时获得。

CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容错性是我们必须需要实现的,所以我们只能在一致性可用性之间进行权衡。

cap.jpg

win、linux双环境安装zookeeper

# 心跳时间
tickTime=2000
# 1o指的是tickTime*10,也就是20s,Follower和Leader之间初始化能容忍的最长时间
initLimit=10
# Follower和Leader之间能容忍的最大失败数,此时是5次
syncLimit=5
# 如果linux则是linux对应的地址
# dataDir=/usr/local/zookeeper-3.4.12/data
dataDir=D:\Soft\zookeeper-3.4.12\data
clientPort=2181
./zkServer.sh start

centos下安装zk

chown -R zookeeper:zookeeper /usr/local/zookeeper-3.4.12

./zkServer.sh start

ZooKeeper数据模型

常用命令之zkCli

zkCli.sh -timeout 0 -r -server ip:port

.\zkCli.cmd -timeout 5000 -server 192.168.40.100:2181

//验证是否开启成功
stat /
ZooKeeper -server host:port cmd args​ 
stat path [watch]​ 
set path data [version]​ 
ls path [watch]​ 
delquota [-n|-b] 
path​ ls2 path [watch]​ 
setAcl path 
acl​ setquota -n|-b val path​ history​ redo cmdno​ printwatches on|off​ 
delete path [version]​ 
sync path​ listquota path​ 
rmr path​ get path [watch]​
create [-s] [-e] path data 
acl​ addauth scheme auth​ quit​ 
getAcl path​ close​ connect host:port

创建节点 create [-s] [-e] path data acl -s表示创建顺序节点 -e表示创建临时节点 data表示创建的节点的数据内容

cZxid = 0x3 --事务id​ 
ctime = Tue Dec 04 10:37:58 EST 2018 --节点创建的时候的时间​ mZxid = 0x3 --最后一次更新时的事务id​ 
mtime = Tue Dec 04 10:37:58 EST 2018 最后一次更新的时间​ 
pZxid = 0x3 该节点的子节点列表最后一次被修改的事务id​ 
cversion = 0 子节点列表的版本​ 
# 例如更新数据:加版本信息的话,必须dataVersion`相等`才能更新数据,不加版本信息则自动变化
dataVersion = 0 数据内容的版本​ 
aclVersion = 0 acl版本​ 
ephemeralOwner = 0x0 用于临时节点,表示创建该临时节点的事务id,如果当前的节点不是临时节点,该字段值为0​ 
dataLength = 8 数据内容的长度​ 
numChildren = 0 子节点的数量

delete path [version] 删除节点,如果此时该节点有子节点,则不允许删除

zookeeper session机制

zookeeper watcher机制

针对每一个节点的操作,都可以有一个监控者,当节点发生变化,会触发watcher事件 zk中watcher是一次性的,触发后立即销毁 所有有监控者的节点的变更操作都能触发watcher事件;watcher的一次性,只是针对zookeeper原生指令,不针对代码级别

zookeeper的acl(access control lists)权限控制

常用的命令

//`设置或者获取权限`直接设置和获取如果提示不行,需要先输入下面一行,然后再设置和获取权限
addauth digest xdclass:xdclass
setAcl /xdclass auth:xdclass:xdclass:cdrwa

acl的组成 [scheme🆔permissions]

scheme 授权机制

nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}"
加上 "-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6HQs="
此处后面的密文为密码加密后的
cdrwa
c create 创建子节点
d delete 删除子节点
r read 读取节点的数据
w write 写入数据
a admin 可管理的权限
cdrwa cdr cdw 
设置访问控制:

方式一:(推荐)
1)增加一个认证用户
addauth digest 用户名:密码明文
eg. addauth digest user1:password1
2)设置权限
setAcl /path auth:用户名:密码明文:权限
eg. setAcl /test auth:user1:password1:cdrwa
3)查看Acl设置
getAcl /path

方式二:
setAcl /path digest:用户名:密码密文:权限

注:这里的加密规则是SHA1加密,然后base64编码。

一致性协议之 ZAB

Zookeeper 设计的分布式一致性协议

  1. ZAB 协议全称:Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)。
  2. Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面,Zookeeper 并没有使用 Paxos ,而是采用了 ZAB 协议。
  3. ZAB 协议定义:ZAB 协议是为分布式协调服务 Zookeeper 专门设计的一种支持崩溃恢复原子广播协议。
  4. 基于该协议,Zookeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间数据一致性。
    1.jpg

消息广播

ZAB 协议的消息广播过程使用的是一个原子广播协议,类似一个 二阶段提交过程。对于客户端发送的写请求,全部由 Leader 接收,Leader 将请求封装成一个事务 Proposal,将其发送给所有 Follwer ,然后,根据所有 Follwer 的反馈,如果超过半数成功响应,则执行 commit 操作(先提交自己,再发送 commit 给所有 Follwer)。

通过以上 3 个步骤,就能够保持集群之间数据的一致性。实际上,在 Leader 和 Follwer 之间还有一个消息队列,用来解耦他们之间的耦合,避免同步,实现异步解耦。

  1. Leader 在收到客户端请求之后,会将这个请求封装成一个事务,并给这个事务分配一个全局递增的唯一 ID,称为事务ID(ZXID),ZAB 兮协议需要保证事务的顺序,因此必须将每一个事务按照 ZXID 进行先后排序然后处理。
  2. 在 Leader 和 Follwer 之间还有一个消息队列,用来解耦他们之间的耦合,解除同步阻塞。
  3. zookeeper集群中为保证任何所有进程能够有序的顺序执行,只能是 Leader 服务器接受写请求,即使是 Follower 服务器接受到客户端的请求,也会转发到 Leader 服务器进行处理。
  4. 实际上,这是一种简化版本的 2PC,不能解决单点问题。等会我们会讲述 ZAB 如何解决单点问题(即 Leader 崩溃问题)。

崩溃恢复

刚刚我们说消息广播过程中,Leader 崩溃怎么办?还能保证数据一致吗?如果 Leader 先本地提交了,然后 commit 请求没有发送出去,怎么办?

实际上,当 Leader 崩溃,即进入我们开头所说的崩溃恢复模式(崩溃即:Leader 失去与过半 Follwer 的联系)。下面来详细讲述。

假设1:Leader 在复制数据给所有 Follwer 之后崩溃,怎么办?
假设2:Leader 在收到 Ack 并提交了自己,同时发送了部分 commit 出去之后崩溃怎么办?

所以,ZAB 设计了下面这样一个选举算法:能够确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务

针对这个要求,如果让 Leader 选举算法能够保证新选举出来的 Leader 服务器拥有集群总所有机器编号(即 ZXID 最大)的事务,那么就能够保证这个新选举出来的 Leader 一定具有所有已经提交的提案。
而且这么做有一个好处是:可以省去 Leader 服务器检查事务的提交和丢弃工作的这一步操作。


5.jpg

这样,我们刚刚假设的两个问题便能够解决。假设 1 最终会丢弃调用没有提交的数据,假设 2 最终会同步所有服务器的数据。这个时候,就引出了一个问题,如何同步?

数据同步

当崩溃恢复之后,需要在正式工作之前(接收客户端请求),Leader 服务器首先确认事务是否都已经被过半的 Follwer 提交了,即是否完成了数据同步。目的是为了保持数据一致。

当所有的 Follwer 服务器都成功同步之后,Leader 会将这些服务器加入到可用服务器列表中。

实际上,Leader 服务器处理或丢弃事务都是依赖着 ZXID 的,那么这个 ZXID 如何生成呢?

答:在 ZAB 协议的事务编号 ZXID 设计中,ZXID 是一个 64 位的数字,其中低 32 位可以看作是一个简单的递增的计数器,针对客户端的每一个事务请求,Leader 都会产生一个新的事务 Proposal 并对该计数器进行 + 1 操作。

而高 32 位则代表了 Leader 服务器上取出本地日志中最大事务 Proposal 的 ZXID,并从该 ZXID 中解析出对应的 epoch 值,然后再对这个值加一。

6.jpg

高 32 位代表了每代 Leader 的唯一性,低 32 代表了每代 Leader 中事务的唯一性。同时,也能让 Follwer 通过高 32 位识别不同的 Leader。简化了数据恢复流程。

基于这样的策略:当 Follower 链接上 Leader 之后,Leader 服务器会根据自己服务器上最后被提交的 ZXID 和 Follower 上的 ZXID 进行比对,比对结果要么回滚,要么和 Leader 同步。

zookeeper选举机制

zookeeper集群中的三种角色以及其各自的作用

observer作用.png

zk集群选举核心概念及选举时状态

zk 集群中服务器的唯一标识,称为 myid。例如,有三个zk服务器,那么编号分别是 1,2,3。

​
高位              低位
0000000000000000  0000000000000000
​
                    epoch                                 xid
00000000000000000000000000000000   00000000000000000000000000000000
zxid 为 Long 类型,其中高 32 位表示 epoch,低 32 位表示 xid。即 zxid 由两部分构成:epoch 与 xid。 
每个 Leader 都会具有一个不同的 epoch 值,表示一个时期、时代;在选举还没成功的时候,所有节点zxid是相同的。新的 Leader 产生,则会更新所有 zkServer 的 zxid 中的 epoch。 而 xid 则为 zk 的事务 id,每一个写操作都是一个事务,都会有一个 xid。每一个写操作都需要由 Leader 发起一个提议,由所有 Follower 表决是否同意本次写操作。

逻辑时钟,Logicalclock,是一个整型数,该概念在选举时称为 logicalclock,而在 zxid 中则为 epoch 的值。即 epoch 与 logicalclock 是同一个值,在不同情况下的不同名称。

zk集群选举发生的时机及选举算法

选举发生的时机及其选举算法.png

zk集群搭建

# 心跳时间
tickTime=2000
# 1o指的是tickTime*10,也就是20s,Follower和Leader之间初始化能容忍的最长时间
initLimit=10
# Follower和Leader之间能容忍的最大失败数,此时是5次
syncLimit=5
dataDir=D:\Soft\zookeeper-3.4.12\data
clientPort=2181
server.1=192.168.40.100:2888:3888 
server.2=192.168.40.101:2888:3888 
server.3=192.168.40.102:2888:3888

systemctl stop firewalld.service

添加防火墙策略,允许2181端口可用,吧几个所需端口都添加进策略即可
firewall-cmd --zone=public --add-port=2181/tcp --permanent
防火墙重新加载
firewall-cmd --reload

查看是leader还是follower

分布式锁

分布式服务中,如果各个服务节点需要去竞争资源,没办法使用单机多线程中JDK自带的锁,故此时需要分布式锁来协调

zookeeper、redis、memcache

案例代码

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.13</version>
</dependency>
package com.wiggin.lock;

import org.apache.zookeeper.*;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

import static org.apache.zookeeper.CreateMode.EPHEMERAL;
import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;

/**
 * 基于zk实现自定义分布式锁
 */
public class ZkLock {

    private ZooKeeper zooKeeper;

    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    private ZkLock() {
        try {
            zooKeeper = new ZooKeeper("192.1.1.101:2181,192.1.1.102:2181,192.1.1.103:2181", 5000, new ZkWatcher());
            System.out.println(zooKeeper.getState());
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("与zk建立连接=====>"+zooKeeper.getState());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static ZkLock getInstance() {
        return Singleton.getInstance();
    }

    private class ZkWatcher implements Watcher {
        @Override
        public void process(WatchedEvent event) {
            //当连接zk成功会触发这个回调,再判断下状态(因为其他监听也可能触发),调用countDown
            //则上面的countDownLatch.await(); 就会放行
            System.out.println("接收到监听事件=====》"+event);
            if (Event.KeeperState.SyncConnected == event.getState()) {
                countDownLatch.countDown();
            }
        }
    }


    public void lock(Integer id) {
        //注释地方是递归节点,但是zk原生api不支持递归创建,所以如果使用会抛出异常,通过catch内部打印可知
        // String path = "/xdclass/product/lock/" + id;
        String path = "/xdclass-product-lock-" + id;
        //创建临时节点,如果创建成功的话,就表示获取锁,如果失败,则不断尝试
        try {
            //路径,内容,代表不设置acl权限,零时节点
            zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            System.out.println("成功获取到锁");
        } catch (Exception e) {
            while (true) {
                try {
                    Thread.sleep(500L);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                try {
                    zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                } catch (Exception e1) {
                    continue;
                }
                break;
            }
        }
    }

    /**
     * 释放锁,直接删除zk节点
     * @param id
     */
    public void unLock(Integer id) {
        String path = "/xdclass-product-lock-" + id;
        try {
            zooKeeper.delete(path,-1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }


    private static class Singleton {

        private static ZkLock instance;
        static {
            instance = new ZkLock();
        }

        private static ZkLock getInstance() {
            return instance;
        }

    }
}
package com.wiggin.lock;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程不安全操作代码实例
 */
public class UnSafeThread {

    private static int num = 0;

    private static CountDownLatch countDownLatch = new CountDownLatch(10);
    private static ZkLock lock = ZkLock.getInstance();

    /**y
     * 每次调用对num进行++操作
     */
    public static void inCreate() {
        lock.lock(1);
        num++;
        System.out.println(num);
        lock.unLock(1);
    }

    public static void test() {
        System.out.println(Thread.currentThread().getName());
    }


    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    inCreate();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //每个线程执行完成之后,调用countdownLatch
                countDownLatch.countDown();
            }).start();
        }

        while (true) {
            if (countDownLatch.getCount() == 0) {
                System.out.println(num);
                break;
            }
        }
    }
}

dubbo核心架构及流程

1.jpg
节点 角色说明
Provider 暴露服务的服务提供方
Consumer 调用远程服务的服务消费方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心
Container 服务运行容器

调用关系说明

dubbo常见的多种开发方式

配置相关

虽然整合springboot时候可以通过注解方式,不过因为历史原因以及注解支持力度不足,所以一般还是通过配置文件xml形式开发。注意:dubbo可以单独使用,不整合spring等,只是配置较为复杂而已。

创建多模块项目

user-service的pom.xml

<?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">
    <parent>
        <artifactId>dubbo</artifactId>
        <groupId>com.tjsmc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-service</artifactId>

    <properties>
        <spring-boot.version>2.1.1.RELEASE</spring-boot.version>
        <dubbo.version>2.7.0</dubbo.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Aapche Dubbo  -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-bom</artifactId>
                <version>${dubbo.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>javax.servlet</groupId>
                        <artifactId>servlet-api</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.tjsmc</groupId>
            <artifactId>user-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- Dubbo Spring Boot Starter -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>

        <!-- Zookeeper -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
        </dependency>
    </dependencies>
user-web的pom.xml

<?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">
    <parent>
        <artifactId>dubbo</artifactId>
        <groupId>com.tjsmc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-web</artifactId>

    <properties>
        <spring-boot.version>2.1.1.RELEASE</spring-boot.version>
        <dubbo.version>2.7.0</dubbo.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Aapche Dubbo  -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-bom</artifactId>
                <version>${dubbo.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>javax.servlet</groupId>
                        <artifactId>servlet-api</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>com.tjsmc</groupId>
            <artifactId>user-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- Dubbo Spring Boot Starter -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Zookeeper -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
        </dependency>
    </dependencies>
</project>
项目的pom.xml

<?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.tjsmc</groupId>
    <artifactId>dubbo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>user-api</module>
        <module>user-service</module>
        <module>user-web</module>
    </modules>

    <build>
        <!--为了解决默认使用jdk1.5当作版本的提示错误信息-->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

基于注解的案例

user-api
UserService.java

package com.xdclass.user.service;
public interface UserService {
    String sayHello();
}
user-service
ServiceApplication.java


package com.xdclass.user.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

@EnableAutoConfiguration
public class ServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class);
    }
}
user-service
UserService.java


package com.xdclass.user.service.impl;

import com.xdclass.user.service.UserService;
import org.apache.dubbo.config.annotation.Service;

@Service(version = "1.0.0")
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "hello";
    }
}

user-service
application.yml

spring:
  application:
    name: dubbo-auto-configuration-provider-demo

dubbo:
  scan:
    base-packages: com.xdclass.user.service.impl
  protocol:
    name: dubbo
    port: 12345
  registry:
    address: N/A
user-web
WebApplication.java

package com.xdclass.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class);
    }
}
user-web
UserController.java

package com.xdclass.user.controller;

import com.xdclass.user.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @Reference(version = "1.0.0", url = "dubbo://127.0.0.1:12345")
    private UserService userService;

    @RequestMapping("/sayHello")
    public String sayHello() {
        return userService.sayHello();
    }
}
user-web
application.yml

spring:
  application:
    name: dubbo-auto-configure-consumer-sample

之后的项目都使用xml文件形式配置

dubbo服务注册中心

参考(https://nacos.io/zh-cn/docs/use-nacos-with-dubbo.html)

整合zk

Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用


3.jpg

流程说明:

支持以下功能:

官方文档

user-service:服务提供者

spring:
  application:
    name: dubbo-auto-configuration-provider-demo


dubbo:
  scan:
    base-packages: com.xdclass.user.service.impl
  protocol:
    name: dubbo
    port: 12345
  registry:
    address: zookeeper://127.0.0.1:2181
user-web:服务消费者
spring:
  application:
    name: dubbo-auto-configure-consumer-sample-terst
dubbo:
  registry:
    address: zookeeper://127.0.0.1:2181

配置如上则,@Reference(version = "1.0.0", url = "dubbo://127.0.0.1:12345")可以只留下@Reference(version = "1.0.0")

另外,不论服务消费还是提供者,都需要有个name代表项目唯一标记,例如上面的spring的name,也就是说如果springname没有则必须添加dubbo的name否则启动报错失败

dubbo使用xml的配置方式

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="12345"/>
    <bean id="userService" class="com.xdclass.user.service.impl.UserServiceImpl"/>
    <dubbo:service interface="com.xdclass.user.service.UserService" ref="userService"/>
</beans>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:reference id="userService" check="false" interface="com.xdclass.user.service.UserService"/>
</beans>

启动依赖检查

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!--    <dubbo:registry group="aaa" address="zookeeper://127.0.0.1:2181"/>-->
    <dubbo:reference id="userService" timeout="3000" check="false" interface="com.tjsmc.user.service.UserService">
<!--        方法级别的优先级高于整个服务的优先级-->
        <dubbo:method name="sayHello" timeout="5000" retries="4"></dubbo:method>
    </dubbo:reference>
    <dubbo:reference id="fileService" timeout="3000" check="false" interface="com.tjsmc.user.service.FileService"/>
</beans>

dubbo配置加载流程

配置来源

-D dubbo.reference.com.xdclass.user.service.UserService.check=true

dubbo超时机制及集群容错机制

集群容错机制

当服务调用失败的时候,默认情况下,dubbo会进行重试,默认重试次数为2(不包含第一次),所以可见被调用三次;通过retries="4" 设置重试次数

注意:集群容错如果设置,则会优先,而重试次数反而不会执行。集群容错类型

<dubbo:service interface="com.tjsmc.user.service.UserService" ref="userService" cluster="failsafe"/>

cluster="failsafe"就是集群容错的一种方式

容错简介

在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试


2.jpg

各节点关系:

集群容错模式

失败自动切换,当出现失败,重试其它服务器 [1]。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

重试次数配置如下(服务级别和方法级别):

<dubbo:service retries="2" />

<dubbo:reference>
    <dubbo:method name="findFoo" retries="2" />
</dubbo:reference>

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

集群模式配置

按照以下示例在服务提供方和消费方配置集群模式

<dubbo:service cluster="failsafe" />

<dubbo:reference cluster="failsafe" />

dubbo各协议及多协议配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-util</artifactId>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-servlet</artifactId>
</dependency>

provider.xml文件中新增想要的协议

<dubbo:protocol name="http" port="8888"/>

暴露服务时使用 protocol="http" 让其使用特性的协议暴露

基本实例:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:registry id="local" address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="12345"/>
<!--    地处声明新协议-->
    <dubbo:protocol name="http" port="8888"/>
    <bean id="userService" class="com.tjsmc.user.service.impl.UserServiceImpl"/>
    <bean id="fileService" class="com.tjsmc.user.service.impl.FileServiceImpl"/>
<!--    暴漏给多个注册中心则用逗号隔开id即可-->
    <dubbo:service interface="com.tjsmc.user.service.UserService" ref="userService" cluster="failsafe" registry="local"/>
<!--    此处使用指定协议,不指定默认dubbo-->
    <dubbo:service interface="com.tjsmc.user.service.FileService" ref="fileService" protocol="http"/>
</beans>

new SpringApplicationBuilder(ServiceApplication.class).web(WebApplicationType.NONE).run();

多注册中心及其配置

Dubbo 支持同一服务向多注册中心同时注册,或者不同服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。另外,注册中心是支持自定义扩展的

多注册中心注册

比如:中文站有些服务来不及在青岛部署,只在杭州部署,而青岛的其它应用需要引用此服务,就可以将服务同时注册到两个注册中心。

<?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://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="world"  />
    <!-- 多注册中心配置 -->
    <dubbo:registry id="hangzhouRegistry" address="10.20.141.150:9090" />
    <dubbo:registry id="qingdaoRegistry" address="10.20.141.151:9010" default="false" />
    <!-- 向多个注册中心注册 -->
    <dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="hangzhouRegistry,qingdaoRegistry" />
</beans>

不同服务使用不同注册中心

比如:CRM 有些服务是专门为国际站设计的,有些服务是专门为中文站设计的。

<?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://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="world"  />
    <!-- 多注册中心配置 -->
    <dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
    <dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
    <!-- 向中文站注册中心注册 -->
    <dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="chinaRegistry" />
    <!-- 向国际站注册中心注册 -->
    <dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" registry="intlRegistry" />
</beans>

多注册中心引用

比如:CRM 需同时调用中文站和国际站的 PC2 服务,PC2 在中文站和国际站均有部署,接口及版本号都一样,但连的数据库不一样。

<?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://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="world"  />
    <!-- 多注册中心配置 -->
    <dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
    <dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
    <!-- 引用中文站服务 -->
    <dubbo:reference id="chinaHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="chinaRegistry" />
    <!-- 引用国际站站服务 -->
    <dubbo:reference id="intlHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="intlRegistry" />
</beans>

如果只是测试环境临时需要连接两个不同注册中心,使用竖号分隔多个不同注册中心地址:

<?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://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="world"  />
    <!-- 多注册中心配置,竖号分隔表示同时连接多个不同注册中心,同一注册中心的多个集群地址用逗号分隔 -->
    <dubbo:registry address="10.20.141.150:9090|10.20.154.177:9010" />
    <!-- 引用服务 -->
    <dubbo:reference id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" />
</beans>

Dubbo服务分组及其配置

当一个接口有多种实现时,可以用 group 区分;服务提供者,在标签里加多个group进行区分

<dubbo:service group="user1" interface="com.xdclass.user.service.UserService" ref="userService" /> 
<dubbo:service group="user2" interface="com.xdclass.user.service.UserService" ref="userService2" />

服务消费者在引用的时候,也在标签里加group

<dubbo:reference group="user2" id="userService" check="false" interface="com.xdclass.user.service.UserService"/>

任意组

<dubbo:reference id="barService" interface="com.foo.BarService" group="*" />

Dubbo服务多版本化及其配置

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

可以按照以下的步骤进行版本迁移:

老版本服务提供者配置:

<dubbo:service interface="com.foo.BarService" version="1.0.0" />

新版本服务提供者配置:

<dubbo:service interface="com.foo.BarService" version="2.0.0" />

老版本服务消费者配置:

<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />

新版本服务消费者配置:

<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />

如果不需要区分版本,可以按照以下的方式配置:

<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />

新版管控台dubbo-ops的编译安装

旧版:dubbo-admin 新版 dubbo-ops

新版管理控制台主要的作用

克隆项目到本地,并编译安装和启动

git clone https://github.com/apache/incubator-dubbo-ops.git

cd incubator-dubbo-ops

mvn clean package

修改配置文件 dubbo-admin-server/src/main/resources/application-production.properties中的注册中心的地址

cd dubbo-distribution/target

java -jar dubbo-admin-0.1.jar

启动完成后,直接访问http://localhost:8080

动态配置中心及配置外部化

外部化配置,启动配置的集中式存储,简单理解为dubbo.properties的外部化存储, 服务治理,服务治理规则的存储与通知。

在dubbo-ops 进行配置信息配置
在项目里使用<dubbo:config-center address="zookeeper://127.0.0.1:2181"/>
配置中心中的配置的作用域

如果全局配置跟应用级别均配置了相同的信息,这个时候以应用级别的为准

元数据中心及服务测试

上一篇下一篇

猜你喜欢

热点阅读