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

一、JFR 飞行记录器

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

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

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

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

1、记录类型

1.1、连续录制

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

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

保持持续录制一直允许是很好的,对于调试非常罕见的问题时非常有用。记录可以用 jcmd 或 JMC 手工转储。你也可以在 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,一个语句块只能有一个注释,而且注释只能放在 SELECT,UPDATE 或 DELETE 关键字的后面。

继续阅读 →

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笔记,可以更及时回复你的讨论。