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的分析聊天源码app三个构造函数
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)是关键
垂直应用架构:
背景:访问量逐渐增大,通过将应用拆分成互不相干的boll传统指标源码几个应用以提升效率。
此时,用于加速前端页面开发的Web框架(MVC)是关键
分布式服务架构:
背景:垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来作为独立的 服务,成为稳定的服务中心,使得前端应用能更快速地响应多变的市场需求。
此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键
流动计算架构:
背景:服务越来越多,显现出小服务资源的浪费等问题,需要增加一个调度中心基 于访问压力实时管理集群容量,提高集群利用率
此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键
3) 什么是dubbo?它做些什么
Dubbo :Dubbo是阿里旗下的一个弹性的分布式服务框架,致力于提供高性能和 透明化的RPC远程服务调用方案,以及SOA服务治理方案。
作为RPC:支持各种传输协议
作为SOA:具有服务治理功能,提供服务的注册和发现!用zookeeper实现注册中 心!
4) 描述一个服务从发布到被消费的详细过程:
一个服务的发布暴露过程:
首先设置一个项目的别名,然后是定义注册中心和设定传输协议,之后定义服务名, 服务接口以jar形式导入到provider。一个服务发布暴露首先由spring的影视app系统源码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节点间建立连接相互通信,单个节点需要维护和多个连入节点的连接。基本的步骤如下:
失败重试:网络情况的复杂多变性使得一件原本在单机上很轻易的事情,在分布式应用中,为确保某类型的操作能发生可能需要重试多次。