在java应用中集成jython

今天是新年第一天,但是上周欠的帐还没还完。 上周在规则引擎的系统中加入了python部分的实现,即jython,碰到了好多坑。。。有种要死的感觉。。。其实系统之前已经集成了groovy,使用的实现是GroovyScriptEngine,就是把一段字符串形式的groovy脚本通过groovy classloader加载进来,相当于直接执行编译后的class文件。为了能平滑支持jython,实现改成jsr223的通用实现,即script engine,同时支持groovy和jython2.7(项目需要这个老版本。。。)。改完之后,jython各种不支持,然后就一点点填坑。最后就剩下最后一个问题了!本地可以正常运行,但是上到demo环境就不行了。。。然后会神奇的抛出一个空指针异常,说对应的engine没有找到。debug跟进去发现jython初始化engine的时候需要到一个叫Lib的地方import一些library,其中有一个“site”没有找到。然后就没法初始化这个jython engine。 我发现之所以,本地可以跑jython是因为跑spring boot的时候我是在intellij中用main函数的方式跑,这时候所引用的第三方jar是从.gradle/cache下读的。而通过gradle build,然后java -jar xxx方式启动的时候,jython会向这里找Lib:/Users/magiclane/git/dr/morpheus/api/build/libs/api-1.0.jar!/BOOT-INF/lib/jython-standalone-2.7.0.jar。 问题就出在路径上了。 spring boot build称可执行的jar之后的结构是这样的: example.jar | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +- +-BOOT-INF +-classes | +-mycompany | +-project | +-YourClasses.class +-lib +-dependency1.jar +-dependency2.jar 而且java -jar xxx的时候,这些第三方jar也不会被打开。 然而jython找Lib的默认位置是通过自己的找一个class的classpath定位的。 String urlString = URLDecoder.decode(rawUrl, "UTF-8"); urlString = urlString.replaceAll(escapedPlus, plus); int jarSeparatorIndex = urlString.lastIndexOf(JAR_SEPARATOR); if (urlString.startsWith(JAR_URL_PREFIX) && jarSeparatorIndex > 0) { // jar:file:/install_dir/jython.jar!/org/python/core/PySystemState.class jarFileName = urlString.substring(JAR_URL_PREFIX.length(), jarSeparatorIndex); } 然而真实路径却是在那之前还有一段:libs/api-1....

January 1, 2017 · 1 min · magicalne

GC是个什么鬼

今天看了一个视频。挺有意思。标题是:Understanding Java Garbage Collection and what you can do about it。Speaker是Gil Tene,他是Azul的Vice President of Technology and CTO, Co-Founder。 这个视频开头就埋了一个大大的伏笔。Gil问大家,各位所在公司开发的系统的服务器内存有多大?1/2 GB? 1GB? 2GB? 4GB? 10GB? 20GB? 50GB???细想起来这个问题真的好有意思啊。 接下来就是各种扫盲。虽然关于GC也看了不少,但是学的不系统,知识总归没成体系。这个视频还是有一定点拨作用的。最重要的是,这个视频不是一板一眼的跟你讲GC,而是很诚实的说出来了GC没能做到的,或者说是很难做到的。然后你就可以以此来判断这个jvm实现是否够好了:)当然,那都是套路。。。 视频说到,GC可能需要分代收集,不同代有不同的收集策略,而GC最终需要解决的就是STOP-THE-WORLD。如何尽量减少stop-the-world的频率和时间是JVM需要考虑的首要问题。然而这里面有各种各样的tradeoff。考虑到当年是2010年,当时的Hotspot的G1和CMS,并不是完全的concurrently,而是mostly。所以CMS还是会stop-the-world。 这里又引出了另一个问题,理论上,如果可用内存足够大,永远不需要GC。而如果可用内存过小,就越需要GC。一个系统理论上,平均每秒会产生1/4G~1/2G对象,这些对象都是GC需要关心的。如果GC回收内存的速度比应用产生垃圾的速度快,那么应用自然会正常运行。一旦GC回收内存速度没能跟上应用产生垃圾的速度,应用就会慢慢死掉。如果应用所使用的server内存很大,stop-the-world所产生的影响会更加明显。所以就会碰到一个问题——Java application memory wall。就是说java 应用不能完全利用server的硬件资源,具体说就是越来越大的内存。 那这个问题看起来好严重啊,内存越来越便宜,但是GC不够屌,只能用那么几G内存,太大了就性能不好。怎么办呢? JVM tuning 完全不去stop-the-world,垃圾回收都是完全并行处理。 这个视频最后很有意思,峰回路转有提到了视频开头提到的问题,就是问各位开发的系统的服务器内存有多大?Gil所在的Azul开发的Zing使用的核心GC算法叫C4(Continuously Concurrent Compacting Collector),比当时的hotspot要屌的多。因为zing没有stop-the-world,是完全concurrently。然后对比jvm tuning,又把hotspot狠狠黑了一把。 一般来说hotspot的jvm调优看起来是这样的: •Examples of actual command line GC tuning parameters: •Java -Xmx12g -XX:MaxPermSize=64M -XX:PermSize=32M -XX:MaxNewSize=2g • -XX:NewSize=1g -XX:SurvivorRatio=128 -XX:+UseParNewGC • -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=0 • -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSParallelRemarkEnabled • -XX:+UseCMSInitiatingOccupancyOnly -XX:ParallelGCThreads=12 • -XX:LargePageSizeInBytes=256m … •Java –Xms8g –Xmx8g –Xmn2g -XX:PermSize=64M -XX:MaxPermSize=256M •-XX:-OmitStackTraceInFastThrow -XX:SurvivorRatio=2 -XX:-UseAdaptiveSizePolicy •-XX:+UseConcMarkSweepGC -XX:+CMSConcurrentMTEnabled •-XX:+CMSParallelRemarkEnabled -XX:+CMSParallelSurvivorRemarkEnabled •-XX:CMSMaxAbortablePrecleanTime=10000 -XX:+UseCMSInitiatingOccupancyOnly •-XX:CMSInitiatingOccupancyFraction=63 -XX:+UseParNewGC –Xnoclassgc...

December 22, 2016 · 1 min · magicalne

Paper -- "The java.util.concurrent Synchronizer Framework"

最近在看Doug Lea的那篇基于AQS实现的理论论文。陆陆续续看了几遍。论文的问题就是作者会把一个或者一类具体问题进行抽象和总结,针对这个或这类问题进行研究,并试图站在一个更高的位置得出结论。这就导致,读这篇论文还是有点难的。主要的难点是细节理论的理解。毕竟这些论文看的少,很多上下文不是很懂,但是若是为了彻底理解这些,把引用的论文都看一遍,有点累。。。 我还是从作者的出发点开始理解吧,试图理解整个AQS为什么被设计成这样的。后续会跟着写几篇关于java memory model,reorder,barrier的文章,试图把整个多线程技术,从最顶层java实现到最底层现代cpu、寄存器、内存全部串起来。另类撸串:) Introduction 首先看看什么是synchronizer——同步器。所谓的synchronizer就是用来维护一组状态的变量,在java1.5发布的AQS中,就是一个32位的int,后面在java1.6发布的时候,又开发了基于long 64位的sycnronizer——AbstractQueuedLongSynchronizer。 Among these components are a set of synchronizers – abstract data type (ADT) classes that maintain an internal synchronization state (for example, representing whether a lock is locked or unlocked), operations to update and inspect that state, and at least one method that will cause a calling thread to block if the state requires it, resuming when some other thread changes the synchronization state to permit it....

December 10, 2016 · 2 min · magicalne

关于java异常

写代码的时候一直使用checkstyle和findbug,checkstyle和findbug可以帮助你写出标准规范的代码。特别是跟intellij插件结合之后,你的缩进、格式、甚至是异常控制都会被规范化。比如之前的项目中,对于异常的管理很不严格,大家都是所以的throw Exception和catch Exception。所以有一天我就抽时间将这些Exception都改成了具体的某个异常。 但是我发现,有些地方catch Exception甚至是catch Throwable是很必要的。 比如rabbitmq处理rpc返回的地方,如果发送的rpc请求在worker端发生异常,这个异常可能是任何异常,甚至可能是一个Error,所以rabbitmq这里其实会throw Throwable。当然,这里其实是spring amqp抛出的。但是在处理这类异常的时候,还是不应该让Throwable流到上层,因为客户端只需要知道这里出现异常应该怎么办,而不是关心整个异常链。所以应该这样: try { xxx } catch (Throwable t) { Throw new SomeRPCException(t, "blablabla"); } 另外在试图从异常中恢复,特别是在多线程中,尝试因为抛出的异常终止或恢复线程,也需要catch Exception/Throwable。

November 16, 2016 · 1 min · magicalne

关于java中的assert

自从最近才发现,我一直在用错误的方式使用assert!这一切都源于自己的想当然没有去阅读java doc! 原来我使用assert的方式就是方法的输入参数校验,这是错误的。特别对于public方法。因为assert校验失败之后抛出AssertError,而不知道到底是什么原因导致了校验失败,所以参数校验还是应该使用IllegalArgumentException。 Do not use assertions for argument checking in public methods. Argument checking is typically part of the published specifications (or contract) of a method, and these specifications must be obeyed whether assertions are enabled or disabled. Another problem with using assertions for argument checking is that erroneous arguments should result in an appropriate runtime exception (such as IllegalArgumentException, IndexOutOfBoundsException, or NullPointerException). An assertion failure will not throw an appropriate exception....

November 4, 2016 · 1 min · magicalne