知识点_20190314

https://www.cnblogs.com/wang-meng/p/5791598.html

Dubbo是什么?

Dubbo是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

Dubbo[]是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。


其核心部分包含:

  • 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。
  • 集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
  • 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。

Dubbo能做什么?

  • 透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
  • 软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
  • 服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。

我们用这张图来形容 , 那么映射到 项目中:
      当我们在多个Tomcat部署不同的系统时, 例如A系统(TomcatA)想调用B系统(TomcatB)中的服务, 这时Dubbo就有了用武之地. 首先我们需要B系统在注册中心将自己的Url注册进去, 然后注册中心将Url返还给系统A, 那么系统A就可以调用了. 当然了我这里说的只是Dubbo的一种用法, 在这个项目中使用的也是Dubbo的远程调用功能. (感觉真的和webservice有点像)

(1) 当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。

此时需要一个服务注册中心,动态的注册和发现服务,使服务的位置透明。

并通过在消费方获取服务提供方地址列表,实现软负载均衡和Failover,降低对F5硬件负载均衡器的依赖,也能减少部分成本。

(2) 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。

这时,需要自动画出应用间的依赖关系图,以帮助架构师理清理关系。

(3) 接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?

为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。

其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阀值,记录此时的访问量,再以此访问量乘以机器数反推总容量

节点角色说明:

Provider: 暴露服务的服务提供方。

Consumer: 调用远程服务的服务消费方。

Registry: 服务注册与发现的注册中心。

Monitor: 统计服务的调用次调和调用时间的监控中心。

Container: 服务运行容器。

调用关系说明:

0. 服务容器负责启动,加载,运行服务提供者。

1. 服务提供者在启动时,向注册中心注册自己提供的服务。

2. 服务消费者在启动时,向注册中心订阅自己所需的服务。

3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Dubbo提供了很多协议,Dubbo协议、RMI协议、Hessian协议,我们查看Dubbo源代码,有各种协议的实现,如图所示:

Zookeeper作为Dubbo服务的注册中心,Dubbo原先基于数据库的注册中心,没采用Zookeeper,Zookeeper一个分布式的服务框架,是树型的目录服务的数据存储,能做到集群管理数据 ,这里能很好的作为Dubbo服务的注册中心,Dubbo能与Zookeeper做到集群部署,当提供者出现断电等异常停机时,Zookeeper注册中心能自动删除提供者信息,当提供者重启时,能自动恢复注册数据,以及订阅请求。

 

服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提供方和服务消费方的业务设计对应的接口和实现。
配置层(Config):对外配置接口,以ServiceConfig和ReferenceConfig为中心,可以直接new配置类,也可以通过spring解析配置生成配置类。
服务代理层(Proxy):服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton,以ServiceProxy为中心,扩展接口为ProxyFactory。
服务注册层(Registry):封装服务地址的注册与发现,以服务URL为中心,扩展接口为RegistryFactory、Registry和RegistryService。可能没有服务注册中心,此时服务提供方直接暴露服务。
集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册中心,以Invoker为中心,扩展接口为Cluster、Directory、Router和LoadBalance。将多个服务提供方组合为一个服务提供方,实现对服务消费方来透明,只需要与一个服务提供方进行交互。
监控层(Monitor):RPC调用次数和调用时间监控,以Statistics为中心,扩展接口为MonitorFactory、Monitor和MonitorService。
远程调用层(Protocol):封将RPC调用,以Invocation和Result为中心,扩展接口为Protocol、Invoker和Exporter。Protocol是服务域,它是Invoker暴露和引用的主功能入口,它负责Invoker的生命周期管理。Invoker是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
信息交换层(Exchange):封装请求响应模式,同步转异步,以Request和Response为中心,扩展接口为Exchanger、ExchangeChannel、ExchangeClient和ExchangeServer。
网络传输层(Transport):抽象mina和netty为统一接口,以Message为中心,扩展接口为Channel、Transporter、Client、Server和Codec。
数据序列化层(Serialize):可复用的一些工具,扩展接口为Serialization、 ObjectInput、ObjectOutput和ThreadPool。

ZooKeeper是一个分布式开源框架,供了协调分布式应用的基本服务。这些天在使用DUBBO,由于开发环境是在windows环境下,需要能够先运行ZooKeeper,然后在此基础上实现服务的地址分配。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper包含一个简单的原语集,提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在zookeeper-3.4.9\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。
1、tickTime:基本事件单元,以毫秒为单位。这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,它用来控制心跳和超时,也就是每个 tickTime 时间就会发送一个心跳。默认情况下最小的会话超时时间为两倍的 tickTime。
2、clientPort:监听客户端连接的端口。这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
3、dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
4、dataLogDir:顾名思义就是 Zookeeper 保存日志文件的目录

路由规则,动态配置,服务降级 
访问控制,权重调整 
负载均衡

Dubbo提供服务分组,用于当一个接口有多种实现时,可用使用group分组。

https://www.jianshu.com/p/c5ebe3e08161

Filter(过滤器)在很多框架中都有使用过这个概念,基本上的作用都是类似的,在请求处理前或者处理后做一些通用的逻辑,而且Filter可以有多个,支持层层嵌套。
Dubbo的Filter概念基本上符合我们正常的预期理解,而且Dubbo官方针对Filter做了很多的原生支持,目前大致有20来个吧,包括我们熟知的RpcContext,accesslog功能都是通过filter来实现了,下面一起详细看一下Filter的实现。
Dubbo的Filter实现入口是在ProtocolFilterWrapper,因为ProtocolFilterWrapper是Protocol的包装类,所以会在加载的Extension的时候被自动包装进来

public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        //向注册中心引用服务的时候并不会进行filter调用链
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }
    
    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        //获得所有激活的Filter(已经排好序的)
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i --) {
                final Filter filter = filters.get(i);
                //复制引用,构建filter调用链
                final Invoker<T> next = last;
                //这里只是构造一个最简化的Invoker作为调用链的载体Invoker
                last = new Invoker<T>() {

                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
    }
//将key在url中对应的配置值切换成字符串信息数组
    public List<T> getActivateExtension(URL url, String key, String group) {
        String value = url.getParameter(key);
        return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
    }
    
    public List<T> getActivateExtension(URL url, String[] values, String group) {
        List<T> exts = new ArrayList<T>();
        //所有用户自己配置的filter信息(有些Filter是默认激活的,有些是配置激活的,这里这里的names就指的配置激活的filter信息)
        List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);

        //如果这些名称里不包括去除default的标志(-default),换言之就是加载Dubbo提供的默认Filter
        if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
            //加载extension信息
            getExtensionClasses();
            for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
                //name指的是SPI读取的配置文件的key
                String name = entry.getKey();
                Activate activate = entry.getValue();
                //group主要是区分实在provider端生效还是consumer端生效
                if (isMatchGroup(group, activate.group())) {
                    T ext = getExtension(name);
                    //这里以Filter为例:三个判断条件的含义依次是:
                    //1.用户配置的filter列表中不包含当前ext
                    //2.用户配置的filter列表中不包含当前ext的加-的key
                    //3.如果用户的配置信息(url中体现)中有可以激活的配置key并且数据不为0,false,null,N/A,也就是说有正常的使用
                    if (! names.contains(name)
                            && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name) 
                            && isActive(activate, url)) {
                        exts.add(ext);
                    }
                }
            }
            //根据Activate注解上的order排序
            Collections.sort(exts, ActivateComparator.COMPARATOR);
        }
        //进行到此步骤的时候Dubbo提供的原生的Filter已经被添加完毕了,下面处理用户自己扩展的Filter
        List<T> usrs = new ArrayList<T>();
        for (int i = 0; i < names.size(); i ++) {
            String name = names.get(i);
            //如果单个name不是以-开头并且所有的key里面并不包含-'name'(也就是说如果配置成了"dubbo,-dubbo"这种的可以,这个if是进不去的)
            if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX)
                    && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
                //可以通过default关键字替换Dubbo原生的Filter链,主要用来控制调用链顺序
                if (Constants.DEFAULT_KEY.equals(name)) {
                    if (usrs.size() > 0) {
                        exts.addAll(0, usrs);
                        usrs.clear();
                    }
                } else {
                    //加入用户自己定义的扩展Filter
                    T ext = getExtension(name);
                    usrs.add(ext);
                }
            }
        }
        if (usrs.size() > 0) {
            exts.addAll(usrs);
        }
        return exts;
    }

特性

缺省协议,使用基于 mina 1.1.7 和 hessian 3.2.1 的 tbremoting 交互。

  • 连接个数:单连接
  • 连接方式:长连接
  • 传输协议:TCP
  • 传输方式:NIO 异步传输
  • 序列化:Hessian 二进制序列化
  • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。
  • 适用场景:常规远程服务方法调用

rmi

RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:TCP
  • 传输方式:同步传输
  • 序列化:Java 标准二进制序列化
  • 适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。
  • 适用场景:常规远程服务方法调用,与原生RMI服务互操作

hessian

Hessian 1 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。

Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:

  • 提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用
  • 或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。
  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:Hessian二进制序列化
  • 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。
  • 适用场景:页面传输,文件传输,或与原生hessian服务互操作

Http

基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现 1

特性

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:表单序列化
  • 适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。
  • 适用场景:需同时给应用程序和浏览器 JS 使用的服务。

WebService

基于 WebService 的远程调用协议,基于 Apache CXF 1 的 frontend-simple 和 transports-http 实现 2

可以和原生 WebService 服务互操作,即:

  • 提供者用 Dubbo 的 WebService 协议暴露服务,消费者直接用标准 WebService 接口调用,
  • 或者提供方用标准 WebService 暴露服务,消费方用 Dubbo 的 WebService 协议调用。
  • 特性

    • 连接个数:多连接
    • 连接方式:短连接
    • 传输协议:HTTP
    • 传输方式:同步传输
    • 序列化:SOAP 文本序列化
    • 适用场景:系统集成,跨语言调用

thrift

当前 dubbo 支持 1的 thrift 协议是对 thrift 原生协议 2 的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。

使用 dubbo thrift 协议同样需要使用 thrift 的 idl compiler 编译生成相应的 java 代码,后续版本中会在这方面做一些增强。

缓存

memcached://

基于 memcached 1 实现的 RPC 协议 2

redis://

基于 Redis 1 实现的 RPC 协议 2

 

zookeeper 注册中心

流程说明:

  • 服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
  • 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
  • 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。

支持以下功能:

  • 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息
  • 当注册中心重启时,能自动恢复注册数据,以及订阅请求
  • 当会话过期时,能自动恢复注册数据,以及订阅请求
  • 当设置 <dubbo:registry check="false" /> 时,记录失败注册和订阅请求,后台定时重试
  • 可通过 <dubbo:registry username="admin" password="1234" /> 设置 zookeeper 登录信息
  • 可通过 <dubbo:registry group="dubbo" /> 设置 zookeeper 的根节点,不设置将使用无根树
  • 支持 * 号通配符 <dubbo:reference group="*" version="*" />,可订阅服务的所有分组和所有版本的提供者

Redis 注册中心

使用 Redis 的 Key/Map 结构存储数据结构:

  • 主 Key 为服务名和类型
  • Map 中的 Key 为 URL 地址
  • Map 中的 Value 为过期时间,用于判断脏数据,脏数据由监控中心删除 3

使用 Redis 的 Publish/Subscribe 事件通知数据变更:

  • 通过事件的值区分事件类型:registerunregistersubscribeunsubscribe
  • 普通消费者直接订阅指定服务提供者的 Key,只会收到指定服务的 registerunregister 事件
  • 监控中心通过 psubscribe 功能订阅 /dubbo/*,会收到所有服务的所有变更事件

调用过程:

  1. 服务提供方启动时,向 Key:/dubbo/com.foo.BarService/providers 下,添加当前提供者的地址
  2. 并向 Channel:/dubbo/com.foo.BarService/providers 发送 register 事件
  3. 服务消费方启动时,从 Channel:/dubbo/com.foo.BarService/providers 订阅 register 和 unregister 事件
  4. 并向 Key:/dubbo/com.foo.BarService/providers 下,添加当前消费者的地址
  5. 服务消费方收到 register 和 unregister 事件后,从 Key:/dubbo/com.foo.BarService/providers 下获取提供者地址列表
  6. 服务监控中心启动时,从 Channel:/dubbo/* 订阅 register 和 unregister,以及 subscribe 和unsubsribe事件
  7. 服务监控中心收到 register 和 unregister 事件后,从 Key:/dubbo/com.foo.BarService/providers 下获取提供者地址列表
  8. 服务监控中心收到 subscribe 和 unsubsribe 事件后,从 Key:/dubbo/com.foo.BarService/consumers 下获取消费者地址列表

三、集群容错

Failover Cluster

<dubbo:service retries="2" />

或<dubbo:reference retries="2" />

Failfast Cluster

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

Failsafe Cluster

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

Failback Cluster

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

Forking Cluster

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

Broadcast Cluster

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

 

6、Dubbo内置了哪几种服务容器?

  • Spring Container

  • Jetty Container

  • Log4j Container

9、Dubbo默认使用什么注册中心,还有别的选择吗?

推荐使用 Zookeeper 作为注册中心,还有 Redis、Multicast、Simple 注册中心,但不推荐。

10、Dubbo有哪几种配置方式?

1)Spring 配置方式
2)Java API 配置方式

12、在 Provider 上可以配置的 Consumer 端的属性有哪些?

1)timeout:方法调用超时
2)retries:失败重试次数,默认重试 2 次
3)loadbalance:负载均衡算法,默认随机
4)actives 消费者端,最大并发调用限制

13、Dubbo启动时如果依赖的服务不可用会怎样?

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,默认 check="true",可以通过 check="false" 关闭检查。

14、Dubbo推荐使用什么序列化框架,你知道的还有哪些?

推荐使用Hessian序列化,还有Duddo、FastJson、Java自带序列化。

15、Dubbo默认使用的是什么通信框架,还有别的选择吗?

Dubbo 默认使用 Netty 框架,也是推荐的选择,另外内容还集成有Mina、Grizzly。

16、Dubbo有哪几种集群容错方案,默认是哪种?

17、Dubbo有哪几种负载均衡策略,默认是哪种?

18、注册了多个同一样的服务,如果测试指定的某一个服务呢?

可以配置环境点对点直连,绕过注册中心,将以服务接口为单位,忽略注册中心的提供者列表。

19、Dubbo支持服务多协议吗?

Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。

20、当一个服务接口有多种实现时怎么做?

当一个接口有多种实现时,可以用 group 属性来分组,服务提供方和消费方都指定同一个 group 即可。

21、服务上线怎么兼容旧版本?

可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用。这个和服务分组的概念有一点类似。

22、Dubbo可以对结果进行缓存吗?

可以,Dubbo 提供了声明式缓存,用于加速热门数据的访问速度,以减少用户加缓存的工作量。

23、Dubbo服务之间的调用是阻塞的吗?

默认是同步等待结果阻塞的,支持异步调用。

Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。

异步调用流程图如下。

640?wx_fmt=jpeg

 

24、Dubbo支持分布式事务吗?

目前暂时不支持,后续可能采用基于 JTA/XA 规范实现,如以图所示。

640?wx_fmt=jpeg

26、Dubbo支持服务降级吗?

Dubbo 2.2.0 以上版本支持。

27、Dubbo如何优雅停机?

Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。

28、服务提供者能实现失效踢出是什么原理?

服务失效踢出基于 Zookeeper 的临时节点原理。

29、如何解决服务调用链过长的问题?

Dubbo 可以使用 Pinpoint 和 Apache Skywalking(Incubator) 实现分布式服务追踪,当然还有其他很多方案。

30、服务读写推荐的容错策略是怎样的?

读操作建议使用 Failover 失败自动切换,默认重试两次其他服务器

写操作建议使用 Failfast 快速失败,发一次调用失败就立即报错。

31、Dubbo必须依赖的包有哪些?

Dubbo 必须依赖 JDK,其他为可选。

32、Dubbo的管理控制台能做什么?

管理控制台主要包含:路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等管理功能。

33、说说 Dubbo 服务暴露的过程。

Dubbo 会在 Spring 实例化完 bean 之后,在刷新容器最后一步发布 ContextRefreshEvent 事件的时候,通知实现了 ApplicationListener 的 ServiceBean 类进行回调 onApplicationEvent 事件方法,Dubbo 会在这个方法中调用 ServiceBean 父类 ServiceConfig 的 export 方法,而该方法真正实现了服务的(异步或者非异步)发布。

34、Dubbo 停止维护了吗?

2014 年开始停止维护过几年,17 年开始重新维护,并进入了 Apache 项目。

35、Dubbo 和 Dubbox 有什么区别?

Dubbox 是继 Dubbo 停止维护后,当当网基于 Dubbo 做的一个扩展项目,如加了服务可 Restful 调用,更新了开源组件等。

36、你还了解别的分布式框架吗?

别的还有 Spring cloud、Facebook 的 Thrift、Twitter 的 Finagle 等。

37、Dubbo 能集成 Spring Boot 吗?

可以的,项目地址如下。

https://github.com/apache/incubator-dubbo-spring-boot-project

38、在使用过程中都遇到了些什么问题?

Dubbo 的设计目的是为了满足高并发小数据量的 rpc 调用,在大数据量下的性能表现并不好,建议使用 rmi 或 http 协议。

39、你读过 Dubbo 的源码吗?

要了解 Dubbo 就必须看其源码,了解其原理,花点时间看下吧,网上也有很多教程,后续有时间我也会在公众号上分享 Dubbo 的源码。

40、你觉得用 Dubbo 好还是 Spring Cloud 好?

扩展性的问题,没有好坏,只有适合不适合,不过我好像更倾向于使用 Dubbo, Spring Cloud 版本升级太快,组件更新替换太频繁,配置太繁琐,还有很多我觉得是没有 Dubbo 顺手的地方……

 

https://blog.csdn.net/qq_35902689/article/details/78113317 

Eureka的优势

1、在Eureka平台中,如果某台服务器宕机,Eureka不会有类似于ZooKeeper的选举leader的过程;客户端请求会自动切换到新的Eureka节点;当宕机的服务器重新恢复后,Eureka会再次将其纳入到服务器集群管理之中;而对于它来说,所有要做的无非是同步一些新的服务注册信息而已。所以,再也不用担心有“掉队”的服务器恢复以后,会从Eureka服务器集群中剔除出去的风险了。Eureka甚至被设计用来应付范围更广的网络分割故障,并实现“0”宕机维护需求。(多个zookeeper之间网络出现问题,造成出现多个leader,发生脑裂)当网络分割故障发生时,每个Eureka节点,会持续的对外提供服务(注:ZooKeeper不会):接收新的服务注册同时将它们提供给下游的服务发现请求。这样一来,就可以实现在同一个子网中(same side of partition),新发布的服务仍然可以被发现与访问。
2、正常配置下,Eureka内置了心跳服务,用于淘汰一些“濒死”的服务器;如果在Eureka中注册的服务,它的“心跳”变得迟缓时,Eureka会将其整个剔除出管理范围(这点有点像ZooKeeper的做法)。这是个很好的功能,但是当网络分割故障发生时,这也是非常危险的;因为,那些因为网络问题(注:心跳慢被剔除了)而被剔除出去的服务器本身是很”健康“的,只是因为网络分割故障把Eureka集群分割成了独立的子网而不能互访而已。
幸运的是,Netflix考虑到了这个缺陷。如果Eureka服务节点在短时间里丢失了大量的心跳连接(注:可能发生了网络故障),那么这个Eureka节点会进入”自我保护模式“,同时保留那些“心跳死亡“的服务注册信息不过期。此时,这个Eureka节点对于新的服务还能提供注册服务,对于”死亡“的仍然保留,以防还有客户端向其发起请求。当网络故障恢复后,这个Eureka节点会退出”自我保护模式“。所以Eureka的哲学是,同时保留”好数据“与”坏数据“总比丢掉任何”好数据“要更好,所以这种模式在实践中非常有效。
3、Eureka还有客户端缓存功能(注:Eureka分为客户端程序与服务器端程序两个部分,客户端程序负责向外提供注册与发现服务接口)。所以即便Eureka集群中所有节点都失效,或者发生网络分割故障导致客户端不能访问任何一台Eureka服务器;Eureka服务的消费者仍然可以通过Eureka客户端缓存来获取现有的服务注册信息。甚至最极端的环境下,所有正常的Eureka节点都不对请求产生相应,也没有更好的服务器解决方案来解决这种问题
时;得益于Eureka的客户端缓存技术,消费者服务仍然可以通过Eureka客户端查询与获取注册服务信息,这点很重要。
4、Eureka的构架保证了它能够成为Service发现服务。它相对与ZooKeeper来说剔除了Leader节点的选取或者事务日志机制,这样做有利于减少使用者维护的难度也保证了Eureka的在运行时的健壮性。而且Eureka就是为发现服务所设计的,它有独立的客户端程序库,同时提供心跳服务、服务健康监测、自动发布服务与自动刷新缓存的功能。但是,如果使用ZooKeeper你必须自己来实现这些功能。Eureka的所有库都是开源的,所有人都能看到与使用这些源代码,这比那些只有一两个人能看或者维护的客户端库要好。
5、维护Eureka服务器也非常的简单,比如,切换一个节点只需要在现有EIP下移除一个现有的节点然后添加一个新的就行。Eureka提供了一个web-based的图形化的运维界面,在这个界面中可以查看Eureka所管理的注册服务的运行状态信息:是否健康,运行日志等。Eureka甚至提供了Restful-API接口,方便第三方程序集成Eureka的功能。

ZooKeeper的劣势

   在分布式系统领域有个著名的CAP定理(C-数据一致性;A-服务可用性;P-服务对网络分区故障的容错性,这三个特性在任何分布式系统中不能同时满足,最多同时满足两个);ZooKeeper是个CP的,即任何时刻对ZooKeeper的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性;但是它不能保证每次服务请求的可用性(注:也就是在极端环境下,ZooKeeper可能会丢弃一些请求,消费者程序需要重新请求才能获得结果)。但是别忘了,ZooKeeper是分布式协调服务,它的职责是保证数据(注:配置数据,状态数据)在其管辖下的所有服务之间保持同步、一致;所以就不难理解为什么ZooKeeper被设计成CP而不是AP特性的了,如果是AP的,那么将会带来恐怖的后果(注:ZooKeeper就像交叉路口的信号灯一样,你能想象在交通要道突然信号灯失灵的情况吗?)。而且,作为ZooKeeper的核心实现算法Zab,就是解决了分布式系统下数据如何在多个服务之间保持同步问题的。

1、对于Service发现服务来说就算是返回了包含不实的信息的结果也比什么都不返回要好;再者,对于Service发现服务而言,宁可返回某服务5分钟之前在哪几个服务器上可用的信息,也不能因为暂时的网络故障而找不到可用的服务器,而不返回任何结果。所以说,用ZooKeeper来做Service发现服务是肯定错误的,如果你这么用就惨了!
   如果被用作Service发现服务,ZooKeeper本身并没有正确的处理网络分割的问题;而在云端,网络分割问题跟其他类型的故障一样的确会发生;所以最好提前对这个问题做好100%的准备。就像Jepsen在ZooKeeper网站上发布的博客中所说:在ZooKeeper中,如果在同一个网络分区(partition)的节点数(nodes)数达不到ZooKeeper选取Leader节点的“法定人数”时,它们就会从ZooKeeper中断开,当然同时也就不能提供Service发现服务了。

2、ZooKeeper下所有节点不可能保证任何时候都能缓存所有的服务注册信息。如果ZooKeeper下所有节点都断开了,或者集群中出现了网络分割的故障(注:由于交换机故障导致交换机底下的子网间不能互访);那么ZooKeeper会将它们都从自己管理范围中剔除出去,外界就不能访问到这些节点了,即便这些节点本身是“健康”的,可以正常提供服务的;所以导致到达这些节点的服务请求被丢失了。(注:这也是为什么ZooKeeper不满足CAP中A的原因)

3、更深层次的原因是,ZooKeeper是按照CP原则构建的,也就是说它能保证每个节点的数据保持一致,而为ZooKeeper加上缓存的做法的目的是为了让ZooKeeper变得更加可靠(available);但是,ZooKeeper设计的本意是保持节点的数据一致,也就是CP。所以,这样一来,你可能既得不到一个数据一致的(CP)也得不到一个高可用的(AP)的Service发现服务了;因为,这相当于你在一个已有的CP系统上强制栓了一个AP的系统,这在本质上就行不通的!一个Service发现服务应该从一开始就被设计成高可用的才行!

4、如果抛开CAP原理不管,正确的设置与维护ZooKeeper服务就非常的困难;错误会经常发生,导致很多工程被建立只是为了减轻维护ZooKeeper的难度。这些错误不仅存在与客户端而且还存在于ZooKeeper服务器本身。Knewton平台很多故障就是由于ZooKeeper使用不当而导致的。那些看似简单的操作,如:正确的重建观察者(reestablishing watcher)、客户端Session与异常的处理与在ZK窗口中管理内存都是非常容易导致ZooKeeper出错的。同时,我们确实也遇到过ZooKeeper的一些经典bug:ZooKeeper-1159 与ZooKeeper-1576;我们甚至在生产环境中遇到过ZooKeeper选举Leader节点失败的情况。这些问题之所以会出现,在于ZooKeeper需要管理与保障所管辖服务群的Session与网络连接资源(注:这些资源的管理在分布式系统环境下是极其困难的);但是它不负责管理服务的发现,所以使用ZooKeeper当Service发现服务得不偿失。

一个集群有3台机器,挂了一台后的影响是什么?挂了两台呢? 
挂了一台:挂了一台后就是收不到其中一台的投票,但是有两台可以参与投票,按照上面的逻辑,它们开始都投给自己,后来按照选举的原则,两个人都投票给其中一个,那么就有一个节点获得的票等于2,2 > (3/2)=1 的,超过了半数,这个时候是能选出leader的。
挂了两台: 挂了两台后,怎么弄也只能获得一张票, 1 不大于 (3/2)=1的,这样就无法选出一个leader了。

ZAB(ZooKeeper Atomic Broadcast ) 全称为:原子消息广播协议;ZAB可以说是在Paxos算法基础上进行了扩展改造而来的,ZAB协议设计了支持崩溃恢复,ZooKeeper使用单一主进程Leader用于处理客户端所有事务请求,采用ZAB协议将服务器数状态以事务形式广播到所有Follower上;由于事务间可能存在着依赖关系,ZAB协议保证Leader广播的变更序列被顺序的处理,:一个状态被处理那么它所依赖的状态也已经提前被处理;ZAB协议支持的崩溃恢复可以保证在Leader进程崩溃的时候可以重新选出Leader并且保证数据的完整性;

过半数(>=N/2+1) 的Follower反馈信息后,Leader将再次向集群内Follower广播Commit信息,Commit为将之前的Proposal提交;
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值