首页 文章 精选 留言 我的

精选列表

搜索[计算机组成原理],共10002篇文章
优秀的个人博客,低调大师

Mybatis架构与原理

MyBatis功能架构设计 image.png 功能架构讲解: 我们把Mybatis的功能架构分为三层: (1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。 (2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。 (3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。 框架架构 框架架构讲解: 这张图从上往下看。MyBatis的初始化,会从mybatis-config.xml配置文件,解析构造成Configuration这个类,就是图中的红框。 (1)加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。 (2)SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。 (3)SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。 (4)结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。 MyBatis核心类 1、SqlSessionFactoryBuilder 每一个MyBatis的应用程序的入口是SqlSessionFactoryBuilder。 它的作用是通过XML配置文件创建Configuration对象(当然也可以在程序中自行创建),然后通过build方法创建SqlSessionFactory对象。没有必要每次访问Mybatis就创建一次SqlSessionFactoryBuilder,通常的做法是创建一个全局的对象就可以了。示例程序如下: private static SqlSessionFactoryBuilder sqlSessionFactoryBuilder; private static SqlSessionFactory sqlSessionFactory; private static void init() throws IOException { String resource = "mybatis-config.xml"; Reader reader = Resources.getResourceAsReader(resource); sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); sqlSessionFactory = sqlSessionFactoryBuilder.build(reader); } org.apache.ibatis.session.Configuration 是mybatis初始化的核心。 mybatis-config.xml中的配置,最后会解析xml成Configuration这个类。 SqlSessionFactoryBuilder根据传入的数据流(XML)生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例。 2、SqlSessionFactory对象由SqlSessionFactoryBuilder创建: 它的主要功能是创建SqlSession对象,和SqlSessionFactoryBuilder对象一样,没有必要每次访问Mybatis就创建一次SqlSessionFactory,通常的做法是创建一个全局的对象就可以了。SqlSessionFactory对象一个必要的属性是Configuration对象,它是保存Mybatis全局配置的一个配置对象,通常由SqlSessionFactoryBuilder从XML配置文件创建。这里给出一个简单的示例: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置别名 --> <typeAliases> <typeAlias type="org.iMybatis.abc.dao.UserDao" alias="UserDao" /> <typeAlias type="org.iMybatis.abc.dto.UserDto" alias="UserDto" /> </typeAliases> <!-- 配置环境变量 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/iMybatis?characterEncoding=GBK" /> <property name="username" value="iMybatis" /> <property name="password" value="iMybatis" /> </dataSource> </environment> </environments> <!-- 配置mappers --> <mappers> <mapper resource="org/iMybatis/abc/dao/UserDao.xml" /> </mappers> </configuration> 3、SqlSession SqlSession对象的主要功能是完成一次数据库的访问和结果的映射,它类似于数据库的session概念,由于不是线程安全的,所以SqlSession对象的作用域需限制方法内。SqlSession的默认实现类是DefaultSqlSession,它有两个必须配置的属性:Configuration和Executor。Configuration前文已经描述这里不再多说。SqlSession对数据库的操作都是通过Executor来完成的。 SqlSession :默认创建DefaultSqlSession 并且开启一级缓存,创建执行器 、赋值。 SqlSession有一个重要的方法getMapper,顾名思义,这个方式是用来获取Mapper对象的。什么是Mapper对象?根据Mybatis的官方手册,应用程序除了要初始并启动Mybatis之外,还需要定义一些接口,接口里定义访问数据库的方法,存放接口的包路径下需要放置同名的XML配置文件。 SqlSession的getMapper方法是联系应用程序和Mybatis纽带,应用程序访问getMapper时,Mybatis会根据传入的接口类型和对应的XML配置文件生成一个代理对象,这个代理对象就叫Mapper对象。应用程序获得Mapper对象后,就应该通过这个Mapper对象来访问Mybatis的SqlSession对象,这样就达到里插入到Mybatis流程的目的。 SqlSession session= sqlSessionFactory.openSession(); UserDao userDao = session.getMapper(UserDao.class); UserDto user = new UserDto(); user.setUsername("iMybatis"); List<UserDto> users = userDao.queryUsers(user); public interface UserDao { public List<UserDto> queryUsers(UserDto user) throws Exception; } <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.iMybatis.abc.dao.UserDao"> <select id="queryUsers" parameterType="UserDto" resultType="UserDto" useCache="false"> <![CDATA[ select * from t_user t where t.username = #{username} ]]> </select> </mapper> 4、Executor Executor对象在创建Configuration对象的时候创建,并且缓存在Configuration对象里。Executor对象的主要功能是调用StatementHandler访问数据库,并将查询结果存入缓存中(如果配置了缓存的话)。 5、StatementHandler StatementHandler是真正访问数据库的地方,并调用ResultSetHandler处理查询结果。 6、ResultSetHandler 处理查询结果。 MyBatis成员层次&职责 image.png SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能 Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护 StatementHandler 封装了JDBC Statement操作,负责对JDBCstatement的操作,如设置参数、将Statement结果集转换成List集合。 ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所需要的参数 ResultSetHandler *负责将JDBC返回的ResultSet结果集对象转换成List类型的集合; TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换 MappedStatement MappedStatement维护了一条<select|update|delete|insert>节点的封 SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回 BoundSql 表示动态生成的SQL语句以及相应的参数信息 Configuration MyBatis所有的配置信息都维持在Configuration对象之中

优秀的个人博客,低调大师

Spark Streaming 原理剖析

通过源码呈现 Spark Streaming 的底层机制。 1. 初始化与接收数据 Spark Streaming 通过分布在各个节点上的接收器缓存接收到的流数据并将流数 据 包 装 成 Spark 能 够 处 理 的 RDD的格式 输入到Spark Streaming 之 后由Spark Streaming将作业提交到Spark集群进行执行如图1所示。 图 1 Spark Streaming 执行模型 初始化的过程主要可以概括为两点 1调度器的初始化。 调度器调度 Spark Streaming 的运行用户可以通过配置相关参数进行调优。 2将输入流的接收器转化为 RDD 在集群进行分布式分配然后启动接收器集合中的每个接收器。针对不同的数据源 Spark Streaming 提供了不同的数据接收器分布在各个节点上的每个接收器可以认为是一个特定的进程接收一部分流数据作为输入。 用户也可以针对自身生产环境状况自定义开发相应的数据接收器。 如图 2 所示接收器分布在各个节点上。通过下面代码创建并行的、在不同Worker 节点分布的 receiver 集合。 val tempRDD = if (hasLocationPreferences) { val receiversWithPreferences = receivers.map(r => (r, Seq(r.preferredLocation.get))) ssc.sc.makeRDD[Receiver[_]](receiversWithPreferences) } else { // 在这里创造 RDD 相当于进入 SparkContext.makeRDD // 此处将 receivers 的集合作为一个 RDD 进行分区 RDD[Receiver] // 即使是只有一个输入流按照这个分布式也是流的输入端在 worker 而不再 Master … // 将 receivers 的集合打散然后启动它们 … ssc.sparkContext.runJob(tempRDD, startReceiver) … } 图 2 Spark Streaming 接收器 2. 数据接收与转化 在上面的“初始化与接收数据”部分中已经介绍过 receiver 集合转换为 RDD在集群上分布式地接收数据流。那么每个 receiver 是怎样接收并处理数据流的呢读者可以通过图 3对输入流的处理有一个全面的了解。图 3为 Spark Streaming 数据接收与转化的示意图。 图 3 的主要流程如下。 1数据缓冲在 receiver 的 receive 函数中接收流数据将接收到的数据源源不断地放入到 BlockGenerator.currentBuffer。 2缓冲数据转化为数据块在 BlockGenerator 中有一个定时器RecurringTimer将 当 前 缓 冲 区 中 的 数 据 以 用 户 定 义 的 时 间 间 隔 封 装 为 一 个 数 据 块 Block 放 入 到 BlockGenerator 的 blocksForPush 队列中这个队列。 3数据块转化为 Spark 数据块在 BlockGenerator 中有一个 BlockPushingThread线程不断地将 blocksForPush 队列中的块传递给 BlockManager让 BlockManager 将 数据存储为块。 BlockManager 负责 Spark 中的块管理。 4元数据存储在 pushArrayBuffer 方法中还会将已经由 BlockManager 存储的元数据信息例如 Block 的 id 号传递给 ReceiverTracker ReceiverTracker 会将存储的 blockId 放到对应 StreamId 的队列中。 图 3 Spark Streaming 数据接收与转化 图中部分组件的作用如下 KeepPushingBlocks调用此方法持续写入和保持数据块。 pushArrayBuffer调用 pushArrayBuffer 方法将数据块存储到 BlockManager 中。 reportPushedBlock存储完成后汇报数据块信息到主节点。 receivedBlockInfo Meta Data已经接收到的数据块元数据记录。 streamId数据流 Id。 BlockInfo数据块元数据信息。 BlockManager.put数据块存储器写入备份数据块到其他节点。 Receiver数据块接收器接收数据块。 BlockGenerator数据块生成器将数据缓存生成 Spark 能处理的数据块。 BlockGenerator.currentBuffer缓存网络接收的数据记录等待之后转换为 Spark的数据块。 BlockGenerator.blocksForPushing将一块连续数据记录暂存为数据块待后续转换为 Spark 能够处理的 BlockManager 中的数据块A Block As a BlockManager’sBlock)。 BlockGenerator.blockPushingThread守护线程负责将数据块转换为 BlockManager中数据块。 ReceiveTracker输入数据块的元数据管理器负责管理和记录数据块。 BlockManager Spark 数据块管理器负责数据块在内存或磁盘的管理。 RecurringTimer时间触发器每隔一定时间进行缓存数据的转换。 上面的过程中涉及最多的类就是 BlockGenerator在数据转化的过程中其扮演者不可或缺的角色。 private[streaming] class BlockGenerator( listener: BlockGeneratorListener, receiverId: Int, conf: SparkConf ) extends Logging 3. 生成 RDD 与提交 Spark Job Spark Streaming 根据时间段将数据切分为 RDD然后触发 RDD 的 Action 提交 Job Job 被 提 交 到 Job Manager 中 的 Job Queue 中 由 Job Scheduler 调 度 之 后Job Scheduler 将 Job 提交到 Spark 的 Job 调度器然后将 Job 转换为大量的任务分发给 Spark 集群执行如图 4 所示。 图4 Spark Streaming 调度模型 Job generator 中通过下面的方法生成 Job 进行调度和执行。 从下面的代码可以看出 job 是从 outputStream 中生成的然后再触发反向回溯执行 整个 DStream DAG类似 RDD 的机制。 private def generateJobs(time: Time) { SparkEnv.set(ssc.env) Try(graph.generateJobs(time)) match { case Success(jobs) => // 获取输入数据块的元数据信息 val receivedBlockInfo = graph.getReceiverInputStreams.map { stream => . . . }.toMap jobScheduler.submitJobSet(JobSet(time, jobs, receivedBlockInfo)) case Failure(e) => jobScheduler.reportError("Error generating jobs for time " + time, e) } eventActor !DoCheckpoint(time) } // 下 面 进 入 JobScheduler 的 submitJobSet 方 法 一 探 究 竟 JobScheduler 是 整 个 Spark Streaming 调度的核心组件 def submitJobSet(jobSet: JobSet) { . . . jobSets.put(jobSet.time, jobSet) jobSet.jobs.foreach(job => jobExecutor.execute(new JobHandler(job))) . . . } // 进入 Graph 生成 job 的方法 Graph 本质是 DStreamGraph 类生成的对象 final private[streaming] class DStreamGraph extends Serializable with Logging { def generateJobs(time: Time): Seq[Job] = { . . . private val inputStreams = new ArrayBuffer[InputDStream[_]]() private val outputStreams = new ArrayBuffer[DStream[_]]() . . . val jobs = this.synchronized { outputStreams.flatMap(outputStream => outputStream.generateJob(time)) . . . } // outputStreams 中的对象是 DStream下面进入 DStream 的 generateJob 一探究竟 private[streaming] def generateJob(time: Time): Option[Job] = { getOrCompute(time) match { case Some(rdd) => { val jobFunc = () => { val emptyFunc = { (iterator: Iterator[T]) => {} } // 此处相当于针对每个时间段生成的一个 RDD会调用 SparkContext 的方法 runJob 提交 Spark 的一 个 Job context.sparkContext.runJob(rdd, emptyFunc) } Some(new Job(time, jobFunc)) } case None => None } } // 在 DStream 算是父类一些具体的 DStream 例如 SocketInputStream 等的类的父类可以通过 SocketInputDStream 看是如何通过上面的 getOrCompute 生成 RDD 的 private[streaming] def getOrCompute(time: Time): Option[RDD[T]] = { generatedRDDs.get(time) match { . . . case None => { if (isTimeValid(time)) { // Dstream 是个父类这里代表的是子类的 compute 方法 DStream 通过 compute 调用用户自定 义函数。当任务执行时同一个 stage 中的 DStream 函数会串联依次执行 compute(time) match { . . . generatedRDDs.put(time, newRDD) . . . } 在 SocketInputDStream 的 compute 方法中生成了对应时间片的 RDD override def compute(validTime: Time): Option[RDD[T]] = { if (validTime >= graph.startTime) { val blockInfo = ssc.scheduler.receiverTracker.getReceivedBlockInfo(id) receivedBlockInfo(validTime) = blockInfo val blockIds = blockInfo.map(_.blockId.asInstanceOf[BlockId]) Some(new BlockRDD[T](ssc.sc, blockIds)) } else { Some(new BlockRDD[T](ssc.sc, Array[BlockId]())) } } Spark Streaming 在保证实时处理的要求下还能够保证高吞吐与容错性。用户的数据分析中很多情况下也存在需要分析图数据运行图算法通过 GraphX 可以简便地开发分布式图分析算法。 本文转自大数据躺过的坑博客园博客原文链接http://www.cnblogs.com/zlslch/p/5725374.html如需转载请自行联系原作者

优秀的个人博客,低调大师

LeakCanary内存检测原理

LeakCanary介绍 LeakCanary内存检测工具是由squar公司开源的著名项目。此项目主要用于内存检测。开源鲜明的目录结构如下: leakcanary | |-leakcanary-analyzer |-leakcanary-android |-leakcanary-android-no-op |-leakcanary-watcher |-leakcanary-simple leakcanary-android,leakcanary-android-no-op是针对android的检查封装。leakcanary-analyzer:用于分析dump文件leakcanary-watcher:用于监控内存泄露leakcanary-a

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册