㈠ Netty源码笔记
Netty版本4.0.29.Final,以构造客户端连接服务端的角度来追踪源码
NioEventLoopGroup的构造器中会调用父类MultithreadEventLoopGroup的构造器
在父类MultithreadEventExecutorGroup的构造器中
上面已经完成了NioEventLoop的创建,并保存在NioEventLoopGroup的数组属性上。而关于NioEventLoop在具备线程池能力时,在何时启动已经创建的线程呢?在SingleThreadEventExecutor::execute中可以找到答案
而关于NioEventLoop内线程启动后的逻辑,可以在创建该线程时看到有向线程提交一个任务。根据任务内SingleThreadEventExecutor.this.run()可以定位到创建线程提交任务的NioEventLoop::run
NioEventLoop::run是Netty的核心所在。它是NioEventLoop的线程执行的唯一任务。方法内无限循环,阻塞等待IO事件或队列中的任务
关于队列中的任务从哪里来?一是源码内部,启动时会直接提交任务到队列中;二是可以直接取出channel中的NioEventLoop向其提交任务;三是使用Channel写数据时,都是以任务的形式提交到队列中。与Channel绑定的NioEventLoop循环时会消费提交到队列中任务并执行,后续在分析unsafe时会一并提及。
客户端Bootstrap配置引导时,需要指定Channel类型,后续会使用反射创建该Channel类型实例。
客户端完成NioEventLoopGroup和Bootstrap的创建及对Bootstrap进行相关的设置后,使用Bootstrap尝试与服务端建立连接。在同步与异步之间,推荐使用异步来监听连接事件的结果。
AbstractBootstrap是Bootstrap的父类,initAndRegister中完成了通道的创建、初始化以及注册
group()取到开始配置的NioEventLoopGroup,register在父类MultithreadEventLoopGroup中
虽说暂时不引入unsafe类逻辑,但在unsafe::register中,通道注册完成后会调用管道的fireChannelRegistered方法,进而执行自定义的ChannelInitializer,最终形成完整的管道。
目前管道上链表有三个节点:head、自定义ChannelInitializer、tail(规定自头向尾的流向为In,自尾向头的为Out)
它们的类型都是AbstractChannelHandlerContext,每个Context上都持有对应的ChannelHandler
至此,Channel的初始化及注册已经完成。回到Bootstrap::doConnect中,在Channel注册这个异步任务完成后,开始真正的连接服务端
channel连接建立完成后,可以使用channel写数据
写数据不会直接发送数据到服务端,而是会缓存起来,直到调用flush才会完成数据的最终发送。flush逻辑与写数据一样,channel到pipeline,tail到head。最终交由unsafe类来完成
以客户端建立连接发送数据流程为例(服务端大部分逻辑相似),总结Netty的工作流程:
㈡ mmdetection源码阅读笔记(2)--Loss
之前写了模型和网络的创建,这里就主要写下训练过程中具体的loss,主要分为以下几部分
rpn_loss 的实现具体定义在 mmdet/models/anchor_head/rpn_head.py
具体的计算方式定义在其父类 mmdet/models/anchor_heads/anchor_head.py ,主要是 loss 和 loss_single 两个函数。
先看 loss 函数
这个主要做了两个事
首先,在此时 rpn 的输出为 feature map 中每个位置的 anchor 分类的 score 以及该 anchor 的 bbox 的修正值,我们要通过和 gt 计算 loss 来优化我们的网络,但是我们的gt是一堆人工标注的 bbox ,无法直接计算 loss 。所以,我们应该要先获取到 anchor 然后将这些 anchor 和 gt 对比在分别得到正负样本以及对应的 target ,之后我们才能计算得到 loss 。
所以第一步通过 anchor_list, valid_flag_list = self.get_anchors(featmap_sizes, img_metas) 获取到了所有的 anchor 以及一个 是否有效的 flag (根据bbox是否超出图片边界来计算。)
拿到了所有的 anchor 之后就是和gt对比来区分正负样本以及生成 label 了,通过定义在 mmdet/core/anchor/anchor_target.py 的 anchor_target() 实现。
在这个函数中调用 assigner 将 anchor 和 gt 关联起来,得到 正样本 和 负样本 ,并用 sampler 将这些结果进行封装,方便之后使用。
得到了 target 过后,就是计算 loss 了,在 self.loss_single 中,
这里用的 loss 就是常见的 CrossEntropyLoss 和 SmoothL1Loss
之前的 rpn_loss 是对候选框的第一次修正,这里的 bbox_loss 就是第二次修正了,两者的实际差别主要体现在分类上,在 rpn 阶段只分两类(前景和背景),这里分类数为 N+1 (真实类别+背景)
具体定义在 mmdet/models/bbox_heads/bbox_head.py
可以看到和 rpn loss 相比,这里要简单很多,因为这里只包含了 rpn loss 中实际计算 loss 的部分,但是他也同样需要 rpn 中的 assign 和 sample 操作,两者的区别只是 assign 的输入不同, rpn 的 assign 输入是该图所有的 anchor , bbox 部分 assign 的输入就是 rpn 的输出。这里的 loss 和 rpn 中的计算方式完全一样,就不在赘述了。
mask 部分计算 loss 之前也有一个获取 target 的步骤。
mmdet/models/mask_heads/fcn_mask_head.py
这里获取 target 相对之前来说就要简单点了,通过定义在 mmdet/core/mask/mask_target.py 的 mask_target() 取到和 prooisals 相同大小的 mask 就行了。
而 loss 部分也比较简单,也是用的 CrossEntropyLoss 。
总的来说这些 loss 还是算比较好理解的,看起来有三部分的 loss ,但是实际上每个部分的都差不多。
下一篇就准备写下整个的训练流程了,相当于将前面这三篇给连起来,有个更具体的理解。
㈢ consul源码笔记
从本周开始对consul的源码做一个简单的阅读和了解,希望能持续下去吧。
consul是使用go编写的,在阅读过程中可能会涉及到对go语法的相关笔记,这个会分开两个系列文章去更新。
1.agent的定义(agent.go)
agent是一个常驻进程部署在所有机器上,可以分client和server两种模式运行(client模式只负责转发请求,轻量级)。
config为agent的配置,包括nodeID等核心的配置都在里
delegate为Server或者client的对象,取决于进程启动选择的方式
2.程序入口(agent.go)
这里主要是构建一个Server或者client的对象,我们接下来看一个server对象是如何构建的
我们看下server对象的重要属性,跟raft协议相关的对象封装在这里。
这里做的事情很简单,启动服务器并输出日志。
最后我们看起构建一个raft对象具体做了哪些事情。
㈣ springboot 2.x源码笔记- 配置文件加载 ConfigFileApplicationListener
至此,整个environment的配置加载过程就完成了。
配置项的注入是要等到bean的实例化后初始化阶段, 参考这里 .实例化后,会通过org.springframework.beans.factory.annotation.#postProcessProperties-->org.springframework.beans.factory.annotation.InjectionMetadata#inject--->org.springframework.beans.factory.annotation..AutowiredMethodElement#inject--->org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency--->org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
总结:
㈤ 源码修炼笔记之Dubbo线程池策略
FixedThreadPool
FixThreadPool内部是通过ThreadPoolExecutor来创建线程,核心线程数和最大线程数都是上下文中指定的线程数量threads,因为不存在空闲线程所以keepAliveTime为0,
当queues=0,创建SynchronousQueue阻塞队列;
当queues<0,创建无界的阻塞队列LinkedBlockingQueue;
当queues>0,创建有界的阻塞队列LinkedBlockingQueue。
采用bbo自己实现的线程工厂NamedInternalThreadFactory,将线程置为守护线程(Demon)
拒绝策略为AbortPolicyWithReport,策略为将调用时的堆栈信息保存到本地文件中,并抛出异常RejectedExecutionException
CachedThreadPool
CachedThreadPool与FixedThreadPool的区别是核心线程数和最大线程数不相等,通过alive来控制空闲线程的释放
LimitedThreadPool
LimitedThreadPool与CachedThreadPool的区别是空闲线程的超时时间为Long.MAX_VALUE,相当于线程数量不会动态变化了,创建的线程不会被释放。
EagerThreadPool
与上述三种线程池不同,EagerThreadPool并非通过JUC中的ThreadPoolExecutor来创建线程池,而是通过EagerThreadPoolExecutor来创建线程池,EagerThreadPoolExecutor继承自ThreadPoolExecutor,实现自定义的execute方法,采用的阻塞队列是TaskQueue,TaskQueue继承自LinkedBlockingQueue。
execute方法首先调用ThreadPoolExecutor的execute方法,如果执行失败会重新放入TaskQueue进行重试。
实现自定义的ThreadPool
ThreadPool被定义为一个扩展点,如下所示,
其默认实现是FixedThreadPool,可以通过实现该扩展来实现自定义的线程池策略。
㈥ String类源码笔记(一):成员变量和构造器
String类表示字符串,所有类似"abc"形式的字符串(或魔法字符串)都被看作是这个类的实例。String是不可变的,当一个字符串在常量池中被创建时,他的值就不会被改变。
所在路径:javalangString.java
为了保证String类是一个不可变类,String类的成员变量多为私有和不可变的。
其中serialPersistentFields在序列化时使用:
JDK8的String类一共有16个构造器,其中两个是@Deprecated,一个是私有构造器,剩下的13个是可以调用的。
无参构造器直接将""的value赋值给当前类的value。""的value是一个空的char[],其length为0。调用使用了无参构造器的String对象的isEmpty()方法会得到true,调用length()方法会得到0,判断其==null会得到false。
入参为String对象时,构造器会对其进行直接取值。
入参为字符串数组时,构造器调用的是Arrays.Of()方法。
其中Arrays.Of()方法是为了将入参的字符串序列深拷贝到this.valuie中,他的源码为:
其中System.array()方法的源码为:
这个方法支持直接传入想要生成的String的母串,通过偏移量和有效长度找出需要赋值给this.value的部分,然后调用Arrays.OfRange()方法进行深拷贝。
当需要将一个Unicode编码序列转换为String时,可以使用以下构造器:
当需要将一个bytes[]转换为String时,可以使用以下构造器:
此外,还有一些将上述构造器进一步封装的构造器,其本质都是简化入参。另外,String类的构造器同样支持传入StringBuffer和StringBuilder。如果传入的是StringBuffer,构造器会为其加锁。如果传入的是StringBuilder则不会加锁。
事实上,StringBuffer和StringBuilder的toString()方法调用的也是String类的构造器,他们最终的底层实现都是Arrays.Of()。
最后,String类还提供了一个保护类型的构造方法。该方法相比入参为char[]的构造器多了一个share参数,这个参数并没有实际作用,只是用来和其他构造器进行区分。当String类内部调用该构造器时:
该构造器不能对外暴露的原因是需要保持String类的不可变性。
㈦ Eureka源码浅读---自我保护机制
Eureka源码采用1.7.2版本
本人小白,此文为本人阅读源码笔记,如果您读到本文,您需要自己甄别是否正确,文中的说明只代表本人理解,不一定是正确的!!!
自我保护机制设计的初衷是防止服务注册服务因为本地网络故障,长时间未接受到心跳请求,造成错误的移除大量服务实例,其实调用服务还是可用的
自我保护机制是和自动故障移除联系在一起的,针对的移除实例也是自动故障移除
在服务故障移除的方法中有这样一个判断,当返回false时候,直接返回,不进行故障实例的摘除
进入该方法
关于获取上一分钟心跳总数,Eureka Server内部采用的是定时线程进行统计,使用两个AtomicLong进行保存当前和上一分钟的心跳总数
该方法初始化了运行了定时调度的线程进行统计,默认执行间隔为1min,执行流程:
那么当前的心跳总数是怎么计算的呢,直接看心跳的renew()方法,是否嵌入了计数器累计操作
如上所示,当接收到心跳时,当前心跳计数器进行了递增操作
而getNumOfRenewsInLastMin()获取上一分钟心跳总数就是获取lastBucket数量,再找下该定时任务启动的入口
和自动故障移除的定时同时启动的,那么lastBucket代表了上一分钟的心跳总数
接下来,我们需要看看期望每分钟最小心跳总数的由来:
numberOfRenewsPerMinThreshold最开始的初始化计算是在Eureka Server初始化计算的,使用当前Server拉取到的服务实例总数 * 0.85
在openForTraffic()方法中使用初始化拉取的服务实例总数作为基数标准进行计算,(int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold()) -> count * 2 * 0.85,
集群模式下,count为其他节点中已注册的服务实例总数,单节点就为0
下面我们看看在注册中心接收到注册,下线等请求执行时,维护numberOfRenewsPerMinThreshold
注册,当前实例数量+2,下线,当前实例数量-2,然后再次*0.85,计算期望每分钟最小心跳数
在Eureka Server中有专门的定时任务进行更新numberOfRenewsPerMinThreshold,默认每15min执行一次
主要流程如下:
注意,自动服务故障移除没有进行numberOfRenewsPerMinThreshold的更新
<font color= 'blue'>服务故障实例的摘除需要判断当前是否处于自我保护模式,而自我保护模式的默认是开启(isSelfPreservationModeEnabled),需要判断上一分钟的心跳总数是否大于期望每分钟最小心跳数,如果在15分钟内,累计丢失了15%以上的节点心跳,那么Eureka Server就会认为当前所处的网络环境异常,从而处于自动保护模式,故障实例将不会移除,再等待15min后,进行expectedNumberOfRenewsPerMin的基于当前服务实例的重新计算后,自我保护模式才会关闭!</font>
自我保护服务开启模拟:
㈧ InteractionManager源码阅读笔记
InteractionManager的直接翻译是交互管理器,在react-native的文档里,其作用描述为:"Interactionmanager 可以将一些耗时较长的工作安排到所有互动或动画完成之后再进行。这样可以保证 JavaScript 动画的流畅运行。"
我们最常用这个类的场景是:从A页面跳转到B页面,然后想让B页面的网络请求或者页面刷新工作放到过场动画结束后再去做,这时可以用InteractionManager的runAfterInteractions函数来实现。
那么问题来了,如果A页面有一个循环不停止的动画,这时候再跳转B页面,B页面为了转场动画的顺畅使用了runAfterInteractions,但由于A页面的循环动画而永远无法进入回调闭包,这个问题怎么解决呢?
react-native官方已经考虑到了,在Animated类里面的 loop 方法,有这么一段话:"In addition, loops can prevent VirtualizedList-based components from rendering more rows while the animation is running. You can pass isInteraction: false in the child animation config to fix this."。虽然这段话不是在解决我们说的问题,但是所提到的isInteraction属性,我们可以通过Animated的源码,看到这个属性是用来干嘛的。
由此可见,这个isInteraction属性是用来控制是否创建句柄的。
那么关键的来了,InteractionManager.createInteractionHandle()和InteractionManager.runAfterInteractions()之间的具体关系是什么样的呢?
我们可以看看InteractionManager的 源码 :
先看这一对函数:
一个是创建句柄一个是清除句柄,其实创建句柄很简单,所谓的句柄就是全局变量_inc自增后的结果,然后加入了_addInteractionSet的集合。清除句柄,就是把handle从_addInteractionSet中移除,加入_deleteInteractionSet。那么整个InteractionManager是如何运作起来的呢?runAfterInteractions中的回调是如何被调用的?这其实最核心的部分是在_scheleUpdate里面:
_scheleUpdate主要是处理了InteractionManager的deadline,然后调用了_processUpdate:
如上图所示,第一个红框里面其实就是InteractionManager最核心的部分。还记得刚才的createInteractionHandle和clearInteractionHandle么,其实整个InteractionManager就是实现了生产者消费者模型。第二个红框部分,其实就是去执行runAfterInteractions里面的闭包回调。我们最后再看runAfterInteractions:
参数task最终加入了_taskQueue中,而这个_taskQueue会在_processUpdate中被遍历执行。
用一个很通俗易懂的方法来解释InteractionManager,比如我们去面馆吃面,跟老板说我要一碗面(createInteractionHandle),然后我们就找个位子等老板把面端上来了(runAfterInteractions(()=>{console.log('吃面')})),过了一阵子老板面做好了,于是端上面(clearInteractionHandle),我们就吃到面了。那么又有个疑问了,为啥InteractionManager要设计的这么复杂呢?直接存一下回调,然后触发回调不就好了么。这就好比我们出去吃面,不可能吃光面一样。比如我们要吃一碗雪菜肉丝面,那么流程就得这样了:
最后,我们看一下react-native里面会有哪些地方默默的为我们createInteractionHandle呢?
一共就这两处,第一处就是创建动画的时候,官方文档上也说了可以用来延迟耗时操作,保证转场动画流畅。第二处是这个 PanResponder ,官方文档也做了解释,保证手势响应顺畅。但其实大家在理解了上面吃面的例子后,也可以扩展一下自己的思维,灵活的运用InteractionManager。
㈨ MyBatisPlus快速入门源码笔记共享,拿走吧你
为什么要学习它呢?
答:MyBatisPlus可以节省我们大量工作时间,所有的CRUD代码它都可以自动化完成!
优点:
1. 易于上手和掌握。
2. sql写在xml里,便于统一管理和优化。
3. 解除sql与程序代码的耦合。
4. 提供映射标签,支持对象与数据库的orm字段关系映射
5. 提供对象关系映射标签,支持对象关系组建维护
6. 提供xml标签,支持编写动态sql。
MyBatis Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
特性:
无侵入 :只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
损耗小 :启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 强大的 CRUD 操作 :内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 支持 Lambda 形式调用 :通过 Lambda 表达式,方便地编写各类查询条件,无需再担心字段写错。
支持组件自动生成 :支持多达 4 种主键策略(内含分布式唯一 ID 生成器 Sequence),可自由配置,完美解决主键问题。
支持 ActiveRecord 模式 :支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作。
支持自定义全局通用操作 :支持全局通用方法注入( Write once, use anywhere )。
内置代码生成器 :采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用 内置分页插件 :基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List查询。
分页插件支持多种数据库 :支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库。
内置性能分析插件 :可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询。
内置全局拦截插件 :提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作。
1.1 、引入mybatis-plus相关maven依赖
引入mybatis-plus在spring boot中的场景启动器
ps:切记不可再在pom.xml文件中引入mybatis与mybatis-spring的maven依赖,这一点,mybatis-plus的官方文档中已经说明的很清楚了.
1.2、创建数据表
(1)SQL语句
(2) 数据表结构
1.3、 创建java bean
根据数据表新建相关实体类
1 package com.example.demo.pojo
1.4、 配置application.proprties
数据源使用druid
ps:在进行crud实验之前,简单对mybatis与mybatis-plus做一个简单的对比
2.1、mybatis与mybatis-plus实现方式对比
(1)提出问题: 假设我们已存在一张 tbl_employee 表,且已有对应的实体类 Employee,实现 tbl_employee 表的 CRUD操作我们需要做什么呢?
(2)实现方式: 基于 Mybatis 需要编写 EmployeeMapper 接口,并手动编写 CRUD 方法 提供 EmployeeMapper.xml 映射文件,并手动编写每个方法对应的 SQL 语句. 基于 Mybatis-plus 只需要创建 EmployeeMapper 接口, 并继承BaseMapper 接口.这就是使用 mybatis-plus 需要完成的所有操作,甚至不需要创建 SQL 映射文件。
2.2、BaseMapper接口介绍
(1)如何理解核心接口BaseMapper?
在使用Mybatis-Plus时,核心操作类是BaseMapper接口,其最终也是利用的Mybatis接口编程的实现机制,其默认提供了一系列的增删改查的基础方法,并且开发人员对于这些基础操作不需要写SQL进行处理操作(Mybatis提供的机制就是需要开发人员在mapper.xml中提供sql语句),那样我们可以猜测肯定是Mybatis-Plus完成了BaseMapper接口提供的方法的SQL语句的生成操作。
(2)BaseMapper接口为我们定义了哪些方法?
BaseMapper接口源码:
(3) mybatis-plus中常用的注解 1
由于我们的数据表名于实体类的类名不一致,并且实体类于数据表还存在字段名不对应的情况,因此我们需要引入mybatis-plus的注解.
编写EmployeeMapper接口继承BaseMapper接口
准备考试环境:
(1)插入
(2)修改
控制台打印出的sql语句
如果我们不设置实体类的email与gender属性,结果是怎样的呢?
控制台sql语句:
显然,mybatis-plus为我们做了非空判断,空值的话,默认不更新对应的字段.想一想,这是不是类似于mybatis中的动态sql呢?
这种处理效果又会带来什么好处呢?
(3)查询
selectById方法
selectBatchIds方法
ps:发现该方法底层使用的竟然是sql的in关键字
selectByMap方法
(4)删除
3、不得不提的条件构造器---Wrapper
3.1.wrapper及其子类介绍
(1)Wrapper :条件构造抽象类,最顶端父类,抽象类中提供3个方法以及其他方法.
㈩ suricata源码笔记:main函数
main()函数位于suricata.c文件,其主要流程如下:
24. 执行PostConfLoadedSetup,即运行那些需要在 配置载入完成 后就立马执行的函数。这里面涉及的流程和函数非常多: