宁静·致远


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • RSS

CTO、技术总监、首席架构师的区别(转)

发表于 2016-06-30 | 阅读次数

注:本文转自微信号erpweixin(简化ERP),如转载请注明原文地址。原文地址请点 这里:

经常有创业公司老板来拜访我,常常会拜托给我一句话:帮我找一个CTO。

我解释的多了,所以想把这个写下来,看看你到底需要的应该是啥。

一、高级程序员

如果你是一个刚刚创业的公司,公司没有专职产品经理和项目经理,你就是公司的产品经理,你如果对你现在的开发员能力不满,那么你只需要的是一个高级程序员。

你定义功能、你做计划推进和管理,他可以带1-2个副手把你规划的功能实现了,他是主力干活者,有技术难题也是他来亲自攻克解决。

所以,一个高级程序员,他的职责很清晰:

1、负责核心复杂功能的实现方案设计、编码实现
2、负责疑难BUG分析诊断、攻关解决

二、研发Leader

公司再长大些。如果你就有一个研发团队(含产品/开发/测试),你就一套主产品,而且你的研发团队小于15人,那么你需要的就是一个研发Leader。

因为你已经有了1-2个高级程序员,核心难题攻克和核心功能研发进度与质量保证,已经可以靠他们自身能力解决掉了。那么你需要研发Leader干什么。

研发Leader的职责是:

1、团队任务管理:开发工作量评估、开发任务分配

2、团队生产质量提升:代码审核、开发风险识别/报告/协调解决

3、团队生产力提升:代码模板研发与推广、最佳实践规范总结与推广、自动化研发生产工具研发与推广

4、团队专业力提升:招聘面试、新人指导、领导复盘总结改进

三、技术总监

如果你的研发团队超过20人了,而且有多套主打产品线了,你可能已经有了多个研发Leader了,那么你需要一个技术总监。

技术总监的职责:

1、组建平台研发部,搭建公共技术平台,方便上面各条产品线开发。

2、通过技术平台、通过高一层的职权,管理和协调各个产品线组。现在每个产品线都应该有合格的研发Leader和高级程序员了。

四、首席架构师

因为你已经有了技术总监了,所以技术平台不错了。技术平台和各条产品线的协调互动,也是技术总监管着。

因为你已经有了各个产品线的高级程序员,他们在靠个人能力维持着核心功能模块的开发进度和代码质量。

因为你已经有了研发Leader,所以代码模板研发与推广、最佳实践规范总结与推广,这些事都已经在日常按份内职责开展了。

那么,啥时候需要首席架构师啊。

也就是说,需要分离管理族和专业族了。你会发现,这个阶段你的研发团队已经超过100来人了,需要有人专注来做架构规划、设计、日常维护。不能让研发总监和研发Leader又做管理又做技术一股脑都扔给他们,你就等着总结果产出。这是不对的。

需要从技术总监和研发Leader身上剥离职责了。让技术总监和研发Leader偏项目管理(管理族),把各个模块之间的架构设计工作,独立出一个岗位,就是架构师,来负责。

每个产品线都有架构师,在技术平台部门也有技术平台的架构师。那么,技术平台和业务产品线的架构互动,就是首席架构师在衔接了。让技术平台架构能够和产品业务系统的架构互相促进和支撑,就是首席架构师的份内之事。

架构师的职责是:

1、架构分析:从功能性需求中识别出需要增加的非功能性需求,好满足性能、可扩展、解耦/集成、安全、可运维、高可用、易部署、易更新。并且识别完非功能型需求,还要做技术选型、技术架构风险识别、技术实现工作量评估

2、架构设计与实现:非功能性模块的架构设计、接口设计、代码实现。所以需要的是有代码实现能力还要有架构思维的工程师,不需要画PPT的工程师

3、业务架构设计与实现:需要对跨系统的接口进行识别、实现、维护,需要对能写成公共代码类库的进行分析、识别、接口设计、实现、变更维护。

4、重构:架构师需要经常做Bug分析、非模板性和公共类库代码检查,以发现代码腐烂程度,以发现还有哪些代码没有做很好的架构与精心的代码设计。所以重构是经常性维护发生的,不是攒到某一刻动大手术,甚至推翻重做,那就不叫重构了。

五、CTO

你把架构师团队组织建立完成,再往大长,你才需要真正意义上的CTO了。否则你一开始就招真正的CTO,他也不满意,你的期望也不对。现在你的期望也对了,他的能力模型也正好和你的期望职能匹配了,你能给他的和他想要的也正好匹配了。

有的公司有软件系统产品副总裁,也有软件系统技术副总裁,而且把软件系统技术副总裁叫CTO,软件系统产品副总裁叫产品VP。这就很怪异。

真正的CTO,是软件产品和技术是统一管理的。

他做的事情,是商业、产品、技术、管理、团队相平衡的综合统管。

CTO的职责:

1、业绩达成:洞察客户需求,捕捉商业机会,规划技术产品,通过技术产品领导业务增长,有清晰的战略规划、主攻方向,带领团队实现组织目标

2、前沿与平台:到这个研发规模规模级别了,一定要有专门的团队做技术应用创新探索和前沿技术预研。而且要和技术平台团队、应用研发团队形成很好的联动作用,让创新原型试点能够很平滑的融入商业平台再让应用研发线规模化的使用起来。大量的前沿探索都死在了内部,做完试点就停滞了,这就需要CTO做好整体的衔接推动工作。

3、研发过程管理:站在全局立场来端到端改进业务流程,为业务增长提供方便

4、组织与人才建设:公司文化和价值观的传承;研发专业族团队梯队建制建设、研发管理族团队梯队建制建设;创建创新激发机制,激发研发人创新向前发展,激发黑马人脱颖而出

阿朱出品必属精品,阿朱出品必属精品,阿朱出品必属精品,阿朱出品必属精品,唉,现在好文章,都需要重要的话说四遍了

JVM 垃圾回收器CMS之各阶段总结

发表于 2016-06-21 | 阅读次数

先贴一张图:

1.初始标记阶段(CMS-initial-mark)

这个阶段的主要任务是找到堆中所有的垃圾回收根节点对象,这个阶段会暂停所有的应用程序线程,即STW(Stop the world)。此阶段会打印1行日志,如下:

[GC [1 CMS-initial-mark: 2905437K(4096000K)] 3134625K(5916480K), 0.2551680 secs] [Times: user=0.26 sys=0.00, real=0.25 secs]

其中第一组数据,第一对数据标识老年代实际占用的空间大小和老年代分配的空间大小,第二对数据标识整个堆的实际使用情况和分配的堆的空间。
细心的人可能发现了CMS-initial-mark前面的数字“1”,这个标志代表了STW,在后面的“重新标记”阶段,你也会发现这个标志。这正好与上面图中说明的阶段是对应的。

2.标记阶段(CMS-concurrent-mark)

这个阶段是和应用线程并发执行的,所谓并发收集器指的就是这个,主要作用是标记可达的对象。由于只是进行标记,所以不会对堆的占用产生实质的改变。

2016-06-21T16:38:53.911+0800: 367848.849: [CMS-concurrent-mark-start]
2016-06-21T16:38:57.241+0800: 367852.178: [CMS-concurrent-mark: 2.787/3.329 secs] [Times: user=12.12 sys=0.64, real=3.33 secs]

这个阶段会打印2行日志,第一行CMS-concurrent-mark-start标识标记阶段开始。第二行中的“2.787/3.329 secs”表示标记阶段的耗时。后面的“user=12.12”表示占用的cpu时间(此JVM运行的服务器CPU为4核)。

3.预清理阶段(CMS-concurrent-preclean)

在之前的标记阶段,标记和应用线程是并发执行的,因此有些对象的状态在标记后会发生改变。这个阶段只要是发现从新生代晋升的对象、新分配到老年代的对象以及在标记阶段被修改了的对象。
这个阶段也会打印2行日志,跟标记阶段类似。

1
2
2016-06-21T16:38:57.241+0800: 367852.178: [CMS-concurrent-preclean-start]
2016-06-21T16:38:57.718+0800: 367852.655: [CMS-concurrent-preclean: 0.342/0.477 secs] [Times: user=1.79 sys=0.10, real=0.48 secs]

4.重新标记阶段

可中断预清理阶段是在JDK1.5中加入的。这个阶段是CMS中比较复杂的一个阶段,因为在这个阶段,JVM会执行很多操作。
首先是CMS-concurrent-abortable-preclean,可中断的预清理。我们可能要问,既然再上一个阶段已经执行了预清理了,为什么还要再做一次?我们知道,CMS是以获取最短停顿时间为目的的GC,所以简单说进行可中断预清理的目的就是希望尽量缩短停顿的时间。
可中断预清理涉及几个参数:
-XX:CMSMaxAbortablePrecleanTime:当abortable-preclean阶段执行达到这个时间时才会结束
-XX:CMSScheduleRemarkEdenSizeThreshold(默认2m):控制abortable-preclean阶段什么时候开始执行,即当eden使用达到此值时,才会开始abortable-preclean阶段
-XX:CMSScheduleRemarkEdenPenetratio(默认50%):控制abortable-preclean阶段什么时候结束执行

1
2
3
2016-06-21T16:38:57.718+0800: 367852.656: [CMS-concurrent-abortable-preclean-start]
2016-06-21T16:38:58.801+0800: 367853.738: [CMS-concurrent-abortable-preclean: 0.920/1.083 secs] [Times: user=4.06 sys=0.20, real=1.08 secs]
2016-06-21T16:38:58.808+0800: 367853.746: [GC[YG occupancy: 777901 K (1820480 K)]367853.746: [Rescan (parallel) , 0.1361120 secs]367853.882: [weak refs processing, 0.0005370 secs]367853.883: [scrub string table, 0.0044130 secs] [1 CMS-remark: 3034451K(4096000K)] 3812352K(5916480K), 0.1412750 secs] [Times: user=0.54 sys=0.00, real=0.14 secs]

其次,是Rescan操作,此阶段暂停应用线程,对对象进行重新扫描并标记。通过上面的日志我们可以看到,在CMS-remark的时候有一次STW。另外,这个过程还会打印出弱引用处理、类卸载等过程的耗时。

5.清除阶段(CMS-concurrent-sweep)

这个阶段开始进行垃圾的清理工作,此时应用线程被重新激活,回收线程与应用线程并发运行,那些无效的对象被清理掉。

1
2
2016-06-21T16:38:58.950+0800: 367853.888: [CMS-concurrent-sweep-start]
2016-06-21T16:39:05.850+0800: 367860.788: [CMS-concurrent-sweep: 5.656/6.900 secs] [Times: user=25.88 sys=1.28, real=6.90 secs]

6.初始标记阶段(CMS-concurrent-reset)

这是CMS一个回收周期的最后一个阶段,在这个阶段,CMS会清除内部状态,为下次回收做准备。

1
2
2016-06-21T16:39:05.850+0800: 367860.788: [CMS-concurrent-reset-start]
2016-06-21T16:39:05.860+0800: 367860.798: [CMS-concurrent-reset: 0.010/0.010 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]

至此,一个CMS周期结束。

性能调优工具-Java Mission Control

发表于 2016-05-20 | 阅读次数

1.引言

最近1年多的时间,因为需要排查线上应用出现的性能问题,会借助一些工具如JProfiler、Yourkit,但是这些工具都具有商业性质,使用时难免受到限制。后来发现Oracle Jdk(version>=7u40)中自带了一个Java Mission Control(以下简称JMC)的应用,也可以实现JVM的监控。

另外,与JProfiler等使用JVMPI/JVMTI方式实现的工具不同,JMC使用了JVM内部特定的基于事件的接口,几乎不会给应用造成额外的压力(默认设置下,对性能影响小于1%),因此可以用在负载很高的生产环境中。

本文就来简单介绍一下使用JMC来监测JVM性能。

注意:需要下载Oracle Jdk 7u40以后的版本,OpenJdk无效,切记!

2.目标JVM配置

在被监控的JVM(目标JVM)上需要开启以下Java Options才能对其进行监控,对于Tomcat来说,在JAVA_OPTS或CATALINA_OPTS中加入以下代码即可:

-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=192.168.32.11
-Dcom.sun.management.jmxremote.port=7777
-Dcom.sun.management.jmxremote
-XX:+UnlockCommercialFeatures
-XX:+FlightRecorder

java.rmi.server.hostname:如果要允许其它机器监控该程序,必须设定,否则就只能在本机监控该程序。
com.sun.management.jmxremote:启用JMX远程监控。
com.sun.management.jmxremote.port:JMX远程监控的端口。
com.sun.management.jmxremote.ssl:将此配置设置为 true 时,将使用服务器证书通过 SSL 来保护通信。
com.sun.management.jmxremote.authenticate:是否开启权限控制,如果设置为true,需要指定两个文件:jmxremote.password和jmxremote.access,password文件主要是配置用户名和密码,access主要是配置权限(可读或者读写)。
在Tomcat的bin目录下增加下面两个文件:jmxremote.password和jmxremote.access,格式如下:

jmxremote.access:

admin readwrite \
  create com.sun.management.*,com.oracle.jrockit.* \
  unregister
monitor readonly

表示admin有操作权限(比如调用GC等操作),monitor只有查看权限,不能进行任何操作。
jmxremote.password:

admin test
monitor test    

表示有两个用户,admin和monitor,密码分别是test和test。

-XX:+UnlockCommercialFeatures:开启商业特性,默认这个选项是关闭的。
-XX:+FlightRecorder:开启飞行记录器。

上述参数配置完毕,重新启动tomcat即可。

3.连接远程JVM

双击本地%JDK_HOME%\bin\jmc.exe,点击左侧“创建新定制JVM连接”图标,

在弹出的窗口中输入远程JVM的IP地址和端口号:

选择“启动JMX控制台”,点击“完成”。

进入JMC主界面:

在左侧飞行记录器的菜单上点击右键,选择“启动飞行记录”,进入到启动飞行记录的界面,在此界面设置飞行记录的文件路径、记录时长(固定时长或固定间隔):

设置监控的事件:

在此界面可以设置监控的详细设置,并点击“完成”

现在你可以愉快地使用JFR的强大功能了。

Java Instrumentation研究之动态植入

发表于 2016-04-25 | 阅读次数

1.前言

前一篇文章我们提到:在Java SE5中,Instrument要求在运行前利用命令行参数或者系统参数来设置代理类,在实际的运行之中,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前),instrumentation的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并完成实际工作。也就是说,在Java SE5中,我们只能使用premain的方式实现Instrumentation。但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了instrument的应用。而Java SE6的新特性改变了这种情况,通过agentmain和Java Tool API中的attach方式,我们可以很方便地在运行过程中动态地设置加载代理类,以达到instrumentation的目的。本文就是对Java动态Instrumentation的实现进行研究。

2.关于agentmain

在Java SE6的Instrumentation 当中,有一个跟 premain“并驾齐驱”的“agentmain”方法,可以在main函数开始运行之后再运行。
跟premain函数一样, 开发者可以编写一个含有“agentmain”函数的Java类:

public static void agentmain(String agentArgs, Instrumentation inst);          [1] 
public static void agentmain(String agentArgs);              [2]

[1]的优先级比[2]高,将会被优先执行。

跟前文提到的premain函数一样,我们可以在agentmain方法中对类进行各种操作。其中的agentArgs和Inst的用法跟premain相同。
与“Premain-Class”类似,我们必须在manifest文件里面设置“Agent-Class”来指定包含agentmain函数的类。
可是,跟premain不同的是,agentmain需要在main函数开始运行后才启动,这样的时机应该如何确定呢,这样的功能又如何实现呢?
在 Java SE6 文档当中,我们也许无法在java.lang.instrument包相关的文档部分看到明确的介绍,更加无法看到具体的应用agnetmain的例子。不过,在 Java SE6的新特性里面,有一个不太起眼的地方,揭示了agentmain的用法。这就是Java SE6当中提供的Attach API。

3.关于Attach API

Attach API不是Java的标准API,而是Sun公司提供的一套扩展API,用来向目标JVM“附着”(Attach)代理工具程序的。有了它,我们可以方便地监控一个JVM,运行一个外加的代理程序。
Attach API很简单,只有2个主要的类,都在com.sun.tools.attach包里面:VirtualMachine代表一个Java虚拟机,也就是程序需要监控的目标虚拟机,提供了JVM枚举,Attach动作和Detach动作(Attach 动作的相反行为,从JVM 上面解除一个代理)等等 ; VirtualMachineDescriptor则是一个描述虚拟机的容器类,配合VirtualMachine类完成各种功能。

4.agentmain实现步骤

与Permain类似,agentmain方式同样需要提供一个agent jar,并且这个jar需要满足:

  1. 在manifest中指定Agent-Class属性,值为代理类全路径;
  2. 代理类需要提供public static void agentmain(String args, Instrumentation inst)或public static void agentmain(String args)方法。并且再二者同时存在时以前者优先。args和inst和premain中的一致。

Attach API中的VirtualMachine代表一个运行中的VM,其提供了loadAgent()方法,可以在运行时动态加载一个代理jar,这样就可以实现类似premain的效果了。

5.agentmain实例

与上文一样,我们要通过Java Instrumentation实现对某个类中所有方法执行时间的统计,只不过不同的是:这次我们采用动态Instrumentation的方式。

  1. 编写一个简单的测试类Test,以便于后面我们通过动态代理实现方法计时功能;
  2. 编写Agent实现类SampleTransformer,实现ClassFileTransformer接口。代码同上篇文章一样,这里不再赘述。
  3. 编写Agent入口类DynamicAgent,实现agentmain方法:

    public static void agentmain(String args, Instrumentation inst)

  4. 编写Agent加载类AgentLoader,以通过Attach API中的VirtualMachine来动态加载我们编写的代理jar;

  5. 编写一个测试入口类SampleApp,能够长时间驻留,便于我们测试动态植入功能;本例中我们通过键盘数据接收用户的输入数据。只要用户输入不为“bye”,那么就会执行Test类的test()方法;在系统刚刚启动的时候,我们没有利用动态植入实现对test()方法的计时;当执行动态植入后,再执行test()方法,我们就能看到屏幕除了输出原test()方法输出的“hello world”之外,还额外输出了test()方法的运行计时。这就能够证明植入代码生效了;
  6. 设置MANIFEST.MF文件,指定Agent-Class、Can-Retransform-Classes、Can-Redefine-Classes等属性。在本实例所付的代码中,是通过maven配置的,具体请参考附件中的代码;
  7. 打包得到DynamicAgent-1.0-SNAPSHOT.jar;
  8. 执行SampleApp,启动应用:

    java -cp /home/fengfu/DynamicAgent/target/DynamicAgent-1.0-SNAPSHOT.jar;/.m2/repository/org/javassist/javassist/3.19.0-GA/javassist-3.19.0-GA.jar io.fengfu.learning.instrument.SampleApp

在交互窗口输入test,我们可以看到系统输出:Hello World!

  1. 得到SampleApp的进程id,比如1234;
  2. 执行AgentLoader,启动动态代理:

    java -cp /home/fengfu/DynamicAgent/target/DynamicAgent-1.0-SNAPSHOT.jar;/usr/local/jdk1.7/lib/tools.jar;/.m2/repository/org/javassist/javassist/3.19.0-GA/javassist-3.19.0-GA.jar io.fengfu.learning.instrument.AgentLoader 1234

  3. 在SampleApp的交互窗口再次输入test,我们可以看到系统输出:Hello World!,同时也输出:Call to method test took 3001 ms.

大功告成,一个简单的动态植入功能就实现了~
本实例的代码可以点 这里) 下载。

6.说明

实例虽然简单,但是在代码编写过程中还是遇到了几个坑,贴出来供大家参考:

  1. 如果动态植入需要通过javassist去修改某些类,那么需要在MANIFEST.MF中设置Can-Retransform-Classes和Can-Redefine-Classes为true;
  2. 要动态修改的类Test不要跟测试入口类SampleApp放在一个类文件中,否则代理启动动态修改Class时会报如下异常:

    java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)
    at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(Unknown Source)
    Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
    at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at sun.instrument.InstrumentationImpl.retransformClasses(Unknown Source)
    at io.fengfu.learning.instrument.DynamicAgent.agentmain(DynamicAgent.java:14)
    ... 6 more
    

分析原因,是因为SampleApp正在运行中,而JVM是不允许reload一个正在运行时的类的。一旦classloader加载了一个class,在运行时就不能重新加载这个class的另一个版本。

Java Instrumentation研究之premain

发表于 2016-04-24 | 阅读次数

1.引言

作为一个软件工程师,你有没有遇到过以下场景:

  1. 为了排查线上问题,非常迫切地想要知道系统中某个方法的运行数据,为此只能临时修改代码,打印日志;
  2. 为了查找系统瓶颈,你需要知道某个方法的耗时,为此你只能在方法头尾处记录时间戳,并打印日志或者记录到监控中;
  3. ……

为了实现上述种种临时需求,你需要修改代码、发布、查看日志。或许,这些场景对于作为码农的你已经习以为常,但是世界是懒人创造的,那有没有一种方式能够避免我们这种重复又无任何积累的劳动呢?答案是有的。

最近,公司的大神 余昭辉 发布了一个新的应用QTracer watch,可以支持通过简单的配置,查看系统运行时的数据。而实现这一点,竟然不用像以前一样在系统中添加一行代码并发布。这种高大上的技术实现立马让我对神的仰慕之情增加了几分。但仰慕归仰慕,对于神作我们还是需要知道其原理的。

通过了解,清楚了这高大上的技术实现来自于Java的Instrumentation(植入)。

2.Java Instrumentation简介

Instrumentation是Java SE5的新特性,它把Java的instrument功能从本地代码中解放出来,使之可以用Java代码的方式解决问题。使用Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在JVM上的程序,甚至能够替换和修改某些类的定义。有了这样的功能,我们就可以实现更为灵活的运行时虚拟机监控和Java类操作了,这样的特性实际上提供了一种虚拟机级别支持的AOP实现方式,使得我们无需对原有代码做任何升级和改动,就可以实现某些 AOP 的功能了。
在Java SE6里面,instrument包被赋予了更强大的功能:启动后的instrument、本地代码(native code)instrument,以及动态改变classpath等。这些改变意味着Java具有了更强的动态控制、解释能力,使得Java语言变得更加灵活多变。
在Java SE6里面,最大的改变是运行时的Instrumentation成为可能。在Java SE5中,Instrument要求在运行前利用命令行参数或者系统参数来设置代理类,在实际的运行之中,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前),instrumentation的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并完成实际工作。但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了instrument的应用。而Java SE 6的新特性改变了这种情况,通过Java Tool API中的attach方式,我们可以很方便地在运行过程中动态地设置加载代理类,以达到 instrumentation的目的。
另外,对native的Instrumentation也是Java SE6的一个崭新的功能,这使以前无法完成的功能–对native接口的instrumentation。我们可以在Java SE6中,通过一个或者一系列的prefix添加而得以完成。
最后,Java SE6里的Instrumentation也增加了动态添加classpath的功能。所有这些新的功能,都使得instrument包的功能更加丰富,从而使Java语言本身更加强大。

3.Java Instrumentation原理

“java.lang.instrument”包的具体实现,依赖于JVMTI。JVMTI(Java Virtual Machine Tool Interface)是一套由 Java虚拟机提供的,为JVM相关的工具提供的本地编程接口集合。JVMTI是从Java SE5开始引入,整合和取代了以前使用的 Java Virtual Machine Profiler Interface (JVMPI) 和 the Java Virtual Machine Debug Interface (JVMDI),而在Java SE6中,JVMPI和JVMDI已经消失了。JVMTI提供了一套“代理”程序机制,可以支持第三方工具程序以代理的方式连接和访问JVM,并利用JVMTI提供的丰富的编程接口,完成很多跟JVM相关的功能。事实上,java.lang.instrument包的实现,也就是基于这种机制的:在Instrumentation的实现当中,存在一个JVMTI的代理程序,通过调用JVMTI当中Java类相关的函数来完成Java类的动态操作。除开Instrumentation功能外,JVMTI还在虚拟机内存管理,线程控制,方法和变量操作等等方面提供了大量有价值的函数,具体可以参考JVMTI官方文档。

4.Java Instrumentation实现步骤

在Java SE5时代,Instrument只提供了premain(命令行)一种方式,即在真正的应用程序(包含main方法的程序)main方法启动前启动一个代理程序。而在Java SE6中则包含两种应用Instrumentation的方式:premain(命令行)和agentmain(运行时)。在本文中,我们首先研究premain方式。
要实现premain方式,我们要遵循的步骤如下:
1)编写Agent实现类,实现ClassFileTransformer接口。ClassFileTransformer中声明了一个方法:

public byte[] transform(
ClassLoader loader, 
String className, 
Class cBR, 
java.security.ProtectionDomain pD, 
byte[] classfileBuffer) throws IllegalClassFormatException

通过这个方法,代理可以得到虚拟机载入的类的字节码(通过 classfileBuffer 参数)。代理的各种功能一般是通过操作这一串字节码得以实现的。

2)编写Agent入口类,实现premain方法:

public static void premain(String agentArgs, Instrumentation inst)

3)打包Agent:将上述步骤1中声明的Java类打包成一个jar文件,并在META-INF/MANIFEST.MF文件中加入“Premain-Class”来指定此Java类(注意此处需要声明全路径);

Manifest-Version: 1.0
Premain-Class: io.fengfu.learning.instrument.SampleAgent

最终我们打包得到SampleAgent.jar。

4)执行命令:

java -javaagent:SampleAgent-1.0-SNAPSHOT.jar -cp /home/fengfu/SampleAgent/SampleAgent-1.0-SNAPSHOT.jar;/home/fengfu/SampleAgent/lib/javassist-3.19.0-GA.jar io.fengfu.learning.instrument.SampleApp

java选项中有-javaagent:xx,xx就是你的agent jar,java通过此选项加载agent,由agent来监控classpath下的应用。
如果你的Agent类引入别的包,那么需要使用-cp参数指定包的路径,否则在执行java命令时,会报找不到类的错误。

5.Java Instrumentation实例(premain)

本实例基于javassist实现了对某个类中所有方法执行时间的统计,具体代码请点击 这里 下载。

浅谈软件工程师的职业发展方向

发表于 2016-04-11 | 阅读次数

今天在跟团队一个工作了2年多的同学做绩效面谈时,该同学问了我一个问题:自己现在比较迷茫,不知道该往哪个方向发展……

相信这是很多有一定工作经验的同学都会有的困惑,很多人每隔几年(一般是3年)就会遇到瓶颈,也会面临着新的选择。这时候,他们都会看不清方向,感到迷茫。如果这个问题得不到解决,那么他的成长就会受到制约,甚至停滞不前。在工作中的表现就是比较沉默甚至消极,严重者则选择离职,寻求新的方向(其实是开始新的轮回……)。此时,如果有人能够根据他们的特点帮助他们选择适合他们的方向,或者给出建议,那么相信他会走出迷雾,豁然开朗,这样团队的稳定性会得到加强。

所以今天简单总结一下软件工程师的职业发展方向,以帮助迷茫的同学早日走出混沌状态。

1.技术专家

技术专家这个方向对于软件工程师来讲应该是比较容易实现的一个目标,因为这是外在因素影响最小的一个,我们所需要的就是专注和毅力。
在技术发展百花齐放、日新月异的今天,我们工作中可能会用到很多的技术,但是很多人只是把自己局限在“用”的层面上,并没有继续去深入研究和改进,更有人只是“知其然不知其所以然”,这样就失去了进一步积累的机会。
作家格拉德威尔在《异数》一书中指出:”人们眼中的天才之所以卓越非凡,并非天资超人一等,而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件”。我们做个简单的算术题:如果每天工作八个小时,一周工作五天,那么成为一个领域的专家至少需要五年。
5年,看起来貌似时间很长,但是试想一下,如果我们借着工作的机会深入研究某个技术栈的原理,并在此基础上加以实践,那么我们只要不断地积累,最终是有机会成为这方面的专家的。在我身边就有这样的例子:以前我的团队中有个非常内向的小伙子(就叫他HX吧),刚来公司的时候各方面资质很平庸,平时寡言少语,也缺乏自信。在做一个项目时,我们采用了hbase作为系统存储。因为之前对hbase缺乏了解,所以项目过程中踩了很多的坑,于是HX同学就不停地填坑,甚至工作之外的时间都贡献了出去。2年过去了,虽然我们的项目早已结束,但是HX却从未放弃对hbase的研究,相反他经常翻看hbase的代码,并经常去hbase社区查看别人的讨论。现在HX已经成为了公司中对hbase最熟悉的人,并开始往hbase社区提交代码。在公司中他也已经小有名气,其他事业部的开发遇到hbase相关的问题也会来找他了解。虽然他现在还算不是hbase的专家,但是很明显,他现在已经度过了最难熬的时间,已经进入了收获期,只要他继续坚持下去,相信会取得更大的成绩。
这是身边真真切切的例子,虽然每个人的情况不完全一样,但是很多时候,我们是可以从别人的故事中找到自己的期待。

2.业务专家

业务专家这个方向也比较好理解,我们长时间接触某一块业务,耳濡目染,久而久之也就会对这个业务比较熟悉甚至精通了。这个方向,其实我们仔细观察一下,身边也能找出很多的例子。我有一个认识了十几年的前同事,他从毕业开始就在做银行的信贷系统,现在还在做。目前他自己拥有一个几十人的开发团队,在国家开发银行驻场帮国开行做信贷系统,小日子过得很滋润。再回想自己曾经做过银行的外汇系统,最开始跟着中国银行学习业务,边学习边做系统,2年之后系统成熟了,业务熟悉了,就开始给建行做系统。但建行做外汇业务起步比较晚,于是我一个开发给银行的人讲业务,那种成就感你自己可以设想一下。
当然,这个方向也需要保持专注,跟上面讲的技术专家是一样的,你需要不断的积累。现在很多同学总是不停地在各个公司跳来跳去,薪水是涨上去了,但是在行业领域却没有太多的积累,从个人发展来讲,这迟早会成为一个限制因素。

3.技术经理

我们身边有这么一类人,各方面能力都比较均衡,比如技术、沟通能力、组织能力。这样的人经过一段时间的锻炼后,可能会被任命为小组长,承担一些管理性的工作。再经过一段时间的历练,有些人可能不太适合这个方向,于是转向其他方向,剩下的做的还不错的人则继续升级,管理更大的团队,于是就成了技术经理。
技术经理的要求不同于前面的2个职位,保持专注就可以;而技术经理的要求就比较广泛,因为你管理的是技术团队,所以技术你不能扔;同时你管的是人,所以人际关系的相关技能你也要提升,比如沟通能力、组织能力,甚至你还要了解心理学、组织行为学等内容。这时候,全方位地充电就成了一个比较好的选择。

4.架构师

现在还有个名词叫“T型人才”。T型人才是指按知识结构区分出来的一种新型人才类型。用字母“T”来表示他们的知识结构特点。“—”表示有广博的知识面,“|”表示知识的深度。两者的结合,既有较深的专业知识,又有广博的知识面,这类集深与博于一身的人才。这种人才结构不仅在纵向的专业知识上具有较深的理解能力和独到见解,而且在横向上具备比较广泛的一般性知识修养。
我理解,架构师就是属于这种人才。他们经过长时间的技术积累,在某个方向上已经具备了比较深厚的沉淀,同时在技术体系上也得到了逐步的完善,于是成为了上天入地无所不能的架构师。
一般来讲,要成为T型人才,最好是先成为那道“|”,再成为那到“——”,这样根基会比较扎实。
所以对于要立志成为架构师的同学来说,最好先做一个技术专家,在技术专家的基础上,再扩大知识面,最终成为架构师。

5.CTO

CTO我还是不写了,仰望吧……

喷了这么多,最后简单梳理了各个方向的能力模型,希望抛砖引玉,引发你的思考。

技术栈选型原则

发表于 2016-03-22 | 阅读次数

未完成

2个能力

  1. 满足需求的能力:开源产品满足自身功能、性能的能力(开发效率?运行效率?);需求,梳理自身的业务、技术需求;
  2. 自身技术团队维护开源产品的能力;学习曲线以及公司内部的技术支持;

3个关注点

  1. 社区成熟度(用户数、bug数)、活跃度(contributors、releases)、技术支持(论坛、文档等);
  2. 成功案例;

避免过度炫技
不要因为一个人的喜好而舍弃掉整个技术团队,在任何时候这都是非常愚蠢的事情

JVM触发FullGC的情况总结

发表于 2016-03-08 | 阅读次数

FullGC是Java应用中一个不容忽视的问题,因为FullGC会引起应用停顿,所以它对那些响应时间要求比较高的应用的影响还是非常大的。
当然,如果要解决FullGC的问题,我们首先需要知道在什么情况下会引起FullGC,这样才能对症下药,避免FullGC的出现。

下面的总结是针对于系统自动进行FullGC的情况分析,不包含System.gc操作。

1. 老年代空间不足

JVM中堆空间主要由新生代和老年代组成。新创建的对象大多在新生代中创建,当对象经过几次Minor GC依然存活,才有机会被转入老年代。这时问题就来了,如果此时老年代的空间不足以容纳从新生代转入的对象,那么JVM就会进行FullGC,以清理老年代的空间。如果进行FullGC后老年代依然无法容纳转入对象,那么系统就会抛出:java.lang.OutOfMemoryError: Java heap space的异常,相比大家都比较熟悉了。

2. 永久代空间不足

永久代(Permanent Generation)一般是用来存放类信息、字符串常量的地方,如果我们永久代设置的空间比较小无法容纳足够的类信息时,或者因为频繁热加载类信息,又或者存储了太多的字符串常量,那么系统就会触发FullGC,以清理永久代。如果FullGC之后永久代还无法容纳足够的信息,那么系统就会抛出:java.lang.OutOfMemoryError: PermGen space的异常,眼熟吧:)

3. promotion failed和concurrent mode failure

promotion failed是指在进行Minor GC时,新生代中的对象从Eden区往survivor区转移,但是survivor区放不下,只能放入老年代,但是悲催的是老年代也放不下,这时就会出现promotion failed的情况,系统会触发FullGC以清理空间。

concurrent mode failure是指在进行CMS GC的过程中有对象要放入老年代,但是老年代空间不够引起的。

4. 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间

这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。
例如程序第一次触发Minor GC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。
当新生代采用PS GC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。
除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。可通过在启动时通过- java -Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。

明确了FullGC出现的原理,我们就能根据JVM垃圾回收的情况来判断系统到底是因为什么原因出现了FullGC,也就能对症下药,避免FullGC的出现。

你若懂我,该有多好

发表于 2016-02-28 | 阅读次数

每个人都有一个死角,
自己走不出来,
别人也闯不进去。
我把最深沉的秘密放在那里,
你不懂我,我不怪你。

每个人都有一道伤口,
或深或浅,
盖上布,
以为不存在。
我把最殷红的血涂在那里,
你不懂我,我不怪你。

每个人都有一场爱恋,
用心,用情,用力,
感动也感伤。
我把最炙热的心情藏在那里,
你不懂我,我不怪你。

每个人都有一行泪,
喝下冰冷的水,
酝酿成的热泪。
我把最辛酸的委屈汇在那里,
你不懂我,我不怪你。

每个人都有一段告白,
忐忑,不安,
却包含真心和勇气。
我把最抒情的语言用在那里,
你不懂我,我不怪你。

你永远也看不见我最爱你的时候,
因为我只有在看不见你的时候,
才最爱你。

同样,你永远也看不见我最寂寞的时候,
因为我只有在你看不见我的时候,
才最寂寞。

也许,我太会隐藏自己的悲伤,
也许,我太会安慰自己的伤痕。
从阴雨走到艳阳,
我路过泥泞,路过风。

一路走来,
你若懂我,
该有多好。

(作者:莫言)

ATPCO项目总结-管理篇

发表于 2015-12-29 | 阅读次数

ATPCO项目从1月初开始投入人力进行研究,4月份进入封闭状态,7月初上线第一版,到现在已经8个月的时间了。在这段时间里,公布运价出票量由最初的10提升到了30%,目前日出票量已经达到了4000+,较好地提升了qunar国际机票的竞争力。

ATPCO项目经历的这接近1年的时间里,有很多的感触,写出来做个总结吧。今天写的是管理方面。

1. 反应速度

项目启动最初,由于与ATPCO的商务合同没有签订,项目只投入了一个人进行研究,推动缓慢。加之团队对公布运价业务不熟悉,给了其他团队切入的机会,并最终导致了2个团队持续竞争的局面。虽然从公司的角度,一个重要的项目由2个团队做是降低风险的举措,但是由此带来的内部消耗也是比较大的,这也导致了后面的核心员工离职。
从这件事情来讲,对于任何事情,都要保持足够的敏感度和反应速度。互联网行业竞争激烈,需要快速反应。qunar内部的狼性文化也要求团队能够快速响应、灵活机动,这样才能更快地响应形势的变化。

2. 团队组建

从团队组建上,有2点做得比较好,1点做得不好。
先说说做得好的地方:
1)在项目初期就把产品经理、技术拉到一个会议室封闭,这样带来的好处是沟通效率很高,项目有问题的时候大家在屋里吼一嗓子就能很快解决问题。当然弊端是十几个人在一个屋子里面,有的时候3、4组人一起说话的时候,那感觉就跟菜市场一样了。
2)投入精兵强将。项目开始封闭时,项目人员短缺,无奈之下,把部门里面6个TL中的4个拉了进来。此举起到了很好的效果,不仅解决了人力的问题,而且每个人都在自己负责的事情上做出了很好的成绩。这也应了那句话:兵不在多,在于精。
再说说做得不好的地方:
1)添油战术不可取:ATPCO项目专业性很强,新人的学习门槛很高,所以在往团队增加人手的时候,最好一次性给够。添油战术对这个项目最大的弊端就是新人需要花很久去学习(最少1个月时间入门),如果每个新来的人都要经历一个学习过程,那么所带来的成本和消耗就比较大了。

3. 沟通

再来说说沟通,沟通一直是管理工作中非常重要的一环,沟通做不好,工作难以开展不说,也会造成人员流失。
1)内部沟通
先说说内部沟通。
团队中每一个人都是一个独立的个体,具备不同的性格和不同的处事方式,每个人的诉求也是不一样的,所以对待每个人的方式也应该因人而异。但是,无论对谁,首先应该了解他的诉求。每个人来到公司都是有自己的诉求的,比如有的人比较关心技术积累、有的人关心成长空间、有的人关心薪水、有的人关心关系简单……如果不了解每个人的需求,那么可能就无法投其所好,给其所要。久而久之,如果这个人不主动跟你沟通,你又不能及时发现问题,那么久而久之,欲求不满的他可能就会闪人了。
2)外部沟通
下面的这些话不展开了说,说得太明白没意思。
有人的地方就有江湖,
有人的地方就有利益,
看不明白的就看利益。

你不可能只靠自己和团队,还需要外围的资源的支持,讲利益互换。你对别人有利,你才有机会,此处省略一万字……

公司大了,就像一个社会,你会遇到形形色色的人,你喜欢的,你讨厌的;你佩服的,你无视的……无论遇到什么样的人,都应该平和对待。一花一世界,一叶一菩提,每个人都值得尊重。

4. 用人

管理就是把合适的人用在合适的位置上,不合适的人要尽快替换,让合适的人顶上去。
这里要说的是招聘,不同的项目,对人的要求是不一样的。针对项目的招聘工作,一定要清楚需要什么样的人,严格按照要求招聘。如果招到的人不合适,不仅浪费时间浪费资源,对项目、对应聘者都会产生不利影响。ATPCO是一个专业性很强的项目,学习门槛很高,对人的学习、理解、沟通能力要求都很高,甚至心理素质。项目组成立半年多以来,项目组成员几进几出。有的人适应不了环境自己离职,有的人则是胜任不了被淘汰,这其实都是损失。

5. 分工

首先是将系统拆分,每个人负责独立的事情。在系统建设初期,可以采用服务化的方式将功能拆分,以加快开发速度。但是到了项目后期,系统稳定后,可以再把服务聚合起来。

其次是要把合适的人放在合适的位置上。那怎么知道这个人适合这个位置呢?前面说到我们已经做了系统拆分,那么根据拆分后的系统来分析各系统需要什么样的能力?比如航路引擎需要对算法比较熟悉,规则系统需要很强的学习能力和技术功底,搜索引擎需要对分布式和性能优化比较熟悉。明确了各个位置需要的能力后,就可以去找相应的人了。

第三是要能够做好backup,如果你不希望在关键时刻因为关键人物不在而导致业务受影响,那么就尽量避免出现单点。如果人手短缺万不得已,那么TL可以加强团队代码review,熟悉每个子系统的逻辑,这样能够确保在关键时刻其他人能够顶上,同时也能够帮助团队提升技术能力。

1…456
宁静·致远

宁静·致远

60 日志
9 标签
友情链接
  • 卡拉搜索
© 2020 宁静·致远
由 Hexo 强力驱动
主题 - NexT.Gemini