网络上相关的资料很少,我又恰好要做这个,索性就把它写出来,我也是在摸索中,理解的不正确的地方这里道个歉,欢迎指正。也算是为amoeba的推广做的一点微不足道的贡献吧!
下图是我的eclipse的Amoeba工程(怎么导入的请看上篇文章)的目录的一部分:
图中的红色标记的就是Amoeba的启动文件。这里需要说明的是,amoeba使用的批处理的方式启动的,批处理里面首先是找到classword,然后加载类,而加载的第一个类就是上述那个类了。
下面打开AmoebaProxyServer.java文件,我为重要的语句加上了注释,请详细查看。
public class AmoebaProxyServer {
private static Logger log = Logger.getLogger(AmoebaProxyServer.class);
private static Logger repoterLog = Logger.getLogger("report");
//定义两个Logger文件,这是Log4j的用法,不是重点
/** Used to generate "state of server" reports. */
protected static ArrayList<Reporter> reporters = new ArrayList<Reporter>();
/** The time at which the server was started. */
protected static long serverStartTime = System.currentTimeMillis();
/** The last time at which {@link #generateReport} was run. */
protected static long lastReportStamp = serverStartTime;
public static void registerReporter(Reporter reporter) {
reporters.add(reporter);
}
/**
* Generates a report for all system services registered as a
* {@link Reporter}.
*/
public static String generateReport() {
return generateReport(System.currentTimeMillis(), false);
}
/**
* Generates and logs a "state of server" report.
*/
protected static String generateReport(long now, boolean reset) {
long sinceLast = now - lastReportStamp;
long uptime = now - serverStartTime;
StringBuilder report = new StringBuilder(" State of server report:"+StringUtil.LINE_SEPARATOR);
report.append("- Uptime: ").append(StringUtil.intervalToString(uptime)).append(StringUtil.LINE_SEPARATOR);
report.append("- Report period: ").append(StringUtil.intervalToString(sinceLast)).append(StringUtil.LINE_SEPARATOR);
// report on the state of memory
Runtime rt = Runtime.getRuntime();
long total = rt.totalMemory(), max = rt.maxMemory();
long used = (total - rt.freeMemory());
report.append("- Memory: ").append(used / 1024).append("k used, ");
report.append(total / 1024).append("k total, ");
report.append(max / 1024).append("k max").append(StringUtil.LINE_SEPARATOR);
for (int ii = 0; ii < reporters.size(); ii++) {
Reporter rptr = reporters.get(ii);
try {
rptr.appendReport(report, now, sinceLast, reset,repoterLog.getLevel());
} catch (Throwable t) {
log.error("Reporter choked [rptr=" + rptr + "].", t);
}
}
// only reset the last report time if this is a periodic report
if (reset) {
lastReportStamp = now;
}
return report.toString();
}
protected static void logReport(String report) {
repoterLog.info(report);
}
//上面都是与日志文件有关的操作,这些我们可以根据自己的需求进行修改
/**
* @param args
* @throws IOException
* @throws Exception
* @throws IllegalAccessException
* @throws InstantiationException
*/
//下面进入main开始开始执行
public static void main(String[] args) throws Exception {
String level = System.getProperty("benchmark.level", "warn");
System.setProperty("benchmark.level", level);
if(args.length>=1){
ShutdownClient client = new ShutdownClient(MonitorConstant.APPLICATION_NAME);
/**
* APPLICATION_NAME是一个常量,这个常量定义在MonitorConstant.java文件中,这个文件在Amoeba/src/java/com/meidusa/amoeba/monitoer中
* ShutdownClient是一个监视类,由它的run函数负责获取网络数据包并对其进行分析建立连接等。
* ShutdownClient的对象client在下面的第二个if中调用了run函数,这个run函数执行成功的话,则输出amoeba server is running with ...的信息
* 这样就是我们看到的amoeba启动时显示的原因了。
*
*/
MonitorCommandPacket packet = new MonitorCommandPacket();
if("start".equalsIgnoreCase(args[0])){
packet.funType = MonitorCommandPacket.FUN_TYPE_PING;
if(client.run(packet)){
/**
* 根据上一个注释可以知道关键及时这个client的run函数了。
* 分析run函数之前,先关注它的参数packet,packet的类型是MonitorCommandPacket。
* 从MonitorCommandPacket.java可以知道这个类继承了AbstractPacket类。
* AbstractPacket类在src/amoeba/src/java/net/packet中。看它的源码知道,这个类是抽象类
* Packet的派生类。
* 进入client的run函数后,请转到run函数继续看分析
*/
System.out.println("amoeba server is running with port="+client.getPort());
System.exit(-1);
}
}else{
packet.funType = MonitorCommandPacket.FUN_TYPE_AMOEBA_SHUTDOWN;
if(client.run(packet)){
System.out.println("amoeba server shutting down with port="+client.getPort());
}else{
System.out.println("amoeba server not running with port="+client.getPort());
}
System.exit(0);
}
}else{
System.out.println("amoeba start|stop");
System.exit(0);
}
//执行完上面的if块后到这里继续执行。
String log4jConf = System.getProperty("log4j.conf","${amoeba.home}/conf/log4j.xml");
log4jConf = ConfigUtil.filter(log4jConf);
File logconf = new File(log4jConf);
if(logconf.exists() && logconf.isFile()){
DOMConfigurator.configureAndWatch(logconf.getAbsolutePath(), System.getProperties());
}
final Logger logger = Logger.getLogger(AmoebaProxyServer.class);
String config = System.getProperty("amoeba.conf","${amoeba.home}/conf/amoeba.xml");
String contextClass = System.getProperty("amoeba.context.class",ProxyRuntimeContext.class.getName());
if(contextClass != null){
ProxyRuntimeContext context = (ProxyRuntimeContext)Class.forName(contextClass).newInstance();
ProxyRuntimeContext.setInstance(context);
}
config = ConfigUtil.filter(config);
File configFile = new File(config);
if(config == null || !configFile.exists()){
logger.error("could not find config file:"+configFile.getAbsolutePath());
System.exit(-1);
}else{
ProxyRuntimeContext.getInstance().init(configFile.getAbsolutePath());
}
registerReporter(ProxyRuntimeContext.getInstance());
for(ConnectionManager connMgr :ProxyRuntimeContext.getInstance().getConnectionManagerList().values()){
registerReporter(connMgr);
}
Map<String,Object> context = new HashMap<String,Object>();
context.putAll(ProxyRuntimeContext.getInstance().getConnectionManagerList());
List<BeanObjectEntityConfig> serviceConfigList = ProxyRuntimeContext.getInstance().getConfig().getServiceConfigList();
for(BeanObjectEntityConfig serverConfig : serviceConfigList){
Service service = (Service)serverConfig.createBeanObject(false,context);
service.init();
service.start();
PriorityShutdownHook.addShutdowner(service);
registerReporter(service);
}
//一直到这里进入线程,每六十秒一次循环
new Thread(){
{
this.setDaemon(true);
this.setName("Amoeba Report Thread");
}
public void run(){
while(true){
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
}
try{
logReport(generateReport());
}catch(Exception e){
logger.error("report error",e);
}
}
}
}.start();
}
}
上述代码从client.run(packet)函数开始就进入到了类ShutdownClient.java文件的run函数执行了。run函数的片断注释如下:
public boolean run(MonitorCommandPacket command) {
//从AmoebaProxyServer类的main函数转到这里继续执行
//可以看到正常启动Amoeba的情况下(因为没有连接建立)这个if都能成立的
if(port <=0){
socketInfoFile = new File(ConfigUtil.filter("${amoeba.home}"),appplicationName+".shutdown.port");
if(!socketInfoFile.exists()){
System.out.println("b");//所以这个输出是可以得到的,然后返回false,又回到main函数中去了
return false;
}
//当有连接建立时,上面的if块就不执行,而是执行下面的块
try {
BufferedReader reader = new BufferedReader(new FileReader(socketInfoFile));
String sport = reader.readLine();
String tmp[] = StringUtil.split(sport, ":");
if(tmp.length <=1){
System.out.println("c");
return false;
}
this.port = Integer.parseInt(tmp[1]);
this.host = tmp[0];
reader.close();
}catch (Exception e) {
e.printStackTrace();
System.out.println("d");
return false;
}
}
启动过程中,上面的run函数会返回false,回到main函数中去。根据main的代码知道下面代码:
if(client.run(packet)){
/**
* 根据上一个注释可以知道关键及时这个client的run函数了。
* 分析run函数之前,先关注它的参数packet,packet的类型是MonitorCommandPacket。
* 从MonitorCommandPacket.java可以知道这个类继承了AbstractPacket类。
* AbstractPacket类在src/amoeba/src/java/net/packet中。看它的源码知道,这个类是抽象类
* Packet的派生类。
* 进入client的run函数后,请转到run函数继续看分析
*/
System.out.println("amoeba server is running with port="+client.getPort());
System.exit(-1);
}
会跳出这个if,然后跳到main函数的下列代码处执行:
//执行完上面的if块后到这里继续执行。
String log4jConf = System.getProperty("log4j.conf","${amoeba.home}/conf/log4j.xml");
log4jConf = ConfigUtil.filter(log4jConf);
File logconf = new File(log4jConf);
if(logconf.exists() && logconf.isFile()){
DOMConfigurator.configureAndWatch(logconf.getAbsolutePath(), System.getProperties());
}
final Logger logger = Logge
最后进入while循环,到此amoeba的启动算是完成了,然后就是一直的监听了。