副本机制
复制功能是 Kafka 架构的核心功能,在 Kafka 文档里面 Kafka 把自己描述为 一个分布式的、可分区的、可复制的提交日志服务。复制之所以这么关键,是因为消息的持久存储非常重要,这能够保证在主节点宕机后依旧能够保证 Kafka 高可用。副本机制也可以称为备份机制(Replication)
,通常指分布式系统在多台网络交互的机器上保存有相同的数据备份/拷贝。
Java 's Blog
复制功能是 Kafka 架构的核心功能,在 Kafka 文档里面 Kafka 把自己描述为 一个分布式的、可分区的、可复制的提交日志服务。复制之所以这么关键,是因为消息的持久存储非常重要,这能够保证在主节点宕机后依旧能够保证 Kafka 高可用。副本机制也可以称为备份机制(Replication)
,通常指分布式系统在多台网络交互的机器上保存有相同的数据备份/拷贝。
Docker 在当下很火,那么,当我们谈 Docker ,谈容器的时候,我们在谈什么? 或者说,你对 Docker ,对容器了解吗?容器,到底是怎么一回事儿?
前面理论性的知识是不是有点太“干货”,所以来点实战性的内容吧,这次记录了如何搭建高可用的 Flink
集群。
在正式配置前,来讲下为何要配置高可用(High Availability
)
目前越来越多公司的线上应用,都采用的是分布式架构(一主多从),从而避免单点故障引起的服务不可用。
而在 Flink
中,同样也有集群保障服务的高可用,任何时候都有一个主 JobManager
和多个备 JobManager
,当前主节点宕机后,立刻会有备 JobManager
当选成为主 JobManager
,接管集群,让每个作业继续正常进行。
出于这样的考虑,在 Flink
应用上线前,需要有这样一个集群来进行保障,下面就来看下该如何进行配置吧。
在逛 programcreek 的时候,我发现了一些专注基础但不容忽视的主题。比如说:Java 的可变参数究竟是怎么一回事?像这类灵魂拷问的主题,非常值得深入地研究一下。
限时 1 秒钟给出答案,来来来,听我口令:“Java 如何获取数组和字符串的长度?length 还是 length()?”
之前我讲了关于 HTTP 的安全性问题,本篇文章将会从一个朋友的面试题入手,来说一下关于 HTTP 的重定向和负载均衡。
如果你只追一个妹子并且对这个妹子特别用心的话,知道的人一定会说你是个好男人;如果你只是浅尝辄止并且追了大部分妹子的话,知道的人一定会骂你渣男。
之前的文章说了一下关于 Cookie 的内容,但是也就引出来了一些问题,比如 HTTP 是怎么进行安全处理的?来了,本文给大家讲述 HTTP 的安全问题。
Tomcat/Jetty 是目前比较流行的 Web 容器,两者接受请求之后都会转交给线程池处理,这样可以有效提高处理的能力与并发度。JDK 提高完整线程池实现,但是 Tomcat/Jetty 都没有直接使用。Jetty 采用自研方案,内部实现 QueuedThreadPool
线程池组件,而 Tomcat 采用扩展方案,踩在 JDK 线程池的肩膀上,扩展 JDK 原生线程池。
JDK 原生线程池可以说功能比较完善,使用也比较简单,那为何 Tomcat/Jetty 却不选择这个方案,反而自己去动手实现那?
前面讲了时间 Time
的概念和实际解决问题后,本篇来看下经常搭配使用的另一个关键工具:窗口 Window
。
窗口也有三种类型可供选择使用:
友情提示,本篇主要翻译自官网以及参考了 wuchong
大神的博客,内容比较干货,介绍这三种窗口的概念以及使用场景,希望看完能对 Flink
的窗口概念加深理解。
Kafka 系列的阶段性总结(万字长文,做好准备)
PriorityQueue 一个特殊的优先级队列,今天咱们一起来揭开它的面纱!
ArrayDeque 一个循环数组,诞生于 JDK 1.6,今天小编想和大家一起来揭开它的面纱!
前面的例子中有出现过 时间窗口 TimeWindow
这个词语,其实是两个概念,时间 Time
和窗口 Window
。
本篇文章比较干货,主要翻译自官网(参考资料一), 来讲下关于 Time
的学习、理解以及配套的概念 Watermark
。
Watermark
有两种译法:水位线、水印。由于个人暂时分不清,所以后面一律以英文 Watermark
出现
作为有追求的程序员,我们日常在写代码的时候往往都会运用很多奇技淫巧,不单单是为了炫耀我们的技术,更是为了追求更高的效率。了解局部性原理,可以有效的帮助我们理解和写出更好的代码,对于局部性原理可能有的小伙伴知道,有的小伙伴不知道,知道的小伙伴就当做复习知识点,不知道的小伙伴也没关系,接着往下看就知道了。
先来回顾一下 Flink
基础的三兄弟:
DataSource
Transaformation
DataSink
前面两篇笔记已经写了数据来源和转换如何使用,那么这篇当然就到了数据存储,接下来将会从以下角度介绍一下(喜闻乐见的 What
/ Why
/ How
)~:
Sink
DataSink
是什么不知不觉 JDK13 发布已有两个月,不知道各位有没有下载学习体验一番?每次下载安装之后,需要重新配置一下 Java 环境变量。等到运行平时的项目又需要切回之前 JDK 版本,这又需要重新环境变量。这么重复配置显然非常低效,又不能灵活切换版本。
所幸通过万能 Google 找到解决方案,使用 jenv 管理 JDK 版本。
说实话,之前面试都是直接去背诵的面试题,关于 Cookie 的一些内容,比如说,记录浏览器端的数据信息啦, Cookie 的生命周期啦,这些内容,也从来没有研究过 Cookie 到底是怎么工作的,今天我们就来了解一下 Cookie 到底是怎么工作的。
日常开发过程有时需要在应用启动之后加载某些资源,或者在应用关闭之前释放资源。Spring 框架提供相关功能,围绕 Spring Bean
生命周期,可以在 Bean
创建过程初始化资源,以及销毁 Bean
过程释放资源。Spring 提供多种不同的方式初始化/销毁 Bean
,如果同时使用这几种方式,Spring 如何处理这几者之间的顺序?
有没有觉得标题很熟悉,没错标题模仿二哥 「@沉默王二」 文章羞,Java 字符串拼接竟然有这么多姿势。
首先我们先来回顾一下 Spring 初始化/销毁 Bean
几种方式,分别为:
init-method/destroy-method
InitializingBean/DisposableBean
@PostConstruct/@PreDestroy
ContextStartedEvent/ContextClosedEvent
PS: 其实还有一种方式,就是继承 Spring
Lifecycle
接口。不过这种方式比较繁琐,这里就不再分析。
这种方式在配置文件文件指定初始化/销毁方法。XML 配置如下
1 |
|
或者也可以使用注解方式配置:
1 |
|
还记得刚开始接触学习 Spring 框架,使用就是这种方式。
这种方式需要继承 Spring 接口 InitializingBean/DisposableBean
,其中 InitializingBean
用于初始化动作,而 DisposableBean
用于销毁之前清理动作。使用方式如下:
1 |
|
这种方式相对于上面两种方式来说,使用方式最简单,只需要在相应的方法上使用注解即可。使用方式如下:
1 |
|
这里踩过一个坑,如果使用 JDK9 之后版本 ,
@PostConstruct/@PreDestroy
需要使用 maven 单独引入javax.annotation-api
,否者注解不会生效。
这种方式使用 Spring 事件机制,日常业务开发比较少见,常用与框架集成中。Spring 启动之后将会发送 ContextStartedEvent
事件,而关闭之前将会发送 ContextClosedEvent
事件。我们需要继承 Spring ApplicationListener
才能监听以上两种事件。
1 |
|
也可以使用 @EventListener
注解,使用方式如下:
1 |
|
PS:只有调用
ApplicationContext#start
才会发送ContextStartedEvent
。若不想这么麻烦,可以监听ContextRefreshedEvent
事件代替。一旦 Spring 容器初始化完成,就会发送ContextRefreshedEvent
。
回顾完上面几种方式,这里我们综合使用上面的四种方式,来看下 Spring 内部的处理顺序。在看结果之前,各位读者大人可以猜测下这几种方式的执行顺序。
1 |
|
xml 配置方式如下:
1 |
|
应用启动方法如下:
1 |
|
程序输出结果如下所示:
最后采用图示说明总结以上结果:
不知道各位读者有没有猜对这几种方式的执行顺序,下面我们就从源码角度解析 Spring 内部处理的顺序。
使用 ClassPathXmlApplicationContext
启动 Spring 容器,将会调用 refresh
方法初始化容器。初始化过程将会创建 Bean
。最后当一切准备完毕,将会发送 ContextRefreshedEvent
。当容器初始化完毕,调用 context.start()
就发送 ContextStartedEvent
事件。
refresh
方法源码如下:
1 |
|
一路跟踪 finishBeanFactoryInitialization
源码,直到 AbstractAutowireCapableBeanFactory#initializeBean
,源码如下:
1 |
|
BeanPostProcessor
将会起着拦截器的作用,一旦 Bean 符合条件,将会执行一些处理。这里带有 @PostConstruct
注解的 Bean
都将会被 CommonAnnotationBeanPostProcessor
类拦截,内部将会触发 @PostConstruct
标注的方法。
接着执行 invokeInitMethods
,方法如下:
1 |
|
如果 Bean
继承 InitializingBean
接口,将会执行 afterPropertiesSet
方法,另外如果在 XML 中指定了 init-method
,也将会触发。
上面源码其实都是围绕着 Bean
创建的过程,当所有 Bean
创建完成之后,调用 context#start
将会发送 ContextStartedEvent
。这里源码比较简单,如下:
1 |
|
调用 ClassPathXmlApplicationContext#close
方法将会关闭容器,具体逻辑将会在 doClose
方法执行。
doClose
这个方法首先发送 ContextClosedEvent
,然再后开始销毁 Bean
。
灵魂拷问:如果我们颠倒上面两者顺序,结果会一样吗?
doClose
源码如下:
1 |
|
destroyBeans
最终将会执行 DisposableBeanAdapter#destroy
,@PreDestroy
、DisposableBean
、destroy-method
三者定义的方法都将会在内部被执行。
首先执行 DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
,这里方法类似与上面 BeanPostProcessor
。
@PreDestroy
注解将会被 CommonAnnotationBeanPostProcessor
拦截,这里类同时也继承了 DestructionAwareBeanPostProcessor
。
最后如果 Bean
为 DisposableBean
的子类,将会执行 destroy
方法,如果在 xml 定义了 destroy-method
方法,该方法也会被执行。
1 |
|
init-method/destroy-method
这种方式需要使用 XML 配置文件或单独注解配置类,相对来说比较繁琐。而InitializingBean/DisposableBean
这种方式需要单独继承 Spring 的接口实现相关方法。@PostConstruct/@PreDestroy
这种注解方式使用方式简单,代码清晰,比较推荐使用这种方式。
另外 ContextStartedEvent/ContextClosedEvent
这种方式比较适合在一些集成框架使用,比如 Dubbo 2.6.X 优雅停机就是用改机制。
六、Spring 历史文章推荐
1、Spring 注解编程之注解属性别名与覆盖
2、Spring 注解编程之 AnnotationMetadata
3、Spring 注解编程之模式注解
4、缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制