作者:肥朝

前言

dubbo服务暴露系列完结之后,按计划来说是应该要开启dubbo服务引用的讲解.但是现在到了年尾,一些朋友也和我谈起了明年跳槽的事.跳槽这件事,无非也就两个原因,一个是钱没给够,另一个是心里委屈了.首先钱没给够这件事我就不多说什么了,因为每个人都觉得自己钱没给够.那心里为啥委屈了?作为一个技术人,我认为心里委屈,无非也就是两个原因,一个是在公司得不到重视,另一个是感觉学不到东西,得不到成长.感觉我所了解到的情况,往往是后者居多.这道理也简单,我们小时候看重的是兴趣和爱好,长大后看重的是投资和回报.

互联网行业加班是常态,首先我不反对加班.但是加班带来的影响也不得不重视.首先对于老板而言,员工加班越多,获得的利润更大.比如:

我们老板来上班事开了一辆崭新的兰博基尼。
我说:“哇喔,这辆车好牛逼”
他回答:“如果你努力工作,全身心投入,力求卓越,那么我明年还会再有一辆”。

再次,一旦你加班多了,学习新技术的时间就少,这样你就会变得越来越不自信,自然不敢随便跳槽.但是加班和学习其实并不冲突.即使加班再多(比如今天周六我也还在加班),肥朝每周一篇dubbo源码解析与你不见不散,请放心保持密切关注肥朝.

如果你是因为学不到东西跳槽,那么往往会遇到一个问题,那就是公司的项目太low,找一下家的时候拿不出手.技术上,增删改查这种东西肯定是拿不出手的,我用freemarker + mybatis generator做代码生成器直接一键生成就可以直接运行跑起来.业务上,每个公司的业务都不同,讨论意义不大.想去网上找点资料装逼,但是却发现无从下手.好不容易找了点技术沙龙逼格高的PPT,不幸的是都是空谈理论,无法和自己公司的项目衔接起来.但是幸运的是无意中关注了肥朝.

首先我们来看看,我在三张图看清项目结构提到的中小型公司比较典型的Java项目的架构.

从中可以看出,典型的就是三层结构,

  • 接入层,逻辑层,数据存储层.

当然也可以分成四层

  • 接入层,逻辑层,原子服务层,数据存储层.

当然是可以分成五层

  • 接入层,序列化层(异步消息队列),原子服务层,数据层,数据存储层.

当然分几层都要根据自身业务,好的架构并不是一蹴而就,而是逐渐演变的过程.从标题就可以知道,本篇着重介绍逻辑层的设计(那剩下的什么时候讲?反正每周一篇,一年也就48篇.dubbo系列完结之后下一个系列由你来定,你可以自己估算一下时间).既然是设计,那么就不能纸上谈兵,必须站在巨人的肩膀上,比如孙玄老师分享的58同城架构设计就很有参考意义.我简单用思维导图做了个总结.如下:

看到这里有朋友可能就不乐意了,不要扯这些原则,老子拿起键盘就是干.

我想说的是,如果没有读万卷书,即使行了万里路,也不过是个邮差.知道了理论,下面我们直入主题,开始实战.

插播面试题

  • 谈一下你们项目架构设计(很多人在回答这个的时候都容易回答SSH或者SSM,注意,所谓是SSH这些是技术选型,不是架构的设计)

  • 既然你们项目用到了dubbo,那你讲讲你们是怎么通过dubbo实现服务降级的,降级的方式有哪些,又有什么区别?

  • dubbo监控平台能够动态改变接口的一些设置,其原理是怎样的?

  • 既然你说你看过dubbo源码,那讲一下有没有遇到过什么坑?(区分度高,也是检验是否看过源码的试金石)

直入主题

我们从两个角度来分析,一个是为什么需要服务降级,一个是怎么做服务降级

为什么需要服务降级

引进一个新技术,必须要看这个新技术解决了什么问题.比如服务降级,他解决了什么问题?从上面的思维导图我们就知道,当网站处于高峰期时,并发量大,服务能力有限,那么我们只能暂时屏蔽边缘业务.那么具体的例子是什么?

比如在某宝某东购物,当支付完成,会向你推荐一些商品.但是在11大促中,并发量过大.我们就要保证"支付"这些核心业务的正常运行,因此像"推荐商品"这些边缘业务,我们就可以不调用,从而减少一定的并发.但是如果双11我先把"推荐商品"接口的代码屏蔽起来,等过后我再打开.这种太简单粗暴的方法肯定不是我们的理想追求,这时候我们就需要一个"服务开关"一样的东西.这个开关,就是服务降级

怎么做服务降级

空谈误国,实战兴邦,光知道思维导图上的这些设计原则还不行,我们以dubbo为例,实战一下服务降级.首先dubbo中的服务降级分成两个

  • 屏蔽(mock=force)

  • 容错(mock=fail)

这两个有什么区别呢?我们引用文档介绍

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

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

那么下面分别演示一下容错的使用方法

首先我们打上断点,造成请求超时,报错如下

配置容错

报错信息立刻消除,结果如下

屏蔽就不在演示,配置方式类似,效果自己调试.

其实从文档介绍我们就能回答出两者的区别.但是老司机可能更享受的是扒光原理的快感.

源码分析

首先我假设你之前看过肥朝每周一篇dubbo源码解析,那么对MockClusterInvoker这个类就不会陌生,那么我们直接看核心代码(应群友反馈,尝试一下代码不贴图)

no mock(正常情况),force:direct mock(屏蔽),fail-mock(容错)三种情况我们也可以看出,普通情况是直接调用,容错的情况是调用失败后,返回一个设置的值.而屏蔽就很暴力了,直接连调用都不调用,就直接返回一个之前设置的值.

public Result invoke(Invocation invocation) throws RpcException {
    Result result = null;
    String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim(); 
    if (value.length() == 0 || value.equalsIgnoreCase("false")){
        //no mock
        result = this.invoker.invoke(invocation);
    } else if (value.startsWith("force")) {
        if (logger.isWarnEnabled()) {
            logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " +  directory.getUrl());
        }
        //force:direct mock
        result = doMockInvoke(invocation, null);
    } else {
        //fail-mock
        try {
            result = this.invoker.invoke(invocation);
        }catch (RpcException e) {
            if (e.isBiz()) {
                throw e;
            } else {
                if (logger.isWarnEnabled()) {
                    logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " +  directory.getUrl(), e);
                }
                result = doMockInvoke(invocation, e);
            }
        }
    }
    return result;
}

敲黑板画重点

有句话叫尽信书不如无书,dubbo中也难免存在一些bug,比如我之前在dubbo源码解析-router就提到过,这个监控平台是有bug的,如今又出现了

你会发现点删除或者点启用和禁用后,会出现多条.解决办法还是和之前一样.清除zookeeper上的节点信息(不懂的可以点回去看看router这篇,这就是我之前反复强调的,一定要系统学习,因为之前提到的我往往一笔带过)

从这个解决bug中,我们也应该有一些逆向思维.为什么这个监控平台这么神奇,能动态改变接口的一些默认设置?你清除了zookeeper节点,监控平台上的一些配置信息就消失了,很明显,他这个原理就是改变注册在zookeeper上的节点信息.从而zookeeper通知重新生成invoker(这些具体细节在zookeeper创建节点,zookeeper连接,zookeeper订阅中都详细讲了,这里不再重复)

当然除了这些坑外,dubbo在集群容错算法中的轮询就有个坑,需要调节当前时间解决(因为这个使用不多,这里暂时不细说),但是当当网的dubbox有一个坑就比较明显.如下图,当你传的参数为null时,这里就有很明显的空指针

在后面当当网也解决了这个问题

当然他这个修复的代码也可以出一个面试题.当然这道题我就不解答了

java中 || 和 | 有什么区别

把握重点

看到这里你就必须要把握一下重点.从标题你就知道,本篇是有三个关键词,分别是dubbo,逻辑层设计,服务降级

我用dubbo演示一种服务降级的方式,并不代表是只有这一种方式,你如果仔细看思维导图就明白,其实也还有很多实现方式.另外如果你觉得你们项目比较low,那么你可以设想,假如用思维导图的这些原则来设计,那么要怎么设计,有什么优缺点?然后自己尝试改造一下,这思考和行动的过程,才是你最宝贵的收获,也是我想传达的学习思想.如果你把重点当成了dubbo如何配置服务降级,那么可能再好的项目,你都只看到了增删改查.

写在最后

写到这里的时候,不知不觉已经是凌晨四点.因为996的模式下,要坚持每周一篇对我来说也是一个挑战,但是同时我也享受着这种挑战的感觉.每次下班的时候,遇到熟人都会问我怎么这么晚才下班,加班这么多,一定很多加班费吧.我说,没有加班费,这个时候都会很自然的反驳到,没有加班费那干嘛加班.同样的道理,一些朋友看到我写博客,也会问,你每周都坚持写博客有钱赚吗,我说没有,这个时候正常的逻辑也是反驳到,没有钱干嘛要做.其实有时候,博客既是写给别人看的,也是写给自己看的.最重要的是从一件事上,看到一个人做事的决心

期待下周与你相遇.鉴于本人才疏学浅,不对的地方还望斧正,