前言html
微服务架构在现在的9102年已经不是什么新鲜的话题了,可是怎么作好微服务架构,却又是一个永恒的话题。好比服务粒度的划分,怎么控制好粗细?服务划分后,对于项目的部署会有什么改变?... 这会是一个很大的话题,之后能够分开篇章探讨一翻,可是咱们本篇并不打算聊这个,而是讨论一下具体的实现技术--dubbo。git
dubbo历史github
2011 年底,阿里巴巴在 GitHub 上开源了基于 Java 的分布式服务治理框架 Dubbo,以后它成为了国内该类开源项目的佼佼者,许多开发者对其表示青睐。同时,前后有很多公司在实践中基于 Dubbo 进行分布式系统架构,目前在 GitHub 上,它的 fork、star 数均已破万。2014 年 10 月 30 号发布版本 dubbo-2.4.11,修复了一个小 Bug,版本又陷入漫长的停滞到2017年九月份。spring
在dubbo停滞的期间呢,当当网 Fork 了阿里的一个 Dubbo 版本开始维护,并命名为 dubbox-2.8.0。值得注意的是,当当网扩展 Dubbo 服务框架支持 REST 风格远程调用,而且跟随着 ZooKeepe 和 Spring 升级了对应的版本。以后 Dubbox 一直在小版本维护,2015 年 3 月 31 号发布了最后一个版本 dubbox-2.8.4。笔者公司用的也是这个版本,并稍微改造了下源码,下面会有说起。docker
其实在当前说到微服务,可能你们第一反应是springcloud,spring全家桶带来的便捷是显而易见的,然而为何咱们这里聊的是dubbo呢?缘由之一是由于笔者公司只用了dubbo(别扔鸡蛋....),其二呢其实rpc框架不少原理是相通的,当咱们理解了其中一个,再去看其余的框架,会有一种似曾相识的感受,最后也不必去争论XX框架的好与坏,选择最适合本身业务的就是最好的。apache
先交代下背景,咱们这边是从2016年开始使用dubbo,使用的是dubbox-2.8.4 版本,而后由于一些场景不合适改了下代码,从新打包成2.8.5提交至公司的私服使用。好了,接下来就开始进入正文,聊聊这几年在dubbo使用过程当中遇到坑,以及须要注意的地方吧。服务器
正文网络
一、超时重试架构
这是一个很经典的坑,当时因为刚使用dubbo,不少配置都是基于默认的。恰好此时在项目中,有一个机器人送礼的逻辑比较复杂,当遇到某些特定的条件时,该逻辑的耗时会比正常状况下变长,这时候就出现了一个很神奇的现象,为什么我只触发了一次送礼的请求,而线上却送了三次?负载均衡
刚遇到这种状况可我惊呆了,从新审视了代码,发现并没有问题。这就奇怪了,哪里来的3次?后来掉了几根头发之后,才在dubbo的文档中发现了服务这块有timeout跟retry属性,默认timeout=1000ms,retry=2。这下就豁然开朗,原来是第一次调用超时,致使又重试了2次,一共就是3次了。
找到问题的缘由,咱们就有办法解决了。因为咱们这个接口不是幂等性的,并且也不用返回什么信息给调用者,因此咱们能够经过一个线程池来执行这段耗时的逻辑,让rpc调用能够比较快的返回给调用者。这样就不存在超时的问题了。或者能够配合增长timeout时间跟retry=0也能实现,具体的业务逻辑须要本身找到合适的解决方案。
二、dubbo使用内网ip
正常状况下,咱们的服务调用推荐走内网链接的方式,效率是比较高的。可是有些特殊的状况,咱们须要dubbo注册服务的时候使用外网ip,该怎么修改呢?这时候就须要修改咱们的服务器上 /etc/hosts 文件了,新增一条 “外网ip 主机名”的记录,restart咱们的服务便可。
三、docker里面注册宿主机内网ip
说到微服务,固然也少不了docker了,咱们当前用的是docker+overlay网络一个结构,直接把dubbo服务丢进容器里面跑的话,注册进zk的ip是容器ip。因此咱们采起了一种折中的方式。
利用docker的特性,咱们在建立容器的时候,把宿主机的ip以及须要暴露的端口写进容器的环境变量里面。而后就是修改dubbox的源码了,源码的com.alibaba.dubbo.registry.integration.RegistryProtocol类的getRegistedProviderUrl
方法,此方法用于返回注册到注册中心的URL。
private URL getRegistedProviderUrl(final Invoker<?> originInvoker){
//targetUrl 注册中心看到的地址
URL targetUrl; URL providerUrl = getProviderUrl(originInvoker); //配置的容器环境变量 String envParameterHost=System.getenv(ENV_HOST_KEY); String envParameterPort=System.getenv(ENV_PORT_KEY); if (StringUtils.isBlank(envParameterHost)||StringUtils.isBlank(envParameterPort)){//非容器环境:执行原来的注册逻辑 targetUrl=providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameter(Constants.MONITOR_KEY); }else {//容器环境,若是环境变量中DOCKER_NAT_HOST和DOCKER_NAT_PORT两个值都不为空则直接将这两个值做为url注册到zk //执行从新拼接url的操做,涉及敏感代码这里不展现了 targetUrl=dockerRegUrlWithHostAndPort; } return targetUrl; }
四、未注意服务重名
其实这是咱们开发人员粗枝大叶出现的状况,开发的时候注册了2个相同签名的服务,可是业务逻辑是彻底不一样的,这会致使一个以前运行的正常的业务会偶尔调用失败,缘由是由于dubbo的负载均衡策略,把一部分流量转移到咱们新注册上来的服务上了,可是处理逻辑不一样,致使错误。
五、版本的一致性
dubbo当前的releases版本已经去到2.7.1了,项目中要注意一下不一样项目间版本的一致性,或者是dubbo跟dubbox的一些差异,最好作到统一,否则出现问题解决的成本会比较高。
六、属性配置的优先级
咱们在dubbo的过程当中会发现,提供者跟消费者中,不少属性是同样的,咱们该怎么配呢?在dubbo的文档当中其实有推荐的用法。
在提供者端尽可能多提供消费者端的属性。
参考文档,缘由以下:
做服务的提供方,比服务消费方更清楚服务的性能参数,如调用的超时时间、合理的重试次数等
在 Provider 端配置后,Consumer 端不配置则会使用 Provider 端的配置,即 Provider 端的配置能够做为 Consumer 的缺省值 。不然,Consumer 会使用 Consumer 端的全局设置,这对于 Provider 是不可控的,而且每每是不合理的
Provider 端尽可能多配置 Consumer 端的属性,让 Provider 的实现者一开始就思考 Provider 端的服务特色和服务质量等问题。
结语
其实在dubbo的使用过程当中,还有挺多问题这里没列出来的,可是解决方法都差很少,首先文档要熟,作到心中有数,好比dubbo功能的成熟度,有些是不推荐在线上使用的,这时你就要谨慎了。而后文档里面确实是有遗漏的问题,咱们有必要能够debug dubbo的源码,这个过程会比较痛苦,可是对于排查问题跟我的能力的提升是有颇有帮助的。
你们在dubbo的使用过程当中有什么问题也能够交流一下~
github:https://github.com/apache/incubator-dubbo
中文使用手册http://dubbo.apache.org/zhcn/docs/user/preface/background.html