去年写了Dubbo源码解析系列,但是因为一些不可抗拒的因素(加班),导致通信、编解码部分还没写.同时在和很多朋友交流过程中,其中反馈最多的一个问题是.看源码到底有什么用?究竟是不是真的只是面试时装装逼,装完继续CRUD.
其实之所以有这样的疑问,主要是因为平时遇到的问题都能通过搜索引擎解决.然而,我近两年的做的都是基础平台相关的工作,还是经常遇到一些必须要看源码才能解决的问题.所以我觉得光把自己看源码的经验分享出来还不够,更重要是讲清楚,看源码,究竟解决了什么实际问题.
本篇是 为什么要看源码系列
的第三篇.先交代一下背景
由于公司做的是海外业务,为了方便海外同事办公.我们把公司的开发服务器(以下简称sit环境)迁移到了海外.但是这样就产生了一个问题.由于服务器在海外,运维部表示由于政策原因(具体细节不透露),那么我们将无法访问到sit环境.既然无法访问,那么这个sit环境对于我们就形同虚设了.经过协调.运维用Nginx做端口转发.把我们常用的中间件(zookeeper,RocketMQ)、数据库地址做映射.但是这样依然存在一些问题,下面画图分析
其实我们常见的中间件的原理都大致相同,你了解一种后,通过类比学习.很容易掌握其他的.下面拿RocketMQ举例.演示一下什么叫类比学习法
RocketMQ的官方文档中物理部署结构图如下:
NameSever
你可能不知道是什么.其实这个是RocketMQ3.X版本引入的,之前用的是zookeeper.就是说,3.X版本发现zookeeper功能很强大, 但是人家其实用不到zookeeper这么多功能,基本上只用到了zookeeper的分布式协调,也就是注册中心的功能,就弄了个更轻量的 NameSever
.
那么 Broker
是什么呢? 这个是数据存储的核心,也是真正的MQ服务器.我们所谓的消息存储,接收,拉取,推送操作都是在这个broker进行的.
简单的说,我们平时在项目中配置的mq地址是 NameSever
.然后 Broker
把信息注册到 NameSever
.在框架的底层,我们访问 NameSever
的时候,它通过注册的信息真正发起网络请求去访问注册的 Broker
.
那么接下来我们就类比一下 fastDFS
从上面两个图你就可以看出.在 fastDFS
中, Tracker Server
就类似 NameSever
, Storage Server
就类似 Broker
.原理基本是一样的.同样的,平时我们项目中配置的地址其实是 Tracker Server
的地址
Dubbo的原理我这里就不想再多做解释了,之前已经从源码角度各种剖析了.这里给之前没看过我Dubbo源码解析的朋友简单科普一下(真的只是简单讲两句)
两个Dubbo服务之间的调用,绝大多数情况是网络调用(为什么是绝大多数.因为这个涉及到本地暴露和远程暴露的问题).那么网络调用,至少要确定三个参数.就是
url地址 请求参数 响应参数
所以Dubbo最简单的原理就是,生产者把本机的ip地址和暴露的接口信息在zookeeper上创建节点.因为本机的ip地址就可以确定请求的url地址,接口信息可以确定请求和响应的参数.然后消费者调用接口的时候,被动态代理拦截到,然后通过网络请求调用提供者.
同样的,我们在项目中配置的地址是zookeeper的地址.原因同上两个中间件原理一致
你会发现,中间件的简单原理基本是一样的.通过这个类比学习,你以后遇到其他的中间件,哪怕是自己公司自研的中间件,他们的原理你也能迅速上手.
分析完原理之后,那我们遇到了什么问题呢?比如RocketMQ,我们配置的是 NameSever
的地址,那么这个地址我们可以通过Nginx映射出来.这样我们就可以访问到 NameSever
.但是 Broker
的地址,是Rocket自己注册上去的.我们即使映射了 192.168.X.XXX
.但是 Broker
注册地址的逻辑代码里面还是会把 192.168.Y.YYY
注册上去.那么我们访问的还是 192.168.Y.YYY
那么我们映射的这个 192.168.X.XXX
就没有作用.
那么我们是不是装个逼秀一波操作,改源码的逻辑,让 Broker
注册的地址按照我们映射的 192.168.X.XXX
注册上去呢?
我只想说,年轻人,你这个思想很危险啊!
编程讲究的是开闭原则,再说,你确定能驾驭得住的RocketMQ的源码改动吗?我都说了,遇到问题,先不要着急看源码.先看官方文档,再通过搜索引擎,如果都不能解决的.我们再看源码.我百度搜索第一条就是答案的我还看个毛线源码!那么我们来看下官方文档
人家这个说得就很清楚了,默认是注册本地的ip地址上去,但是你可以设置.所以我们把自己的映射的ip地址配置上去就OK了.
RocketMQ的问题我们通过官方文档解决了.但是Dubbo的问题文档没有.搜索了有类似答案,但是依然没有把原理讲清楚.那么我们就直入源码一探究竟(好吧,让我装个逼)
如果你之前就关注肥朝,一直有看过我的dubbo源码解析系列,比如 dubbo源码解析-zookeeper创建节点
那么你就能像我这种风一般的男子一样迅速定位到源码中获取ip地址的源码片段.如下
这部分的逻辑是,先调用 InetAddress.getLocalHost().getHostAddress()
,如果该方法返回一个合法地址,就直接认为这个地址是本地的ip地址.否则会遍历本地的所有网卡,并返回找到的第一个合法地址
//这部分是jdk的api host = InetAddress.getLocalHost().getHostAddress(); 复制代码
既然是jdk的api,那么要弄懂原理就好搞很多了.我们直接看文档注释
/** * Returns the address of the local host. This is achieved by retrieving * the name of the host from the system, then resolving that name into * an {@code InetAddress}. * * <P>Note: The resolved address may be cached for a short period of time. * </P> * * <p>If there is a security manager, its * {@code checkConnect} method is called * with the local host name and {@code -1} * as its arguments to see if the operation is allowed. * If the operation is not allowed, an InetAddress representing * the loopback address is returned. * * @return the address of the local host. * * @exception UnknownHostException if the local host name could not * be resolved into an address. * * @see SecurityManager#checkConnect * @see java.net.InetAddress#getByName(java.lang.String) */ 复制代码
从文档的大概意思可以看出,他获取的ip地址,和 hostName
有关.那么这样我们就有了突破口.
我们通过linux命令( uname -n
)查看机器的hostName,比如
然后编辑host文件
vi /etc/hosts 复制代码
比如设置
192.168.1.102 testdemo 复制代码
那么我们启动dubbo服务
当然这样我还是不放心,我们去zookeeper上看看节点
果然和我们看到的是一样的.