1.HashMap底层实现原理及Dubbo
2.《深潜Dubbo》· HashedWheelTimer定时轮算法
HashMap底层实现原理及Dubbo
一、算公式源实战学习内容
1. Java中HashMap的源码底层实现原理
1) HashMap的数据结构
JDK1.8以前 HashMap 的实现是 数组加链表
JDK1.8开始 HashMap 的实现是 数组加链表加红黑树
HashMap中有两个常量:
当链表中节点数量大于等于TREEIFY_THRESHOLD时,链表会转成红黑树。分析
当链表中节点数量小于等于UNTREEIFY_THRESHOLD时,算公式源实战红黑树会转成链表。源码
2) HashMap的分析html下载上传源码三个构造函数
HashMap():构造一个具有默认初始容量 () 和默认加载因子 (0.) 的空 HashMap。
HashMap(int initialCapacity):构造一个带指定初始容量和默认加载因子 (0.) 的算公式源实战空 HashMap。
HashMap(int initialCapacity,源码 float loadFactor):构造一个带指定初始容量和加载因子的空 HashMap。
关于构造器中的分析参数:
initialCapacity:初始容量,初始容量数值没有存起来,算公式源实战而且使用它计算阀值threshold。源码计算方法就是分析返回大于initialCapacity且最接近initialCapacity的一个2的正数幂的数字作为初始阀值。
capacity:容量。算公式源实战capacity就是源码指HashMap中桶的数量。默认值为。分析一般第一次扩容时会扩容到,之后都是以2的幂数增加。
loadFactor:装载因子,用来衡量HashMap满的程度,加载因子越大,填满的元素越多,空间利用率越高。loadFactor的默认值为0.f。计算HashMap的实时装载因子的方法为size/capacity。
threshold:阀值,蓝梦源码论坛满足公式threshold=loadFactor*capacity。当HashMap的size大于threshold时会执行扩容(resize)操作。
3) HashMap的工作原理
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
2. 学习框架dubbo
1) zookeeper的安装以及配置
安装教程: my.oschina.net/langwang...
2) 几种架构:
单一应用架构:
背景:网站流量很小,只需一个应用将所有功能部署在一起,减少部署节点和成本。
此时,用于简化增删改查工作量的数据访问框架(ORM)是关键
垂直应用架构:
背景:访问量逐渐增大,通过将应用拆分成互不相干的筹码搬家指标源码几个应用以提升效率。
此时,用于加速前端页面开发的Web框架(MVC)是关键
分布式服务架构:
背景:垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来作为独立的 服务,成为稳定的服务中心,使得前端应用能更快速地响应多变的市场需求。
此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键
流动计算架构:
背景:服务越来越多,显现出小服务资源的浪费等问题,需要增加一个调度中心基 于访问压力实时管理集群容量,提高集群利用率
此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键
3) 什么是dubbo?它做些什么
Dubbo :Dubbo是阿里旗下的一个弹性的分布式服务框架,致力于提供高性能和 透明化的RPC远程服务调用方案,以及SOA服务治理方案。
作为RPC:支持各种传输协议
作为SOA:具有服务治理功能,提供服务的注册和发现!用zookeeper实现注册中 心!
4) 描述一个服务从发布到被消费的详细过程:
一个服务的发布暴露过程:
首先设置一个项目的别名,然后是定义注册中心和设定传输协议,之后定义服务名, 服务接口以jar形式导入到provider。一个服务发布暴露首先由spring的人气附图指标源码spacehander 把相关的xml或者注解全部转化为springBean,之后通过ServiceConfig.exerp()方法 把bean传化为传输所需的url和参数注册到注册中心,发布后provder端的 ref(helloImpl)通过protocl(传输协议,如dubboprotocl,hessionprotocl)转化为Invoker 对象,即调用信息,包括类,方法,参数等等,再通过proxy操作(代理)如jdkproxy 代理转为为Exporter对象,以上就是整个的服务暴露过程。
消费过程:
一个Renfence类,通过RenfenceConfig的init 调用proxy的refer方法生产一个 invoker,invoker再通过proctol转化成具体的ref(hello),进行消费
首先 ReferenceConfig 类的 init 方法调用 Protocol 的 refer 方法生成 Invoker 实 例(如上图中的红色部分),这是服务消费的关键。接下来把 Invoker 转换为客户端 需要的接口(如:HelloWorld)
5) dubbo调用流程
首先介绍dubbo中的几个角色:
服务提供者(Provider)、服务消费者(Consumer)、注册中心( Registry)、监控中 心(Monitor)
具体的调用流程:
0. 服务容器负责启动、加载,运行Provider。
1. Provider在启动时,向Registry注册自己提供的服务,Registry缓存服务列表,并建立长连接心跳检测。
2. Consumer在启动时,白边图生成源码向Registry订阅自己所需的服务,并建立长连接心跳检测。
3. Registry返回服务提供者地址列表给Consumer并缓存,如果服务有变更,Registry将基于长连接推送变更数据给Consumer并更新。
4. Consumer在使用服务时,基于软负载均衡算法,从提供者地址列表中,选一台Provider进行调用,如果调用失败,则切换到另一台调用。
5. Consumer和Provider,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到Monitor。
6) 创建一个dubbo demo,并且成功运行
代码结构:
3. 其他内容
重新配置电脑开发环境
二、存在的问题
1. 问题解决记录
1) 安装zookeeper报错:JAVA_HOME is not set
主要原因:zookeeper在启动服务端的时候回基于Java环境启动,所以在启动的时候会检测jdk是否安装,在zkservice启动的时候会找%JAVA_HOME%\bin\java.jar 进行java基础环境的启动。在zkEvn文件里有JAVA_HOME的验证,本身我已经设置了JAVA_HOME这个变量,但是在验证的时候没有获取到,所以解决的办法就是手动设置,在zkEvn.cmd文件中添加下面的语句
详细解决办法: cnblogs.com/china-baizh...
2. 未解决问题
对dubbo一个服务的发布暴露过程以及消费过程的细节部分不理解
三、明天学习计划
1. 开始算法部分的学习(算法设计中的五大常用算法)
2. 对前面近五天的内容进行回顾和总结,设计数据库
《深潜Dubbo》· HashedWheelTimer定时轮算法
HashedWheelTimer定时轮算法被广泛应用,包括netty、dubbo乃至操作系统Linux,主要用于管理及维护大量Timer调度算法。
HashedWheelTimer呈环形结构,类似时钟,分为众多槽位,每个槽位代表一个时间间隔,存储定时任务的双向链表。指针周期性跳动,到达某个槽位即执行该槽位定时任务。
定时轮实现中,根据职责不同,分为时钟引擎、时钟槽、定时任务三个主要角色,深入理解其实现,本文将略去具体实现语言的特性。
定时任务——HashedWheelTimeout在具体实现中扮演双重角色,既是双向链表的节点,也是实际调度任务TimerTask的容器。引擎在滴答运行起始时刻使用&取hash装入对应的时钟槽。
关键属性包括:next和prev(当前定时任务在链表中的前驱和后继引用)、task(实际被调度的任务)、deadline(时间单位为纳秒,由currentTime + delay - startTime计算得出)、state(定时任务当前所处状态,如INIT、CANCELLED、EXPIRED)。
HashedWheelTimeout支持的操作有限,如时钟槽——HashedWheelBucket,它是一个用于缓存和管理定时任务的双向链表容器,每个节点即一个定时任务。持有链表的首尾两个节点,可以完成相关操作。
时钟引擎——HashedWheelTimer有节律地周期性运作,根据当前时钟滴答选定对应时钟槽,从链表头部开始迭代,对每个任务计算是否属于当前时钟周期,属于则执行,否则执行减一操作。
时钟引擎维持两个缓存定时任务的阻塞队列,一个用于接收外界提交的任务,另一个用于缓存主动取消的任务。引擎需要在滴答开始期间将它们装入对应的时钟槽或从中移除。
关键属性包括:timeouts和cancelledTimeouts(队列,用于缓存外界提交或取消的任务)、workerState(定时轮当前所处状态,如init、started、shutdown)、startTime(当前定时轮正式开始调度任务的时间)、ticks(滴答,步长为1的单调递增计数)、ticksDuration(滴答时长)、pendingTimeouts(当前定时轮实时任务剩余数)、n(时钟轮槽数)、mask(掩码)。
引擎内核——Worker,时钟引擎分为对外接口和调度运行两部分,可以想象内核是引擎的心脏起搏器,驱动定时轮运行,完成任务调度。实现上对应一个工作线程。
内核状态包括:状态机是关键组成部分,因此状态值控制对其至关重要。内核状态由定时轮维护管理,对外提供的接口都要借助它实现。初始时为init状态,当引擎被设计为不可复活时,不存在init/started/shutdown → init这样的迁移过程。
外部接口包括:start(用于定时轮开启引擎)、stop(完成定时轮引擎的关闭过程,返回未被处理的定时任务)、Timeout newTimeout(用于向引擎提交任务)。
调度运行:简单而言,就是周期性地执行滴答操作,包括相邻两个滴答周期的开始时间理论上等距,但结束时间会随该周期所需处理任务的数目及时长变化。因此,引擎剩下的休眠时间需要使用特定公式获得。
定时轮在dubbo中的应用:实际上,定时轮算法并不直接用于等周期性地执行某些提交任务,向其提交的任务只会到期执行一次。但具体应用中,会利用每次任务的执行,调用newTimeout()提交Timer所引用的当前任务,使其在若干单位时间后重新继续执行。
Dubbo中对定时轮的应用主要体现在以下几个方面:定时轮用单一的线程去管理触发Task的运行,Task执行期间,不能直接抛异常,否则会导致整个定时轮引擎崩溃而使得提交的后续任务无法执行。
周期任务:在dubbo中,每个连接被表征为一个Channel通道,dubbo节点间建立连接相互通信,单个节点需要维护和多个连入节点的连接。基本的步骤如下:
失败重试:网络情况的复杂多变性使得一件原本在单机上很轻易的事情,在分布式应用中,为确保某类型的操作能发生可能需要重试多次。