<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    coolfiry

    認認真真做人,兢兢業業做事!
    posts - 39, comments - 17, trackbacks - 0, articles - 0

    2006年9月1日

    在這篇文章中將我們一起來探討當前的API網關的作用。 

    一、API網關的用處

    API網關我的分析中會用到以下三種場景。 

    1. Open API。 企業需要將自身數據、能力等作為開發平臺向外開放,通常會以rest的方式向外提供,最好的例子就是淘寶開放平臺、騰訊公司的QQ開放平臺、微信開放平臺。 Open API開放平臺必然涉及到客戶應用的接入、API權限的管理、調用次數管理等,必然會有一個統一的入口進行管理,這正是API網關可以發揮作用的時候。
    2. 微服務網關。微服務的概念最早在2012年提出,在Martin Fowler的大力推廣下,微服務在2014年后得到了大力發展。 在微服務架構中,有一個組件可以說是必不可少的,那就是微服務網關,微服務網關處理了負載均衡,緩存,路由,訪問控制,服務代理,監控,日志等。API網關在微服務架構中正是以微服務網關的身份存在。 
    3. API服務管理平臺。上述的微服務架構對企業來說有可能實施上是困難的,企業有很多遺留系統,要全部抽取為微服務器改動太大,對企業來說成本太高。但是由于不同系統間存在大量的API服務互相調用,因此需要對系統間服務調用進行管理,清晰地看到各系統調用關系,對系統間調用進行監控等。 API網關可以解決這些問題,我們可以認為如果沒有大規模的實施微服務架構,那么對企業來說微服務網關就是企業的API服務管理平臺。

    二、API網關在企業整體架構中的地位

    一個企業隨著信息系統復雜度的提高,必然出現外部合作伙伴應用、企業自身的公網應用、企業內網應用等,在架構上應該將這三種應用區別開,三種應用的安排級別、訪問方式也不一樣。 因此在我的設計中將這三種應用分別用不同的網關進行API管理,分別是:API網關(OpenAPI合伙伙伴應用)、API網關(內部應用)、API網關(內部公網應用)。

     

    三、企業中在如何應用API網關

    1、對于OpenAPI使用的API網關來說,一般合作伙伴要以應用的形式接入到OpenAPI平臺,合作伙伴需要到 OpenAPI平臺申請應用。 因此在OpenAPI網關之外,需要有一個面向合作伙伴的使用的平臺用于合作伙伴,這就要求OpenAPI網關需要提供API給這個用戶平臺進行訪問。 如下架構:

     

    當然如果是在簡單的場景下,可能并不需要提供一個面向合作伙伴的門戶,只需要由公司的運營人員直接添加合作伙伴應用id/密鑰等,這種情況下也就不需要合作伙伴門戶子系統。 

    2、對于內網的API網關,在起到的作用上來說可以認為是微服務網關,也可以認為是內網的API服務治理平臺。 當企業將所有的應用使用微服務的架構管理起來,那么API網關就起到了微服務網關的作用。 而當企業只是將系統與系統之間的調用使用rest api的方式進行訪問時使用API網關對調用進行管理,那么API網關起到的就是API服務治理的作用。 架構參考如下:

    3、對于公司內部公網應用(如APP、公司的網站),如果管理上比較細致,在架構上是可能由獨立的API網關來處理這部分內部公網應用,如果想比較簡單的處理,也可以是使用面向合作伙伴的API網關。 如果使用獨立的API網關,有以下的好處:

    • 面向合作伙伴和面向公司主體業務的優先級不一樣,不同的API網關可以做到業務影響的隔離。
    • 內部API使用的管理流程和面向合作伙伴的管理流程可能不一樣。
    • 內部的API在功能擴展等方面的需求一般會大于OpenAPI對于功能的要求。

    基于以上的分析,如果公司有能力,那么還是建議分開使用合作伙伴OPEN API網關和內部公網應用網關。

    四、API網關有哪些競爭方案

    1、對于Open API平臺的API網關,我分析只能選擇API網關作為解決方案,業界沒有發現比較好的可以用來作為Open API平臺的入口的其他方案。 

    2、對于作為微服務網關的API網關,業界的選擇可以選擇的解決方案比較多,也取決于微服務器的實現方案,有一些微服務架構的實現方案是不需要微服務網關的。

    • Service Mesh,這是新興的基于無API網關的架構,通過在客戶端上的代理完成屏蔽網絡層的訪問,這樣達到對應用層最小的改動,當前Service Mesh的產品還正在開發中,并沒有非常成熟可直接應用的產品。 發展最迅速的產品是Istio。 建議大家密切關注相關產品的研發、業務使用進展。

    • 基于duboo架構,在這個架構中通常是不需要網關的,是由客戶端直接訪問服務提供方,由注冊中心向客戶端返回服務方的地址。

    五、API網關解決方案

    私有云開源解決方案如下:

    • Kong kong是基于Nginx+Lua進行二次開發的方案, https://konghq.com/
    • Netflix Zuul,zuul是spring cloud的一個推薦組件,https://github.com/Netflix/zuul
    • orange,這個開源程序是國人開發的, http://orange.sumory.com/

    公有云解決方案:

    • Amazon API Gateway,https://aws.amazon.com/cn/api-gateway/
    • 阿里云API網關,https://www.aliyun.com/product/apigateway/
    • 騰訊云API網關, https://cloud.tencent.com/product/apigateway

    自開發解決方案:

    • 基于Nginx+Lua+ OpenResty的方案,可以看到Kong,orange都是基于這個方案
    • 基于Netty、非阻塞IO模型。 通過網上搜索可以看到國內的宜人貸等一些公司是基于這種方案,是一種成熟的方案。
    • 基于Node.js的方案。 這種方案是應用了Node.js天生的非阻塞的特性。
    • 基于java Servlet的方案。 zuul基于的就是這種方案,這種方案的效率不高,這也是zuul總是被詬病的原因。

    六、企業怎么選擇API網關

    如果是要選擇一款已有的API網關,那么需要從以下幾個方面去考慮。 

    1、性能與可用性
    如果一旦采用了API網關,那么API網關就會作為企業應用核心,因此性能和可用性是必須要求的。

    • 從性能上來說,需要讓網關增加的時間消耗越短越好,個人覺得需要10ms以下。 系統需要采用非阻塞的IO,如epoll,NIO等。網關和各種依賴的交互也需要是非阻塞的,這樣才能保證整體系統的高可用性,如:Node.js的響應式編程和基于java體現的RxJava和Future。
    • 網關必須支持集群部署,任務一臺服務器的crash都應該不影響整體系統的可用性。
    • 多套網關應該支持同一管理平臺和同一監控中心。 如: 一個企業的OpenAPI網關和內部應用的多個系統群的不同的微服務網關可以在同一監控中心進行監控。

    2、可擴展性、可維護性
    一款產品總有不能滿足生產需求的地方,因此需求思考產品在如何進行二次開發和維護,是否方便公司團隊接手維護產品。 
    3、需求匹配度
    需要評估各API網關在需求上是否能滿足,如: 如果是OpenAPI平臺需要使用API網關,那么需要看API網關在合作伙伴應用接入、合作伙伴門戶集成、訪問次數限額等OpenAPI核心需求上去思考產品是否能滿足要求。 如果是微服務網關,那么要從微服務的運維、監控、管理等方面去思考產品是否足夠強大。
    4、是否開源?公司是否有自開發的能力?
    現有的開源產品如kong,zuul,orange都有基礎的API網關的核心功能,這些開源產品大多離很好的使用有一定的距離,如:沒有提供管理功能的UI界面、監控功能弱小,不支持OpenAPI平臺,沒有公司運營與運維的功能等。 當然開源產品能獲取源代碼,如果公司有比較強的研發能力,能hold住這些開源產品,經過二次開發kong、zuul應該還是適應一些公司,不過需求注意以下一些點:

    • kong是基于ngnix+lua的,從公司的角度比較難于找到能去維護這種架構產品的人。 需求評估當前公司是否有這個能力去維護這個產品。
    • zuul因為架構的原因在高并發的情況下性能不高,同時需要去基于研究整合開源的適配zuul的監控和管理系統。
    • orange由于沒有被大量使用,同時是國內個人在開源,在可持續性和社區資源上不夠豐富,出了問題后可能不容易找到人問。

    另外kong提供企業版本的API網關,當然也是基于ngnix+lua的,企業版本可以購買他們的技術支持、培訓等服務、以及擁有界面的管理、監控等功能。

    5、公有云還是私有云
    現在的亞馬遜、阿里、騰訊云都在提供基礎公有云的API網關,當然這些網關的基礎功能肯定是沒有問題,但是二次開發,擴展功能、監控功能可能就不能滿足部分用戶的定制需求了。另外很多企業因為自身信息安全的原因,不能使用外網公有網的API網關服務,這樣就只有選擇私有云的方案了。 
    在需求上如果基于公有云的API網關只能做到由內部人員為外網人員申請應用,無法做到定制的合作伙伴門戶,這也不適合于部分企業的需求。 
    如果作為微服務網關,大多數情況下是希望網關服務器和服務提供方服務器是要在內網的,在這里情況下也只有私有云的API網關才能滿足需求。 

    綜合上面的分析,基礎公有云的API網關只有滿足一部分簡單客戶的需求,對于很多企業來說私有云的API網關才是正確的選擇。


    文章作者介紹:
    來自于小豹科技的架構師-專注于軟件研發基于平臺性軟件的研發,目前我正在研發一款基于Netty、響應式架構的插件式的API網關,希望能對行業帶來一些改變。 我希望與對OpenAPI、微服務、API網關、Service Mesh等感興趣的朋友多交流。 有興趣的朋友請加我的QQ群244054462。

    posted @ 2018-01-05 13:42 Coolfiry 閱讀(4699) | 評論 (0)編輯 收藏

    虞美人 李煜
    春花秋月何時了,往事知多少?小樓昨夜又東風,故國不堪回首月明中。雕欄玉砌應猶在,只是朱顏改。問君能有幾多愁,恰似一江春水向東流。 

    posted @ 2009-01-19 10:49 Coolfiry 閱讀(265) | 評論 (0)編輯 收藏

    雨霖鈴 ·柳永


    寒蟬凄切。對長亭晚,驟雨初歇。都門帳飲無緒,留戀處、蘭舟催發。執手相看淚眼,竟無語凝噎。念去去、千里煙波,暮靄沉沉楚天闊。
    多情自古傷離別,更那堪冷落清秋節!今宵酒醒何處?楊柳岸、曉風殘月。此去經年,應是良辰好景虛設。便縱有千種風情,更與何人說?

    posted @ 2009-01-19 10:48 Coolfiry 閱讀(260) | 評論 (0)編輯 收藏

    1、python的入門級內容。
    2、java mail的使用基本用法和注意事項。
    3、CXF中相關BUG的解決方法。
    4、UNIX 網絡編程步步提升系列。

    posted @ 2008-12-11 15:48 Coolfiry 閱讀(1072) | 評論 (5)編輯 收藏

    轉自:http://bbs.chinaunix.net/viewthread.php?tid=691982&extra=&page=1
    snoop 抓包
    solaris自帶snoop抓包工具,抓所有數據流

    # snoop
    Using device /dev/pcn0 (promiscuous mode)
    192.168.8.18 -> 192.168.255.255 NBT NS Query Request for WORKGROUP[1c], Success
    192.168.253.35 -> solaris      TELNET C port=1246
         solaris -> 192.168.253.35 TELNET R port=1246 Using device /dev/pc
         solaris -> 192.168.253.35 TELNET R port=1246 Using device /dev/pc
    192.168.4.150 -> (broadcast)  ARP C Who is 192.168.4.200, 192.168.4.200 ?
    192.168.4.200 -> (broadcast)  ARP C Who is 192.168.4.150, 192.168.4.150 ?
    #

    抓源地址或目的為 202.101.98.55的數據流:

    # snoop 202.101.98.55
    Using device /dev/pcn0 (promiscuous mode)
    192.168.253.35 -> dns.fz.fj.cn DNS C www.163.com. Internet Addr ?
    dns.fz.fj.cn -> 192.168.253.35 DNS R www.163.com. Internet CNAME www.cache.split.netease.com.

    #

    說明:internet cname 后的為解析www.163.com的名字時,代表www.163.com回答的主機的域名。

    抓 192.168.253.35和202.101.98.55之間的數據流(雙向都抓)

    # snoop 192.168.253.35 202.101.98.55
    Using device /dev/pcn0 (promiscuous mode)
    192.168.253.35 -> dns.fz.fj.cn DNS C www.google.com. Internet Addr ?
    dns.fz.fj.cn -> 192.168.253.35 DNS R www.google.com. Internet CNAME www.l.google.com.
    #

    抓完存在當前目錄下的cap文件中并查看

    # snoop -o cap1 -P      -P表示處在非混雜模式抓數據,只抓廣播、主播、目的為本機的數據
    Using device /dev/pcn0 (non promiscuous)
    15 ^C                           15的含義是:顯示目前抓了多少個數據流
    #

    # snoop -i cap1
      1   0.00000 192.168.253.35 -> solaris      TELNET C port=1246
      2   0.18198 192.168.253.35 -> solaris      TELNET C port=1246
      3   0.37232 192.168.4.199 -> 192.168.255.255 NBT Datagram Service Type=17 Source=WB-200[20]
      4   0.00016            ? -> (multicast)  ETHER Type=EF08 (Unknown), size = 180bytes
      5   0.62546 192.168.253.35 -> solaris      TELNET C port=1246
      6   0.13822            ? -> (multicast)  ETHER Type=0000 (LLC/802.3), size = 52 bytes
      7   0.06283 192.168.253.35 -> solaris      TELNET C port=1246
      8   0.90301 192.168.253.35 -> solaris      TELNET C port=1246
      9   0.19781 192.168.253.35 -> solaris      TELNET C port=1246
    10   0.81493            ? -> (multicast)  ETHER Type=0000 (LLC/802.3), size = 52 bytes
    11   0.07018 192.168.253.35 -> solaris      TELNET C port=1246
    12   0.19939 192.168.253.35 -> solaris      TELNET C port=1246
    13   0.90151 192.168.253.35 -> solaris      TELNET C port=1246
    14   0.18904 192.168.253.35 -> solaris      TELNET C port=1246
    15   0.68422            ? -> (multicast)  ETHER Type=0000 (LLC/802.3), size = 52 bytes
    #snoop -i cap1 -p 10,12            只看10-12條記錄

    #snoop -i cap1 -p10                  只看第10條記錄

    # snoop -i cap1 -v -p101            查看第10條數據流的包頭的詳細內容

    #snoop -i cap1 -v -x 0 -p101   查看第10條數據流的全部的詳細內容

    抓主機192.168.253.35和202.101.98.55之間的tcp或者udp端口53的數據

    # snoop 192.168.253.35 and 202.101.98.55 and \(tcp or udp\) and port 53

    輸入(的時候要加轉義符號\


    snoop的詳細參數
    Snoop 是Solaris 系統中自帶的工具, 是一個用于顯示網絡通訊的程序, 它可捕獲IP 包并將其顯示或保存到指定文件. (限超級用戶使用snoop)
    Snoop 可將捕獲的包以一行的形式加以總結或用多行加以詳細的描述(有調用不同的參數–v -V來實現). 在總結方式下(-V ) , 將僅顯示最高層的相關協議, 例如一個NFS 包將僅顯示NFS 信息, 其低層的RPC, UDP, IP, Ethernet 幀信息將不會顯示, 但是當加上相應的參數(-v ), 這些信息都能被顯示出來.

    -C

    -D

    -N

    -P 在非混雜模式下抓包

    -S 抓包的時候顯示數據包的大小

    -V 半詳細的顯示抓的數據的信息

    -t [ r | a | d ] 顯示時間戳,-ta顯示當前系統時間,精確到毫秒

    -v 最詳細的顯示數據的信息

    -x offset [ , length] 以16進制或ACSII方式顯示某數據的部分內容,比如 -x 0,10 只顯示0-10字節

    #snoop -i cap1 -v -x 0 -p101 查看被抓獲的第101個數據流的全部內容


    表達式:

    根據地址:

    #snoop x.x.x.x         IPV4的IP

    #snoop 0XX:XX:XX:XX    ETHERNET的MAC地址

    數據的方向:

    from x.x.x.x 或者 src x.x.x.x

    to x.x.x.x 或者 dst x.x.x.x

    可用的數據類型的關鍵詞:

    ip, ip6, arp, rarp, pppoed, pppoes,pppoe,broadcast,multicast,apple,decnet

    udp, tcp, icmp, icmp6, ah, esp

    greater length
          True if the packet is longer than length.

    less length
          True if the packet is shorter than length.

    net net

    # snoop from net 192.168.1.0 抓來自192.168.1.0/24的數據

    # snoop from net 192.168.0.0 抓來自192.168.0.0/16的數據

    port xx XX為TCP或者UDP的端口號或者 /etc/services里定義的名字

    #snoop to udp and port 53    抓到UDP53的數據

    posted @ 2008-10-21 21:30 Coolfiry 閱讀(723) | 評論 (0)編輯 收藏

    在項目使用CXF的過程中,遇到了有關List作為傳輸參數的時候,如果WebService端沒有明確給出List的泛型類型會報錯。
    例如
    CXF的WebService端口接口的一個方法為為:
    1 public boolean updateMessageStatus(List batchIds);

    客戶端的的調用為:
    1 //預先初始化cxf對象cxfObj
    2 List<String> list=new ArrayList<String>();
    3 list.add("1");
    4 cxfObj.updateMessageStatus(list);


    在客戶端進行調用WebService時會發生錯誤,錯誤為:unexpected element (uri:"", local:"arg0")等,據分析生成的wsdl,這是因為CXF在進行數據marshal時不知道要將要轉換的類型。

    解決辦法是:在WebService端的接口必須用List的泛型類型參數,如:

    1 public boolean updateMessageStatus(List<String> batchIds);

    這樣就完全解決問題了。

    posted @ 2008-08-05 20:09 Coolfiry 閱讀(4956) | 評論 (1)編輯 收藏

    現在正在學習linux shell編程
    first.sh
    while read line
    do
            echo 
    "$line"
    done 
    <"$1"
    這是第一個shell程序小例子,就相當于一個學習其他語言的hello world了吧。用法first.sh test,將test文件中的每一行輸出到stdout中。

    second.sh
    number=0;
    while [ "$number" -lt 100 ]
    do
            echo 
    "$number"
            number
    ='expr $number + 1'
    done
    echo
    這是第二個shell程序小例子,作用是輸出0到99的數字到stdout中。其中用到的expr的作用是使expr的參數轉化為數字并相加。兩個單引號的作用是引號所包圍的命令被命令的標準輸出替換,并輸出賦值給我number,得到了如同java中number=number+1的效果。


    posted @ 2008-07-20 20:34 Coolfiry 閱讀(587) | 評論 (2)編輯 收藏

    在項目開發過程中,遇到在本機和windows環境中部署用CXF框架開發的的webService沒有任何問題,但是當將工程部署到solaris 的SUN ONE application上時,再用本機的cxf Web服務客戶端訪問對應的web服務時,如果傳輸的數據量小于大約4K不會出問題,否則則會報一些數據綁定的異常如:
    Marshalling Error: Error writing request body to server。
    解決這個問題花了我足足兩天時間,原因是有關CXF的資料太少了,而且有關于這個錯誤的解決都必須使用google才能search到,用baidu完全search不到相關的資料。
    解決方案:
    在客戶端的class-path中加上cxf.xml。cxf.xml的配置如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi
    ="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:http
    ="http://cxf.apache.org/transports/http/configuration"
        xmlns:jaxws
    ="http://cxf.apache.org/jaxws"
        xsi:schemaLocation
    ="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
     http://cxf.apache.org/transports/http/configuration
    http://cxf.apache.org/schemas/configuration/http-conf.xsd"
    >
        
    <http:conduit name="*.http-conduit">
            
    <http:client AutoRedirect="true" />
        
    </http:conduit>
    </beans>
    這個問題的解決方案是我在cxf的官網上找了很久才找到的,雖然問題解決了,但是我感到很迷惑。主要在windows tomcat環境下沒有問題,而到了SUN ONE的環境就有問題,經過的思考和找了一資料,我認為問題出于solaris對于HTTP數據傳輸的某些限制,如果真要去搞清楚的話可能要去參看cxf的source code了,但是我不想花這個時間去研究這個問題了。

    我把這個解決方案寫出來,希望可以幫助到使用CXF的網友,也希望高手們能幫我解決我的迷惑。



    posted @ 2008-07-18 19:11 Coolfiry 閱讀(2568) | 評論 (0)編輯 收藏

    Internet的快速增長使多媒體網絡服務器,特別是Web服務器,面對的訪問者數量快速增加,網絡服務器需要具備提供大量并發訪問服務的能力。 例如Yahoo每天會收到數百萬次的訪問請求,因此對于提供大負載Web服務的服務器來講,CPU、I/O處理能力很快會成為瓶頸。

    簡單的 提高硬件性能并不能真正解決這個問題,因為單臺服務器的性能總是有限的,一般來講,一臺PC服務器所能提供的并發訪問處理能力大約為1000個,更為高檔 的專用服務器能夠支持3000-5000個并發訪問,這樣的能力還是無法滿足負載較大的網站的要求。尤其是網絡請求具有突發性,當某些重大事件發生時,網 絡訪問就會急劇上升,從而造成網絡瓶頸,例如在網上發布的克林頓彈劾書就是很明顯的例子。必須采用多臺服務器提供網絡服務,并將網絡請求分配給這些服務器 分擔,才能提供處理大量并發服務的能力。

    當使用多臺服務器來分擔負載的時候,最簡單的辦法是將不同的服務器用在不同的方面。 按提供的內容進行分割時,可以將一臺服務器用于提供新聞頁面,而另一臺用于提供游戲頁面;或者可以按服務器的功能進行分割,將一臺服務器用于提供靜態頁面 訪問,而另一些用于提供CGI等需要大量消耗資源的動態頁面訪問。然而由于網絡訪問的突發性,使得很難確定那些頁面造成的負載太大,如果將服務的頁面分割 的過細就會造成很大浪費。事實上造成負載過大的頁面常常是在變化中的,如果要經常按照負載變化來調整頁面所在的服務器,那么勢必對管理和維護造成極大的問 題。因此這種分割方法只能是大方向的調整,對于大負載的網站,根本的解決辦法還需要應用負載均衡技術。

    負載均衡的思路下多臺 服務器為對稱方式,每臺服務器都具備等價的地位,都可以單獨對外提供服務而無須其他服務器的輔助。然后通過某種負載分擔技術,將外部發送來的請求均勻分配 到對稱結構中的某一臺服務器上,而接收到請求的服務器都獨立回應客戶機的請求。由于建立內容完全一致的Web服務器并不復雜,可以使用服務器同步更新或者 共享存儲空間等方法來完成,因此負載均衡技術就成為建立一個高負載Web站點的關鍵性技術。

    1. 基于特定服務器軟件的負載均衡

      很 多網絡協議都支持“重定向”功能,例如在HTTP協議中支持Location指令,接收到這個指令的瀏覽器將自動重定向到Location指明的另一個 URL上。由于發送Location指令比起執行服務請求,對Web服務器的負載要小的多,因此可以根據這個功能來設計一種負載均衡的服務器。任何時候 Web服務器認為自己負載較大的時候,它就不再直接發送回瀏覽器請求的網頁,而是送回一個Locaction指令,讓瀏覽器去服務器集群中的其他服務器上 獲得所需要的網頁。

      在這種方式下,服務器本身必須支持這種功能,然而具體實現起來卻有很多困難,例如一臺服務器如何能保證它重定向過的服務 器是比較空閑的,并且不會再次發送Location指令?Location指令和瀏覽器都沒有這方面的支持能力,這樣很容易在瀏覽器上形成一種死循環。因 此這種方式實際應用當中并不多見,使用這種方式實現的服務器集群軟件也較少。有些特定情況下可以使用CGI(包括使用FastCGI或mod_perl擴 展來改善性能)來模擬這種方式去分擔負載,而Web服務器仍然保持簡潔、高效的特性,此時避免Location循環的任務將由用戶的CGI程序來承擔。

    2. 基于DNS的負載均衡

      由 于基于服務器軟件的負載均衡需要改動軟件,因此常常是得不償失,負載均衡最好是在服務器軟件之外來完成,這樣才能利用現有服務器軟件的種種優勢。最早的負 載均衡技術是通過DNS服務中的隨機名字解析來實現的,在DNS服務器中,可以為多個不同的地址配置同一個名字,而最終查詢這個名字的客戶機將在解析這個 名字時得到其中的一個地址。因此,對于同一個名字,不同的客戶機會得到不同的地址,他們也就訪問不同地址上的Web服務器,從而達到負載均衡的目的。

      例如如果希望使用三個Web服務器來回應對www.exampleorg.org.cn的HTTP請求,就可以設置該域的DNS服務器中關于該域的數據包括有與下面例子類似的結果:

      www1		IN		A 		192.168.1.1
      www2		IN		A 		192.168.1.2
      www3		IN		A 		192.168.1.3
      www		IN		CNAME		www1
      www		IN		CNAME		www2
      www		IN		CNAME		www3

      此后外部的客戶機就可能隨機的得到對應www的不同地址,那么隨后的HTTP請求也就發送給不同地址了。

      DNS 負載均衡的優點是簡單、易行,并且服務器可以位于互聯網的任意位置上,當前使用在包括Yahoo在內的Web站點上。然而它也存在不少缺點,一個缺點是為 了保證DNS數據及時更新,一般都要將DNS的刷新時間設置的較小,但太小就會造成太大的額外網絡流量,并且更改了DNS數據之后也不能立即生效;第二點 是DNS負載均衡無法得知服務器之間的差異,它不能做到為性能較好的服務器多分配請求,也不能了解到服務器的當前狀態,甚至會出現客戶請求集中在某一臺服 務器上的偶然情況。

    3. 反向代理負載均衡

      使用代理服務器可以將請求轉發給內部的Web服務器,使用這種加速 模式顯然可以提升靜態網頁的訪問速度。因此也可以考慮使用這種技術,讓代理服務器將請求均勻轉發給多臺內部Web服務器之一上,從而達到負載均衡的目的。 這種代理方式與普通的代理方式有所不同,標準代理方式是客戶使用代理訪問多個外部Web服務器,而這種代理方式是多個客戶使用它訪問內部Web服務器,因 此也被稱為反向代理模式。

      實現這個反向代理能力并不能算是一個特別復雜的任務,但是在負載均衡中要求特別高的效率,這樣實現起來就不是十分 簡單的了。每針對一次代理,代理服務器就必須打開兩個連接,一個為對外的連接,一個為對內的連接,因此對于連接請求數量非常大的時候,代理服務器的負載也 就非常之大了,在最后反向代理服務器會成為服務的瓶頸。例如,使用Apache的mod_rproxy模塊來實現負載均衡功能時,提供的并發連接數量受 Apache本身的并發連接數量的限制。一般來講,可以使用它來對連接數量不是特別大,但每次連接都需要消耗大量處理資源的站點進行負載均衡,例如搜尋。

      使 用反向代理的好處是,可以將負載均衡和代理服務器的高速緩存技術結合在一起,提供有益的性能,具備額外的安全性,外部客戶不能直接訪問真實的服務器。并且 實現起來可以實現較好的負載均衡策略,將負載可以非常均衡的分給內部服務器,不會出現負載集中到某個服務器的偶然現象。

    4. 基于NAT的負載均衡技術

      網 絡地址轉換為在內部地址和外部地址之間進行轉換,以便具備內部地址的計算機能訪問外部網絡,而當外部網絡中的計算機訪問地址轉換網關擁有的某一外部地址 時,地址轉換網關能將其轉發到一個映射的內部地址上。因此如果地址轉換網關能將每個連接均勻轉換為不同的內部服務器地址,此后外部網絡中的計算機就各自與 自己轉換得到的地址上服務器進行通信,從而達到負載分擔的目的。

      地 址轉換可以通過軟件方式來實現,也可以通過硬件方式來實現。使用硬件方式進行操作一般稱為交換,而當交換必須保存TCP連接信息的時候,這種針對OSI網 絡層的操作就被稱為第四層交換。支持負載均衡的網絡地址轉換為第四層交換機的一種重要功能,由于它基于定制的硬件芯片,因此其性能非常優秀,很多交換機聲 稱具備400MB-800MB的第四層交換能力,然而也有一些資料表明,在如此快的速度下,大部分交換機就不再具備第四層交換能力了,而僅僅支持第三層甚 至第二層交換。

      然而對于大部分站點來講,當前負載均衡主要是解決Web服務器處理能力瓶頸的,而非網絡傳輸能力,很多站點的互聯網連接帶寬總共也不過10MB,只有極少的站點能夠擁有較高速的網絡連接,因此一般沒有必要使用這些負載均衡器這樣的昂貴設備。

      使 用軟件方式來實現基于網絡地址轉換的負載均衡則要實際的多,除了一些廠商提供的解決方法之外,更有效的方法是使用免費的自由軟件來完成這項任務。其中包括 Linux Virtual Server Project中的NAT實現方式,或者本文作者在FreeBSD下對natd的修訂版本。一般來講,使用這種軟件方式來實現地址轉換,中心負載均衡器存 在帶寬限制,在100MB的快速以太網條件下,能得到最快達80MB的帶寬,然而在實際應用中,可能只有40MB-60MB的可用帶寬。

    5. 擴展的負載均衡技術

    上 面使用網絡地址轉換來實現負載分擔,毫無疑問所有的網絡連接都必須通過中心負載均衡器,那么如果負載特別大,以至于后臺的服務器數量不再在是幾臺、十幾 臺,而是上百臺甚至更多,即便是使用性能優秀的硬件交換機也回遇到瓶頸。此時問題將轉變為,如何將那么多臺服務器分布到各個互聯網的多個位置,分散網絡負 擔。當然這可以通過綜合使用DNS和NAT兩種方法來實現,然而更好的方式是使用一種半中心的負載均衡方式。

    在這種半中心的負載均衡方式下,即當客戶請求發送給負載均衡器的時候,中心負載均衡器將請求打包并發送給某個服務器,而服務器的回應請求不再返回給中心負載均衡器,而是直接返回給客戶,因此中心負載均衡器只負責接受并轉發請求,其網絡負擔就較小了。

    上圖來自Linux Virtual Server Project,為他們使用IP隧道實現的這種負載分擔能力的請求/回應過程,此時每個后臺服務器都需要進行特別的地址轉換,以欺騙瀏覽器客戶,認為它的回應為正確的回應。

    同樣,這種方式的硬件實現方式也非常昂貴,但是會根據廠商的不同,具備不同的特殊功能,例如對SSL的支持等。

    由于這種方式比較復雜,因此實現起來比較困難,它的起點也很高,當前情況下網站并不需要這么大的處理能力。

    比 較上面的負載均衡方式,DNS最容易,也最常用,能夠滿足一般的需求。但如果需要進一步的管理和控制,可以選用反向代理方式或NAT方式,這兩種之間進行 選擇主要依賴緩沖是不是很重要,最大的并發訪問數量是多少等條件。而如果網站上對負載影響很厲害的CGI程序是由網站自己開發的,也可以考慮在程序中自己 使用Locaction來支持負載均衡。半中心化的負載分擔方式至少在國內當前的情況下還不需要。
    http://galaxystar.javaeye.com/blog/50546

    posted @ 2008-07-18 14:23 Coolfiry 閱讀(255) | 評論 (0)編輯 收藏

    在Java版發表這篇文章,似乎有點把矛頭指向Java了。其實不是,GC是所有新一代語言共有的特征,
    Python, Eiffel,C#,Roby等無一例外地都使用了GC機制。但既然Java中的GC最為著名,所以天塌
    下來自然應該抗著。

    這篇短文源于comp.lang.java.programmer跟comp.lang.c++上發生的一場大辯論,支持C++和Java
    的兩派不同勢力展開了新世紀第一場沖突,跟貼發言超過350,兩派都有名角壓陣。C++陣營的擂主是
    Pete Becker,ACM會員,Dinkumware Ltd. 的技術副總監。此君精通C++和Java,開發過兩種語言的
    核心類庫,但是卻對C++狂熱之極,而對于Java頗不以為然。平時談到Java的時候還好,一旦有人膽
    敢用Java來批判C++,立刻忍不住火爆脾氣跳將出來,以堅韌不拔的毅力和大無畏精神與對手周旋,
    舌戰群儒,哪怕只剩下一個人也要血戰到底。這等奇人當真少見!我真奇怪他整天泡在usenet上,
    不用工作么?他的老板P.J. Plauger如此寬宏大量?Java陣營主角是一個網名Razzi的兄弟,另外有
    Sun公司大名鼎鼎的Peter van der Linden助陣,妙語連珠,寸土必爭,加上人多勢眾,一度占據優勢。
    C++陣營里大拿雖然很多,但是大多數沒有Pete那么多閑工夫,例如Greg Comeau,Comeau公司老板,
    每次來個只言片語,實在幫不了Pete多大忙。但是自從C++陣營中冒出一個無名小子,網名Courage(勇氣),
    發動對Java GC機制的批判,形勢為之一變。C++陣營眼下處于全攻之勢,Java陣營疲于防守,只能
    招架說:“你們沒有證據,沒有統計資料”,形勢很被動。

    垃圾收集(GC)不是一直被Java fans用來炫耀,引以為傲的優點么?怎么成了弱點了?我大惑不解,定睛
    一看,才覺得此中頗有道理。

    首先,Java Swing庫存在大量資源泄漏問題,這一點SUN非常清楚,稱之為bugs,正在極力修正。但是看來
    這里的問題恐怕不僅是庫編寫者的疏忽,可能根源在于深層的機制,未必能夠輕易解決,搞不好要傷筋動骨。
    不過這個問題不是那么根本,C++陣營覺得如果抓住對方的弱點攻擊,就算是占了上風也沒什么說服力。誰
    沒有缺點呢?于是反其道而行之,猛烈攻擊Java陣營覺得最得意的東西,Java的GC機制本身。

    首先來想一想,memory leak到底意味著什么。在C++中,new出來的對象沒有delete,這就導致了memory
    leak。但是C++早就有了克服這一問題的辦法——smart pointer。通過使用標準庫里設計精致的auto_ptr
    以及各種STL容器,還有例如boost庫(差不多是個準標準庫了)中的四個smart pointers,C++
    程序員只要
    花上一個星期的時間學習最新的資料,就可以拍著胸脯說:“我寫的
    程序沒有memory leak!”。

    相比之下,Java似乎更優秀,因為從一開始你就不用考慮什么特殊的機制,大膽地往前new,自有GC替你
    收拾殘局。Java的GC實際上是
    JVM中的一個獨立線程,采用不同的算法策略來收集heap中那些不再有
    reference指向的垃圾對象所占用的內存。但是,通常情況下,GC線程的優先級比較低,只有在當前
    程序
    空閑的時候才會被調度,收集垃圾。當然,如果JVM感到內存緊張了,JVM會主動調用GC來收集垃圾,獲取
    更多的內存。請注意,Java的GC工作的時機是:1. 當前
    程序不忙,有空閑時間。2. 空閑內存不足。
    現在我們考慮一種常見的情況,
    程序在緊張運行之中,沒喲空閑時間給GC來運行,同時機器內存很大,
    JVM也沒有感到內存不足,結果是什么?對了,GC形同虛設,得不到調用。于是,內存被不斷吞噬,而那些
    早已經用不著的垃圾對象仍在在寶貴的內存里睡大覺。例如:

    class BadGc {

        public void job1() {
            String garbage = "I am a garbage, and just sleeping in your precious memory, " +
                      "how do you think you can deal with me? Daydreaming! HAHA!!!";
            ....
        }

        public void job2() {...}

        ...
        ...

        public void job1000() {...}

        public static void main(String[] args) {
            bgc = new BadGc();
     bgc.job1();
     bgc.job2();
     ...
     bgc.job1000();
        }
    }

    運行中,雖然garbage對象在離開job1()之后,就再也沒有用了。但是因為程序忙,內存還夠用,所以GC得
    不到調度,garbage始終不會被回收,直到
    程序運行到bgc.job1000()時還躺在內存里嘲笑你。沒轍吧!

    好了,我承認這段程序很傻。但是你不要以為這只是理論上的假設,恰恰相反,大多數實用中的Java程序都有
    類似的效應。這就是為什么Java
    程序狂耗內存,而且好像給它多少內存吃都不夠。你花上大筆的銀子把內存
    從128升到256,再升到512,結果是,一旦執行復雜任務,內存還是被輕易填滿,而且多出來的這些內存只是
    用來裝垃圾,GC還是不給面子地千呼萬喚不出來。等到你的內存終于心力交瘁,GC才姍姍來遲,收拾殘局。而
    且GC工作的方式也很不好評價,一種方法是一旦有機會回收內存,就把所有的垃圾都回收。你可以想象,這要
    花很長時間(幾百M的垃圾啊!),如果你這時侯正在壓下開炮的按鈕,GC卻叫了暫定,好了,你等死吧!另一
    種方法,得到機會之后,回收一些內存,讓JVM感到內存不那么緊張時就收手。結果呢,內存里始終有大批垃
    圾,
    程序始終在半死不活的蕩著。最后,GC可以每隔一段時間就運行一次,每次只回收一部分垃圾,這是現在
    大部分JVM的方式,結果是內存也浪費了,還動不動暫停幾百毫秒。難啊!

    反過來看看C++利用smart pointer達成的效果,一旦某對象不再被引用,系統刻不容緩,立刻回收內存。這
    通常發生在關鍵任務完成后的清理(cleanup)時期,不會影響關鍵任務的實時性,同時,內存里所有的對象
    都是有用的,絕對沒有垃圾空占內存。怎么樣?傳統、樸素的C++是不是更勝一籌?

    據統計,目前的Java程序運行期間占用的內存通常為對應C++程序的4-20倍。除了其它的原因,上面所說的是一個
    非常主要的因素。我們對memory leak如此憤恨,不就是因為它導致大量的內存垃圾得不到清除嗎?如果有了
    GC之后,垃圾比以前還來勢洶洶,那么GC又有什么好處呢?

    當然,C++的smart pointer現在會使用的人不多,所以現在的C++程序普遍存在更嚴重的memory leak問題。
    但是,如果我奶奶跟舒馬赫比賽車輸掉了,你能夠埋怨那輛車子么?
    http://www.594k.com/java/html/y2007m1/12051/

    posted @ 2007-10-12 10:43 Coolfiry 閱讀(642) | 評論 (1)編輯 收藏

    從LiveJournal后臺發展看大規模網站性能優化方法

    一、LiveJournal發展歷程

    LiveJournal是99年始于校園中的項目,幾個人出于愛好做了這樣一個應用,以實現以下功能:

    • 博客,論壇
    • 社會性網絡,找到朋友
    • 聚合,把朋友的文章聚合在一起

    LiveJournal采用了大量的開源軟件,甚至它本身也是一個開源軟件。

    在上線后,LiveJournal實現了非常快速的增長:

    • 2004年4月份:280萬注冊用戶。
    • 2005年4月份:680萬注冊用戶。
    • 2005年8月份:790萬注冊用戶。
    • 達到了每秒鐘上千次的頁面請求及處理。
    • 使用了大量MySQL服務器。
    • 使用了大量通用組件。

    二、LiveJournal架構現狀概況

    livejournal_backend.png

    三、從LiveJournal發展中學習

     

    LiveJournal從1臺服務器發展到100臺服務器,這其中經歷了無數的傷痛,但同時也摸索出了解決這些問題的方法,通過對LiveJournal的學習,可以讓我們避免LJ曾經犯過的錯誤,并且從一開始就對系統進行良好的設計,以避免后期的痛苦。

    下面我們一步一步看LJ發展的腳步。

    1、一臺服務器

    一 臺別人捐助的服務器,LJ最初就跑在上面,就像Google開始時候用的破服務器一樣,值得我們尊敬。這個階段,LJ的人以驚人的速度熟悉的Unix的操 作管理,服務器性能出現過問題,不過還好,可以通過一些小修小改應付過去。在這個階段里LJ把CGI升級到了FastCGI。

    最終問題出現了,網站越來越慢,已經無法通過優過化來解決的地步,需要更多的服務器,這時LJ開始提供付費服務,可能是想通過這些錢來購買新的服務器,以解決當時的困境。
    毫無疑問,當時LJ存在巨大的單點問題,所有的東西都在那臺服務器的鐵皮盒子里裝著。

    LJ-backend-7.png

    2、兩臺服務器

    用付費服務賺來的錢LJ買了兩臺服務器:一臺叫做Kenny的Dell 6U機器用于提供Web服務,一臺叫做Cartman的Dell 6U服務器用于提供數據庫服務。

    LJ-backend-8.png

    LJ有了更大的磁盤,更多的計算資源。但同時網絡結構還是非常簡單,每臺機器兩塊網卡,Cartman通過內網為Kenny提供MySQL數據庫服務。

    暫時解決了負載的問題,新的問題又出現了:

    • 原來的一個單點變成了兩個單點。
    • 沒有冷備份或熱備份。
    • 網站速度慢的問題又開始出現了,沒辦法,增長太快了。
    • Web服務器上CPU達到上限,需要更多的Web服務器。

    3、四臺服務器

    又買了兩臺,Kyle和Stan,這次都是1U的,都用于提供Web服務。目前LJ一共有3臺Web服務器和一臺數據庫服務器。這時需要在3臺Web服務器上進行負載均橫。

    LJ-backend-9.png

    LJ把Kenny用于外部的網關,使用mod_backhand進行負載均橫。

    然后問題又出現了:

    • 單點故障。數據庫和用于做網關的Web服務器都是單點,一旦任何一臺機器出現問題將導致所有服務不可用。雖然用于做網關的Web服務器可以通過保持心跳同步迅速切換,但還是無法解決數據庫的單點,LJ當時也沒做這個。
    • 網站又變慢了,這次是因為IO和數據庫的問題,問題是怎么往應用里面添加數據庫呢?

    4、五臺服務器

    又買了一臺數據庫服務器。在兩臺數據庫服務器上使用了數據庫同步(Mysql支持的Master-Slave模式),寫操作全部針對主數據庫(通過Binlog,主服務器上的寫操作可以迅速同步到從服務器上),讀操作在兩個數據庫上同時進行(也算是負載均橫的一種吧)。

    LJ-backend-10.png

    實現同步時要注意幾個事項:

    • 讀操作數據庫選擇算法處理,要選一個當前負載輕一點的數據庫。
    • 在從數據庫服務器上只能進行讀操作
    • 準備好應對同步過程中的延遲,處理不好可能會導致數據庫同步的中斷。只需要對寫操作進行判斷即可,讀操作不存在同步問題。

    5、更多服務器

    有錢了,當然要多買些服務器。部署后快了沒多久,又開始慢了。這次有更多的Web服務器,更多的數據庫服務器,存在 IO與CPU爭用。于是采用了BIG-IP作為負載均衡解決方案。

    LJ-backend-11.png

    6、現在我們在哪里:

    LJ-backend-1.png

    現在服務器基本上夠了,但性能還是有問題,原因出在架構上。

    數據庫的架構是最大的問題。由于增加的數據庫都是以Slave模式添加到應用內,這樣唯一的好處就是將讀操作分布到了多臺機器,但這樣帶來的后果就是寫操作被大量分發,每臺機器都要執行,服務器越多,浪費就越大,隨著寫操作的增加,用于服務讀操作的資源越來越少。

    LJ-backend-2.png

    由一臺分布到兩臺

    LJ-backend-3.png

    最終效果

    現在我們發現,我們并不需要把這些數據在如此多的服務器上都保留一份。服務器上已經做了RAID,數據庫也進行了備份,這么多的備份完全是對資源的浪費,屬于冗余極端過度。那為什么不把數據分布存儲呢?

    問題發現了,開始考慮如何解決。現在要做的就是把不同用戶的數據分布到不同的服務器上進行存儲,以實現數據的分布式存儲,讓每臺機器只為相對固定的用戶服務,以實現平行的架構和良好的可擴展性。

    為 了實現用戶分組,我們需要為每一個用戶分配一個組標記,用于標記此用戶的數據存放在哪一組數據庫服務器中。每組數據庫由一個master及幾個slave 組成,并且slave的數量在2-3臺,以實現系統資源的最合理分配,既保證數據讀操作分布,又避免數據過度冗余以及同步操作對系統資源的過度消耗。

    LJ-backend-4.png

    由一臺(一組)中心服務器提供用戶分組控制。所有用戶的分組信息都存儲在這臺機器上,所有針對用戶的操作需要先查詢這臺機器得到用戶的組號,然后再到相應的數據庫組中獲取數據。

    這樣的用戶架構與目前LJ的架構已經很相像了。

    在具體的實現時需要注意幾個問題:

    • 在數據庫組內不要使用自增ID,以便于以后在數據庫組之間遷移用戶,以實現更合理的I/O,磁盤空間及負載分布。
    • 將userid,postid存儲在全局服務器上,可以使用自增,數據庫組中的相應值必須以全局服務器上的值為準。全局服務器上使用事務型數據庫InnoDB。
    • 在數據庫組之間遷移用戶時要萬分小心,當遷移時用戶不能有寫操作。

    7、現在我們在哪里

    LJ-backend-5.png

    問題:

    • 一個全局主服務器,掛掉的話所有用戶注冊及寫操作就掛掉。
    • 每個數據庫組一個主服務器,掛掉的話這組用戶的寫操作就掛掉。
    • 數據庫組從服務器掛掉的話會導致其它服務器負載過大。

    對于Master-Slave模式的單點問題,LJ采取了Master-Master模式來解決。所謂Master-Master實際上是人工實現的,并不是由MySQL直接提供的,實際上也就是兩臺機器同時是Master,也同時是Slave,互相同步。

    Master-Master實現時需要注意:

    • 一個Master出錯后恢復同步,最好由服務器自動完成。
    • 數字分配,由于同時在兩臺機器上寫,有些ID可能會沖突。

    解決方案:

    • 奇偶數分配ID,一臺機器上寫奇數,一臺機器上寫偶數
    • 通過全局服務器進行分配(LJ采用的做法)。

     

    Master-Master模式還有一種用法,這種方法與前一種相比,仍然保持兩臺機器的同步,但只有一臺機器提供服務(讀和寫),在每天晚上的時候進行輪換,或者出現問題的時候進行切換。

    8、現在我們在哪里

    LJ-backend-6.png

    現在插播一條廣告,MyISAM VS InnoDB。

    使用InnoDB:

    • 支持事務
    • 需要做更多的配置,不過值得,可以更安全的存儲數據,以及得到更快的速度。

    使用MyISAM:

    • 記錄日志(LJ用它來記網絡訪問日志)
    • 存儲只讀靜態數據,足夠快。
    • 并發性很差,無法同時讀寫數據(添加數據可以)
    • MySQL非正常關閉或死機時會導致索引錯誤,需要使用myisamchk修復,而且當訪問量大時出現非常頻繁。

    9、緩存

    去年我寫過一篇文章介紹memcached,它就是由LJ的團隊開發的一款緩存工具,以key-value的方式將數據存儲到分布的內存中。LJ緩存的數據:

    • 12臺獨立服務器(不是捐贈的)
    • 28個實例
    • 30GB總容量
    • 90-93%的命中率(用過squid的人可能知道,squid內存加磁盤的命中率大概在70-80%)

    如何建立緩存策略?

    想緩存所有的東西?那是不可能的,我們只需要緩存已經或者可能導致系統瓶頸的地方,最大程度的提交系統運行效率。通過對MySQL的日志的分析我們可以找到緩存的對象。

    緩存的缺點?

    • 沒有完美的事物,緩存也有缺點:
    • 增大開發量,需要針對緩存處理編寫特殊的代碼。
    • 管理難度增加,需要更多人參與系統維護。
    • 當然大內存也需要錢。

    10、Web訪問負載均衡

    在數據包級別使用BIG-IP,但BIG-IP并不知道我們內部的處理機制,無法判斷由哪臺服務器對這些請求進行處理。反向代理并不能很好的起到作用,不是已經夠快了,就是達不到我們想要的效果。

    所以,LJ又開發了Perlbal。特點:

    • 快,小,可管理的http web 服務器/代理
    • 可以在內部進行轉發
    • 使用Perl開發
    • 單線程,異步,基于事件,使用epoll , kqueue
    • 支持Console管理與http遠程管理,支持動態配置加載
    • 多種模式:web服務器,反向代理,插件
    • 支持插件:GIF/PNG互換?

    11、MogileFS

    LJ使用開源的MogileFS作為分布式文件存儲系統。MogileFS使用非常簡單,它的主要設計思想是:

    • 文件屬于類(類是最小的復制單位)
    • 跟蹤文件存儲位置
    • 在不同主機上存儲
    • 使用MySQL集群統一存儲分布信息
    • 大容易廉價磁盤

    到目前為止就這么多了,更多文檔可以在http://www.danga.com/words/找到。Danga.comLiveJournal.com的 同學們拿這個文檔參加了兩次MySQL Con,兩次OS Con,以及眾多的其它會議,無私的把他們的經驗分享出來,值得我們學習。在web2.0時代快速開發得到大家越來越多的重視,但良好的設計仍是每一個應 用的基礎,希望web2.0們在成長為Top500網站的路上,不要因為架構阻礙了網站的發展。

     http://blog.csdn.net/xmr_gxcfe/archive/2007/09/14/1785292.aspx

     

    posted @ 2007-09-29 21:26 Coolfiry 閱讀(550) | 評論 (0)編輯 收藏

    posted @ 2007-09-25 14:30 Coolfiry 閱讀(356) | 評論 (0)編輯 收藏

    UML類圖的各種標識法
    關鍵字:   UML    
    ·------>虛線箭頭表示依賴關系(dependency),一個類需要與另外一個類一起工作,是它一種最弱的關聯關系,常見于各種工具類之間的關系
    ·——實線表示聯合關系(association),一個類包含對另外一個類對象的引用,這個通常是使用屬性來實現的,為了表明之間的包含關系,有時候會在實線的一端加上箭頭(navigability arrow)來表示導航關系,如果關聯的雙方又都和第三個類有關聯關系,那么可以在實線的中間加一個虛線和第三個類關聯來表示這種association classes關系
    ·◇——空心菱形加實線表示聚合關系(aggregation),它是一種更強的關聯關系,表示一個類可以擁有或者享有一個類的實例對象,在java代碼表現上跟聯合是一樣的。
    ·◆——實心菱形加實線表示組合關系(composition),它的關聯性比聚合更強,被組合的對象是組合對象的一部分,沒法跟其他的對象共享,而且如果組合對象銷毀的話,被組合的對象也會同時被銷毀,其表現形式跟聯合一樣
    ·空心箭頭加實線,表示泛化generalization(繼承inheritance)關系,這個很簡單
    ·在rose中要建立enumeration,只需要在建立的class中將其stereotype設置為enumeration即可。stereotype只是用來做一個標記,并不包含別的意義

    posted @ 2007-06-10 18:03 Coolfiry 閱讀(480) | 評論 (0)編輯 收藏

    明天18號,要從沈陽回成都了

    posted @ 2007-05-17 09:46 Coolfiry 閱讀(212) | 評論 (0)編輯 收藏

    PO BO VO DTO POJO DAO概念及其作用(附轉換圖)

       J2EE開發中大量的專業縮略語很是讓人迷惑,尤其是跟一些高手討論問題的時候,三分鐘就被人家滿口的專業術語噴暈了,PO VO BO DTO POJO DAO,一大堆的就來了(聽過老羅對這種現象的批判的朋友會會心一笑)。

        首先聲明偶也不是什么高手,以下總結都是自己的體會。不對之處請您多指教。

    PO:
    persistant object持久對象

    最形象的理解就是一個PO就是數據庫中的一條記錄。
    好處是可以把一條記錄作為一個對象處理,可以方便的轉為其它對象。

     



    BO:
    business object業務對象

    主要作用是把業務邏輯封裝為一個對象。這個對象可以包括一個或多個其它的對象。
    比如一個簡歷,有教育經歷、工作經歷、社會關系等等。
    我們可以把教育經歷對應一個PO,工作經歷對應一個PO,社會關系對應一個PO。
    建立一個對應簡歷的BO對象處理簡歷,每個BO包含這些PO。
    這樣處理業務邏輯時,我們就可以針對BO去處理。

     



    VO :
    value object值對象
    ViewObject表現層對象

    主要對應界面顯示的數據對象。對于一個WEB頁面,或者SWT、SWING的一個界面,用一個VO對象對應整個界面的值。

     



    DTO :
    Data Transfer Object數據傳輸對象
    主要用于遠程調用等需要大量傳輸對象的地方。
    比如我們一張表有100個字段,那么對應的PO就有100個屬性。
    但是我們界面上只要顯示10個字段,
    客戶端用WEB service來獲取數據,沒有必要把整個PO對象傳遞到客戶端,
    這時我們就可以用只有這10個屬性的DTO來傳遞結果到客戶端,這樣也不會暴露服務端表結構.到達客戶端以后,如果用這個對象來對應界面顯示,那此時它的身份就轉為VO

     



    POJO :
    plain ordinary java object 簡單java對象
    個人感覺POJO是最常見最多變的對象,是一個中間對象,也是我們最常打交道的對象。

    一個POJO持久化以后就是PO
    直接用它傳遞、傳遞過程中就是DTO
    直接用來對應表示層就是VO

     


    DAO:
    data access object數據訪問對象
    這個大家最熟悉,和上面幾個O區別最大,基本沒有互相轉化的可能性和必要.
    主要用來封裝對數據庫的訪問。通過它可以把POJO持久化為PO,用PO組裝出來VO、DTO


          總結下我認為一個對象究竟是什么O要看具體環境,在不同的層、不同的應用場合,對象的身份也不一樣,而且對象身份的轉化也是很自然的。就像你對老婆來說就是老公,對父母來說就是子女。設計這些概念的初衷不是為了唬人而是為了更好的理解和處理各種邏輯,讓大家能更好的去用面向對象的方式處理問題.

          大家千萬不要陷入過度設計,大可不必為了設計而設計一定要在代碼中區分各個對象。一句話技術是為應用服務的。

    歡迎指正。



    畫了個圖,感覺沒有完全表達出自己的意思。。。。。誰幫忙完善下,最好能體現各個O在MVC中的位置
    snap20070108.jpg 


    轉自:http://www.tkk7.com/vip01/archive/2007/01/08/92430.html

    posted @ 2007-05-17 09:44 Coolfiry 閱讀(336) | 評論 (0)編輯 收藏

    I love English from then on.I study English hard form then on.I love it.It is very lovely.

    posted @ 2006-11-26 14:13 Coolfiry 閱讀(269) | 評論 (0)編輯 收藏

    一、引言

      隨著Internet的飛速發展,人們越來越依靠網絡來 查找他們所需要的信息,但是,由于網上的信息源多不勝數,也就是我們經常所說的"Rich Data, Poor Information"。所以如何有效的去發現我們所需要的信息,就成了一個很關鍵的問題。為了解決這個問題,搜索引擎就隨之誕生。

       現在在網上的搜索引擎也已經有很多,比較著名的有AltaVista, Yahoo, InfoSeek, Metacrawler, SavvySearch等等。國內也建立了很多的搜索引擎,比如:搜狐、新浪、北極星等等,當然由于它們建立的時間不長,在信息搜索的取全率和取準率上都 有待于改進和提高。

      Alta Vista是一個速度很快的搜索引擎,由于它強大的硬件配置,使它能夠做及其復雜的查詢。它主要是基于關鍵字進行查詢,它漫游的領域有Web和 Usenet。支持布爾查詢的"AND","OR"和"NOT",同時還加上最相近定位"NEAR",允許通配符和"向后"搜索(比如:你可以查找鏈接到 某一頁的所有Web站點)。你可以決定是否對搜索的短語加上權值,在文檔的什么部位去查找它們。能夠進行短語查詢而不是簡單的單詞查詢的優點是很明顯的, 比如,我們想要查找一個短語"to be or not to be",如果只是把它們分解成單詞的話,這些單詞都是屬于Stop Word,這樣這個查詢就不會有任何結果,但是把它當作一個整體來查詢,就很容易返回一些結果,比如關于哈姆雷特或者是莎士比亞等等的信息。系統對查詢結 果所得到的網頁的打分是根據在網頁中所包含的你的搜索短語的多少,它們在文檔的什么位置以及搜索短語在文檔內部之間的距離來決定的。同時可以把得到的搜索 結果翻譯成其他的語言。

      Exite是稱為具有"智能"的搜索引擎,因為它建立了一個基于概念的索引。當然,它所謂的"智能"是基 于對概率統計的靈活應用。它能夠同時進行基于概念和關鍵字的索引。它能夠索引Web,Usenet和分類的廣告。支持"AND","OR","NOT"等 布爾操作,同時也可以使用符號"+"和"-"。缺點是在返回的查詢結果中沒有指定網頁的尺寸和格式。

      InfoSeek是一個簡單 但是功能強大的索引,它的一個優點是有一個面向主題搜索的可擴展的分類。你可以把你的搜索短語和相似的分類目錄的主題短語相互參照,而那些主題短語會自動 加到你的查詢中去。使你的搜索有更好的主題相關性。同時它也支持對圖象的查詢。它能夠漫游Web,Usenet,Usenet FAQs等等。不支持布爾操作,但是可以使用符號"+"和"-"(相當于"AND"和"NOT")

      Yahoo實際上不能稱為是一 個搜索引擎站點,但是它提供了一個分層的主題索引,使你能夠從一個通常的主題進入到一個特定的主題,Yahoo對Web進行了有效的組織和分類。比如你想 要建立一個網頁,但是你不知道如何操作,為了在Yahoo上找到關于建立網頁的信息,你可以先在Yahoo上選擇一個主題:計算機和Internet,然 后在這個主題下,你可以發現一些子主題,比如:Web網頁制作,CGI編程,JAVA,HTML,網頁設計等,選擇一個和你要找的相關的子主題,最終你就 可以得到和該子主題相關的所有的網頁的鏈接。也就是說,如果你對要查找的內容屬于哪個主題十分清楚的話,通過目錄查詢的方法要比一般的使用搜索引擎有更好 的準確率。你可以搜索Yahoo的索引,但是事實上,你并沒有在搜索整個Web。但是Yahoo提供了選項使你可以同時搜索其他的搜索引擎,比如: Alta Vista。但是要注意的是Yahoo實際上只是對Web的一小部分進行了分類和組織,而且它的實效性也不是很好。

      搜索引擎的基本原理是通過網絡機器人定期在web網頁上爬行,然后發現新的網頁,把它們取回來放到本地的數據庫中,用戶的查詢請求可以通過查詢本地的數據庫來得到。如yahoo每天會找到大約500萬個新的網頁。

       搜索引擎的實現機制一般有兩種,一種是通過手工方式對網頁進行索引,比如yahoo的網頁是通過手工分類的方式實現的,它的缺點是Web的覆蓋率比較 低,同時不能保證最新的信息。查詢匹配是通過用戶寫入的關鍵字和網頁的描述和標題來進行匹配,而不是通過全文的匹配進行的。第二種是對網頁進行自動的索 引,象AltaVista則是完全通過自動索引實現的。這種能實現自動的文檔分類,實際上采用了信息提取的技術。但是在分類準確性上可能不如手工分類。

      搜索引擎一般都有一個Robot定期的訪問一些站點,來檢查這些站點的變化,同時查找新的站點。一般站點有一個robot.txt文 件用來說明服務器不希望Robot訪問的區域,Robot 都必須遵守這個規定。如果是自動索引的話,Robot在得到頁面以后,需要對該頁面根據其內容進行索引,根據它的關鍵字的情況把它歸到某一類中。頁面的信 息是通過元數據的形式保存的,典型的元數據包括標題、IP地址、一個該頁面的簡要的介紹,關鍵字或者是索引短語、文件的大小和最后的更新的日期。盡管元數 據有一定的標準,但是很多站點都采用自己的模板。文檔提取機制和索引策略對Web搜索引擎的有效性有很大的關系。高級的搜索選項一般包括:布爾方法或者是 短語匹配和自然語言處理。一個查詢所產生的結果按照提取機制被分成不同的等級提交給用戶。最相關的放在最前面。每一個提取出來的文檔的元數據被顯示給用 戶。同時包括該文檔所在的URL地址。

      另外有一些關于某一個主題的專門的引擎,它們只對某一個主題的內容進行搜索和處理,這樣信息的取全率和精度相對就比較高。

       同時,有一類搜索引擎,它本身不用Robot去定期的采集網頁。象SavvySearch 和 MetaCrawler是通過向多個搜索引擎同時發出詢問并對結果進行綜合返回給用戶實現搜索功能。當然實際上象SavvySearch能夠對各個搜索引 擎的功能進行分析和比較,根據不同的用戶查詢提交給不同的搜索引擎進行處理,當然用戶自己也可以指定利用哪一個搜索引擎。

      一個優秀的搜索引擎必須處理以下幾個問題:1 網頁的分類2 自然語言的處理3 搜索策略的調度和協作 4 面向特定用戶的搜索。所以很多搜索引擎不同程度的使用了一些人工智能的技術來解決這些方面的問題。

      二、網絡Spider的實現描述

       現在有很多文章對Web引擎做了大量的介紹和分析,但是很少有對它們的實現做一個詳細的描述,這里我們主要來介紹一個具有基本功能的Web引擎的實現。 本文,我們以類C++語言的形式來描述Web引擎如何采集網頁并存放到數據庫中的過程。同時描述了如何根據用戶輸入的關鍵字查詢數據庫并得到相關網頁的過 程。

      2.1數據庫結構

      首先,我們要建立一個數據庫表用來存放我們得到的網頁。這里一般需要建立如下的表:

      1.字典表的建立,事實上這里是用文檔中有意義的單詞和它們的出現頻率來代表一個文檔。

      該表(WordDictionaryTbl)主要要包括三個字段,主要是用來存放和一個網頁相關的單詞的情況

    ????url_id? ? 對每一個URL的唯一的ID號
    ? ? word? ? ? 該URL中的經過stem的單詞
    ? ? intag? ? 該單詞在該網頁中的出現的次數

      2.存儲每一個URL信息的表

      該表(URLTbl)中主要的關鍵字段有:

    ? rec_id? ? ? ? 每一條記錄的唯一的ID號
    ? status? ? 得到該URL內容的狀態,比如HTTP_STATUS_TIMEOUT表示
    ? ? ? ? ? ? 下載網頁的最大允許超時
    ? url? ? ? ? URL的字符串名稱
    ? content_type? ? ? 內容的類型
    ? last_modified? ? 最新的更改時間
    ? title? ? ? ? ? ? 該URL的標題
    ? docsize? ? ? ? ? 該URL的文件的尺寸
    ? last_index_time? 最近一次索引的時間
    ? next_index_time? 下一次索引的時間
    ? tag? ? 對于網頁,用來表示它的類型,比如:是text,或者是html,
    ? ? ? ? ? ? ? ? ? ? 或者是圖片等等
    ? hops? ? ? ? ? ? ? 得到文件時候的曾經失敗的次數
    ? keywords? ? ? ? ? 對于網頁,和該網頁相關的關鍵字
    ? description? ? ? 對于網頁,指網頁的內容的描述
    ? lang? ? ? ? ? ? ? 文檔所使用的語言

       3.因為網頁中有很多單詞是一些介詞和語氣助詞或者是非常常用的常用詞,它們本身沒有多少意義。比如:英語中的about,in,at,we,this 等等。中文中的如"和","一起","關于"等等。我們統一的把它們稱為停止詞(stop word)。所以我們要建立一個表,來包括所有這些停止詞。該表(StopWordTbl)主要有兩個字段。
    word char(32)? ? 表示那些停止詞
    lang char(2)? ? ? 表示所使用的語言

      4.我們要建立一個關于robot的表,我們在前面說過,所有的網站一般都有一個robot.txt文件用來表示網絡上的robot可以訪問的權限。該表(RobotTbl)主要有以下字段。
    ? ? hostinfo? ? ? ? ? Web站點主機的信息
    ? ? path? ? ? ? ? ? ? 不允許robot訪問的目錄

      5.建立我們需要屏蔽的那些網頁(比如一些內容不健康的或者沒有必要去搜索的站點)的一張表(ForbiddenWWWTbl),主要的字段就是網頁的URL。

       6.另外我們需要建立一個我們所要得到的文件類型的表(FileTypeTbl),比如,對于一個簡單的Web搜索引擎,我們可能只需要得到后綴為. html,htm,.shtml和txt的類型文件。其他的我們只是簡單的忽略它們。主要的字段就是文件的類型和說明。

      其中關于停止詞的表的內容是我們要實現要根據各種語言的統計結果,把那些意義不大的單詞放進去。關于文檔單詞、URL和Robot的表的內容都是在獲取Web網頁的時候動態增加記錄的。

      2.2 具體網頁獲取算法描述

      具體的網頁的獲取步驟是這樣的:

       我們可以設定我們的搜索程序最大可以開的線程的數目,然后這些線程可以同時在網上進行搜索,它們根據數據庫中已有的關于網頁的信息,找出那些需要更新的 網頁(如何判斷哪些網頁需要更新是一個值得研究的過程,現在有很多啟發式和智能的算法,基本上是基于統計規律進行建模。最簡單的當然是設定一個時間范圍, 在某個時間范圍以前的網頁被重新去搜索一遍),然后判斷那些網頁是否在屏蔽表中,如果是的話,就從關于URL的表中刪除該條記錄。否則,我們就到相應的 WWW站點去得到URL指定的文件(這里需要注意的是根據不同的URL的特點,需要使用不同的協議,比如對于FTP站點要采用FTP協議,對于HTTP站 點要采用HTTP協議,新聞站點要采用NNTP協議等等)事實上,我們先得到關于該網頁的頭信息,如果該網頁的最新修改時間和我們最近提取的時間是一樣的 話,表示該網頁內容沒有任何更新,則我們就不必去得到它的內容,只需要修改最近一次更新它的時間為當前的時間就可以了。如果該網頁最近做了修改,我們就要 得到該網頁,并對它的內容進行分析,主要要包括和它相關的鏈接,把它們加到相應的數據庫中,同時判斷網頁所包含的各種其他的文件,如文本文件、圖形文件、 聲音文件和其他多媒體文件是否是我們所需要的文件,如果是的話,就把它加到我們響應的數據庫中。同時要根據網頁的內容提取所有的有意義的單詞和它們的出現 的次數,放到相應的數據庫中。為了更好的描述這個過程,我們來看跟這個過程相關的主要的幾個對象和數據結構。對象主要是針對三個層次來講的。第一層是針對 WWW服務器,第二層是針對每一個頁面,第三層是針對每一個頁面的全文的索引。

      2.3 和實現相關的主要類對象和功能描述下面的結構是針對一個站點來說的。

    ? ? Class? CServer {
    ? ? 主要的屬性有:
    ????char *url;? ? ? ? ? ? //WWW站點的URL名稱
    ????char *proxy;? ? ? ? ? //使用的代理的名稱
    ????char *basic_auth;? ? ? //進行基本的HTTP認證
    ????int? proxy_port;? ? ? //代理的端口號
    ????int? period;? ? ? ? ? //再次索引的周期
    ????int? net_errors;? ? ? //網絡連接不通的次數
    ????int? max_net_errors;? //可以允許的最大的網絡錯誤
    ????int? read_timeout;? ? //下載文件允許的最大的延遲
    ????int? maxhops;? ? ? ? ? //表示URL可以最大跳轉的深度
    ????int? userobots;? ? ? ? //是否遵守robot.txt中的約定
    ????int? bodyweight;? // 在< body >....< /body >之間的單詞的權重
    ????int? titleweight; // 在< title >....< /title >之間的單詞的權重
    ????int? urlweight;? // 在文檔的URL中的單詞的權重
    ????int descweight;//在? ? < META
    NAME="Description"? ? ? ? Content="..." >之間單詞的權重
    ????int? keywordweight; //在< META NAME="Keywords" Content="..." >
    ? 之間的單詞的權重

      主要方法有:
    FindServer();//用來查找該服務器是否存在并可以連接
    FillDefaultAttribute() //用來針對所有的WWW服務器填寫默認的屬};

    以上的對象中的成員變量是和一個站點相關的參數的設置,我們對所有的站點有一個默認的設置,但是可以對某些站點做一些特殊的設置。這些設置可以在配置文件中設定。
      下面是關于文檔的結構的主要的數據成員:

    Class CNetDocument
    ? ? 主要屬性有:
    ? ? int????url_id; //該URL的ID號
    ? ? int????status;? //獲取該文檔時候的狀態
    ? ? int????size;? //文檔的尺寸
    int????tag;? //和該文檔相關的標簽,表示該文檔是
    HTML,TEXT或者是其他類型
    ? ? int????hops;? ? //URL跳轉的次數
    ? ? char????*url; //和該文檔相關的URL的名稱
    ? ? char????*content_type;? ? ? //該內容的類型
    ? ? char????*last_modified;? ? //最近一次的更新時間
    ? ? char????*title;? ? ? ? ? ? //該文檔的標題
    ? ? char????*last_index_time;? //上次索引的時間
    ? ? char????*next_index_time;? //下次索引的時間
    ? ? char????*keywords;? ? ? ? ? //該文檔中的關鍵字
    ? ? char????*description;? ? ? //該文檔的描述

    ? 主要方法有:
    ? FillDocInfo(…) //根據數據庫,得到該文檔相關信息
    ? AddHerf(…)? ? //加入網頁中存在的新的鏈接的網址
    ? DeleteURL(…)? //刪除一個存在的網址
    ? CanGetThisURL(…) //根據配置決定是否去得到該網頁
    ? //下面三個方法是根據不同的URL,用不同的協議去獲得文檔
    ? NNTPGet(…)? ? ?
    ? FTPGet(….)
    ? HTTPGet(….)
    ? ParseHead(…)? //如果是HTTP協議得到的話,分析頭信息
    ? ParseMainBody(…)? ? //對獲得的文檔的主體進行分析
    ? ServerResponseType (….)? //得到服務器端的響應消息
    ? UpdateURLDB(….)? //更新的數據入庫
    } ;

      事實上,我們在要提取一個網頁的時候,都要建立一個CNetDocument對象,然后再對這個網頁進行分析的時候,把相關的內容放到這個CNetDocument的成員變量里面。下面是關于頁面全文索引的結構的主要數據成員:
    Class CIndexer {
    主要屬性有:
    ? char????*url;? ? ? //我們要處理的文檔相關的URL的名稱
    ? int mwords;????? //? 我們事先設定的一個網頁的最大的單詞數目
    ????int nwords;????????? // 實際的得到的單詞的數目
    ????int swords;????????? // 我們已經排序的單詞的數目
    ????WORD *Word;????? //所有單詞的內容
    ????char *buf;? ? ? //我們為文檔所分配的空間
    主要方法有:
    ? InitIndexer(…)? ? //進行初始設置和分配
    ? ParseGetFile(…)? //對得到的網頁進行全文索引
    ? AddWord(…)? ? //把網頁的可以索引的單詞加到Word數組中去
    ? InToDB(….)? ? //關于網頁全文索引的信息入庫
    };

      進行網頁提取前,我們要建立一個CIndexer對象,它主要是用來對網頁進行全文的索引。一般來說我們只對兩種類型的URL進行全文索引,一個是text/html,另外一個是text/plain。其中WORD的數據結構如下:
    ? ? ? ? typedef struct word_struct {
    ????int count;? //該單詞出現的次數
    ????int code;? //該單詞的正常的形式,
    比如單詞可能為 encouraging,它的正常的形式應該為
    encourage,這其實是一種對單詞的stem。
    即我們只取單詞的主干部分。
    ????char *word;? //該單詞的內容
    } WORD;

      以下的結構是和網頁中的一些鏈接的對象相關的一個數據結構
    ? ? typedef struct href_struct {
    ????char *href;? ? //該鏈接的名稱
    ????int hops;? ? ? //發生的跳轉次數
    ????int stored;? ? //是否已經存儲到數據庫中
    } HREF;
    ?

      所有需要更新的和新產生的URL都被放到這個結構中,當它的數量超過一定的范圍以后,被一次性的存入數據庫。
      關于URL的一個數據結構如下:

    typedef struct url {
    char *schema; //表示該URL是通過什么協議得到的,比如HTTP,
    ? ? ? ? ? ? ? FTP,NNTP等。
    char *specific;? ? //主機的名稱加上路徑
    char *hostinfo;? ? //主機的名稱加上相關的協議端口
    char *hostname;? ? //主機的名稱
    char *path;? ? ? ? //在主機的具體的路徑
    char *filename;? ? //文件的名稱
    char *anchor;? ? ? //相關的anchor
    int? port;? ? ? ? //協議相關的端口
    } URL;

      這是針對URL的一些相關的屬性的描述的一個數據結構。事實上在數據庫中,我們存儲的只是對網頁的描述和對一些文本和HTML頁面的關鍵詞的索引信息。我們并不存儲網頁的實際的內容。

      三、用戶查詢實現描述

      關于對用戶提交的查詢請求的實現分析:

      用戶想要查詢某一方面的信息一般都是通過提供和該領域相關的幾個關鍵字來進行的。

      我們來看一下關于用戶查詢的相關的數據結構和類:

      下面是一個關于單詞和它的權值的基本結構:

    ? typedef struct word_weight_pair
    ? ? {
    ? ? ? char word[WORD_LEN];
    ? ? ? int weight;
    ? ? }word_weight_pair;
    ? ?

      下面的類主要是用來對用戶的查詢進行處理和分析:
    ? ? Class CUserQuery
    ? ? {
    char m_UserQuery[MAX_QUERYLEN];? //用戶的查詢表達式
    CPtrArray word_weight_col;
    //是關于結構word_weight_pair的動態數組
    int m_maxReturnSum;? //用戶希望返回的最多的網頁數
    int search_mode;
    CObArray m_returnDoc;? //是關于CNetDocument對象的一個動態數組
    NormalizeWord(char* OneWord);? //對單詞進行歸整化,即Stem.
    Find(char* odbcName);? //進行數據庫查找和匹配
    };

      系統實現的基本的步驟如下:

      1.對用戶輸入的查詢表達式進行分析。事實上,我們在前面的Spider搜索過程中對文檔的表示是通過關鍵字形式描述的,每一個文檔可以表示為這樣的一個集合

    ? ? 其中 ::=< 單詞或短語名稱 >< 單詞或短語的權值 >

      實際上就是采用矢量空間的表示方法來表示的文檔。

       我們對用戶輸入的查詢表達式也采用矢量空間的表示方法。我們認為用戶輸入的關鍵字的順序代表了它的重要性的程度,所以對于位置靠前的單詞有相對比較高的 優先級,同時我們對所有的內容以短語或者是單詞為最小原子,進行Stem操作,即象前面所提到的:比如單詞Encouraging就轉化成 Encourage的格式。然后去掉那些Stop Word,比如is ,as等等的單詞,這些單詞存放在StopWordTbl表中。 然后把所有歸整化后的內容放入動態數組word_weight_col中去。

      2.對于動態數組word_weight_col中 的每一個元素,即結構word_weight_pair(包括單詞和該單詞的權重),我們從表WordDictionaryTbl中可以找到和這些單詞相 關的記錄,這些記錄應該是包括了所有的在word_weight_col中的單詞。

      進行網頁是否和查詢相匹配的計算。匹配計算的 過程如下:首先我們對所有的記錄按URL地址進行排序。因為可能好幾條記錄對應的是一個URL,然后對每一個網頁進行打分,每一條記錄的單詞權值為 INITSCORE*WEIGHT+(TOTALTIMES-1)*WEIGHT* INCREMENT。其中INITSCORE為每一個單詞的基準分數,TOTALTIMES為該單詞在網頁中的出現的次數,WEIGHT是該單詞在不同的 內容段出現有不同的權值(比如在KEYWORD段,或者是標題段,或者是內容段等等)。INCREMENT是該單詞每多出現一次所增加的分數。

      3.根據用戶指定的m_maxReturnSum,顯示匹配程度最高的前m_maxReturnSum頁。

      四、結束語

       我們利用上面所討論的機制,在WINDOWS NT操作系統下,用VC++和SQL SERVER實現了一個Web搜索引擎的網頁搜集過程。在建立了一個基本的搜索引擎的框架以后,我們可以基于這個框架,實現一些我們自己設計的算法,比如 如何更好的進行Spider的調度,如何更好的進行文檔的歸類,如何更好的理解用戶的查詢,用來使Web搜索引擎具有更好的智能性和個性化的特點。

    posted @ 2006-11-11 21:37 Coolfiry 閱讀(471) | 評論 (0)編輯 收藏

         摘要: 1.?? 目標 使用 apache 和 tomcat 配置一個可以應用的 web 網站,要達到以下要求: 1、? Apache 做為 HttpServer ,后面連接多個 tomcat...  閱讀全文

    posted @ 2006-11-06 17:20 Coolfiry 閱讀(724) | 評論 (0)編輯 收藏

    JDBC學習筆記
    2004-9-13?????星期一?????小雨

    l.?連接到數據庫的方法
    答:1)?ODBC(Open?Database?Connectivity)
    ???????一個以C語言為基礎訪問SQL為基礎數據庫引擎的接口,它提供了一致的接口用于和數據庫溝通以及訪問數據。
    ????2)?JDBC
    ???????Java版本的ODBC

    2.?JDBC應用編程接口
    答:JDBC應用編程接口是:
    ????1)?標準的數據訪問接口,可以連到不同的數據庫;
    ????2)?JAVA編程語言的一組類和接口。
    ????JDBC應用編程接口能夠:
    ????1)?連接到數據庫;
    ????2)?發SQL查詢字符串到數據庫;
    ????3)?處理結果。
    ????JDBC應用編程接口有二個主要的部分:
    ????1)?JAVA應用程序開發接口面向JAVA應用程序開發者;
    ????2)?JDBC驅動程序開發接口
    ????
    3.?JDBC?Driver
    答:1)?一大堆實現了JDBC類和接口的類;
    ????2)?提供了一個實現java.sql.Driver接口的類。

    4.?JDBC?Driver的四種類型
    答:1)?JDBC-ODBC橋
    ????由ODBC驅動提供JDBC訪問
    ????2)?本地API
    ????部分Java?driver把JDBC調用轉化成本地的客戶端API
    ????3)?JDBC-net
    ????純的Java?driver,將JDBC調用轉入DBMS,與網絡協議無關。然后通過服務器將調用轉為DBMS協議。
    ????4)?本地協議
    ????純的java?driver,將JDBC調用直接轉為DBMS使用的網絡協議

    5.?JDBC開發者接口
    答:1)?java.sql--java?2平臺下JDBC的主要功能,標準版(J2SE)
    ????2)?javax.sql--java?2平臺下JDBC增強功能,企業版(J2EE)

    6.?使用URL確認數據庫
    答:我們使用URL來確定一個數據庫(正確的Driver,正確的主機,正確的協議,正確的協議,正確的用戶名和密碼);
    ????語法:protocol:subprotocol:subname
    ????范例:jdbc:db2:MyTest
    ??????????jdbc:db2://localhost:6789/MyTest

    7.?javax.sql包JDBC2.0的增強功能
    答:1)?數據源接口;
    ????2)?連接池;
    ????3)?分布式交易;
    ????4)?行集;

    8.?創建一個基本的JDBC應用
    答:1)?步驟一:注冊一個driver;
    ????2)?步驟二:建立一個到數據庫的連接;
    ????3)?步驟三:創建一個statement;
    ????4)?步驟四:執行SQL語句;
    ????5)?步驟五:處理結果;
    ????6)?步驟六:關閉JDBC對象

    9.?注冊一個Driver(步驟一)
    答:1)?driver被用于連接到數據庫;
    ????2)?JDBC應用編程接口使用第一個能成功連接到給定URL的driver;
    ????3)?在同一時間可以裝載多個driver

    10.注冊一個driver的方法:
    答:1)?使用類loader(裝載;實例化;注冊入DriverManager)
    ???????a.?Class.forName("Com.ibm.db2.jdbc.app.DB2Driver");
    ???????b.?Class.forName("Com.ibm.db2.jdbc.net.DB2Driver");
    ???????c.?Class.forName("Com.microsoft.jdbc.sqlServer.SQLServerDriver);
    ???????d.?Class.forName("oracl.jdbc.driver.OracleDriver");
    ???????e.?Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    ????2)?實例化一個Driver
    ???????a.?Driver?drv?=?new?COM.cloudscape.core.RmiJdbcDriver();

    2004-9-14?????星期二?????陰

    1.?建立一個到數據庫的連接(步驟二)
    答:DriverManager調用getConnection(urlString)方法,實際上調用的是driver的connect(urlString)方法;
    ????1)?當一個driver肯定地對應到一個數據庫URL,DriverManager建立一個連接;
    ????2)?當沒有driver匹配,返回null然后下一個driver被檢驗;
    ????3)?假如沒有建立連接,拋出一個SQLExcepiton異常

    2.?經常使用的一些JDBC?URL
    答:1)?JDBC-ODBC:?jdbc:odbc:<DB>
    ????2)?Oracle:?jdbc:oracle:oci:@<sid>?or?jdbc:oracle:thin:@<SID>
    ????3)?Weblogic?MS-SQL:?jdbc:weblogic:mssqlserver4:<DB>@<HOST>:<PORT>
    ????4)?DB2:?jdbc:db2:MyTest?or?jdbc.db2://localhost:6789/MyTest(需要用戶名和密碼)

    3.?Driver連接方法
    答:1)?創建一個到指定Driver實例的直接調用;
    ????2)?避免一般訪問的問題
    ???????Driver?drv?=?new?COM.ibm.db2.jdbc.app.DB2Driver();
    ???????Connection?con?=?null;
    ???????try?{con?=?drv.connect("jdbc:db2:MyTest",new?Properties())}
    ???????catch(SQLException?e){}

    4.?創建一個Statement(步驟三)
    答:1)?Statement的三個接口:
    ???????a.?Statement;
    ???????b.?PreparedStatement(繼承自Statement);
    ???????c.?CallableStatement(繼承自PreparedStatement);
    ????2)?使用方法Connection.createStatement()得到一個Statement對象

    5.?PreparedStatement對象
    答:1)?調用ProparedStatement比statement更為高效;
    ????2)?繼承自Statement;
    ????3)?語法:PreparedStatement?pstm?=?connection.prepareStatement(sqlString);

    6.?CallableStatement對象
    答:1)?通過CallableStatement調用數據庫中的存儲過程;
    ????2)?繼承自PreparedStatement;
    ????3)?CallableStatement?cstm?=?connection.prepareCall("{call?return_student[?,?]}");
    ???????cstm.setString(1,"8623034");
    ???????cstm.registerOutparameter(2,?Types.REAL);
    ???????cstm.execute();
    ???????float?gpa?=?cstm.getFloat(2);

    7.?Statement接口的比較
    答:?????????????|?Statement???????????|?PreparedStatement?????????|??CallableStatement
    ????------------------------------------------------------------------------------
    ????寫代碼位置???|???客戶端????????????|?客戶端????????????????????|??服務器端
    ????------------------------------------------------------------------------------
    ????寫代碼位置???|???客戶端????????????|?服務器端??????????????????|??服務器端
    ????------------------------------------------------------------------------------
    ????編寫代碼技術?|Java,SQL操作????????|Java,SQL操作??????????????|??數據庫的程序語言,如PL/SQL
    ????------------------------------------------------------------------------------
    ????可配置性?????|???高????????????????|第一次高,以后低???????????|??低
    ????------------------------------------------------------------------------------
    ????可移植性?????|???高????????????????|假設支持PreparedStatement的話高????
    ????------------------------------------------------------------------------------
    ????傳輸效率?????|???低????????????????|第一次低,以后高???????????|??高

    8.?執行SQL?Statement(步驟四)
    答:通過接口方法將SQL語句傳輸至?認的數據庫連接,返回結果可能是一個數據表,可以通過java.sql.ResultSet訪問。
    ????1)?Statement的接口方法:
    ????a.?executeQuery(sqlString):?執行給定的SQL聲明,返回一個結果集(ResultSet)對象;
    ????b.?executeUpdate(sqlString):?執行給定的SQL聲明,可以是INSERT、UPDATE或DELETE聲明,也可以是SQL?DDL聲明;
    ????c.?execute(sqlString):?執行給定的SQL聲明。

    9.?處理結果(步驟五)
    答:1)?使用結果集(ResultSet)對象的訪問方法獲取數據;
    ???????a.?next():下一個記錄
    ???????b.?first():第一個記錄
    ???????c.?last():最后一個記錄
    ???????d.?previous():上一個記錄
    ????2)?通過字段名或索引取得數據
    ????3)?結果集保持了一個指向了當前行的指針,初始化位置為第一個記錄前。

    10.?關閉JDBC對象(步驟六)
    答:1)?首先關閉記錄集;
    ????2)?其次關閉聲明;
    ????3)?最后關閉連接對象。

    11.?數據表和類對應的三種關系:
    答:1)?一個表對應一個類;
    ????2)?一個表對應相關類;
    ????3)?一個表對應整個類關系層

    12.?類間關系的幾種表設計:
    答:1)?多對一,
    ????2)?一對一:?
    ????3)?一對多:
    ????4)?多對多:

    13.?SQL數據類型及其相應的Java數據類型
    答:SQL數據類型?????????????????????Java數據類型??????????????說明
    ????------------------------------------------------------------------
    ????INTEGER或者INT??????????????????int?????????????????????通常是個32位整數
    ????SMALLINT????????????????????????short???????????????????通常是個16位整數
    ????NUMBER(m,n)?DECIMAL(m,n)????????Java.sql.Numeric????????合計位數是m的定點十進制數,小數后面有n位數
    ????DEC(m,n)????????????????????????Java.sql.Numeric????????合計位數是m的定點十進制數,小數后面有n位數
    ????FLOAT(n)????????????????????????double??????????????????運算精度為n位二進制數的浮點數
    ????REAL????????????????????????????float???????????????????通常是32位浮點數
    ????DOUBLE??????????????????????????double??????????????????通常是64位浮點數
    ????CHARACTER(n)或CHAR(n)???????????String??????????????????長度為n的固定長度字符串
    ????VARCHAR(n)??????????????????????String??????????????????最大長度為n的可變長度字符串
    ????BOOLEAN?????????????????????????boolean?????????????????布爾值
    ????DATE????????????????????????????Java.sql.Date???????????根據具體設備而實現的日歷日期
    ????TIME????????????????????????????Java.sql.Time???????????根據具體設備而實現的時戳
    ????TIMESTAMP???????????????????????Java.sql.Timestamp??????根據具體設備而實現的當日日期和時間
    ????BLOB????????????????????????????Java.sql.Blob???????????二進制大型對象
    ????CLOB????????????????????????????Java.sql.Clob???????????字符大型對象
    ????ARRAY???????????????????????????Java.sql.Array
    ????

    2004-9-15?????星期三??????陰

    1.?元數據
    答:關于數據的信息,例如類型或者容量。通過JDBC?API可以訪問:
    ????1)?數據庫元數據;
    ???????a.?使用connection.getMetadata方法返回DataMetaData引用
    ???????b.?能夠使用isReadOnly此類方法獲取信息
    ????2)?結果集元數據;
    ???????a.?使用ResultSet.getMetadata方法返回ResultSetMetaData引用
    ???????b.?能夠使用getColumnCount此類方法獲取信息

    2.?事務處理
    答:1)?一系列的動作作為一個不可分的操作;
    ????2)?JDBC?API中使用事務處理步驟:
    ???????a.?用false作為參數調用setAutoCommit方法;
    ???????b.?執行一或多個關于數據庫的操作;
    ???????c.?調用commit方法完成改變;
    ???????d.?恢復上次提交后的改變,調用rollback方法.

    ???????try
    ???????{
    ??????????con.setAutoCommit(false);
    ??????????Statement?stm?=?con.createStatement();
    ??????????stm.executeUpdate("insert?into?student(name,?age,?gpa)?values('gzhu',?30,?4.8)");
    ??????????stm.commit();
    ???????}
    ???????catch(SQLException?e)
    ???????{
    ??????????try
    ??????????{
    ?????????????con.rollback();
    ??????????}
    ??????????catch(Exception?e)
    ??????????{
    ??????????}
    ???????}

    3.?并發控制
    答:1)?設置隔離級別方法:setTransactionIsolation
    ????2)?隔離級別靜態變量
    ???????a.?TRANSACTION_NONE:只讀的數據字典;
    ???????b.?TRANSACTION_READ_UNCOMMITTED:只讀未提交數據;
    ???????c.?TRANSACTION_READ_COMMITTED:只讀未提交數據;
    ???????d.?TRANSACTION_REPEATABLE_READ:重復讀取數據;
    ???????e.?TRANSACTION_SERIALIZABLE:無論做什么操作都不許別人動。
    ????3)?示例:con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

    4.?JDBC?2.0?應用程序編程接口增強功能
    答:1)?ResultSet增強:
    ???????a.?可以回卷;
    ???????b.?可以修改;
    ???????設置示例:Statement?stm?=?con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    ????2)?Statement增強了批量修改能力(batch?updates);
    ????3)?更高級的數據類型(例:Struct)。

    5.?JDBC?2.0標準擴展
    答:1)?JNDI(Java?Naming?and?Directory?Interface):?解決離散狀態下Object的查找;
    ????2)?連接池:在內存中保存了一個數據庫連接,不需要注冊驅動器,提高性能的重要方法。

    posted @ 2006-11-03 10:14 Coolfiry 閱讀(237) | 評論 (0)編輯 收藏

    問題引入:
    在實習過程中發現了一個以前一直默認的錯誤,同樣char *c = "abc"和char c[]="abc",前者改變其內

    容程序是會崩潰的,而后者完全正確。
    程序演示:
    測試環境Devc++
    代碼
    #include <iostream>
    using namespace std;

    main()
    {
    ?? char *c1 = "abc";
    ?? char c2[] = "abc";
    ?? char *c3 = ( char* )malloc(3);
    ?? c3 = "abc";
    ?? printf("%d %d %s\n",&c1,c1,c1);
    ?? printf("%d %d %s\n",&c2,c2,c2);
    ?? printf("%d %d %s\n",&c3,c3,c3);
    ?? getchar();
    }??
    運行結果
    2293628 4199056 abc
    2293624 2293624 abc
    2293620 4199056 abc

    參考資料:
    首先要搞清楚編譯程序占用的內存的分區形式:
    一、預備知識—程序的內存分配
    一個由c/C++編譯的程序占用的內存分為以下幾個部分
    1、棧區(stack)—由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似于

    數據結構中的棧。
    2、堆區(heap)—一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。注意它與數據

    結構中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
    3、全局區(靜態區)(static)—全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態

    變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束后由系統

    釋放。
    4、文字常量區—常量字符串就是放在這里的。程序結束后由系統釋放。
    5、程序代碼區
    這是一個前輩寫的,非常詳細
    //main.cpp
    ? int a=0;??? //全局初始化區
    ? char *p1;?? //全局未初始化區
    ? main()
    ? {
    ?? int b;棧
    ?? char s[]="abc";?? //棧
    ?? char *p2;???????? //棧
    ?? char *p3="123456";?? //123456\0在常量區,p3在棧上。
    ?? static int c=0;?? //全局(靜態)初始化區
    ?? p1 = (char*)malloc(10);
    ?? p2 = (char*)malloc(20);?? //分配得來得10和20字節的區域就在堆區。
    ?? strcpy(p1,"123456");?? //123456\0放在常量區,編譯器可能會將它與p3所向"123456"優化成一個

    地方。
    }
    二、堆和棧的理論知識
    2.1申請方式
    stack:
    由系統自動分配。例如,聲明在函數中一個局部變量int b;系統自動在棧中為b開辟空間
    heap:
    需要程序員自己申請,并指明大小,在c中malloc函數
    如p1=(char*)malloc(10);
    在C++中用new運算符
    如p2=(char*)malloc(10);
    但是注意p1、p2本身是在棧中的。
    2.2
    申請后系統的響應
    棧:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。
    堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,
    會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,并將

    該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大

    小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由于找到的堆結點的大小不一定正

    好等于申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。
    2.3申請大小的限制
    棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地

    址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯

    時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間

    較小。
    堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地

    址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的

    虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
    2.4申請效率的比較:
    棧:由系統自動分配,速度較快。但程序員是無法控制的。
    堆:是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.
    另外,在WINDOWS下,最好的方式是用Virtual Alloc分配內存,他不是在堆,也不是在棧,而是直接在進

    程的地址空間中保留一塊內存,雖然用起來最不方便。但是速度快,也最靈活。
    2.5堆和棧中的存儲內容
    棧:在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可執行語句)的

    地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變

    量。注意靜態變量是不入棧的。
    當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主

    函數中的下一條指令,程序由該點繼續運行。
    堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容由程序員安排。
    2.6存取效率的比較
    char s1[]="aaaaaaaaaaaaaaa";
    char *s2="bbbbbbbbbbbbbbbbb";
    aaaaaaaaaaa是在運行時刻賦值的;
    而bbbbbbbbbbb是在編譯時就確定的;
    但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。
    比如:
    #include
    voidmain()
    {
    char a=1;
    char c[]="1234567890";
    char *p="1234567890";
    a = c[1];
    a = p[1];
    return;
    }
    對應的匯編代碼
    10:a=c[1];
    004010678A4DF1movcl,byteptr[ebp-0Fh]
    0040106A884DFCmovbyteptr[ebp-4],cl
    11:a=p[1];
    0040106D8B55ECmovedx,dwordptr[ebp-14h]
    004010708A4201moval,byteptr[edx+1]
    004010738845FCmovbyteptr[ebp-4],al
    第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據

    edx讀取字符,顯然慢了。
    2.7小結:
    堆和棧的區別可以用如下的比喻來看出:
    使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會

    切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
    使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。

    自我總結:
    char *c1 = "abc";實際上先是在文字常量區分配了一塊內存放"abc",然后在棧上分配一地址給c1并指向

    這塊地址,然后改變常量"abc"自然會崩潰

    然而char c2[] = "abc",實際上abc分配內存的地方和上者并不一樣,可以從
    4199056
    2293624 看出,完全是兩塊地方,推斷4199056處于常量區,而2293624處于棧區

    2293628
    2293624
    2293620 這段輸出看出三個指針分配的區域為棧區,而且是從高地址到低地址

    2293620 4199056 abc 看出編譯器將c3優化指向常量區的"abc"


    繼續思考:
    代碼:
    #include <iostream>
    using namespace std;

    main()
    {
    ?? char *c1 = "abc";
    ?? char c2[] = "abc";
    ?? char *c3 = ( char* )malloc(3);
    ?? //? *c3 = "abc" //error
    ?? strcpy(c3,"abc");
    ?? c3[0] = 'g';
    ?? printf("%d %d %s\n",&c1,c1,c1);
    ?? printf("%d %d %s\n",&c2,c2,c2);
    ?? printf("%d %d %s\n",&c3,c3,c3);
    ?? getchar();
    }??
    輸出:
    2293628 4199056 abc
    2293624 2293624 abc
    2293620 4012976 gbc
    寫成注釋那樣,后面改動就會崩潰
    可見strcpy(c3,"abc");abc是另一塊地方分配的,而且可以改變,和上面的參考文檔說法有些不一定,

    而且我不能斷定4012976是哪個區的,可能要通過算區的長度,希望高人繼續深入解釋,謝謝
    ?

    posted @ 2006-10-16 19:06 Coolfiry 閱讀(1147) | 評論 (2)編輯 收藏

    ?和?'f'標志指定的相同順序。

    示例1:將兩個class文件存檔到一個名為?'classes.jar'?的存檔文件中:
    ?& nbsp;?????jar?cvf& nbsp;classes.jar?Foo.class?Bar.class
    示例2:用一個存在的清單(manifest)文件?'mymanifest'?將?foo/& nbsp;目錄下的所有
    ????? ??????文件存檔到一個名為 ?'classes.jar'?的存檔文件中:
    ?? ?????jar?cvfm? classes.jar?mymanifest?-C?foo/?.

    來個小例子試試看:
    我們只有一個HelloWorld,如下:
    public& nbsp;?class??HelloWorld{
    ?public& nbsp;static?void?main(String[]?args){
    ?System.out.println(“Hi,?Hello?World!”);
    }
    }
    我將這個java文件存到C盤跟目錄下,ok,接下來,
    在先前打開的命令提示符下(跳轉到C盤提示符下),我們輸入javac?HelloWorld.java,然后繼續輸入:jar?& nbsp;cvf??hello.jar??HelloWorld.class,回車后去你的C盤看看,多了什么,沒錯?hello.jar?。
    基本的步驟我們現在都知道了,你可以自己去嘗試一下隨著jar后面的參數的不同,結果有什么變化。
    緊接著我們看看如何運行我們的jar包。
    在進入正題之前,你要先打開我們剛剛做好的jar包看看,多了什么呢,META-INF目錄?再看看里面是什么,還有一個MANIFEST.MF文件是不是?用文本編輯器(我這里是UltraEdit)打開它看看:
    Manifest-Version:? 1.0
    Created-By:?1.4.2?(Sun?Microsystems& nbsp;Inc.)
    就是這樣。這里我們對它進行修改,加一句:Main-Class:? HelloWorld?(在第三行)。這個就是我們之前寫的那個類,也就是我們的入口類。也即,
    Manifest -Version:?1.0
    Created-By:?1.4.2?(Sun& nbsp;Microsystems?Inc.)
    Main-Class:?HelloWorld< BR>接下來,我們在命令提示符里執行:
    jar?umf? MANIFEST.MF?app.jar
    這樣我們使用了我們自己的MANIFEST.MF文件對原來默認的進行了更新。你不妨可以再進去看看是不是添上了Main-Class:?HelloWorld這一句。
    Ok,這個最后的一步了,來驗證我們做的一切,在命令提示符中輸入:
    java?-jar?hello.jar (執行)
    出現了什么,――Hi,?Hello?World!
    我們再來看看 jar文件在tomcat中發布,注意:在tomcat中我們就不能再用jar這種格式,而改war格式,它是專門用于web應用的,其實整個過程下來基本上和jar是類似的:
    先準備我們要打包的資源。
    找到存放tomcat的webapps目錄,進到其中,新建一個文件夾,這里命名為hello,再進去新建WEB-INF文件夾,再進去新建classes文件夾,此時我們也將我們唯一的servlet, HelloWorld.java放到這里,在與classes目錄同級下建立一文件web.xml。Ok,目前我們初步建立了一個簡單的web應用。
    這是HelloWorld.java:
    import?java.io.*;< BR>import?javax.servlet.*;
    import?javax.servlet.http.*;
    public?class?HelloWorld?extends& nbsp;HttpServlet?{
    ?public?void? doGet(HttpServletRequest?req,?HttpServletResponse? res)
    ?????? ???????& nbsp;??????? ???????& nbsp;?throws?ServletException,?IOException& nbsp;{
    ??res.setContentType("text/html");
    ??PrintWriter?out?=?res.getWriter ();
    ??out.println("<HTML>");< BR>??out.println("<HEAD><TITLE& gt;Hello,?World!</TITLE></HEAD>");< BR>??out.println("<BODY>");
    & nbsp;?out.println("Hello,?World!");
    ? ?out.println("</BODY></HTML>");
    ?}
    }//end?here!
    對它編譯。下面是web.xml:< BR><?xml?version="1.0"?encoding="ISO-8859-1"?>
    <!DOCTYPE?web-app?PUBLIC
    ??'-//Sun?Microsystems,?Inc.//DTD?Web?Application?2.3//EN'
    ??'http://java.sun.com/j2ee/dtds/web-app_2_3.dtd&< /FONT>#39;>
    <web-app>
    ??<servlet>
    ?? ??<servlet-name>hello</servlet-name& gt;
    ????<servlet-class& gt;HelloWorld</servlet-class>
    ?? </servlet>
    ??<servlet-mapping& gt;
    ?<servlet-name>hello</servlet- name>
    ?<url-pattern>/HelloWorld& lt;/url-pattern>
    ??</servlet-mapping& gt;
    </web-app>
    開始壓縮,形成war檔:
    在命令提示符下進到先前創制的hello目錄下,執行?jar??cvf? ?hello.war??*?,我們便得到hello.war。將它拷貝至 webapps目錄下,ok,來看最后一步,打開tomcat的目錄conf中的server.xml,加入:
    ? ?<Context?path="/hello"?docBase="hello.war"& nbsp;debug="0"
    ????reloadable ="true"/>
    大功告成!運行它,啟動tomcat,后在瀏覽器中輸入http://localhost:8080/hello/HelloWorld,有了嗎?
    最后,如果你想用ant來完成以上的打包活動,下面就告訴你:
    對于jar來說。在build.xml 中,
    ?<target?name="jar">< BR>??<jar?destfile="${app_home}/hello.jar"& gt;
    ???<fileset?dir=" ${dest}"?includes="**"/>
    ??& nbsp;???<!--fileset?dir="${dest} "?includes="**/action.properties"/-->
    ? ???</jar>
    ?& lt;/target>

    對于war,
    <war& nbsp;warfile="hello.war"?webxml="./WEB-INF/web.xml">
    ????<fileset?dir="html"/& gt;
    ????<lib? dir="lib/">
    ????& nbsp;???<exclude?name="oracle*.jar"/& gt;
    ????</lib>
    ????<classes? dir="build/servlets">
    ???& nbsp;?????<include& nbsp;name="**/*.class"/>
    ??</classes& gt;
    </war>?
    好了,就這么多,希望對你有點幫助。:)
    我上傳了上面打過的兩個包,hello.jar和hello.war。『 點擊下載』
    『 點擊下載』
    第一rar文件對應的是hello.jar,下載后將其名改為 hello.jar
    第二rar文件對應hello.war,下載后改為hello.war。
    這是由于上傳不了 jar格式和war格式的文件,你只好照我上面說的去做了?:)

    補充:
    ############
    jar基本操作:
    ############
    1.& nbsp;創建jar文件
    ??jar?cf?jar- file?input-file(s)
    c---want?to?Create& nbsp;a?JAR?file.
    f---want?the? output?to?go?to?a?file? rather?than?to?stdout.
    eg:?1) jar?cf?myjar.jar?query_maintain_insert.htm< BR>????2)jar?cvf? myjar.jar?query_maintain_insert.htm
    ?? ????v---Produces?verbose(詳細的)?output.
    ????3)jar& nbsp;cvf?myjar.jar?query_maintain_insert.htm?mydirectory< BR>????4)jar?cv0f? myjar.jar?query_maintain_insert.htm?mydirectory
    ??????0---don't& nbsp;want?the?JAR?file?to?be& nbsp;compressed.
    ????5)jar& nbsp;cmf?MANIFEST.MF?myjar.jar?yahh.txt
    ??????m---Used& nbsp;to?include?manifest?information? from?an?existing?manifest?file.
    ????6)jar?cMf?MANIFEST.MF& nbsp;myjar.jar?yahh.txt
    ???& nbsp;??M---the?default?manifest? file?should?not?be?produced.
    ????7)jar?cvf?myjar.jar& nbsp;*
    ?????? *---create?all?contents?in?current& nbsp;directory.
    2.?察看jar文件
    ?& nbsp;jar?tf?jar-file
    t---want?to& nbsp;view?the?Table?of?contents? of?the?JAR?file.
    eg:?1)jar& nbsp;vft?yahh.jar
    ???? ??v---Produces?verbose(詳細的)? output.
    3.?提取jar文件
    ?? jar?xf?jar-file?[archived-file(s)]
    x- --want?to?extract?files?from? the?JAR?archive.
    eg:?1)jar?xf& nbsp;yahh.jar?yahh.txt(僅提取文件yahh.txt)
    ?& nbsp;??2)jar?xf?yahh.jar?alex/yahhalex.txt (僅提取目錄alex下的文件yahhalex.txt)
    ???& nbsp;3)jar?xf?yahh.jar(提取該jar包中的所有文件或目錄)
    4.?修改Manifest文件
    ??jar? cmf?manifest-addition?jar-file?input-file(s)< BR>m---Used?to?include?manifest?information& nbsp;from?an?existing?manifest?file.< BR>5.?更新jar文件
    ??jar? uf?jar-file?input-file(s)
    u---want?to?update?a

    posted @ 2006-10-12 18:40 Coolfiry 閱讀(347) | 評論 (0)編輯 收藏

    發布Java應用程序時你會感到困難?好在Java提供了一系列打包和發布工具,可以顯著的簡化發布過程
      
      該文章提供了打包Java code的幾種方法,我們將會探討Java manifest 文件,給出用于管理JAR文件所依賴文件、估計跨平臺發布所需的CLasspath的合適方法.我也會解釋如何使用manifest包版本特性來確認包的兼容性...
      
      什么是JAR文件?
      
      在開發過程中,我們可以直接使用Java class文件來運行程序,但這并不是一個好方式,好在Java 提供了 JAR(Java Archive)文件來提供發布和運行。
      
      jar 文件實際上是class 文件的ZIP壓縮存檔,這種格式被廣泛使用,因此易與使用,有很多中工具可以操作這種格式的文件。也正是因為這個原因,jar文件本身并不能表達所包含應用程序的標簽信息。
      
      Manifest 因此得以出現
      
       為了要提供存檔的標簽信息,jar 文件指定了一個特定目錄來存放標簽信息:META-INF 目錄,其中我們來關注該目錄中的MANIFEST.MF文件,他就是JAR的manifest文件,他包含了JAR文件的內容描述,并在運行時向JVM提 供應用程序的信息,大多數JAR文件含有一個默認生成的manifest 文件,執行JAR命令或使用zip工具,都可以產生它
      
      如果是由jar命令產生的 manifest 文件,形如:
      Manifest-Version: 1.0
      Created-By:1.4.0-beta
      (Sun Microsystems Inc.)
      
       這些信息沒甚么用,僅僅告訴我們使用的是1.0的manifest文件,第一行定義manifest的格式,第二行說明使用 SUN 的JDK1.4的jar工具生成該文件,如果manifest文件是由其他 (如ant) 創建的,那將會出現 “Created-By: Ant 1.2” 之類的內容,如果你是自己創建manifest文件,你可以加入自己的一些相關信息.
      
      基礎格式
      
      manifest 文件的格式 是很簡單的,每一行都是 名-值 對應的:屬性名開頭,接著是 ":" ,然后是屬性值,每行最多72個字符,如果需要增加,你可以在下一行續行,續行以空格開頭,以空格開頭的行都會被視為前一行的續行。
      
      所有在開頭的屬性都是全局的,你也可以定義特定class 或package的屬性,稍后將介紹這種
      
      把manifest文件插入JAR文件
      
      使用 m 選項,把指定文件名的manifest文件 傳入,例如
      jar cvfm myapplication.jar myapplication.mf -C classdir
      
      如果你使用ant來創建時,在ant 的build.xml 加入如下條目
      <target name="jar">
      <jar jarfile ="myapplication.jar"
      manifest="myapplication.mf">
      <fileset dir="classdir"
      includes="**/*.class"/>
      </jar>
      </target>
      
      運行Java程序
      
      現在我們來體驗一下manifest文件的作用,如果現在我們有一個Java 應用程序打包在myapplication.jar中, main class為 com.example.myapp.MyAppMain ,那么我們可以用以下的命令來運行
      java -classpath myapplication.jar com.example.myapp.MyAppMain
      
      這顯然太麻煩了,現在我們來創建自己的manifest文件,如下:
      Manifest-Version: 1.0
      Created-By: JDJ example
      Main-Class: com.example.myapp.MyAppMain
      
      這樣我們就可以使用如下的命令來運行程序了:(明顯簡單多了,也不會造成無謂的拼寫錯誤)
      java -jar myapplication.jar
      
      管理JAR的依賴資源
      
       很少Java應用會僅僅只有一個jar文件,一般還需要 其他類庫。比如我的應用程序用到了Sun 的 Javamail classes ,在classpath中我需要包含activation.jar 和 mail.jar,這樣在運行程序時,相比上面的例子,我們要增加一些:
      java -classpath mail.jar:activation.jar -jar myapplication.jar
      
      在不同的操作系統中,jar包間的分隔符也不一樣,在UNIX用“:”,在window中使用 “;”,這樣也不方便
      
      同樣,我們改寫我們的manifest文件,如下
      Manifest-Version: 1.0
      Created-By: JDJ example
      Main-Class: com.example.myapp.MyAppMain
      Class-Path: mail.jar activation.jar
      
      (加入了Class-Path: mail.jar activation.jar,用空格分隔兩個jar包)
      
      這樣我們仍然可以使用和上例中相同的命令來執行該程序:
      java -jar myapplication.jar
      
       Class-Path屬性中包含了用空格分隔的jar文件,在這些jar文件名中要對特定的字符使用逃逸符,比如空格,要表示成"%20",在路徑的表 示中,都采用“/”來分隔目錄,無論是在什么操作系統中,(即使在window中),而且這里用的是相對路徑(相對于本身的JAR文件):
      Manifest-Version: 1.0
      Created-By: JDJ example
      Main-Class: com.example.myapp.MyAppMain
      Class-Path: ext/mail.jar ext/activation.jar
      Multiple Main Classes(多主類)
      
       還有一種Multiple Main Classes情況,如果你的應用程序可能有命令行版本 和GUI版本,或者一些不同的應用卻共享很多相同的代碼,這時你可能有多個Main Class,我們建議你采取這樣的策略:把共享的類打成lib包,然后把不同的應用打成不同的包,分別標志主類:如下
      Manifest for myapplicationlib.jar:
      Manifest-Version: 1.0
      Created-By: JDJ example
      Class-Path: mail.jar activation.jar
      Manifest for myappconsole.jar:
      Manifest-Version: 1.0
      Created-By: JDJ example
      Class-Path: myapplicationlib.jar
      Main-Class: com.example.myapp.MyAppMain
      Manifest for myappadmin.jar:
      Manifest-Version: 1.0
      Created-By: JDJ example
      Class-Path: myapplicationlib.jar
      Main-Class: com.example.myapp.MyAdminTool
      在myappconsole.jar 和 myappadmin.jar的manifest文件中分別注明各自的 Main Class
      Package Versioning
      
      完成發布后,如果使用者想了解 ,哪些代碼是誰的?目前是什么版本?使用什么版本的類庫?解決的方法很多 ,manifest提供了一個較好的方法,你可以在manifest文件中描述每一個包的信息。
      
       Java 秉承了實現說明與描述分離的原則,package 的描述 定義了package 是什么,實現說明 定義了誰提供了描述的實現,描述和實現包含 名、版本號和提供者。要得到這些信息,可以查看JVM的系統屬性(使用 java.lang.System.getProperty() )
      
      在manifest文件中,我可以為每個package定義描述和實現版本,聲明名字,并加入描述屬性和實現屬性,這些屬性是
      
      Specification-Title
      Specification-Version
      Specification-Vendor
      Implementation-Title
      Implementation-Version
      Implementation-Vendor
      
      當要提供一個類庫或編程接口時,描述信息顯得是很重要,見以下范例:
      
      Manifest-Version: 1.0
      Created-By: JDJ example
      Class-Path: mail.jar activation.jar
      Name: com/example/myapp/
      Specification-Title: MyApp
      Specification-Version: 2.4
      Specification-Vendor: example.com
      Implementation-Title: com.example.myapp
      Implementation-Version: 2002-03-05-A
      Implementation-Vendor: example.com
      
      Package Version 查詢
      
      在manifest文件中加入package描述后,就可以使用Java提供的java.lang.Package class進行Package 的信息查詢,這里列舉3個最基本的獲取package object的方法
      
      1.Package.getPackages():返回系統中所有定義的package列表
      
      2.Package.getPackage(String packagename):按名返回package
      
      3.Class.getPackage():返回給定class所在的package
      
      使用者這方法就可以動態的獲取package信息.
      
      需要注意的是如果給定的package中沒有class被加載,則也無法獲得package 對象
      
      Manifest 技巧
      
      總是以Manifest-Version屬性開頭
      
      每行最長72個字符,如果超過的化,采用續行
      
      確認每行都以回車結束,否則改行將會被忽略
      
      如果Class-Path 中的存在路徑,使用"/"分隔目錄,與平臺無關
      
      使用空行分隔主屬性和package屬性
      
      使用"/"而不是"."來分隔package 和class ,比如 com/example/myapp/
      
      class 要以.class結尾,package 要以 / 結尾

    posted @ 2006-10-12 18:38 Coolfiry 閱讀(293) | 評論 (0)編輯 收藏

    下面就來看看什么是 JAR 文件包吧:

    1. JAR 文件包

    JAR 文件就是 Java Archive File,顧名思意,它的應用是與 Java 息息相關的,是 Java 的一種文檔格式。JAR 文件非常類似 ZIP 文件——準確的說,它就是 ZIP 文件,所以叫它文件包。JAR 文件與 ZIP 文件唯一的區別就是在 JAR 文件的內容中,包含了一個 META-INF/MANIFEST.MF 文件,這個文件是在生成 JAR 文件的時候自動創建的。舉個例子,如果我們具有如下目錄結構的一些文件:

      ==

      `-- test

        `-- Test.class

    把它壓縮成 ZIP 文件 test.zip,則這個 ZIP 文件的內部目錄結構為:

      test.zip

      `-- test

        `-- Test.class

    如果我們使用 JDK 的 jar 命令把它打成 JAR 文件包 test.jar,則這個 JAR 文件的內部目錄結構為:

      test.jar

      |-- META-INF

      |  `-- MANIFEST.MF

      `-- test

        `--Test.class

    2. 創建可執行的 JAR 文件包

    制作一個可執行的 JAR 文件包來發布你的程序是 JAR 文件包最典型的用法。

    Java 程序是由若干個 .class 文件組成的。這些 .class 文件必須根據它們所屬的包不同而分級分目錄存放;運行前需要把所有用到的包的根目錄指定給 CLASSPATH 環境變量或者 java 命令的 -cp 參數;運行時還要到控制臺下去使用 java 命令來運行,如果需要直接雙擊運行必須寫 Windows 的批處理文件 (.bat) 或者 Linux 的 Shell 程序。因此,許多人說,Java 是一種方便開發者苦了用戶的程序設計語言。

    其實不然,如果開發者能夠制作一個可執行的 JAR 文件包交給用戶,那么用戶使用起來就方便了。在 Windows 下安裝 JRE (Java Runtime Environment) 的時候,安裝文件會將 .jar 文件映射給 javaw.exe 打開。那么,對于一個可執行的 JAR 文件包,用戶只需要雙擊它就可以運行程序了,和閱讀 .chm 文檔一樣方便 (.chm 文檔默認是由 hh.exe 打開的)。那么,現在的關鍵,就是如何來創建這個可執行的 JAR 文件包。

    創建可執行的 JAR 文件包,需要使用帶 cvfm 參數的 jar 命令,同樣以上述 test 目錄為例,命令如下:

    jar cvfm test.jar manifest.mf test

    這里 test.jar 和 manifest.mf 兩個文件,分別是對應的參數 f 和 m,其重頭戲在 manifest.mf。因為要創建可執行的 JAR 文件包,光靠指定一個 manifest.mf 文件是不夠的,因為 MANIFEST 是 JAR 文件包的特征,可執行的 JAR 文件包和不可執行的 JAR 文件包都包含 MANIFEST。關鍵在于可執行 JAR 文件包的 MANIFEST,其內容包含了 Main-Class 一項。這在 MANIFEST 中書寫格式如下:

    Main-Class: 可執行主類全名(包含包名)

    例如,假設上例中的 Test.class 是屬于 test 包的,而且是可執行的類 (定義了 public static void main(String[]) 方法),那么這個 manifest.mf 可以編輯如下:

    Main-Class: test.Test <回車>

    這個 manifest.mf 可以放在任何位置,也可以是其它的文件名,只需要有 Main-Class: test.Test 一行,且該行以一個回車符結束即可。創建了 manifest.mf 文件之后,我們的目錄結構變為:

      ==

      |-- test

      |  `-- Test.class

      `-- manifest.mf

    這時候,需要到 test 目錄的上級目錄中去使用 jar 命令來創建 JAR 文件包。也就是在目錄樹中使用“==”表示的那個目錄中,使用如下命令:

    jar cvfm test.jar manifest.mf test

    之后在“==”目錄中創建了 test.jar,這個 test.jar 就是執行的 JAR 文件包。運行時只需要使用 java -jar test.jar 命令即可。

    需要注意的是,創建的 JAR 文件包中需要包含完整的、與 Java 程序的包結構對應的目錄結構,就像上例一樣。而 Main-Class 指定的類,也必須是完整的、包含包路徑的類名,如上例的 test.Test;而且在沒有打成 JAR 文件包之前可以使用 java <類名> 來運行這個類,即在上例中 java test.Test 是可以正確運行的 (當然要在 CLASSPATH 正確的情況下)。

    3. jar 命令詳解

    jar 是隨 JDK 安裝的,在 JDK 安裝目錄下的 bin 目錄中,Windows 下文件名為 jar.exe,Linux 下文件名為 jar。它的運行需要用到 JDK 安裝目錄下 lib 目錄中的 tools.jar 文件。不過我們除了安裝 JDK 什么也不需要做,因為 SUN 已經幫我們做好了。我們甚至不需要將 tools.jar 放到 CLASSPATH 中。

    使用不帶任何的 jar 命令我們可以看到 jar 命令的用法如下:

    jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目錄] 文件名 ...

    其中 {ctxu} 是 jar 命令的子命令,每次 jar 命令只能包含 ctxu 中的一個,它們分別表示:

    -c 創建新的 JAR 文件包

    -t 列出 JAR 文件包的內容列表

    -x 展開 JAR 文件包的指定文件或者所有文件

    -u 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中)

    [vfm0M] 中的選項可以任選,也可以不選,它們是 jar 命令的選項參數

    -v 生成詳細報告并打印到標準輸出

    -f 指定 JAR 文件名,通常這個參數是必須的

    -m 指定需要包含的 MANIFEST 清單文件

    -0 只存儲,不壓縮,這樣產生的 JAR 文件包會比不用該參數產生的體積大,但速度更快

    -M 不產生所有項的清單(MANIFEST〕文件,此參數會忽略 -m 參數

    [jar-文件] 即需要生成、查看、更新或者解開的 JAR 文件包,它是 -f 參數的附屬參數

    [manifest-文件] 即 MANIFEST 清單文件,它是 -m 參數的附屬參數

    [-C 目錄] 表示轉到指定目錄下去執行這個 jar 命令的操作。它相當于先使用 cd 命令轉該目錄下再執行不帶 -C 參數的 jar 命令,它只能在創建和更新 JAR 文件包的時候可用。  

    文件名 ... 指定一個文件/目錄列表,這些文件/目錄就是要添加到 JAR 文件包中的文件/目錄。如果指定了目錄,那么 jar 命令打包的時候會自動把該目錄中的所有文件和子目錄打入包中。

    下面舉一些例子來說明 jar 命令的用法:

    1) jar cf test.jar test

    該命令沒有執行過程的顯示,執行結果是在當前目錄生成了 test.jar 文件。如果當前目錄已經存在 test.jar,那么該文件將被覆蓋。

    2) jar cvf test.jar test

    該命令與上例中的結果相同,但是由于 v 參數的作用,顯示出了打包過程,如下:

    標明清單(manifest)

    增加:test/(讀入= 0) (寫出= 0)(存儲了 0%)

    增加:test/Test.class(讀入= 7) (寫出= 6)(壓縮了 14%)

    3) jar cvfM test.jar test

    該命令與 2) 結果類似,但在生成的 test.jar 中沒有包含 META-INF/MANIFEST 文件,打包過程的信息也略有差別:

    增加:test/(讀入= 0) (寫出= 0)(存儲了 0%)

    增加:test/Test.class(讀入= 7) (寫出= 6)(壓縮了 14%)

    4) jar cvfm test.jar manifest.mf test

    運行結果與 2) 相似,顯示信息也相同,只是生成 JAR 包中的 META-INF/MANIFEST 內容不同,是包含了 manifest.mf 的內容

    5) jar tf test.jar

    在 test.jar 已經存在的情況下,可以查看 test.jar 中的內容,如對于 2) 和 3) 生成的 test.jar 分別應該此命令,結果如下;

    對于 2)

    META-INF/

    META-INF/MANIFEST.MF

    test/

    test/Test.class

    對于 3)

    test/

    test/Test.class

    6) jar tvf test.jar

    除顯示 5) 中顯示的內容外,還包括包內文件的詳細信息,如:

    0 Wed Jun 19 15:39:06 GMT 2002 META-INF/

    86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF

    0 Wed Jun 19 15:33:04 GMT 2002 test/

    7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class

    7) jar xf test.jar

    解開 test.jar 到當前目錄,不顯示任何信息,對于 2) 生成的 test.jar,解開后的目錄結構如下:

      ==

      |-- META-INF

      |  `-- MANIFEST

      `-- test

        `--Test.class

    .net/forum/images/smiles/icon_cool.gif border=0> jar xvf test.jar

    運行結果與 7) 相同,對于解壓過程有詳細信息顯示,如:

    創建:META-INF/

    展開:META-INF/MANIFEST.MF

    創建:test/

    展開:test/Test.class

    9) jar uf test.jar manifest.mf

    在 test.jar 中添加了文件 manifest.mf,此使用 jar tf 來查看 test.jar 可以發現 test.jar 中比原來多了一個 manifest。這里順便提一下,如果使用 -m 參數并指定 manifest.mf 文件,那么 manifest.mf 是作為清單文件 MANIFEST 來使用的,它的內容會被添加到 MANIFEST 中;但是,如果作為一般文件添加到 JAR 文件包中,它跟一般文件無異。

    10) jar uvf test.jar manifest.mf

    與 9) 結果相同,同時有詳細信息顯示,如:

    增加:manifest.mf(讀入= 17) (寫出= 19)(壓縮了 -11%)

    4. 關于 JAR 文件包的一些技巧

    1) 使用 unzip 來解壓 JAR 文件

    在介紹 JAR 文件的時候就已經說過了,JAR 文件實際上就是 ZIP 文件,所以可以使用常見的一些解壓 ZIP 文件的工具來解壓 JAR 文件,如 Windows 下的 WinZip、WinRAR 等和 Linux 下的 unzip 等。使用 WinZip 和 WinRAR 等來解壓是因為它們解壓比較直觀,方便。而使用 unzip,則是因為它解壓時可以使用 -d 參數指定目標目錄。

    在解壓一個 JAR 文件的時候是不能使用 jar 的 -C 參數來指定解壓的目標的,因為 -C 參數只在創建或者更新包的時候可用。那么需要將文件解壓到某個指定目錄下的時候就需要先將這具 JAR 文件拷貝到目標目錄下,再進行解壓,比較麻煩。如果使用 unzip,就不需要這么麻煩了,只需要指定一個 -d 參數即可。如:

    unzip test.jar -d dest/

    2) 使用 WinZip 或者 WinRAR 等工具創建 JAR 文件

    上面提到 JAR 文件就是包含了 META-INF/MANIFEST 的 ZIP 文件,所以,只需要使用 WinZip、WinRAR 等工具創建所需要 ZIP 壓縮包,再往這個 ZIP 壓縮包中添加一個包含 MANIFEST 文件的 META-INF 目錄即可。對于使用 jar 命令的 -m 參數指定清單文件的情況,只需要將這個 MANIFEST 按需要修改即可。

    3) 使用 jar 命令創建 ZIP 文件

    有些 Linux 下提供了 unzip 命令,但沒有 zip 命令,所以需要可以對 ZIP 文件進行解壓,即不能創建 ZIP 文件。如要創建一個 ZIP 文件,使用帶 -M 參數的 jar 命令即可,因為 -M 參數表示制作 JAR 包的時候不添加 MANIFEST 清單,那么只需要在指定目標 JAR 文件的地方將 .jar 擴展名改為 .zip 擴展名,創建的就是一個不折不扣的 ZIP 文件了,如將上一節的第 3) 個例子略作改動:

    jar cvfM test.zip test

    posted @ 2006-10-12 15:54 Coolfiry 閱讀(355) | 評論 (0)編輯 收藏

         摘要: 周 登朋 (zhoudengpeng@yahoo.com.cn), 軟件工程師, 上海交通大學 2006 年 9 月 06 日? 在本篇文章中,你會學習到如何利用 Lucene 實現高級搜索功能以及如何利用 Lucene 來創建 Web 搜索應用程序。通過這些學習,你就可以利用 Lucene 來創建自己的搜索應用程序。 ...  閱讀全文

    posted @ 2006-10-03 20:11 Coolfiry 閱讀(365) | 評論 (1)編輯 收藏

    一、閑聊

      今天要談的話題是COM,稍微深入一點,不知道大家用過C++Test或者Visual Assistant之類的軟件沒有,它們都有個非常引人注目的功能,那就是把它們自身嵌入到VC開發環境中去。這個功能讓我癡迷不已,原因只有一個:我想做一個可以嵌入VC開發環境的VC工程解析器,這樣用戶在VC開發環境中就可以直接對當前或所有工程進行各種分析,統計。那么實現它簡單嗎?簡單,Next和Copy即可輕松完成;僅僅這些嗎?不是,它的背后還有博大精深的COM做支撐。不管困難與否,還是讓我們先試為快。

      二、效果圖

    ?

      三、實現步驟:

      <3.1>新建一個<DevStudio Add-in Wizard>類型工程,輸入工程名稱"CodeAnalyser".

      <3.2>進入第二個畫面,系統要求用戶輸入插件的名稱和描述信息。并且要求用戶選擇是否需要生成工具欄以及是否自動添加VC事件響應代碼。


      <3.3>點擊"Finish"結束向導,進入代碼編輯窗口。

      在這里我們要說的一點是:該工程引用了ICommands接口,并從該接口上派生出 CCommands類。該類完成了所有用戶自定義函數接口,VC應用程序消息響應和VC調試動作的消息響應工作。當我們真正為CCommands類添加成員函數之前我們必須先為ICommands接口添加相應的函數接口聲明。在本工程中我總共為ICommands接口添加了兩個函數接口,它們名字分別為:GetCurDirCommandMethod和QuitCommandMethod聲明如下:(在CodeAnalyer.odl文件中)

    interface ICommands : IDispatch
    {
     // methods
     [id(1)] //在Vtable中的函數索引號
     HRESULT GetCurDirCommandMethod(); //得到VC當前工作目錄

     [id(2)] //在Vtable中的函數索引號
     HRESULT QuitCommandMethod (); //退出VC編輯器
    };

      在接口ICommands添加接口函數,那么相應的我們也要在類CCommands中聲明和實現ICommands接口函數,函數的內部代碼和普通工程代碼沒什么區別。

    //Implement(CCommands類內部接口函數的聲明)
    public:
    STDMETHOD(GetCurDirCommandMethod)(THIS);
    STDMETHOD(QuitCommandMethod)(THIS);

    //Function Code(Ccommands類內部接口函數的實現)
    //得到當前VC開發環境的工作目錄[您也可以讓它成為你想要實現的功能代碼]
    STDMETHODIMP CCommands::GetCurDirCommandMethod()
    {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE));
     BSTR bstrCurDir;
     m_pApplication->get_CurrentDirectory(&bstrCurDir);
     CString str(bstrCurDir);
     ::MessageBox(NULL, str, "VC工作目錄", MB_OK | MB_ICONINFORMATION);
     VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
     return S_OK;
    }

    //退出VC開發環境

    STDMETHODIMP CCommands::QuitCommandMethod()
    {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE));
     if(::MessageBox(NULL,"您想退出VC++編輯器嗎(Y/N)?","詢問信息...", MB_YESNO | MB_ICONQUESTION) == IDYES)
      m_pApplication->Quit();
      VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
     return S_OK;
    }

      <3.4> 創建工具欄,連接工具欄按鈕事件

      所有的幕后工作已經準備就緒,只差個工具欄界面就一切OK了。打開類CDSAddIn,它里面有三個成員函數,其中OnConnection和OnDisconnection成員函數的意義非常重要。它們的意義如下:

      <1>OnConnection:插件的初始化任務都在這里完成。如COM服務的啟動,工具欄/菜單欄的創建,工具欄按鈕/菜單項的添加與修改等等。

      <2>OnDisconnection:插件的卸載工作都在這里完成。如COM服務的卸載,工具欄/菜單欄的銷毀,釋放等等。

      了解了它們各自的用途之后我們就可以在相應的消息事件中添加代碼了。很顯然工具欄的初始化應該在OnConnection事件中完成。

      在OnConnection事件中系統首先獲得了VC應用程序接口,然后調用一個接口函數:AddCommand來為插件添加命令和命令影射函數。然后再使用另外一個接口函數AddCommandBarButton向工具欄中添加工具欄按鈕,其中每個工具欄按鈕會和一個命令標志符號相連接,這樣就能實現按鈕和命令(消息)之間的一一對應。下面是添加一個命令和一個工具欄按鈕的代碼(如果你要添加多個工具欄按鈕只要重復此步驟即可):

    LPCTSTR szCommand = _T("GetCurDirCommand");
    VARIANT_BOOL bRet;
    CString strCmdString;
    strCmdString.LoadString(IDS_CMD_STRING);
    strCmdString = szCommand + strCmdString;
    CComBSTR bszCmdString(strCmdString);
    CComBSTR bszMethod(_T("GetCurDirCommandMethod"));

    CComBSTR bszCmdName(szCommand); //和下面添加工具欄按鈕對應

    VERIFY_OK(pApplication->AddCommand(bszCmdString,bszMethod,0,dwCookie,&bRet));
    //AddCommand 參數含義:
    //bszCmdString:命令字符串。
    //bszMethod:Icommands接口函數名。
    //第三個參數代表位圖偏移量。
    //第四和第五個參數分貝為系統參數和返回值(參照MSDN的IApplication介紹)

    if (bRet == VARIANT_FALSE)
    {
     *OnConnection = VARIANT_FALSE;
     return S_OK;
    }

    //添加工具欄按鈕
    if (bFirstTime == VARIANT_TRUE)
    {
     VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));
    }

      <3.5> 編譯,連接及在VC中引入插件

      以上就是我們所有的代碼工作,接下來趕快Build以下吧。編譯通過的話,在你的工程Debug目錄下會有個dll文件。然后打開VC編輯器,在VC任何一個工具欄上點擊鼠標右鍵,彈出如下圖所示菜單。然后選擇”Customize”子菜單,打開如下圖所示的工具欄定制窗口:


      接著選擇該窗口的最后一頁"Add-Ins and Macro Files"出現下圖所示窗口。


      然后點擊”Browse...”按鈕,這時打開你工程下的Debug目錄中的DLL文件,這樣你就可以看到你制作的工具欄了。同樣你再次打開上面的菜單,這次可以看到多了一個工具欄,并且名字亂七八糟的,怎么改變工具欄的名字呢?方法很簡單:打開上面窗口中的”Toolbars”選項頁,在工具欄列表框中找到你的工具欄,然后在”Toolbar name”編輯框中輸入你想要的名字即可。再打開上面的菜單看看名字是不是變了,哈哈!


      OK,今天的話題就聊到這里。

    posted @ 2006-09-30 22:58 Coolfiry 閱讀(278) | 評論 (0)編輯 收藏

      插件式設計近年來非常流行,其中eclipse起了推波助瀾的作用,提到插件式就會不由自主的想到eclipse。其實插件式設計并不是什么新事物,早在幾十年前就有了。像X Server就是基于插件式設計的,除了核心功能外,它所有的擴展功能和設備驅動都是以插件方式加入進來的。

      基于插件的設計好處很多:把擴展功能從框架中剝離出來,降低了框架的復雜度,讓框架更容易實現。擴展功能與框架以一種很松的方式耦合,兩者在保持接口不變的情況下,可以獨立變化和發布。公開插件接口,讓第三方有機會擴展應用程序的功能,有財大家一起發。另外,還可以讓開源與閉源共存于一套軟件,你的插件是開源還是閉源,完全由你自己決定。

      基于插件設計并不神秘,相反它比起一團泥的設計更簡單,更容易理解。各種基于插件設計的架構都有自己的特色,但從總體架構上看,其模型都大同小異。這里我們介紹一個簡單的模型,并給出幾個實例,希望對新手有所啟發。

      1. 基本架構

    plugin.jpg

      插件式設計的應用程序,基本上可以用上圖來表示。當然,此圖是一種較高層次的表示,實際的設計會更復雜一些。我們在這里為了闡述方便,不用故意搞得那么復雜。

      應用程序由應用程序框架、插件接口、插件和公共函數庫四部分組成。

      應用程序框架負責應用程序的整體運作,它清楚程序整個流程,但并不知道每個過程具體要做什么。它在適當的時候調用一些插件,來完成真正的功能。

      插件接口是一個協議,可能用IDL描述,可能是頭文件,也可能一段文字說明。插件按照這個協議實現出來,就可以加入到應用程序中來。當然,對于復雜的系統,插件接口可能有多個,各自具有獨立的功能。

      插件是完成實際功能的實體,實現了要求的插件接口。盡管實現什么以及怎么實現,完全是插件自己的自由。在實際情況來,一般還是有些限制,因為插件接口本身可能就是一個限制。如,實現編譯功能的插件,自然不能實現成一個聊天功能的插件。

      公共函數庫是一組函數或者類,應用程序框架和插件都可以調用。它通常是一個獨立的動態庫(DLL)。應用程序框架本身是公用的,是代碼復用的一種方式。但并不是所有可復用代碼都可以放在框架中,特別是插件會用到的公共代碼,那會造成插件對框架的依賴。把這些公共代碼提取到一個獨立的庫中,是一種好的方法。

      另外,值得補充說明一下的是插件接口。插件接口通常有兩種:

      通用插件接口:這一類插件接口是通用的,你無法從接口函數看出這個插件的功能。它的接口函數通常有這些函數:

      init : 用于初始化插件,通常在插件被加載時調用。

      deinit:用于反初始化插件,通常在插件被卸載時調用。

      run:讓插件起動。

      stop:讓插件停止。

      至于插件要完成什么功能,要插到哪里,在init函數里決定,它調用公共函數庫里的函數把自己注冊到框架中某個位置。

      專用插件接口:這一類插件接口是專用的,看到它的接口函數說明,你就可以大致了解它的功能了。

      加入插件的方式通常采用配置信息來實現,配置信息可以是注冊表,也可以配置文件。也可以動態注冊進來,或者把插件放到指定的位置。

      下面我們來看幾個實例:

      2. 桌面設計

      最近一段時間完成了桌面模塊的設計和實現。按照以往的經驗,桌面模塊通常是變化最多的一個模塊,SPEC總是在不斷的調整的效果,不同客戶要求實現具有個性化的桌面,直到產品快發布了,桌面的SPEC還在不停的修改。另外,在智能手機中,桌面占有特殊的地位,很多東西都可能往桌面里塞,桌面不但是各種功能的大雜燴,還是一些系統消息的中轉站。

      這個任務比較棘手,所以在設計時就分外小心。首先想到的就是采用插件式設計,把外圍功能獨立出來,盡量簡化框架的實現。

      插件:每一個最小功能單元都是一個插件,它可以是可見的,也可以是不可的,也可以是動態變化的。比如時間、電池電量、網絡連接、信號強弱、新事件(如SMS、MMS、EMAL、ALARM和未接電話等)、應用程序快捷方式、左右操作按鈕和其它處理系統事件的功能單元。每個插件都用一個.desktop來描述,這是遵循freedesktop.org的標準的。

      桌面框架包括:狀態欄、開始菜單、操作欄、桌面區、事件管理器和主題管理器。而狀態欄、開始菜單、操作欄、桌面區和事件管理器都是容器,容納各種插件。對于可見的插件,可以有自己的表現方式,也可以采用通用的表現方式。

      公共函數庫:一些抽象的類、實現插件的輔助類以及其它一些可能被公用的類。

      插件接口:對于不可見的插件要求實現事件處理功能,可見的插件還要求實現繪制功能。

      3. 模擬器設計

      一個同事負責設計另外一個平臺的PC模擬環境設計。在我的建議下,他對架構作了調整。調整后的架構非常簡單,也可以認為是插件式的設計,它由下面幾部分組成:

      應用程序框架:負責模擬器基本功能,如模擬鍵盤和顯示設備、換膚功能等。

      插件:就是被模擬的平臺,如microwindow及相應的手機應用程序。盡管運行時通常只有一個插件運行,這樣做仍然有意義,如果要換成minigui或者其它平臺時,模擬器不需要作任何修改。

      公共函數庫:它由應用程序框架初始化一些信息和回調函數,然后供插件(即microwindow)調用,插件利用它來實現顯示和輸入等驅動程序。

      插件接口:如起動和停止模擬平臺等。

      4. GIMP

      GIMP是一個功能強大的圖形圖像編輯器,典型的基于插件式的設計,在《unix編程藝術》中,作為插件式設計示例介紹過。

      應用程序框架:GUI

      插件:完成圖像的各種轉換和處理功能,如模糊、去斑和色彩調整等。

      公共函數庫:放在libgimp.so里。

      插件接口:對GIMP感興趣的朋友,可以到官方網站上去閱讀更多的文檔。

    posted @ 2006-09-30 22:57 Coolfiry 閱讀(705) | 評論 (0)編輯 收藏

      Google推薦的開發環境是VS 2003,GoogleDesktop的插件是基于COM的,而COM是語言無關的,所以你可以用任何能開發COM的工具(語言)開發。

      如果你使用的VS 2003或者VS 2005,建立開發環境非常容易。不過,如果你像我一樣戀舊,還是喜愛VC6的簡潔快速,排斥龐大緩慢的VS 2003或者VS 2005,可能就要費一點周折了。

      這里只討論VC6的環境設置。

      Google沒有為VC6 提供開發向導,也就是說,所有代碼你都得手工就編寫。如果是出于學習的目的,手工去寫這些代碼,付出的勞動會有所回報的。另外,VC6所帶的ATL版本也有點老,一些類只有在新版本中才有,在VC6中無法使用,所以有時你不得不面對一些COM的細節問題。同樣,同樣如果出于學習的目的,所花費的時間也是值得的。

      建立開發環境的第一步就是下載GoogleDesktop的SDK,下載地址為http://desktop.google.com/。

      解開之后,GD_SDK\api目錄下有下面幾個目錄:

    documentation
    samples
    tools
    wizards

      建議先大概看一下documentation中的文檔,然后閱讀samples中的部分代碼,找一下感覺。

      GoogleDesktop提供全部接口都在三個IDL文件中聲明:

    GoogleDesktopActionAPI.idl
    GoogleDesktopAPI.idl
    GoogleDesktopDisplayAPI.idl

      開發GoogleDesktop的插件,有以上文件已經足夠(當然你要安裝GoogleDesktop本身)了。但是C++中不能直接使用idl文件,要通過midl.exe編譯成頭文件,才能使用。其實不用這么麻煩,GD_SDK\api\samples\common目錄中已經有相關頭文件了:

    GoogleDesktopDisplayAPI.h

    GoogleDesktopComponentRegistration.h

    GoogleDesktopAPI.h

    GoogleDesktopActionAPI.h

      直接使用這幾個頭文件,可以省去用midl編譯步驟。只要修改VC6的設置,讓它可以找到上述頭文件就行了。有兩種方式可以做到這一點。一種方式是針對當前項目的:

      1. 打開菜單Project->Settings

      2. 打開屬性頁的C/C++標簽

      3. 選擇Categary的Preprocessor項

      4. 在Additional Include directories一欄加入上述文件所在的目錄

      另一種方式是針對VC6所有的項目的:

      1. 打開菜單Tool->Options…

      2. 打開屬性頁的Directories標簽

      3. 選擇Show directories for中的include files項

      4. 在Directories中加上述文件所在的目錄

      至于選擇哪一種方式,完全看你個人愛好,后者會方便一點,對懶人比較適用,但它會影響所有的VC6項目,或許會有某些副作用。

    posted @ 2006-09-30 22:55 Coolfiry 閱讀(251) | 評論 (0)編輯 收藏

    ?

    原文地址:http://www.tkk7.com/BlueDavy/archive/2006/05/28/48593.html

    摘要:插件開發框架其實和目前開源界流行的MVC框架之類的相同,都決定了基于這個框架的開發方式,如基于MVC框架,就會按照MVC思想來進行開發,而插件開發框架呢,也是同樣如此,就要求基于插件的方式來進行開發,不過插件開發框架和MVC框架又有不同,插件開發框架是一個可以成為系統基礎架構的框架,而MVC框架通常來講不足以成為,如在目前的MVC框架Webwork、Struts上我們通常都需要加上Spring、Hibernate來構成系統完整的基礎架構,這個時候由于MVC框架的實現是沒有標準可參照的,就造成了在各種系統中形成了不同的但很類似的基礎架構,但卻造成了無法復用的現象;插件開發框架則是作為統一系統基礎架構的一種開發方式,它使得系統的復用成為了可能,而同時由于插件開發框架對于動態性的支持,使得系統更加的靈活和可擴展。來看看一個插件開發框架,應該提供些什么東西,作為改變系統架構思想的框架,插件框架需要考慮很多方面,如開發、測試、部署等,總結下來一個插件框架應提供插件的開發規范;插件開發、調試的IDE;

    posted @ 2006-09-30 22:53 Coolfiry 閱讀(271) | 評論 (0)編輯 收藏

         摘要: N皇后問題是一個典型的需要用回溯算法來解決的問題。回溯算法可以用遞歸方法來實現,也可以用非遞歸方法來實現。用遞歸的方法來解決回溯的問題思路很清晰,但是耗費的內存資源較多,速度也較慢;非遞歸方法具有速度快和耗費較少內存資源的優點,但是程序的邏輯結構卻很復雜——不過搞懂之后覺得也很簡單。???在寫非遞歸算法之前,參考了網上的一些文章,但是覺得那些程序都很晦澀難懂,而且存在一些問題,我索性自己寫了一個,...  閱讀全文

    posted @ 2006-09-27 22:20 Coolfiry 閱讀(468) | 評論 (0)編輯 收藏

    煩的很啊,但要認認真真做人,兢兢業業做事哦

    posted @ 2006-09-27 22:15 Coolfiry 閱讀(264) | 評論 (0)編輯 收藏

    網站如何做分布式(集群)的大綱

    何時要用分布式

    • 單臺服務器無法承受壓力。
    • 需要實現發生錯誤時候,自動切換
    • 學習或者測試分布式技術

    應用分布式的場景


    一、提供多個對外的接口,按照一定規則,分派不同請求由不同接口來處理。
    這時候需要考慮:
    • 如何實現負載均衡
      • 在哪個層次實現轉移負載
      • 負載的均衡如何實現
    • 如何實現故障轉移
      • 如何監控故障
      • 如何切換服務

    二、把一個功能拆分成多個功能,不同功能分布部署到不同服務器上

    • 對外功能的拆分?
      • http://news.sina.com.cn/ http://sports.sina.com.cn/ http://mobile.sina.com.cn/
      • http://www.microsoft.com/china/? http://www.microsoft.com/downloads/
      • SOA
    • n層架構,其中的一些層分布到不同服務器上
      • WEB + DB 模式

    網站請求中的分布式

    按照請求流程,我們可以在以下環節按照一定規則,把用戶的請求分流到不同服務器上:

    • Web Client Level
      • 例子:QQ 設置中你可以選擇登陸的服務器IP
    • DNS Based Selection
      • 優點:
      • 缺點:
        • 不能區分服務器的差異,也不能反映服務器的當前運行狀態。
        • DNS 的刷新需要時間,無法及時故障切換。
    • TCP balancing proxies
      • 硬件
      • 軟件
    • HTTP-aware routers

    • URL重定向

    網站應用中的分布式

    • 代理服務器實現請求的分離
      • Squid是Linux下一個緩存Internet數據的代理服務器軟件
    • 拆分網站對外功能
      • 不同域名前、后綴
      • URL 重寫
    • SOA
      • 每個Service 分布到一臺服務器上
    • n 層架構
      • 緩存分布式部署
        • 文件Cache
        • 內存Cache (memcached )
          • http://www.danga.com/memcached/
          • https://sourceforge.net/projects/memcacheddotnet/
      • DB分布式集群部署
        • 故障轉移
        • 發布訂閱
        • 分布式分區視圖
      • 應用服務器(比如定時發送郵件通知的服務)

      • 相關技術:
        • 企業服務
        • .net Remoting
        • WCF
        • Web Service

    ?

    如何判斷一個應用是否支持分布式

    如果發現某一部分應用需要做分布式了,就可以按照以下思路來考慮如何改造:

    從應用所用數據看是否支持分布式

    • 多份并存數據(一個數據存在多份)最大多長時間同步一次是可接受的。
      • 內存緩存的數據跟數據庫的數據(頁面級緩存和業務邏輯緩存)
      • 靜態文件跟數據庫
      • 查詢數據庫跟業務變更數據庫
    • 數據按照一定規則拆分(一個數據只存在一份)對業務是否有影響
      • 過去每年的數據遷移到一個對應歷史庫中。
      • 專用的圖片服務器 http://pics.ebaystatic.com/

    此處可分析:QQ的在線用戶數據,會是如何處理的呢?

    從應用邏輯過程看是否支持分布式

    • 是否可以并行執行這個邏輯過程

    • 這個邏輯過程是否可以拆分成幾個松耦合的過程

    微軟技術支持的5種分布式

    夏桅的這篇博客中的圖表就可以很詳細的對比這5種分布式:

    Windows的第五種群集方案 - CCS

    posted @ 2006-09-21 15:34 Coolfiry 閱讀(789) | 評論 (0)編輯 收藏

    http://www.tiobe.com/index.htm?tiobe_index

    這次 vb?和 vb.net 的數據再次合并了。C 系的語言 (java, c, c++, C#) 總體占了 50% 強。在 .net 環境中,vb 大概還是多過新生代的 C# 。
    這次仔細了看了看。才發覺這個升降是去年同期的比較。是能夠使用此語言的工程師,課程,獨立軟件生產商的相關比例。不直接代表用語言出了多少代碼。

    原來 Ruby 和 Java 基本上是同時代的東西。http://www.ruby-lang.org/? http://rubycn.ce-lab.net/?由日本人發明、流行的東西。
    從運行效率、性能上來看,C/C++ 比 Java 強。Java 比 Ruby 強。在比較簡單的功能中,Java 和 C/C++ 可以相當接近。當程序的算法比較復雜,涉及大量數據的時候,Java 速度還是會落后近一個數量級。此時,內存使用也會有幾倍的差異。
    http://blog.csdn.net/rmartin/archive/2006/08/30/1143161.aspx
    http://www.butunclebob.com/ArticleS.UncleBob.SpeedOfJavaCppRuby

    不知道 D 是什么東東了。倒是在許久以前,從公司的首設博士那里聽到過一耳朵。感覺語法結構還是 C like 的。http://www.digitalmars.com/d/index.htmlhttp://www.soho-works.net/BLOG/326.asp?http://libai.math.ncu.edu.tw/bcc16/user/forum/read.php?f=15&i=14

    http://www.tiobe.com/index.htm?tiobe_index

    TIOBE Programming Community Index for September 2006

    September Headline: Ruby and D are the hot languages of today

    tiobe language 200609

    tpci trends 200609

    posted @ 2006-09-14 13:35 Coolfiry 閱讀(602) | 評論 (0)編輯 收藏

    摘要:

    近日, Sun 宣布收購 JRuby 項目.Sun終于開始表露出讓不僅僅是Java運行在JVM上的興趣了.

    ?
    近日, Sun 宣布收購 JRuby 項目.

    Sun終于開始表露出讓不僅僅是Java運行在JVM上的興趣了.

    近日,SUN邁出了重要的一步.

    Charles Nutter, JRuby 的核心開發者之一, 宣布: Sun 已經整體收購了JRuby 項目. 他與JRuby的另外一個核心開發者
    Thomas Enobo 都將會加入Sun.


    從這次動作看的出來Ruby在Sun戰略中的地位,??看來Ruby很有可能成為JVM支持的第一個非Java語言.

    動態語言, 已經不遙遠了.

    官方站點:
    http://jruby.sourceforge.net/
    (http://www.matrix.org.cn/resource/news/JRuby+Sun_954.html)

    posted @ 2006-09-11 13:58 Coolfiry 閱讀(217) | 評論 (0)編輯 收藏

         摘要: 用 JNI 調用 C 或 C++ 動態聯接庫原來如此簡單 ...  閱讀全文

    posted @ 2006-09-09 16:30 Coolfiry 閱讀(546) | 評論 (0)編輯 收藏

    開學了

    posted @ 2006-09-04 13:45 Coolfiry 閱讀(211) | 評論 (0)編輯 收藏

    回成都了

    posted @ 2006-09-01 22:12 Coolfiry 閱讀(211) | 評論 (0)編輯 收藏

    主站蜘蛛池模板: 最近高清中文字幕无吗免费看| 精品熟女少妇aⅴ免费久久| 中文字幕av免费专区| 成人无遮挡裸免费视频在线观看| 亚洲精品福利在线观看| 久久综合国产乱子伦精品免费| 亚洲日韩av无码| 天天爽亚洲中文字幕| 亚洲精品国产免费| 亚洲国产人成精品| 亚洲国产美女福利直播秀一区二区| 日本免费久久久久久久网站| 久久国产亚洲电影天堂| 久久精品成人免费看| 国产小视频免费观看| 亚洲国产天堂久久综合网站| 亚洲色大成WWW亚洲女子| 国产成人久久AV免费| 久久精品国产亚洲AV电影| 国产91成人精品亚洲精品| 国产男女猛烈无遮挡免费视频网站 | 国产一区二区免费在线| 久久久久久久亚洲Av无码| 久久免费看黄a级毛片| 国产91在线|亚洲| 国产在线19禁免费观看国产| 狼色精品人妻在线视频免费| 亚洲精品无码专区2| 国产精品免费福利久久| 亚洲精品熟女国产| 免费观看国产精品| 国内精品免费视频精选在线观看 | 久久久久久亚洲Av无码精品专口| 2021国产精品成人免费视频| 亚洲成av人在线观看网站| 亚洲午夜福利精品久久| 性xxxx视频免费播放直播| 亚洲乱色熟女一区二区三区蜜臀| 亚洲三级在线免费观看| 亚洲乱码中文字幕在线| 亚洲AV综合色区无码另类小说|