监控治理平台后续dubbo功能点梳理

转载自:https://www.sohu.com/a/288054675_463994

Dubbo 也具有一些服务治理的功能和服务监控的功能。下面我将介绍考拉基于 Dubbo 做的一些服务化实践。

熔断

首先来说一下熔断。在进行服务化拆分之后,应用中原有的本地调用就会变成远程调用,这样就引入了更多的复杂性。

比如说服务 A 依赖于服务 B,这个过程中可能会出现网络抖动、网络异常。

或者说服务 B 变得不可用或者不好用时,也会影响到 A 的服务性能,甚至可能会使得服务 A 占满整个线程池,导致这个应用上其他的服务也受影响,从而引发更严重的雪崩效应。

因此,服务之间有这样一种依赖关系之后,需要意识到服务的依赖其实是不稳定的。

此时,需要通过采取一些服务治理的措施,例如熔断、降级、限流、隔离和超时等,来保障应用不被外部的异常拖垮。

Dubbo 提供了降级的特性,比如可以通过 mock 参数来配置一些服务的失败降级或者强制降级,但是 Dubbo 缺少自动熔断的特性,所以我们在 Dubbo 上引入了 Hystrix。

Dubbo默认支持两种降级策略:

  1. mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。

  2. 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

我们可以在Dubbo Admin控制台上来处理降级。

为了模拟错误情况,我们改造服务提供者实现的hello方法:

@Override
public String hello(String message) {
    System.out.println("调用 cc.mrbird.provider.service.HelloServiceImpl#hello");
    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello," + message;
}

方法中让线程阻塞了2秒。

接着改造服务消费者,在@Reference注解上配置超时时间:

@Reference(timeout = 1000)
private HelloService helloService;

在不进行服务降级的情况下,访问http://localhost:8081/hello/mrbird将看抛出异常:

 

 

 

 

 

 

 

 Dubbo整合hystrix

https://www.cnblogs.com/niugang0920/p/12187307.html

Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。

演示代码,只显示重要部分,全部代码文章末尾有代码下载地址

 

<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>org.niugang</groupId>
    <artifactId>dubbo-spring-boot-hystrix</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <!--dependencyManagement父类工程管理包 -->
    <dependencyManagement>
        <dependencies>
            <!--引入springboot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.0.3.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--引入springcloud  -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <modules>
        <module>dubbo-spring-boot-hystrix-api</module>
        <module>dubbo-spring-boot-hystrix-provider</module>
        <module>dubbo-spring-boot-hystrix-consumer</module>
    </modules>
</project>

2.3dubbo-spring-boot-hystrix-provider
2.3.1pom.xml

<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>
    <parent>
        <groupId>org.niugang</groupId>
        <artifactId>dubbo-spring-boot-hystrix</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>dubbo-spring-boot-hystrix-provider</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入dubbo 集成springboot starter -->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>0.2.0</version>
        </dependency>

        <dependency>
            <groupId>org.niugang</groupId>
            <artifactId>dubbo-spring-boot-hystrix-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <!--redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <!--服务降级服务容错 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>
</project>

2.3.2 对外暴露RPCs实现类

package org.niugang.service;

import java.util.Random;

import org.springframework.stereotype.Component;
import com.alibaba.dubbo.config.annotation.Service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;

/**
 * 
 * @ClassName: DefaultDemoService
 * @Description:对外暴露接口实现类
 * @author: niugang
 * @date: 2018年8月17日 下午7:50:47
 * @Copyright: 863263957@qq.com. All rights reserved.
 *
 */
// demo.service.version 在application.properties中配置过了
@Service // dubbo注解
@Component
@Service // dubbo注解
@Component
public class DefaultServiceImpl implements DefaultApiService {

    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
    public String defaultMethod(String str) {
        /*
         * Hystrix超时配置的为2s,当实现类睡眠超过2s,服务调用者将执行服务降级函数
         */
        int nextInt = new Random().nextInt(4000);
        System.out.println("sleep " + nextInt + "ms");
        try {
            Thread.sleep(nextInt);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "HELLO  " + str + " from Dubbo Spring Boot";
    }

}

2.4 dubbo-spring-boot-hystrix-consumer
2.4.1 pom.xml

<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>
    <parent>
        <groupId>org.niugang</groupId>
        <artifactId>dubbo-spring-boot-hystrix</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>dubbo-spring-boot-hystrix-consumer</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>0.2.0</version>
        </dependency>
        <!--redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <!--引入对外暴露的rpc -->
        <dependency>
            <groupId>org.niugang</groupId>
            <artifactId>dubbo-spring-boot-hystrix-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!--服务降级服务容错 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>
</project>

2.4.2调用服务提供者

/**
     * 引入服务提供者
     */
    // com.alibaba.dubbo.config.annotation.Reference
    @Reference
    private DefaultApiService demoService;

    @RequestMapping("/sayHello")
    @HystrixCommand(fallbackMethod = "failBackMethod")
    public String sayHello(@RequestParam String name) {
        return demoService.defaultMethod(name);
    }

    /**
     * 服务降级
     * @param name
     * @return
     */
    public String failBackMethod(String name) {
        return "service request fail";
    }

5.结果

 

 

dubbo治理功能点2:

限流

下面再说一下限流。当用户的请求量,调用超过系统可承受的并发时,系统 QPS 会降低、出现不可用甚至存在宕机的风险。

这就需要一个机制来保护我们的系统,当预期并发超过系统可承受的范围时,进行快速失败、直接返回,以保护系统。

Dubbo 提供了一些基础的限流特性,例如可以通过信号量的配置来限制我们消费者的调用并发,或者限制提供者的执行并发。

但是这些是远远不够的,考拉自研了限流框架 NFC,并基于 Dubbo filter 的形式,实现了对 Dubbo 的支持,同时也支持对 URL 等其他资源的限流。

通过配置中心动态获取流控规则,对于资源的请求,比如 Dubbo 调用会经过流控客户端,进行处理并判断是否触发限流,一旦请求超出定义的阈值,就会快速失败。

一个近乎***基于Dubbo的微服务改造实践

同时,这些限流的结果会上报到监控平台。上图中的页面就是考拉流控平台的一个监控页面。

我们在页面上可以对每一个资源(URL、Dubbo 接口)进行一个阈值的配置,并对限流进行准实时监控,包括流控比率、限流次数和当前的 QPS 等。

限流框架除了实现基本的并发限流之外,也基于令牌桶和漏桶算法实现了 QPS 限流,并基于 Redis 实现了集群级别的限流。这些措施保障系统在高流量的情况下不会被打垮。

 

 

接下来,围绕 Dubbo 来介绍一下考拉在监控方面的改造实践。

***个是服务监控。Dubbo 提供了服务监控功能,支持定期上报服务监控数据,通过代码增强的方式,采集 Dubbo 调用数据,存储到时序数据库里面,将 Dubbo 的调用监控功能接入到考拉自己的监控平台。

一个近乎***基于Dubbo的微服务改造实践

上图中的页面是对 Dubbo 提供者的服务监控,包括对服务接口、源集群等不同维度的监控,除了全局的调用监控,还包括不同维度的监控,例如监控项里的调用次数。

有时候我们更关心慢请求的情况,所以会将响应时间分为多个范围,比如说从 0 到 10 毫秒,或是从 10 到 50 毫秒等,这样就可以看到在各个范围内请求的数量,从而更好地了解服务质量。

同时,也可以通过各种报警规则,对报警进行定义,当服务调用出现异常时,通过邮件、短信和电话的形式通知相关人员。

监控平台也会对异常堆栈进行采集,例如说这次服务调用的异常的原因,是超时还是线程满了的,可以在监控平台上直接看到。同时生成一些监控报表,帮助我们更好地了解服务的性能,推进开发去改进。

一个近乎***基于Dubbo的微服务改造实践

第二个是 Trace。我们参考了 Dapper,自研了 Trace 平台,并通过代码增强的方式,实现了对 Dubbo 调用链路的采集。

相关调用链参数如 TarceID,SpanID 等是通过 Dubbo 的隐式传参来传递的。Trace 可以了解在服务调用链路中的一个耗时分析和瓶颈分析等。

Trace 平台上可以展示一次服务调用,经历了哪些节点,最耗时的那个节点是在哪里,从而可以有针对性的去进行性能优化。

Trace 还可以进行依赖分析,这些依赖是否合理,能否通过一些业务手段或者其它手段去减少一些不合理的依赖。

Trace 对异常链路进行监控报警,及时的探测到系统异常并帮助我们快速的定位问题,同时和日志平台做了打通,通过 TraceID 可以很快的获取到关联的异常日志。

一个近乎***基于Dubbo的微服务改造实践

 上面这些可以通过skywalking来实现

第三个是健康检查。健康检查也是监控中很重要的一个方面,以更优雅的方式上线应用实例。我们和自动部署平台结合,实现应用的健康检查。

服务启动的时候可以通过 Readiness 接口判断应用依赖的各种资源,包括数据库、消息队列等等是否已经准备就绪。

只有健康检查成功的时候才会触发出注册操作。同时 Agent 也会在程序运行的过程中定时的检查服务的运行状态。

同时,也通过这些接口实现更优雅的停机,仅依赖 shutdownhook,在某些情况下不一定靠谱,比如会有 shutdownhook 执行先后顺序的问题。

应用发布的时候,首先调用 offline 接口,将注册服务全部从注册中心反注册,这时不再有新的流量进来,等到一段时间后,再执行停机发布操作,可以实现更加优雅的停机。

考拉在服务测试方面的改造

下面来介绍一下考拉在服务测试方面的实践。服务测试分为接口测试、单链路压测、全链路压测和异常测试四个维度。

接口测试

通过接口测试,可以来验证对外提供的 Dubbo 服务是否正确,因此我们也有接口测试平台,帮助 QA 更好的进行接口测试,包括对接口的编辑(入参、出参),用例的编辑和测试场景的执行等。

点击进入看图评论

单链路压测

单链路的压测,主要面对单个功能的压测,比如要上线一个重要功能或者比较重要的接口之前,必须通过性能测试的指标才可以上线。

全链路压测

考拉作为电商平台,在大促前都会做全链路压测,用以探测系统的性能瓶颈,和对系统容量的预估。

例如,探测系统的各类服务的容量是否够,需要扩容多少,以及限流的阈值要定多少合适,都可以通过全链路压测来给出一些合理的值。

异常测试

对服务调用链路中的一些节点进行系统异常和服务异常的注入,也可以获取他们的强度依赖关系。

比如一个非常重要的接口,可以从 Trace 获取的调用链路,然后对调用链的依赖的各个服务节点进行异常注入。

通过接口的表现,系统就会判断这个接口的强度依赖关系,以改善这些不合理的强依赖关系。

考拉的 API 网关是通过泛化调用的方式来调用后台 Dubbo 的服务的。Dubbo 原生的泛化调用的性能比普通 API 调用要差一些。

所以我们也对泛化调用性能做了一些优化,也就是去掉了泛化调用在返回结果时的一次对象转换。最终压测的结果泛化的性能甚至比正常的调用性能还要好些。

泛化调用
API网关在进行协议转换的时候有两步,第一步是把客户端的请求协议转为相应微服务的接口协议,然后再调用相应的微服务;第二步是把微服务返回的结果转换为客户端的协议,然后返回给客户端。

客户端的请求协议可能有多种,而微服务的接口协议也可能有多种,不同的协议之间是无法直接通讯的,那么就需要一个模块来负责把不同的协议转换为用某种语言描述的对象,然后把这个对象再转化为相应的协议代码块。如下图所示:

注册中心性能的问题:

1、 注册中心瓶颈可能是大部分电商企业都会遇到的问题,考拉也不例外。我们现在线上的 Dubbo 服务实例大概有 4000 多个,但是在 ZooKeeper 中注册的节点有一百多万个,包括服务注册的 URL 和消费者订阅的 URL。Dubbo 应用发布时的惊群效应、重复通知和消费者拉取带来的瞬时流量一下就把 ZooKeeper 集群的网卡打满,ZooKeeper 还有另外一个问题,他的强一致性模型导致 CPU 的利用率不高。

Dubbo 应用发布时的惊群效应、重复通知和消费者拉取带来的瞬时流量一下就把 ZooKeeper 集群的网卡打满,ZooKeeper 还有另外一个问题,他的强一致性模型导致 CPU 的利用率不高。

就算扩容,也解决不了 ZooKeeper 写性能的问题,ZooKeeper 写是不可扩展的,并且应用发布时有大量的请求排队,从而使得接口性能急剧下降,表现出来的现象就是应用启动十分缓慢。

因此,在今年年初的时候就我们决定把 ZooKeeper 注册中心给替换掉,对比了现有的一些开源的注册中心,包括 Consul、Eruka、etcd 等,觉得他们并不适合 Dubbo 这种单进程多服务的注册模型,同时容量能否应对未来考拉的发展,也是一个问号。

于是,我们决定自研注册中心,目前正在注册中心的迁移过程当中,采用的是双注册中心的迁移方案,即服务会同时注册 ZooKeeper 注册中心,还有新的注册中心,这样对原有的架构不会产生太大的影响。

考拉新的注册中心改造方案和现在社区的差不多,比如说也做了一个注册数据的拆分,往注册中心注册的数据只包含 IP, Port 等关键数据,其他的数据都写到了 Redis 里面。

注册中心实现使用了去中心化的一个架构,包括使用最终一致性来换取我们接口性能的一个提升。

后面如果接入 Dubbo,会考虑使用 Nacos 而不是 ZooKeeper 作为注册中心。

未来规划

一个近乎***基于Dubbo的微服务改造实践

 

考拉最近也在进行第二机房的建设,通过两个机房独立部署相同的一套系统,以实现同城双活。

针对双机房的场景,Dubbo 会做一定的改造,例如同机房优先调用,类似于即将发布的 Dubbo 2.7.0 中的路由特性。

在 Dubbo 服务注册的时候,读取系统环境变量的环境标或者机房标,再将这些机房标注册到注册中心,然后消费端会做一个优先级路由,优先进行同机房的服务调用。

容器化也是我们在规划的一个方向。随着服务化进程的演进,服务数也变得越来越多,通过容器化、DevOps 可以提升测试、部署和运维效率。

Service Mesh 在今年非常火,通过 Service Mesh 将服务框架的能力比如注册发布,路由和负载均衡,服务治理等下沉到 Sidecar,使用独立进程的方式来运行。

对于业务工程的一个解耦,帮助我们实现一个异构系统,对多语言支持,也可以解决中间件升级推动困难以及各种依赖的冲突,业务方也可以更好的关注于业务开发,这也会是未来探索的一个方向。

posted on 2020-09-01 11:20  luzhouxiaoshuai  阅读(360)  评论(0编辑  收藏  举报

导航