隨筆-72  評論-20  文章-0  trackbacks-1

          本文目的在于分析Jetspeed支持集群的現(xiàn)狀。首先介紹了集群計(jì)算的背景知識(shí),然后使用tomcat作為例子配置了一個(gè)集群,接著分析了 jetspeed對集群的支持現(xiàn)狀,提出了解決這些問題的辦法,最后詳細(xì)解釋了jetspeed保存sesson數(shù)據(jù)的操作,這將對jetspeed的改造有幫助。

          1 、 集群背景介紹

          1.1 術(shù)語定義

          服務(wù)軟體是b/s或c/s結(jié)構(gòu)的s部分,是為b或c提供服務(wù)的服務(wù)性軟件系統(tǒng)。

          服務(wù)硬體指提供計(jì)算服務(wù)的硬件、比如pc機(jī)、pc服務(wù)器。

          服務(wù)實(shí)體通指服務(wù)軟體和服務(wù)硬體。

          客戶端指接受服務(wù)實(shí)體服務(wù)的軟件或硬件。

          1.2 兩大關(guān)鍵特性

          集群是一組協(xié)同工作的服務(wù)實(shí)體,用以提供比單一服務(wù)實(shí)體更具擴(kuò)展性與可用性的服務(wù)平臺(tái)。在客戶端看來,一個(gè)集群就象是一個(gè)服務(wù)實(shí)體,但事實(shí)上集群由一組服務(wù)實(shí)體組成。與單一服務(wù)實(shí)體相比較,集群提供了以下兩個(gè)關(guān)鍵特性:

          可擴(kuò)展性--集群的性能不限于單一的服務(wù)實(shí)體,新的服務(wù)實(shí)體可以動(dòng)態(tài)地加入到集群,從而增強(qiáng)集群的性能。

          高可用性--集群通過服務(wù)實(shí)體冗余使客戶端免于輕易遇到out of service的警告。在集群中,同樣的服務(wù)可以由多個(gè)服務(wù)實(shí)體提供。如果一個(gè)服務(wù)實(shí)體失敗了,另一個(gè)服務(wù)實(shí)體會(huì)接管失敗的服務(wù)實(shí)體。集群提供的從一個(gè)出錯(cuò)的服務(wù)實(shí)體恢復(fù)到另一個(gè)服務(wù)實(shí)體的功能增強(qiáng)了應(yīng)用的可用性。

          1.3 兩大能力

          為了具有可擴(kuò)展性和高可用性特點(diǎn),集群的必須具備以下兩大能力:

          負(fù)載均衡--負(fù)載均衡能把任務(wù)比較均衡地分布到集群環(huán)境下的計(jì)算和網(wǎng)絡(luò)資源。

          錯(cuò)誤恢復(fù)--由于某種原因,執(zhí)行某個(gè)任務(wù)的資源出現(xiàn)故障,另一服務(wù)實(shí)體中執(zhí)行同一任務(wù)的資源接著完成任務(wù)。這種由于一個(gè)實(shí)體中的資源不能工作,另一個(gè)實(shí)體中的資源透明的繼續(xù)完成任務(wù)的過程叫錯(cuò)誤恢復(fù)。

          負(fù)載均衡和錯(cuò)誤恢復(fù)都要求各服務(wù)實(shí)體中有執(zhí)行同一任務(wù)的資源存在,而且對于同一任務(wù)的各個(gè)資源來說,執(zhí)行任務(wù)所需的信息視圖(信息上下文)必須是一樣的。

          1.4 兩大技術(shù)

          實(shí)現(xiàn)集群務(wù)必要有以下兩大技術(shù):

          集群地址--集群由多個(gè)服務(wù)實(shí)體組成,集群客戶端通過訪問集群的集群地址獲取集群內(nèi)部各服務(wù)實(shí)體的功能。具有單一集群地址(也叫單一影像)是集群的一個(gè)基本特征。維護(hù)集群地址的設(shè)置被稱為負(fù)載均衡器。負(fù)載均衡器內(nèi)部負(fù)責(zé)管理各個(gè)服務(wù)實(shí)體的加入和退出,外部負(fù)責(zé)集群地址向內(nèi)部服務(wù)實(shí)體地址的轉(zhuǎn)換。有的負(fù)載均衡器實(shí)現(xiàn)真正的負(fù)載均衡算法,有的只支持任務(wù)的轉(zhuǎn)換。只實(shí)現(xiàn)任務(wù)轉(zhuǎn)換的負(fù)載均衡器適用于支持ACTIVE-STANDBY的集群環(huán)境,在那里,集群中只有一個(gè)服務(wù)實(shí)體工作,當(dāng)正在工作的服務(wù)實(shí)體發(fā)生故障時(shí),負(fù)載均衡器把后來的任務(wù)轉(zhuǎn)向另外一個(gè)服務(wù)實(shí)體。

          內(nèi)部通信--為了能協(xié)同工作、實(shí)現(xiàn)負(fù)載均衡和錯(cuò)誤恢復(fù),集群各實(shí)體間必須時(shí)常通信,比如負(fù)載均衡器對服務(wù)實(shí)體心跳測試信息、服務(wù)實(shí)體間任務(wù)執(zhí)行上下文信息的通信。

          具有同一個(gè)集群地址使得客戶端能訪問集群提供的計(jì)算服務(wù),一個(gè)集群地址下隱藏了各個(gè)服務(wù)實(shí)體的內(nèi)部地址,使得客戶要求的計(jì)算服務(wù)能在各個(gè)服務(wù)實(shí)體之間分布。內(nèi)部通信是集群能正常運(yùn)轉(zhuǎn)的基礎(chǔ),它使得集群具有均衡負(fù)載和錯(cuò)誤恢復(fù)的能力。

          2 集群配置

           



          從上圖可知,由服務(wù)實(shí)體1、服務(wù)實(shí)體2和負(fù)載均衡器組成了一個(gè)集群。服務(wù)實(shí)體1和服務(wù)實(shí)體2參與對客戶端的服務(wù)支持工作,均衡負(fù)載器為客戶端維護(hù)集群的單一影像。集群實(shí)體間通過內(nèi)部的通信網(wǎng)交流信息,這種交流機(jī)制一般采用組播協(xié)議。負(fù)載均衡器通過內(nèi)部通信網(wǎng)探測各服務(wù)實(shí)體的心跳信息,服務(wù)實(shí)體間通過內(nèi)部通信網(wǎng)完成任務(wù)資源的傳播。可以看出,配置集群主要由配置服務(wù)實(shí)體和配置負(fù)載均衡器兩部分組成。本文使用tomcat 4.12、apache 2.0.43配置集群環(huán)境,相關(guān)軟件的部署圖如下:

           



          服務(wù)實(shí)體1/2,負(fù)載均衡器可以部署在不同的機(jī)器上,也可以在同一機(jī)器上,本文環(huán)境為同一機(jī)器。

          2.1 準(zhǔn)備軟件

          tomcat是開源servlet \jsp服務(wù)器,下載地點(diǎn)http://jakarta.apache.org/ ;

          apache 2.0.43 是開源的www服務(wù)器,下載地點(diǎn)http://www.apache.org/dist/httpd/binaries/ ;

          JavaGroups是一個(gè)實(shí)現(xiàn)集群服務(wù)實(shí)體間通信的通信協(xié)議,下載地址:http://www.javagroups.com/ ;

          Tomcat 會(huì)話復(fù)制庫,基于JavaGroups通信協(xié)議,完成集群服務(wù)實(shí)體間任務(wù)執(zhí)行上下文的復(fù)制,下載地址: http://www.filip.net/tomcat/tomcat-javagroups.jar

          jk2模塊,jk 是mod_jserv的替代者,它是Tomcat-Apache插件,處理Tomcat和Apache之間的通信,在集群配置中充當(dāng)負(fù)載均衡器的作用。 JK2是符合apache 2.x系列的新品,下載地址:http://jakarta.apache.org/builds/jakarta-tomcat- connectors/jk2/release/v2.0.2/bin/ 。

          2.2 配置負(fù)載均衡器

          在apache下配置負(fù)載均衡器分為三步,注意每次修改httpd.conf和workers2.properties時(shí)不要忘了重新啟動(dòng)apache。

          第一步,安裝和調(diào)試apache

          負(fù)載均衡器jk2模塊是apache www 服務(wù)的插件,所以配置負(fù)載均衡器就得先安裝apache。本文下載的是windows版本 2.0.43,執(zhí)行setup.exe并回答一些簡單問題就可完成apache的任務(wù)。值得注意的是,安裝并啟動(dòng)apache后如果apache對 http://localhost/ 地址沒反應(yīng),你得修改apache安裝路徑下htdocs目錄下的index.html.xx文件,比如把index.html.en改成 index.html。

          第二步,安裝jk2

          把下載到的mod_jk2-2.0.43.dll 改成mod_jk2.dll 放到apache的modules目錄下,修改apache的httpd.conf,即在LoadModule foo_module modules/mod_foo.so 行下插入mod_jk2模塊的裝載信息:

          # Example:

          # LoadModule foo_module modules/mod_foo.so

          #

          LoadModule jk2_module modules/mod_jk2.dll

          第三步,配置jk2

          jk2的配置全在一個(gè)配置文件中,文件名為workers2.properties,和apache 的httpd.conf放在同一個(gè)目錄下。以下是這個(gè)文件的內(nèi)容:

          #++++++++++++++++++++++++++++++++++++

          # only at beginnin. In production uncomment it out

          [logger.apache2]

          level=DEBUG

          #shm必須配

          [shm]

          file=D:\Program Files\Apache Group\Apache2\logs\shm.file

          size=1048576

          # 第一個(gè)tomcat 的地址

          # Example socket channel, override port and host.

          [channel.socket:tomcat1]

          port=11009

          host=127.0.0.1

          # 定義第一個(gè)工作者指向第一個(gè)tomcat

          # define the worker

          [ajp13:tomcat1]

          channel=channel.socket:tomcat1

          #第二個(gè)tomcat 得地址

          # Example socket channel, override port and host.

          [channel.socket:tomcat2]

          port=12009

          host=10.1.36.123

          # 定義第二個(gè)工作者指向第二個(gè)tomcat

          # define the worker

          [ajp13:tomcat2]

          channel=channel.socket:tomcat2

          #定義負(fù)載均衡器,使其包含兩個(gè)工作者

          [lb:lb1]

          worker=ajp13:tomcat2

          worker=ajp13:tomcat1

          #指定負(fù)載均衡器完成單一地址映射,使得apache 服務(wù)所在的uri全部指向 兩個(gè)#tomcat 上的 root Uri mapping

          [uri:/*]

          group=lb:lb1

          #++++++++++++++++++++++++++++++++++++++++++

          對于jk2模塊的負(fù)載均衡配置可參見相關(guān)站點(diǎn),值得提及的是jk2的負(fù)載均衡還支持權(quán)重分配等優(yōu)秀功能。

          2.3 配置tomcat

          同屬于一個(gè)集群下的兩個(gè)服務(wù)實(shí)體,要求功能的同一性,所以我們可先安裝和配置第一個(gè)tomcat,接著拷貝形成第二個(gè)tomcat,最后配置第二個(gè)tomcat。

          2.3.1 安裝第一個(gè)tomcat

          安裝tomcat 非常簡單,本文就不再描述。我們假設(shè)第一個(gè)tomcat的安裝路徑為d:\tomcat1。

          拷貝tomcat-javagroups.jar和javagroups.jar到d:\tomcat1\ server\lib 路徑下。

          2.3.2 配置第一個(gè)tomcat

          2.3.2.1 配置jk2

          tomcat 中的jk2 connector缺省端口為8009,為了在一臺(tái)機(jī)器上運(yùn)行兩個(gè)tomcat,修改D:\Tomcat1\conf\jk2.properties,設(shè)置jk2 connector的端口為11009,整個(gè)文件內(nèi)容如下:

          #++++++++++++++

          channelSocket.port=11009

          #++++++++++++++

          2.3.2.2 修改server.conf

          首先為了讓一臺(tái)機(jī)器上運(yùn)行兩個(gè)tomcat,修改server.conf的tomcat 停止指令監(jiān)聽端口:

          改為



          然后打開JK2 AJP connector ,關(guān)閉其它c(diǎn)onnector,下面是JK2 AJP 1.3的樣子,這里已把它的端口改為11009:




          port="11009" minProcessors="5" maxProcessors="75"

          enableLookups="true" redirectPort="8443"

          acceptCount="10" debug="0" connectionTimeout="20000"

          useURIValidationHack="false"

          protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/>

          接著配置需要集群支持的webapp(比如examples) 的context,添加如下manager:


          className="org.apache.catalina.session.InMemoryReplicationManager"

          protocolStack="UDP(mcast_addr=228.1.2.3;mcast_port=45566;ip_ttl=32):PING(timeout=3000;

          num_initial_members=6):FD(timeout=5000):VERIFY_SUSPECT(timeout=1500):

          pbcast.STABLE(desired_avg_gossip=10000):pbcast.NAKACK(gc_lag=10;

          retransmit_timeout=3000):UNICAST(timeout=5000;min_wait_time=2000):

          MERGE2:FRAG:pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;

          shun=false;print_local_addr=false)">



          注意protocolStack的值必須在一行內(nèi)寫完。

          2.3.3 配置第二個(gè)tomcat

          我們先把已經(jīng)配好的第一個(gè)tomcat復(fù)制一份,形成第二個(gè)tomcat,假設(shè)路徑為d:\tomcat2。

          2.3.3.1 配置jk2

          修改D:\Tomcat2\conf\jk2.properties,設(shè)置jk2 connector的端口12009,整個(gè)文件內(nèi)容如下:

          #++++++++++++++

          channelSocket.port=12009

          #++++++++++++++

          2.3.3.2 修改server.conf

          有了第一個(gè)tomcat的配置我們只需修改server.conf的tomcat 停止指令監(jiān)聽端口:

          改為



          然后設(shè)置JK2 AJP connector 端口為12009。

          2.4 運(yùn)行測試

          啟動(dòng)apache,tomcat1和tomcat2。

          2.4.1 測試負(fù)載均衡

          我們先準(zhǔn)備兩個(gè)文件,第一個(gè)文件為test.jsp,拷貝到第一個(gè)tomcat 的根web應(yīng)用的目錄即d:\tomcat1\webapps\ROOT 下:









          Tomcat 1







          第二個(gè)文件也為test.jsp,拷貝到第二個(gè)tomcat 的根web應(yīng)用的目錄即d:\tomcat2\webapps\ROOT 下:









          Tomcat 2







          從不同的瀏覽器中多次輸入地址http://localhost/test.jsp 會(huì)看到不同的顏色,這表明apache中的jk2模塊起到了負(fù)載均衡的作用。
          2.4.2 測試錯(cuò)誤恢復(fù)

          訪問url: http://localhost/examples/servlet/SessionExample 可以得到一個(gè)關(guān)于session的例子,我們用它來測試集群的錯(cuò)誤恢復(fù)能力。

          測試步驟如下:

          關(guān)閉tomcat1和tomcat2;

          啟動(dòng)tomcat1

          在瀏覽器中輸入屬性名tomcat1和屬性值tomcat1再提交,返回的頁面顯示session中有剛剛輸入的tomcat1屬性;

          啟動(dòng)tomcat2;

          過一會(huì)后(等待tomcat2和tomcat1通信并復(fù)制信息)關(guān)閉tomcat1;

          在瀏覽器中輸入屬性名tomcat2和屬性值tomcat2再提交,返回的頁面顯示session中有剛剛輸入的tomcat2屬性,還有先前輸入的tomcat1屬性;

          啟動(dòng)tomcat1;

          過一會(huì)后(等待tomcat2和tomcat1通信并復(fù)制信息)關(guān)閉tomcat2;

          在瀏覽器中輸入屬性名tomcat11和屬性值tomcat11再提交,返回的頁面顯示session中有剛剛輸入的tomcat11屬性,還有先前輸入的tomcat1和tomcat2屬性;

          ……

          2.4.3 測試多目傳輸?shù)姆椒?br />
          如果運(yùn)行測試失敗,可以使用下面的JAVAGROUP方法測試機(jī)器的多目傳輸性:

          啟動(dòng)多目接收器:

          java org.javagroups.tests.McastReceiverTest -mcast_addr 224.10.10.10 -port 5555

          啟動(dòng)多目傳輸器:

          java org.javagroups.tests.McastSenderTest -mcast_addr 224.10.10.10 -port 5555

          這樣你在McastSenderTest窗口中輸入內(nèi)容,應(yīng)該在McastReceiverWindow中可以看到結(jié)果。如果看不到結(jié)果,在 McastSenderTest運(yùn)行參數(shù)中加入-ttl 32,如果還不行,可以修改多目地址再試試(注意避開系統(tǒng)保留用的多目地址);如果還不行,就去問問網(wǎng)管吧!

          2.4.4 對tomcat-javagroups的修改

          tomcat-javagroups.jar中的org.apache.catalina.session.ReplicatedSession類的removeAttribute方法會(huì)導(dǎo)致stackoverflow錯(cuò)誤,請按下面的代碼對其進(jìn)行修改:

          public void removeAttribute(String name, boolean notify, boolean jgnotify) {

          super.removeAttribute(name);

          if ( jgnotify )

          {

          SessionMessage msg =

          new SessionMessage(notify?SessionMessage.EVT_ATTRIBUTE_REMOVED_WNOTIFY:SessionMessage. EVT_ATTRIBUTE_REMOVED_WONOTIFY,

          null,

          getId(),

          name,

          null,

          null);

          sendMessage(msg);

          }

          }

          public void removeAttribute(String name, boolean notify) {

          removeAttribute(name,notify,true);

          }

          3 jetspeed集群

          我們現(xiàn)在知道了如何配置、甚至擁有一個(gè)集群環(huán)境,接下來本文分析Jetspeed的集群現(xiàn)狀,主要包括repository和Session數(shù)據(jù);為了使分析具有目的,在分析Jetspeed的集群現(xiàn)狀之前,先講述了集群需求和RunData對象。讀者可以用集群環(huán)境來驗(yàn)證和調(diào)試Jetspeed的集群功能。

          3.1 集群要求

          《Memory Session Replication》一文中講述了支持集群的應(yīng)用程序需注意的要點(diǎn),現(xiàn)在對關(guān)于應(yīng)用系統(tǒng)開發(fā)時(shí)應(yīng)注意的事項(xiàng)總結(jié)如下:

          保存在Session中的對象必須實(shí)現(xiàn)java.io.Serializable接口;

          從session中獲取對象修改后必須用session.setAttribute方法重置session中的屬性,因?yàn)橹挥衧etAttribute能導(dǎo)致session復(fù)制。

          Java VM不支持類變量的序列化,所以要注意failover不能依賴類變量;

          保證各個(gè)服務(wù)實(shí)體的配置完全一樣;

          保證session狀態(tài)是唯一決定當(dāng)前任務(wù)狀態(tài)的東西,臨時(shí)文件、類變量等會(huì)使得錯(cuò)誤恢復(fù)難以實(shí)現(xiàn)、行為可能琢磨不定;

          利用request.setAttribute()保存當(dāng)前請求級(jí)的狀態(tài),減少服務(wù)實(shí)體間通信次數(shù)。

          盡量不要在session中保存大對象,提高服務(wù)實(shí)體間通信性能。

          3.2 RunData對象

          RunData對象概念來自于Turbine,在Jetspeed中RunData對象的類型是DefaultJetspeedRunData,這個(gè)類擴(kuò)展了Turbine中的DefaultTurbineRunData類。Jetspeed系統(tǒng)接到用戶瀏覽器的URL請求,進(jìn)行計(jì)算和信息處理,最后返回給瀏覽器HTTP代碼流的整個(gè)過程中的代碼都可以訪問同一個(gè)RunData對象。所以RunData對象是Jetspeed系統(tǒng)中各個(gè)代碼模塊共享信息的機(jī)制。

          3.3 Jetspeed的Repository

          Repository 一般指一個(gè)軟件系統(tǒng)賴以啟動(dòng)、運(yùn)行的持久性環(huán)境,包括啟動(dòng)Repository和運(yùn)行Repository兩部分。啟動(dòng)Repository用于決定系統(tǒng)啟動(dòng)時(shí)的參數(shù),系統(tǒng)運(yùn)行時(shí)不會(huì)改變它,如果改變了這些參數(shù),軟件系統(tǒng)必須重新啟動(dòng);運(yùn)行Repository指實(shí)時(shí)影響軟件系統(tǒng)業(yè)務(wù)操作的參數(shù),這些參數(shù)可以被用戶或管理員當(dāng)系統(tǒng)在線時(shí)改變。現(xiàn)在的趨勢是:盡量減少啟動(dòng)Repository,而擴(kuò)大運(yùn)行Repository;針對Repository的修改最好能使用管理性框架,比如SNMP和JMX。Jetspeed的repository主要在Xreg、psml和Properties文件中實(shí)現(xiàn)。

          Xreg是jetspeed的注冊表,用于登記portlet、control、controller、skin、mediatype等原始資源的定義,jetspeed中缺省地把它實(shí)現(xiàn)為文件形式,各種類型有自己的注冊表文件;

          Psml 是門戶結(jié)構(gòu)標(biāo)記語言的簡稱,用于組織xreg中的原始資源形成一個(gè)對門戶視圖的定義,當(dāng)用戶使用桌面瀏覽器訪問jetspeed系統(tǒng)時(shí),這個(gè)系統(tǒng)根據(jù)用戶的URL定位一個(gè)Psml文檔,接著解釋這個(gè)文檔形成HTML代碼流返回給瀏覽器,瀏覽器展現(xiàn)這個(gè)代碼流從而形成視窗化的門戶視圖。Jetspeed中包括了對psml的數(shù)據(jù)庫和文件兩種實(shí)現(xiàn)方式;

          Properties定義了Jetspeed的重要服務(wù)及其參數(shù),目前只有文件實(shí)現(xiàn)方式。

          Jetspeed的啟動(dòng)Repository主要在Properties文件中,運(yùn)行Repository在xreg和psml中。文件形式的實(shí)現(xiàn)大大阻礙了jetspeed支持集群的能力和表現(xiàn),因?yàn)楝F(xiàn)在很少的應(yīng)用服務(wù)器集群能在一個(gè)文件系統(tǒng)上運(yùn)行,如果Repository需要在運(yùn)行時(shí)改變,就必須同步多個(gè)服務(wù)實(shí)體上的文件,這是一個(gè)相當(dāng)麻煩的問題。如果Repository支持?jǐn)?shù)據(jù)庫實(shí)現(xiàn)形式,Jetspeed可以充分利用數(shù)據(jù)庫的存儲(chǔ)和同步機(jī)制實(shí)現(xiàn)同一個(gè)Repository服務(wù)于多個(gè)Jetspeed。所以要想 jetspeed支持集群、擁有更佳表現(xiàn),對Repository的數(shù)據(jù)庫化是一個(gè)不可忽視的任務(wù)。

          支持?jǐn)?shù)據(jù)庫的集群配置如下圖:

           


          這個(gè)圖顯示了在數(shù)據(jù)庫集群環(huán)境下的jetspeed集群配置,數(shù)據(jù)庫負(fù)載均衡器實(shí)現(xiàn)數(shù)據(jù)庫集群的單一影像,例子有weblogic server中的multipool datasource,sql server 基于的windows 2000集群的單一集群IP,ORACLE RAC 的支持多連接地址的thin jdbc driver。

          3.4 Jetspeed的Session數(shù)據(jù)

          支持集群必須使得各個(gè)服務(wù)實(shí)體針對某個(gè)任務(wù)的執(zhí)行環(huán)境是相同的,對于jetspeed來說就是針對各個(gè)URL請求,session的數(shù)據(jù)能在各個(gè) jetspeed上復(fù)制。這些session被同一個(gè)sessionid所標(biāo)識(shí),這些標(biāo)識(shí)可能來自瀏覽器的cookies或URL中。我們首先用一個(gè) velocityportlet來顯示Jetspeed的session中到底保存了什么數(shù)據(jù),這個(gè)portlet的注冊名字為 SessionPortlet。

          3.4.1 SessionPortlet

          SessionPortlet是一個(gè)velocityPortlet,其類名可以是CustomizerVelocityPortlet或 VelocityPortlet,一般情況下沒有必要開發(fā)一個(gè)新的portlet class。關(guān)于如何開發(fā)部署portlet的教程可見參考部分,現(xiàn)在我們分注冊、控制助手、portlet模版和運(yùn)行來講述這個(gè)portlet。

          3.4.1.1 注冊

          SessionPortlet用于顯示目前的session數(shù)據(jù)。它在xreg中的注冊代碼為:


          parent="CustomizerVelocity" application="false">





          check infomation in session



          org.apache.jetspeed.portal.portlets.CustomizerVelocityPortlet


          posted on 2007-10-16 01:52 前方的路 閱讀(508) 評論(0)  編輯  收藏 所屬分類: Java技術(shù)
          主站蜘蛛池模板: 丽江市| 吴江市| 隆安县| 丘北县| 齐齐哈尔市| 金平| 博客| 西乌珠穆沁旗| 镇坪县| 察隅县| 志丹县| 登封市| 伊春市| 襄垣县| 汉中市| 新安县| 新晃| 紫金县| 南汇区| 镇康县| 泗洪县| 平江县| 丁青县| 勃利县| 文登市| 临洮县| 迁安市| 青田县| 定边县| 青河县| 蒙阴县| 蓝山县| 随州市| 会同县| 莱芜市| 龙井市| 图们市| 翼城县| 凤城市| 同仁县| 田林县|