Java Flight Recordings (JFR) — Java 飞行记录器 – part 1

一、JFR 飞行记录器

Java Flight Recorder(JFR)是一个商业特性,用在生产服务器上是需要商业许可的。

JFR 记录了关于 Java 运行时及运行在其内的 Java 应用程序的详细信息,记录用少量的开销完成。数据是作为时间上的数据点(称为事件)记录的。典型的事件可以是线程等待锁、GC、CPU 周期使用数据等。

在创建飞行记录时,你可以选择哪些事件应当保存,这叫做记录模板。有些模板只保存基本事件,对性能几乎没有影响。其他模板可能有轻微的性能开销,还可能触发 GC 来收集更多信息。通常,超过百分之几的开销是很罕见。

飞行记录可用于调试很大范围的问题,从性能问题到内存泄漏或严重的锁竞争。

1、记录类型

1.1、连续录制

一个连续记录是指记录总是开着并保存,例如,过去六小时的数据。如果应用程序陷入问题,你可以转储(dump)这些数据,例如,从过去一小时的,看看出生问题时发生了什么。

连续记录的默认设置是使用记录 profile,开销极低。这个 profile 不收集堆统计信息或(内存)分配性能分析,但仍然收集了很多有用数据。

保持持续录制一直允许是很好的,对于调试非常罕见的问题时非常有用。记录可以用 jcmdJMC 手工转储。你也可以在 JMC 里设置触发器在一些特定的条件被满足后转储飞行记录。

1.2、性能分析录制

性能分析录制是指记录开着,运行一定时间,然后停止。通常, 性能分析录制允许更多的时间,有可能对性能有较大的冲击。被开启的时间可以被修改,取决于你的 性能分析录制。

使用 性能分析录制 的典型场景如下:

  • 剖析运行最频繁的方法和创建对象最多的地方。
  • 查找使用了越来越多内存的类,暗示着内存泄漏。
  • 查找因为同步而导致的瓶颈,更多类型的情景。

性能分析录制 将给出很多信息,即使你不是在定位特定的问题。 性能分析录制 将给你关于应用程序的很好的视图,可以帮助你找出瓶颈或需要提高的地方。

继续阅读

Akka 文档:督导与监控

翻译自:http://doc.akka.io/docs/akka/2.3.8/general/supervision.html

督导与监控

本章大概描述督导背后的概念,提供的原语和语义。如何转换成实际代码的细节请参考相关的 Scala 和 Java API。

督导意味着什么

Actor 系统 里描述的,督导描述了 actors 之间的依赖关系:督导者委托任务给下属,因此必须为它们的失败负责。当一个下属检测到一个失败(例如抛出异常),它挂起自己和它的所有下属,它的督导者发送一个提示失败的消息。取决于被督导的工作的性质和失败的性质,督导者有下面四个选择:

  1. 让下属继续执行,保持当前的内部状态;
  2. 重启下属,清理它的当前内部状态;
  3. 永久停止下属;
  4. 向上传递失败,使自己失败。

总是把一个 actor 当作督导层级的一部分是很重要的,这解释了四种选择的存在(一个督导者也是另一个更高级的督导者的下属),且前三者暗示着:让一个 actor 继续执行将执行它的所有下属,重启一个 actor 将重启它的所有下属(但有更多细节见下面),相似地,终止一个 actor 将终止它的所有下属。要注意的是,Actor 类的 preRestart 钩子的默认行为是在重启前终止它的所有下属,但这个钩子可以被覆写;在这个钩子被执行后,递归重启被应用到所有的孩子上。

每个督导者配置了一个函数来翻译所有可能的失败原因(如异常)到上面给出的四种选择之一;注意,这个函数不接受失败的 actor 本身作为输入。很容易提出一种示例结构让这个看起来不够灵活,例如,希望为不同的下属应用不同的策略。此刻,明白督导是形成递归的失败处理结构是很重要的。如果你尝试在某一层做太多,将难以理解,因此,对这种情况推荐的方式是添加一个督导层级。

继续阅读

Akka 文档: Actor 是什么?

翻译自:http://doc.akka.io/docs/akka/2.3.8/general/actors.html

Actor 是什么?

前一章节关于 Actor 系统 解释了 actors 如何组成层级,且是构建应用的最小单元。这章只关注这样的 actor,解释你在实现它时会遇到的概念。更多深入细节的参考见 Actors(Scala)Untyped Actors(Java)

一个Actor是一个容器,它包含了 状态行为,一个 邮箱,子Actors 和一个 督导策略。所有这些封装在一个 Actor 引用 后面。当 Actor 终止时,会发生 这个

Actor 引用

一个 actor 对象需要与外界隔离开才能从 actor 模型中获益。所以 actor 是以 actor 引用的形式展现给外界的,actor 引用可以被自由的无限制地传来传去。内部对象和外部对象的这种划分使得所有想要的操作能够透明:重启 actor 而不需要更新别处的引用,将实际 actor 对象放置到远程主机上,向另外一个完全不同的应用程序的 actors 发送消息。但最重要的方面是从外界不可能访问到 actor 对象的内部状态,除非这个 actor 非常不明智地将信息(内部状态)公布出去。

继续阅读

Akka 文档:Actor 系统

翻译自:http://doc.akka.io/docs/akka/2.3.8/general/actor-systems.html

Actor 系统

Actor 是封装状态和行为的对象,他们的唯一通讯方式是交换消息,交换的消息存放在接收方的邮箱里。从某种意义上来说,actor 是面向对象编程的最严格的形式,但是最好把它们看作一些人:在使用 actor 来建模解决方案时,把 actor 想象成一群人,把子任务分配给他们,将他们的功能整理成一个有组织的结构,考虑如何将失败逐级上传(受益于不实际与人交互,意思是我们不需要担心他们的情绪状态或精神问题)。这样的结果就可以在脑中形成进行软件实现的框架。这个结果可以作为构建软件实现的脚手架。

注意:一个 ActorSystem 是一个重量级结构,会分配 1....N 个线程,所以每个逻辑应用创建一个即可。

分层的结构

像在一个经济组织里,actors 天生地形成层级。一个 actor,整天上看是程序的一个功能,可能想把它的任务分割成更小的,更容易管理的分块。为了这个目的,它启动由它督导(supervise)的子 actors。督导的细节将在这里 解释,我们先专注于本章的底层概念。唯一的先决条件是知道每个 actor 有且只有一个督导者,也就是创建它的 actor。

actor 系统的精髓是任务被分割和委托,直到足够小可以完整地处理。按这样做,不止任务本身被清晰地分出结构,而且使 actors 对它们应该处理什么消息、如何响应(react)和如何处理失败 更合理(译注:按这个三个角度来组织)。如果一个 actor 没法处理某个特定情况,它可以发送一个对应的失败消息给它的督导者,寻求帮助。递归的结构允许在正确的层级处理失败。

可以将这与分层的设计方法进行比较。分层的设计方法最终很容易形成防护性编程,以防止任何失败被泄露出来。把问题交由正确的人处理会是比将所有的事情“藏在深处”更好的解决方案。

继续阅读

Akka 文档:术语、概念

翻译自:http://doc.akka.io/docs/akka/2.3.8/general/terminology.html

术语、概念

在本章,我们尝试建立一个通用的术语来定义一个坚固的基础,用于沟通 Akka 致力于的关于并发、分布式系统。请注意,对于很多这些术语,没有单一的认同的定义。我们仅仅试图给出在这篇 Akka 文档范围内使用的可行定义。

并发 vs. 并行

并发和并行是相关的概念,但他们有细微的不同。并发意味着两个或多个任务在取得进展,即使它们可能不是同时执行。例如通过时间分片来实现,任务的不同部分顺序地执行,混杂着其他任务的部分。并行在另一方面是 在真正地同时执行时出现。

异步 vs. 同步

如果调用者在方法返回一个值或抛出异常前不能取得进展,则个方法调用被认为是同步的。在另一方面,一个异步调用允许调用者取得进展,在一个有限数量的步骤后,并且方法完成后会收到通知,通过额外的机制(可以是注册一个回调、一个 Future 或 一个消息)。

一个同步的 API 可能使用阻塞来实现同步,但不是必须的。一个 CPU 敏感的任务可能表现出类似阻塞的行为。一般情况下,倾向于使用异步的 API,因为它们保证系统可以取得进展。Actors 天生是异步的:一个 actor 在发出消息后可以继续取得进展,不需要等待实际的发送(delivery)发生。

非阻塞 vs. 阻塞

如果一个线程能够无限延迟其他一些线程,我们认为是阻塞的。一个好的例子是,资源可以被一个线程通过互斥独占地使用。如果某个线程无限地(例如意外地跑入一个死循环)持有资源,其他在等待资源的线程将不能取得进展。与此相反,非阻塞意味着没有线程能够无限地延迟其他线程。

非阻塞操作优于阻塞的,因为,当包含阻塞操作时系统的整体进展没法保证。

继续阅读

Oracle hint

公司的DBA要求把他们固化的执行计划改用 Oracle hint 来实现,正好了解下平时没注意去用的 hint。

优化方式

Oracle的优化器有两种优化方式:

  • 基于规则的优化方式(Rule-Based Optimization,简称为RBO) :优化器在分析 SQL 语句时,所遵循的是 Oracle 内部预定的一些规则。比如我们常见的,当一个 where 子句中的一列有索引时去走索引。

  • 基于代价的优化方式(Cost-Based Optimization,简称为CBO) ;它是看语句的代价(Cost),这里的代价主要指Cpu和内存。优化器在判断是否用这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有少行、每行的长度等信息。这些统计信息起初在库内是没有的,是做 analyze 后才出现的,很多的时侯过期统计信息会令优化器做出一个错误的执行计划,因些应及时更新这些信息。

hint 也不例外,除了 /*+ rule */ 其他的都是 CBO 优化方式 。

优化模式

优化模式包括 Rule、Choose、First rows、All rows 四种方式:

  • Rule:基于规则的方式。
  • Choolse:默认的情况下 Oracle 用的便是这种方式。指的是当一个表或或索引有统计信息,则走 CBO 的方式,如果表或索引没统计信息,表又不是特别的小,而且相应的列有索引时,那么就走索引,走 RBO 的方式。
  • First Rows:它与 Choose 方式是类似的,所不同的是当一个表有统计信息时,它将是以最快的方式返回查询的最先的几行,从总体上减少了响应时间。
  • All Rows:也就是我们所说的 Cost 的方式,当一个表有统计信息时,它将以最快的方式返回表的所有的行,从总体上提高查询的吞吐量。没有统计信息则走 RBO 的方式。

hint

hint 语法

{DELETE|INSERT|SELECT|UPDATE} /*+ hint [text] [hint[text]]... */
多个hint之间需要用空格分开,如果没有指定正确的hint,oracle讲忽视该hint,没有任何错误提示。

我们可以使用注释(comment)来为一个语句添加 hints,一个语句块只能有一个注释,而且注释只能放在 SELECTUPDATEDELETE 关键字的后面。

继续阅读

oracle 表连接类型 join type

Join 是一种试图将两个表结合在一起的谓词,一次只能连接 2 个表,表连接也可以被称为表关联。Join 过程的各个步骤经常是串行操作,即使相关的 row source 可以被并行访问,但是在将表中符合限制条件的数据读入到内存形成 row source 后,join 的其它步骤一般是串行的。

row source (表)之间的连接顺序对于查询的效率有非常大的影响。通过首先存取特定的表,即将该表作为驱动表,这样可以先应用某些限制条件,从而得到一个较小的 row source,使连接的效率较高,这也就是我们常说的要先执行限制条件的原因。一般是在将表读入内存时,应用 where 子句中对该表的限制条件。

根据 2 个 row source 的连接条件的中操作符的不同,可以将连接分为等值连接(如 WHERE A.COL1 = B.COL2)、非等值连接(WHERE A.COL1> B.COL2)、外连接(WHERE A.COL1= B.COL2+))。

继续阅读

2014 年终总结

今年过完春节回来就提离职流程走人,2月底到深圳后大概找了两周多,确定了现在的公司后是等入职流程两周,趁这个时间去了一趟帝都,也算出过省了哈,回来后就是往常的上班日子。

一、工作

在现在这家公司做的项目是面向互联网用户的一个网站,普通用户的访问量不大,数据量一般,千万级到亿级。系统的主要压力是在跟公司核心系统对接的 MQ 消息处理上,还有就是其他关联系统对我们系统的 API 调用。

这个项目是 13 年拼命赶出来的,今年是进入运维期,代码质量真不咋地;每天看着那样的代码,也经常在想,如果自己负责一个项目的研发,该怎么保证代码质量,在整理一份自己的项目规范,希望以后有机会实践下。

技术上没有太难的东西,能让我记起来的也就是定位解决了几个生产问题。

继续阅读

Spring Hessian 集成

一、介绍

Hessian 是工作在 HTTP 协议上的远程调用框架:请求信息被序列化为二进制数据通过 HTTP 请求传输到服务器端,服务端查找目标方法,用请求参数进行调用,然后把响应结果序列化为二进制数据,作为 HTTP 响应返回给客户端,客户端再解析数据组成应用所使用的 Java 对象。

二、服务接口

首先要定义一个 Java 接口来表示远程服务:

public interface IHessianService {
     User getUser();

     User createUser(String name, int age, char sex);

     Map<String, Object> getMap();

     String getString(String value);

     int getInt(int i);
}

定义多个方法是为了测试下不同的数据类型的支持度。User 是个自定义的类型,必须实现 java.io.Serializable

服务端暴露这个接口来表示可以被调用的服务,客户端使用这个接口来表示可以调用的远程服务。

继续阅读

数据不可变 与 方法副作用

一、问题

之前的文章提到公司要求项目组做所谓的网络标准化,在 Spring MVC 的 Controller 层与 Service 层之间拆分成两个项目来部署,中间通过 Hessian 远程调用来通信。

这样可就有大问题了:

Controller 调用 Service 的方法时,传递一个 POJO 作为参数,Service 里修改了这个参数的某个属性,最终返还 Controller,Controller 再根据这个被修改的属性继续处理;

Service 调用的深层方法通过 Spring MVC 通过的 org.springframework.web.context.request.RequestContextHolder 获取了 HttpSession 对象,然后设置了一些状态值。

在同一 JVM 里面,上面的都没有问题;但按标准化的要求,部署在两个 JVM 上时,Service 层对 Controller 层传入的 POJO 所做的修改在 Controller 层就不可见了。

这些问题是实现这个标准化的最大困难,几乎所有代码都要检查是否存在上面的情况。

二、如果上天再给一次机会。。。

上面的问题可以说是“参数的可变性”和“方法副作用”导致的,“参数” 是个特殊的东西,它穿过两个方法之间调用时形成的边界。如果“参数”的属性在被调用的方法里被修改了,可以说这个方法是有副作用的。

如果上天再给一次机会:把所有方法都实现为无副作用,参数封装为不可变的对象,方法的所有效果都通过返回值来体现。这样每一个方法都可以变成一个远程服务。

之前在微博上看到一个说他的架构宣言是:Passing Message Everywhere。有点感触。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。