zddava 发表于 2013-1-14 23:05:11

Derby源代码分析 -

一直以来都想研究下数据库的实现原理,Derby在我眼里绝对是一个完美的切入点。首先它是100%纯Java实现,对于我这种Java程序员来说简直完美的,不需要去懂C,C++...等等无疑减少了很大的难度;其次,它是开源的,当然这个就是废话,不然我去哪里弄它的源码去。废话少说,首先简单的介绍下Derby。

Derby的启动方式有两种,一种是网络服务器的方式,这种方式就和大家平时用的Oracle,SQL Server,DB2等等没什么区别,另外一种是嵌入式方式,是一种类似于Access的方式,在这种方式下,Derby与应用程序运行于同一个JVM下,随着应用程序的关闭而关闭(我很欣赏这个方式,呵呵)。另外,Java6中包含的Java DB其实就是Derby。具体的可以google下Derby,会有更加详细的介绍,我这里就不赘述了。

好了,简单的介绍后,来转入正题,看一下Derby服务器的启动上。

1. 首先来了解下网络服务器的方式

Derby有几种启动方式,其中一种就是用bin目录下的startNetworkServer.bat这个脚本文件。可以大致的看一下它,可以发现Derby网络服务器方式是在org.apache.derby.drda.NetworkServerControl类启动的,并且为命令行传入一个参数start。好了,下面就可以以这个类为起点来看Derby的启动部分了。

根据我对Derby源代码的大致理解,Derby的网络服务器方式的启动所涉及的类大致如下图:

http://zddava.iteye.com/upload/attachment/124532/47411fb4-cd2d-3ffc-90c8-56c9d0016b17.png

下面首先来看看NetworkServerControl的源代码吧,

public static void main(String args[]) {NetworkServerControlImpl server = null;boolean printErrors = true;try {server = new NetworkServerControlImpl();int command = server.parseArgs(args);if (needsSecurityManager(server, command)) {verifySecurityState(server);installSecurityManager(server);}printErrors = false;/* 执行命令 */server.executeWork(command);} catch (Exception e) {if ((e.getMessage() == null) || !e.getMessage().equals(NetworkServerControlImpl.UNEXPECTED_ERR) || printErrors) {if (server != null)server.consoleExceptionPrint(e);elsee.printStackTrace();}System.exit(1);}System.exit(0);}

这段代码没有什么复杂的,最主要的不服是调用了server的executeWork()方法,其中server是一个NetworkServerControlImpl的实例。
executeWork()方法主要是对各种不同的参数进行不同的调用,由于我们是看的启动部分,所以就只看一部分就可以了。

public void executeWork(int command) throws Exception {if (command == COMMAND_UNKNOWN)return;if (commandArgs.size() != COMMAND_ARGS)consolePropertyMessage("DRDA_InvalidNoArgs.U", COMMANDS);int min;int max;switch (command) {case COMMAND_START:shutdownDatabasesOnShutdown = true;/* 开启网络服务 */blockingStart(makePrintWriter(System.out));break;case COMMAND_SHUTDOWN:......}}

这里没什么可说的,主要就是要看这个blockStart()方法了,这个方法比较长,我把一些异常处理的部分去掉了

public void blockingStart(PrintWriter consoleWriter) throws Exception {/* 1. 载入Derby的Driver */startNetworkServer();setLogWriter(consoleWriter);cloudscapeLogWriter = Monitor.getStream().getPrintWriter();if (SanityManager.DEBUG && debugOutput) {memCheck.showmem();mc = new memCheck(200000);mc.start();}/* 2. 打开一个ServerSocket */try {serverSocket = (ServerSocket) AccessController.doPrivileged(new PrivilegedExceptionAction() {public Object run() throws IOException {return createServerSocket();}});} catch (PrivilegedActionException e) {......} catch (Exception e) {......}switch (getSSLMode()) {default:case SSL_OFF:consolePropertyMessage("DRDA_Ready.I", new String[] { Integer.toString(portNumber), att_srvclsnm, versionString });break;case SSL_BASIC:consolePropertyMessage("DRDA_SSLReady.I", new String[] { Integer.toString(portNumber), att_srvclsnm, versionString });break;case SSL_PEER_AUTHENTICATION:consolePropertyMessage("DRDA_SSLClientAuthReady.I", new String[] { Integer.toString(portNumber), att_srvclsnm, versionString });break;}/* 3. 新开一个ClientThread去接受用户请求 */final ClientThread clientThread = (ClientThread) AccessController.doPrivileged(new PrivilegedExceptionAction() {public Object run() throws Exception {return new ClientThread(thisControl, serverSocket);}});clientThread.start();ManagementService mgmtService = ((ManagementService) Monitor.getSystemModule(Module.JMX));final Object versionMBean = mgmtService.registerMBean(new Version(getNetProductVersionHolder(), SystemPermission.SERVER), VersionMBean.class, "type=Version,jar=derbynet.jar");final Object networkServerMBean = mgmtService.registerMBean(new NetworkServerMBeanImpl(this), NetworkServerMBean.class,"type=NetworkServer");/* 直到shutdown或者出现InterruptedException,否则一直wait() */synchronized (shutdownSync) {try {shutdownSync.wait();} catch (InterruptedException e) {shutdown = true;}}AccessController.doPrivileged(new PrivilegedAction() {public Object run() {if (mc != null)mc.interrupt();clientThread.interrupt();return null;}});/* 关闭Session连接 */synchronized (sessionTable) {for (Enumeration e = sessionTable.elements(); e.hasMoreElements();) {Session session = (Session) e.nextElement();session.close();}}/* 关闭DRDA协议客户端连接 */synchronized (threadList) {for (int i = 0; i < threadList.size(); i++) {final DRDAConnThread threadi = (DRDAConnThread) threadList.get(i);threadi.close();AccessController.doPrivileged(new PrivilegedAction() {public Object run() {threadi.interrupt();return null;}});}threadList.clear();}/* 关闭ServerSocket */try {serverSocket.close();} catch (IOException e) {consolePropertyMessage("DRDA_ListenerClose.S", true);}/* 唤醒等待中的线程 */synchronized (runQueue) {runQueue.notifyAll();}mgmtService.unregisterMBean(versionMBean);mgmtService.unregisterMBean(networkServerMBean);/* 关闭Derby */if (shutdownDatabasesOnShutdown) {try {if (cloudscapeDriver != null) {final Properties p = new Properties();if (userArg != null) {p.setProperty("user", userArg);}if (passwordArg != null) {p.setProperty("password", passwordArg);}cloudscapeDriver.connect("jdbc:derby:;shutdown=true", p);}} catch (SQLException sqle) {......}}consolePropertyMessage("DRDA_ShutdownSuccess.I", new String[] { att_srvclsnm, versionString });}

这段代码有3个地方需要仔细研究下,分别在上述代码中用注释标注的1,2,3处。

首先来看一下第1处,就是Derby驱动的载入,

protected void startNetworkServer() throws Exception {boolean restartCheck = this.restartFlag;synchronized (serverStartSync) {if (restartCheck == this.restartFlag) {try {/* 清理Session队列 */if (cleanupOnStart) {synchronized (runQueue) {for (int i = 0; i < runQueue.size(); i++) {Session s = (Session) runQueue.get(i);s.close();removeFromSessionTable(s.getConnNum());}runQueue.clear();}cloudscapeDriver = null; // so it gets collected.System.gc();}/* 先载入org.apache.derby.jdbc.EmbeddedDriver这个驱动,这个是嵌入式方式的驱动 *//* 在这里会载入嵌入式方式的驱动备用 */Class.forName(CLOUDSCAPE_DRIVER).newInstance();cloudscapeDriver = DriverManager.getDriver(Attribute.PROTOCOL);} catch (Exception e) {this.consoleExceptionPrintTrace(e);consolePropertyMessage("DRDA_LoadException.S", e.getMessage());}cleanupOnStart = true;this.restartFlag = !this.restartFlag;}}}

这里主要做的就是驱动的载入,这里还不清楚网络服务器方式载入嵌入式驱动的原因,随着研究的深入会慢慢了解的。

下面来看第二个重要部分,就是ServerSocket的创建,

private ServerSocket createServerSocket() throws IOException {if (hostAddress == null)hostAddress = InetAddress.getByName(hostArg);// 创建本地地址列表buildLocalAddressList(hostAddress);// 根据不同的情况来创建Socketswitch (getSSLMode()) {case SSL_OFF:default:ServerSocketFactory sf = ServerSocketFactory.getDefault();return sf.createServerSocket(portNumber, 0, hostAddress);case SSL_BASIC:SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();return (SSLServerSocket) ssf.createServerSocket(portNumber, 0,hostAddress);case SSL_PEER_AUTHENTICATION:SSLServerSocketFactory ssf2 = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();SSLServerSocket sss2 = (SSLServerSocket) ssf2.createServerSocket(portNumber, 0, hostAddress);sss2.setNeedClientAuth(true);return sss2;}}

最后一个部分是新开一个ClientThread去接受用户请求,由于ClientThread是一个线程类,可以看下它的#run()的实现。

public void run() {Socket clientSocket = null;for (;;) { // 用一个死循环去不断的处理客户端连接try { // 捕获所有异常的trytry { // 捕获InterruptedException,SSLException和IOExceptiontry { // 捕获PrivilegedActionException/* 从ServerSocket获得Socket */clientSocket = (Socket) AccessController.doPrivileged(new PrivilegedExceptionAction() {public Object run() throws IOException {return serverSocket.accept();}});/* 判断服务器是否关闭了 */if (parent.getShutdown()) {clientSocket.close();return;}/* 设定属性 */clientSocket.setKeepAlive(parent.getKeepAlive());if (timeSlice > 0)clientSocket.setSoTimeout(timeSlice);/* 加入Session队列 */parent.addSession(clientSocket);} catch (PrivilegedActionException e) {throw e.getException();}} catch (InterruptedException ie) {return;} catch (javax.net.ssl.SSLException ssle) {parent.consoleExceptionPrintTrace(ssle);parent.directShutdownInternal();return;} catch (IOException ioe) {synchronized (parent.getShutdownSync()) {if (!parent.getShutdown()) {parent.consoleExceptionPrintTrace(ioe);if (clientSocket != null)clientSocket.close();}}return;}} catch (Exception e) {parent.consoleExceptionPrintTrace(e);try {if (clientSocket != null)clientSocket.close();} catch (IOException closeioe) {parent.consoleExceptionPrintTrace(closeioe);}}}}

这样,当有新的Socket请求到了服务器时,会把这个请求加入到NetworkServerControlImpl的Session队列中等待处理。
页: [1]
查看完整版本: Derby源代码分析 -