现在看的代码版本还是3.2.2 develop。先看张内部结构代码图:
从依赖层次再来看,越是被依赖的,越在底层:
rocketmq包含9个子模块:
PooledHeapByteBuf,带有池的堆内buffer,顾名思义,肯定比一般通过new出来的buffer性能好。把对象放入对象池缓存起来,一般都是因为创建该对象开销比较大,常见的有线程池(ThreadPool)、连接池(ConnectionPool)等。
PooledHeapByteBuf继承关系如下:
PooledHeapByteBuf –》 PooledByteBuf –》 AbstractReferenceCountedByteBuf –》 AbstractByteBuf –》 ByteBuf。
继承关系比较简单清晰。
先介绍几个相关的类:
PooledByteBufAllocator:buffer分配器,用来分配buffer(包括堆内和堆外)。
PoolArena:一块逻辑上的内存池,用来管理和组织buffer的,内部数据结构较复杂。
FastThreadLocal:较快的ThreadLocal(相对于jdk自带的),实现:线程T扩展于FastThreadLocalAccess,InternalThreadLocalMap是它的成员变量,set()时放入InternalThreadLocalMap的成员变量数组,下标是index,get()时从InternalThreadLocalMap的成员变量数组中下标是index处取。
GraphicsMagick官网地址:http://www.graphicsmagick.org/,介绍不多说网上都有,我这里只使用了它图片缩放功能,用java调用练习。
第一步:下载安装。
我下载的是:GraphicsMagick-1.3.20-Q8-win32-dll.exe,安装目录:C:\Program Files (x86)\GraphicsMagick-1.3.20-Q8。安装完毕后,安装目录自动配置到环境变量的Path中了,安装目录里有gm.exe。测试一下,gm -version,如果打印出GraphicsMagick相关信息则ok。这时就可以在命令行里使用gm命令了。
如果使用java调用报错:Caused by: java.io.IOException: CreateProcess error=2。则表示没有gm命令找不到,也就是说环境变量有可能更改了但是没有生效,我的做法是重启eclipse,运行同样程序就ok。如果还是报错,可能环境变量还是没有生效,重启电脑,又或者是安装目录压根就没有配置到环境变量Path中去,手动配置即可。
第二步:写代码。
RocketMQ是什么?
引用官方描述:
RocketMQ是一款分布式、队列模型的消息中间件,具有以下特点:
支持严格的消息顺序
支持Topic与Queue两种模式
亿级消息堆积能力
比较友好的分布式特性
同时支持Push与Pull方式消费消息
历经多次天猫双十一海量消息考验
RocketMQ是纯java编写,基于通信框架Netty。
代码地址:https://github.com/alibaba/RocketMQ,目前分支是3.2.2 develop。
下载完代码后,将各个模块导入eclipse,本地尝试启动看看。
1.启动nameServer,运行rocketmq-namesrv的NamesrvStartup,运行之前需设置环境变量ROCKETMQ_HOME为RocketMQ项目的根目录,这样有一个作用是,指向logback的配置文件路径,保证在nameServer启动时,logback的正常初始化。我本机设置的是:ROCKETMQ_HOME=C:\Users\Administrator\git\RocketMQ。
The Name Server boot success. 表示启动成功。
2.启动brokerServer,运行rocketmq-broker的BrokerStartup,同样,运行之前需设置环境变量ROCKETMQ_HOME,然后启动参数需要带上【-n “192.168.0.109:9876″】,我本机的ip是192.168.0.109。如果不带-n的参数,那么broker会去访问http://jmenv.tbsite.net:8080/rocketmq/nsaddr获取nameServer的地址,这个地址不是我们自己的nameServer。
The broker[LENOVO-PC, 192.168.0.109:10911] boot success. and name server is 192.168.0.109:9876表示成功。
3.这个非必选项,不运行也可以。还可以启动rocketmq-srvutil的FiltersrvStartup,这是Consumer使用Java代码,在服务器做消息过滤。启动方式和broker一样,具体的过滤原理以后再详细的说。
到此就可以运行demo了。
继续阅读
log4j是很常用的日志框架,这里总结几个小知识点:
1. logger是以名称为key,logger为value的形式存储在Hashtable里,所以,logger作为入参不需要传入引用,直接输入名称get即可。
LogFactoryImpl.java
/** * The {@link org.apache.commons.logging.Log} instances that have * already been created, keyed by logger name. */ protected Hashtable instances = new Hashtable(); public Log getInstance(String name) throws LogConfigurationException { Log instance = (Log) instances.get(name); if (instance == null) { instance = newInstance(name); instances.put(name, instance); } return (instance); }
2. logger的继承关系是根据名称的“.”分割来区分,表现为子logger打印日志时遍历父logger里的appender的进行日志打印。
Category.java
public void callAppenders(LoggingEvent event) { int writes = 0; for(Category c = this; c != null; c=c.parent) { // Protected against simultaneous call to addAppender, removeAppender,... synchronized(c) { if(c.aai != null) { writes += c.aai.appendLoopOnAppenders(event); } if(!c.additive) { break; } } } if(writes == 0) { repository.emitNoAppenderWarning(this); } }
3.定义模块名为常量,按模块名称取logger,解决项目里包名类名相同日志无法区分问题。
贴代码:
示例一
package com.zoo; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.PropertyConfigurator; /** * * @author yankai913@gmail.com * @date 2014年12月22日 */ public class LogTest { static final Log log = LogFactory.getLog(LogTest.class); public static void main(String[] args) { Properties properties = new Properties(); properties.setProperty("log4j.rootLogger", "DEBUG,stdout"); properties.setProperty("log4j.appender.stdout", "org.apache.log4j.ConsoleAppender"); properties.setProperty("log4j.appender.stdout.layout", "org.apache.log4j.PatternLayout"); properties.setProperty("log4j.appender.stdout.layout.ConversionPattern", "%d [%t] %-5p %C{6} (%F:%L) - %m%n"); // log4j的继承关系是"." // log --> com.zoo.LogTest // log2(父log) --> com.zoo // log3(父log2) --> com // 以此类推 // 子logger的继承关系表现是,子logger打印日志时,遍历父logger里的appender // 进行日志打印,"com.zoo.LogTest"的父子logger分别是: // "com.zoo.LogTest"的logger -> "com.zoo"的logger -> "root"的logger。 // 以下这句话定义了上述的log2(即父log),导致日志打印2次。 properties.setProperty("log4j.logger.com.zoo", "INFO, stdout"); // 以下这句话定义com.zoo包的logger(即log2)的appender不被继承,日志打印只有一条。 properties.setProperty("log4j.additivity.com.zoo", "false"); PropertyConfigurator.configure(properties); System.out.println(log); log.info("Hello World!"); System.out.println("end"); } }
示例二
package com.zoo; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.PropertyConfigurator; /** * * @author yankai913@gmail.com * @date 2014年12月22日 */ public class LogTest2 { // 定义公共的logerName,按module分 static final String Module1_LogName = "Module1"; static final String Module2_LogName = "Module2"; public static void main(String[] args) { Properties properties = new Properties(); properties.setProperty("log4j.rootLogger", "DEBUG,file"); properties.setProperty("log4j.appender.file", "org.apache.log4j.FileAppender"); properties.setProperty("log4j.appender.file.layout", "org.apache.log4j.PatternLayout"); properties.setProperty("log4j.appender.file.layout.ConversionPattern", "%d [%t] %-5p %c{1} %C{6} (%F:%L) - %m%n"); properties.setProperty("log4j.appender.file.append", "true"); properties.setProperty("log4j.appender.file.file", "logtest2.log"); PropertyConfigurator.configure(properties); // 比较同名称的log,结果为true,所以log作为入参不需要传入引用,直接get即可。 System.out.println(Module1.log.equals(ServiceImpl_1.log)); System.out.println(Module2.log.equals(ServiceImpl_2.log)); // 按模块搜索查看日志,关键词就是模块名称,即上面的Module1,Module2。 // 解决包名类名完全一样,但是在不同模块的日志查询。 Module1.println(); Module2.println(); ServiceImpl_1.println(); ServiceImpl_2.println(); System.out.println("end"); } public static class Module1 { public static Log log = LogFactory.getLog(Module1_LogName); public static void println() { log.info("this is module1"); } } public static class Module2 { public static Log log = LogFactory.getLog(Module2_LogName); public static void println() { log.info("this is module2"); } } public static class ServiceImpl_1 { public static Log log = LogFactory.getLog(Module1_LogName); public static void println() { log.info("this is service"); } } public static class ServiceImpl_2 { public static Log log = LogFactory.getLog(Module2_LogName); public static void println() { log.info("this is service"); } } }
平时,我们用maven来管理项目的依赖包,可是有时候还是会出现jar包多版本同时存在的问题,这时,我们可以利用classloader的隔离机制来解决。
1.我们自定义classloader,然后设置它为应用的contextClassLoader运行。
2.自定义classLoader加载特定目录的jar包,同时把加载过的Class缓存起来,这时classLoader就起到隔离作用,像一个容器,只装自己的jar包。
3.Class的查找机制,如果自定义classLoader里加载过,取出来直接返回,没有则用父classLoader查找加载。
4.自定义classLoader加载自己指定目录的jar包,父classLoader则加载classpath里的包或者类。
可能说的比较简洁,大家感兴趣还是直接看代码吧。
练习代码看这里
用go语言写项目时,难免会引用到一些Github上的开源项目,下面说说在开发时如何引用。
系统:windows
Go:1.3.1
开发工具:LiteIDE
比如:
import ( "bytes" "fmt" "github.com/yankai913/go-tools/timetool" "net" "os" )
当正确引用后,在写“timetool.”时,后面接着会有提示。
步骤:
前提是配好go的环境变量,包括GOROOT和GOPATH。LiteIDE分系统GOPATH和用户自定义GOPATH。
示例要引用的项目地址是:https://github.com/yankai913/go-tools
1. 打开cmd,执行go get github.com/yankai913/go-tools,会把项目源代码下载下来,同时控制台输出 package github.com/yankai913/go-tools等字样以及下载到本地的地址,我的本地地址是C:\Go\bin\src\github.com\yankai913\go-tools。
2. 进入go-tools的timetool包,即执行 cd C:\Go\bin\src\github.com\yankai913\go-tools\timetool,然后,编译,go build,安装,go install。完成后,记得刷新GOPATH,我用的LiteIDE,“package浏览”,“重新加载所有”就行了,此时在编辑器里输入“包名+点”,后面跟着提示信息。
另外,如果你需要go-tools里其他的package,那么你只需要进入相应的包里,执行编译,安装,即可。
最近新做的项目使用了webx,项目做完后,对webx还不是很有感觉,只知道webx是基于springext的,然后上网查了下springext,结果都是照搬webx官网文档里讲springext部分,没有自己的见解和代码,所以就打算自己写个demo练练手。
先贴代码,练习代码看这里。
webx官方文档地址,http://www.openwebx.org/。
看图说话:
1.看练习代码之前建议还是先看下webx官方文档里讲的springext部分,了解下概念和装插件。
2.文件《spring.configuration-points》是用来定义扩展点的。里面的内容定义是:“扩展点名称=uri命名空间; nsPrefix=前缀”。注意,扩展点名称推荐用“/”来区分,要唯一。
3.扩展点名称.bean-definition-parsers用来定义捐献文件,里面扩展点名称places/countries这里要转换成places-countries。
4.为捐献定义的schema的路径与扩展点的命名空间相匹配,扩展点是places,捐献是country,那么schema的路径就是“/META-INF/places/country.xsd”。
5.spring.schemas和META-INF/springext/springext-base.xsd可要可不要,因为webx框架已经包含定义。
6.springext和spring完全兼容。
7.schema的编写基础知识了解一点。
感觉大概就是这么多了,主要还是看代码,有兴趣的哥们可以上去看下。
这几天在看ClassLoader,推荐一篇讲ClassLoader的文章,地址:http://longdick.iteye.com/blog/442213,然后又看了下《深入java虚拟机》里的ClassLoader章节,下面就随便说点,主要还是以练习代码为主。
1.对于任意一个类,由加载它的ClassLoader和它本身决定了在java虚拟机中的唯一性。
也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。否则,即使是同一个类文件,如果加载它们的ClassLoader不同,那么这2个类必定不相等。
2.类加载的双亲委派机制是可以破坏的,通过改变CallClassLoader和ContextClassLoader。
被当前类引用的类的加载也是由加载当前类的ClassLoader加载,子线程的ContextClassLoader是由父线程的ContextClassLoader派生出来。
3.扩展ClassLoader一般应该建议重写findClass(String)方法。
原因是自定义的ClassLoader只专注于加载自己的Class,在加载自己的Class的过程中,又会先加载父类Object,Object类是由启动类加载器加载的,默认可以直接由loadClass(String)执行,这么做可以用ClassLoader来选择性加载某一些类做隔离。
贴代码
自定义ClassLoader,重写loadClass()。
@Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) { return super.loadClass(name); } byte[] b = new byte[is.available()]; is.read(b); return defineClass(name, b, 0, b.length); } catch (Exception e) { throw new ClassNotFoundException(name); } }
测试类
public void say() { System.out.println("hello world"); } public static void main(String[] args) throws Exception { // 打印java虚拟机的ClassLoader System.out.println(Thread.currentThread().getContextClassLoader()); System.out.println(Thread.currentThread().getContextClassLoader().getParent()); System.out.println(Thread.currentThread().getContextClassLoader().getParent().getParent()); // 自定义ClassLoader ClassLoader myLoader = new MyClassLoader(); Class<?> clazz = myLoader.loadClass("com.zoo.classloader.ClassLoaderTest"); Object obj = clazz.newInstance(); // 打印自定义ClassLoader加载的Class对象 System.out.println(obj.getClass()); // 打印被加载的Class对象是由哪个ClassLoader加载的 System.out.println(obj.getClass().getClassLoader()); /* * 对于任意一个类,由加载它的ClassLoader和它本身决定了在jvm虚拟机中的唯一性。 * 也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。 * 否则,即使是同一个类文件,只要加载它们的ClassLoader不同,那么这2个类必定不相等。 */ System.out.println(obj instanceof com.zoo.classloader.ClassLoaderTest); // 由自定义ClassLoader加载后,在程序里运行。 Method method = clazz.getDeclaredMethod("say", new Class<?>[] {}); method.invoke(obj, new Object[] {}); // 获取当前上下文的ClassLoader System.out.println(Thread.currentThread().getContextClassLoader()); // 改变上下文的ClassLoader Thread.currentThread().setContextClassLoader(myLoader); // 获取当前上下文的ClassLoader System.out.println(Thread.currentThread().getContextClassLoader()); // 改变当前上下文的ClassLoader可以改变在当前线程派生出的子线程的上下文ClassLoader Thread t = new Thread(new Runnable() { @Override public void run() { Thread t2 = Thread.currentThread(); System.out.println(t2.getName() + ":" + t2.getContextClassLoader()); } }); t.start(); Thread.sleep(3000); // 获取CallClassLoader Class<?> clz = Class.forName("com.zoo.classloader.ClassLoaderTest"); System.out.println(clz); System.out.println(clz.getClassLoader()); System.out.println(clz.getClass()); System.out.println(clz.getClass().getClassLoader()); }
执行结果:
sun.misc.Launcher$AppClassLoader@addbf1 ==> 当前上下文类加载器是应用类加载器
sun.misc.Launcher$ExtClassLoader@42e816 ==> 应用类加载器的父类加载器是扩展类加载器
null ==> 扩展类加载器的父类加载器是启动类加载器,因为启动类加载器是C++编写,java里获取不到
class com.zoo.classloader.ClassLoaderTest ==> 自定义类加载器加载进行加载
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 通过Class对象获取它的类加载器
false ==> 同一个Class,不同的类加载器加载,那么Class不相等
hello world ==> 被自定义类加载器加载的Class在程序中执行
sun.misc.Launcher$AppClassLoader@addbf1 ==> 获取当前上下文类加载器
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 设置上下文类加载器
Thread-0:com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 派生出来的子线程的类加载器
class com.zoo.classloader.ClassLoaderTest ==> 默认加载类
sun.misc.Launcher$AppClassLoader@addbf1 ==> 默认加载用的是应用类加载器
class java.lang.Class ==> 获取Class
null ==> 获取类加载器,由于是启动类加载器,所以为null,java基础类是由启动类加载器加载。