The important thing in life is to have a great aim , and the determination

          常用鏈接

          統(tǒng)計

          IT技術(shù)鏈接

          保險相關

          友情鏈接

          基金知識

          生活相關

          最新評論

          #

          使用memcached實現(xiàn)session遠程分布式存儲

          為了使web應用能使用saas模式的大規(guī)模訪問,必須實現(xiàn)應用的集群部署.要實現(xiàn)集群部署主要需要實現(xiàn)session共享機制,使得多臺應用服務器之間會話統(tǒng)一, tomcat等多數(shù)服務都采用了session復制技術(shù)實現(xiàn)session的共享.

             session復制技術(shù)的問題:

             (1)技術(shù)復雜,必須在同一種中間件之間完成(如:tomcat-tomcat之間).

             (2)在節(jié)點持續(xù)增多的情況下,session復制帶來的性能損失會快速增加.特別是當session中保存了較大的對象,而且對象變化較快時,性能下降更加顯著.這種特性使得web應用的水平擴展受到了限制.

             session共享的另一種思路就是把session集中起來管理,首先想到的是采用數(shù)據(jù)庫來集中存儲session,但數(shù)據(jù)庫是文件存儲相對內(nèi)存慢了一個數(shù)量級,同時這勢必加大數(shù)據(jù)庫系統(tǒng)的負擔.所以需要一種既速度快又能遠程集中存儲的服務,所以就想到了memcached.

            

          memcached是什么?

            memcached是由Danga Interactive開發(fā)的,高性能的,分布式的內(nèi)存對象緩存系統(tǒng),用于在動態(tài)應用中減少數(shù)據(jù)庫負載,提升訪問速度。

          memcached能緩存什么?

            通過在內(nèi)存里維護一個統(tǒng)一的巨大的hash表,Memcached能夠用來存儲各種格式的數(shù)據(jù),包括圖像、視頻、文件以及數(shù)據(jù)庫檢索的結(jié)果等。

          memcached快么?

            非常快。memcached使用了libevent(如果可以的話,在linux下使用epoll)來均衡任何數(shù)量的打開鏈接,使用非阻塞的網(wǎng)絡I/O,對內(nèi)部對象實現(xiàn)引用計數(shù)(因此,針對多樣的客戶端,對象可以處在多樣的狀態(tài)), 使用自己的頁塊分配器和哈希表, 因此虛擬內(nèi)存不會產(chǎn)生碎片并且虛擬內(nèi)存分配的時間復雜度可以保證為O(1).。

          Danga Interactive為提升Danga Interactive的速度研發(fā)了memcached。目前,LiveJournal.com每天已經(jīng)在向一百萬用戶提供多達兩千萬次的頁面訪問。而這些,是由一個由web服務器和數(shù)據(jù)庫服務器組成的集群完成的。memcached幾乎完全放棄了任何數(shù)據(jù)都從數(shù)據(jù)庫讀取的方式,同時,它還縮短了用戶查看頁面的速度、更好的資源分配方式,以及memcache失效時對數(shù)據(jù)庫的訪問速度。

          memcached的特點

            memcached的緩存是一種分布式的,可以讓不同主機上的多個用戶同時訪問, 因此解決了共享內(nèi)存只能單機應用的局限,更不會出現(xiàn)使用數(shù)據(jù)庫做類似事情的時候,磁盤開銷和阻塞的發(fā)生。

            使用memcached來存儲session有兩種方案:

          (1)直接通過tomcat6的擴展機制實現(xiàn).

            參考: http://www.javaeye.com/topic/81641

          (2)通過自己編寫filter實現(xiàn).

          考慮到系統(tǒng)的擴展,我們采用這種方案.這樣可以使session共享機制和中間件脫鉤.

           參考: http://www.javaeye.com/topic/82565

          主要思路:

          (1)繼承重構(gòu)HttpServletRequestWrapper,HttpSessionWrapper類,覆蓋原來和session存取相關的方法呢,都通過SessionService類來實現(xiàn).

          (2)使用filter攔截cookie中的sessionId,通過sessionId構(gòu)造新的HttpServletRequestWrapper對象,傳給后面的應用.

          (3)SessionService連接memcached服務,以sessionId作為key,存取的對象是一個map.map的內(nèi)容即為session的內(nèi)容.

          使用過程注意幾個問題和改進思路:
          1、memcache的內(nèi)存應該足夠大,這樣不會出現(xiàn)用戶session從Cache中被清除的問題(可以關閉memcached的對象退出機制)。
          2、如果session的讀取比寫入要多很多,可以在memcache前再加一個Oscache等本地緩存,減少對memcache的讀操作,從而減小網(wǎng)絡開銷,提高性能。
          3、如果用戶非常多,可以使用memcached組,通過set方法中帶hashCode,插入到某個memcached服務器

          對于session的清除有幾種方案:

          (1)可以在凌晨人最少的時候,對memcached做一次清空。(簡單)

          (2)保存在緩存中的對象設置一個失效時間,通過過濾器獲取sessionId的值,定期刷新memcached中的對象.長時間沒有被刷新的對象自動被清除.(相對復雜,消耗資源)

           

          posted @ 2011-03-29 17:21 鴻雁 閱讀(3074) | 評論 (0)編輯 收藏

          雙緩沖隊列

          提出問題:為啥要有雙緩沖隊列?
              引用09年9月《程序員》上的一句話:雙緩沖隊列就是沖著同步/互斥的開銷來的。我們知道,在多個線程并發(fā)訪問同一個資源的時候,需要特別注意線程的同步問題。稍稍不注意,哦活,程序結(jié)果不正確了。最經(jīng)典的就是“銀行取錢”的例子,想想,都跟現(xiàn)金掛上鉤了,看來這真不容忽視。
              今天我們要談的不是如何去給資源加鎖解鎖來解決同步問題,今天的重點在于,如何將線程同步的開銷降低到我們力所能及的程度。如果你覺得,你可以通過增加硬件資源來彌補程序開銷,那么,你將不可能成為一個優(yōu)秀的程序員。
              進入正題,先引入一個例子,兩個實體:一個是玩具工廠,它的工作就是不停地生產(chǎn)玩具;另外一個實體就是小孩,它的工作就是不停地從工廠拿玩具。小孩不可能直接到工廠去“拿”玩具吧?呵呵,媽媽是絕對不會放心的。所以,我們有一個“搬運工”,搬運工自然要具備“存放”的功能,不然他怎么將玩具帶給小孩呢,是吧。所以,我們先將搬運工定義為一個List,用來存放工廠生產(chǎn)出來的玩具。
          代碼如下
          玩具類,定義一個玩具實體
          public class Toy {
              private String name;

              public String getName() {
                  return name;
              }
          public void setName(String name) {
                  this.name = name;
              }
          }

          接下來是玩具工廠,它得“不停地”生產(chǎn),所以,把它設計成一個線程類

          玩具工廠,將玩具對象,放到list中
          public class Factory extends Thread{
             

              public void run(){
                  while(true){
                 
                  Toy t = new Toy ();
                 
                  t.setName("玩具");
                  synchronized (Tools.lT){
                      Tools.lT.add(t);
          }
                      try {
                          Thread.sleep(2);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                 
                   }
              }
          }
          注意到,在上面這段代碼當中,必須得用synchronized (Tools.lT)將Tools.lT給加鎖。不然會出現(xiàn)線程同步問題(開銷就在這里)。

          再接下來,看看小孩是怎么“玩”玩具的:
          小孩類,從list中取出玩具對象
          public class Kid extends Thread {
              long time1 = System.currentTimeMillis();
              int count = 0;
              public void run() {
                  while(true){
                 
                      synchronized(Tools.lT){
                          if(Tools.lT.size()!=0)
                      Tools.lT.remove(0);
                      }
                      count++;
                      if(count==100000){
                          javax.swing.JOptionPane.showMessageDialog(null, "用時間: "+(System.currentTimeMillis()-time1));
                          System.exit(0);
                      }
                 
                  }
                  }
          }
          當list不為空的時候,將list中的玩具對象,一個一個取出來,玩完!這個小孩可真夠厲害的,呵呵。可以想象為,該類的工作就是不停地向list中取出玩具對象。OK,再來編寫方法,如下

          主方法
          public class MainTest {

              /**
               * @param args
               */
              public static void main(String[] args) {
                  Factory f = new Factory();
                  f.start();
                  Kid k = new Kid();
                  k.start();
                 
              }
          }

          最后是Tools類,他里面有個靜態(tài)的List對象:
          Tools類
          public class Tools {
              public static List<Toy>lT = new ArrayList<Toy>(10000);
          }

          這樣,我們運行一下主方法,看看我們這位“天才”玩完100000個玩具,得花銷多少的時間。

           

           


          好,31毫秒。


              這是我們的第一種解決方法,下面再來看第二種解決方法:
          其實我們在Factory類和Kid類中都進行了同步處理,這樣一來,浪費了很多時間,到底是不是這樣的呢?我們可不可以直接用一個不用處理線程同步的容器來放Toy類對象呢?這樣以來是不是就可以節(jié)省很多開銷了?這個想法是有道理的,但是,事實是不是這樣的呢?馬上實踐!

          代碼就不具體貼出來了,只是我們在Tools類中用到的是一個如下的對象
          Public static LinkedBlockingQueue<Toy> lT= new LinkedBlockingQueue<Toy>(1000);

          對,阻塞隊列,這樣我們就只管往里面取,從里面拿了,不用自己考慮同步問題,F(xiàn)actory類和Kid類中也不同特意去加關鍵字進行同步了。
          那么這種方案的結(jié)果是多少呢?同樣是100000個玩具,看結(jié)果
            

           

           

           


          哦哦,變成16毫秒了,著實提高了不少效果呢。看來,在處理同步的時候擠時間,是有發(fā)展空間的,呵呵。

          等等,有人要發(fā)話了,你在這磨嘰了半天,還是沒有說什么是雙緩沖啊,對!有了前面的兩種方案,我們再來看看“雙緩沖隊列”。

          所謂雙緩沖隊列,自然起碼要有兩個隊列吧,呵呵,在這個例子中,我們可以設計兩個List來存放工廠生產(chǎn)出來的玩具對象。
          下面分析一下:
          兩個List,一個用來存,一個用來取。有點迷糊?就是有一個listP從工廠那里得到玩具對象,另外一個listT就專門把它得到的玩具對象送去給 Kid類處理。當listT變成空的了以后,再將listP中在這段時間內(nèi)取到的所有玩具對象放到listT中,好,這完了之后,他們兩個就又各自干各自的去了:listP再去取,listT再去送。這樣是不是就減少了很多次的線程同步呢?至少,在它們交換之前,listP是完全被工廠類線程占有,listT是完全被Kid類線程占有的,不用處理同步。只有在listT放完了,沒得給了,再去跟ListP換過來,這個時候就要處理同步了。

          跟實際聯(lián)系一下,有兩個搬運工A,B,A在工廠,專門從工廠取玩具;B在小孩子身邊,專門送玩具給小孩玩。當B身上沒有玩具了,自然要回A那里,把A身上的玩具全部拿過來,再來送給小孩玩。在A還有玩具的時候,A和B是在各自的線程里被處理的,即A在拿,B在給。不用擔心同步問題。
          這樣以來,處理同步問題的次數(shù)是不是大大減少了呢?沒錯,就是這樣的。那么怎么跟代碼結(jié)合呢?
          我們要設計一個監(jiān)視線程,監(jiān)視listP是不是空了,要是空了,把它同步起來,把listT也同步起來,讓他們交換。完了就各自干各自的了。
          我們來看看這個監(jiān)視類:

          public class DoubleBufferList {

              private List<Object> lP;
              private List<Object> lT;
              private int gap;

              /**
               * 構(gòu)造方法
               *
               * @param lP
               *            用來存放對象的隊列
               * @param lT
               *            用來取對象的隊列
               * @param gap
               *            交換的間隔
               */
              public DoubleBufferList(List lP, List lT, int gap) {
                  this.lP = lP;
                  this.lT = lT;
                  this.gap = gap;
              }

              public void check() {
                  Runnable runner = new Runnable() {
                      public void run() {
                          while (true) {
                              if (lT.size() == 0) {
                                  synchronized (lT) {
                                      synchronized (lP) {
                                          lT.addAll(lP);
                                      }
                                      lP.clear();
                                  }
                              }
                              try {
                                  Thread.sleep(gap);

                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          }
                      }
                  };
                  Thread thread = new Thread(runner);
                  thread.start();
              }

          }


          這個類中的線程方法就是用來交換的,目的就是為了減少處理同步的次數(shù)。這種方案中,F(xiàn)acotry類和Kid類跟前面單個List的情況差不多,就不再給出了。只是有一點要注意,F(xiàn)acory類中將玩具對象是放到了lP中,而Kid類中,也只是從lT中去取對象。看看Tools類中,多了一個變量:
          Tools類,聲明兩個隊列
              public static List<Toy>lT = new ArrayList<Toy>(10000);
              public static List<Toy>lP = new ArrayList<Toy>(10000);

          同樣是讓我們的“天才”玩完100000個玩具,來看看運行需要的時間:

           

           

           


           
          哈哈,似乎跟我們上面的第二種方案,單阻塞隊列,沒有太大的差異。怎么解釋呢?
          不用著急,來,我將額定的玩具量后多加個“0”,讓他玩完1000000個!改一下單阻塞隊列方案的輸出結(jié)果,給他們一個標記。再來看看結(jié)果:

           
          效果出來了吧,我們再加大量,讓他們同時處理10000000個玩具對象: 

           

           

           

          充分說明,使用雙緩沖隊列,比單緩沖阻塞隊列的效果要好,更別說單緩沖隊列了。

          總結(jié):
          從上面的分析,我們可以得知,在處理線程同步的時候,是要花費我們的時間的,雖然在有些時候,這樣的花費是我們可以接受的,但是在很多情況下,如果我們能注意到這樣的浪費,并且及時地完善我們的程序,這樣可以更大限度地提高我們程序的運行效率。尤其是在大的程序里面,這樣的效果體現(xiàn)得更明顯。而往往越大的系統(tǒng),對性能的要求也就越高。

          posted @ 2011-03-17 16:08 鴻雁 閱讀(1255) | 評論 (0)編輯 收藏

          Drawable、Bitmap、byte[]之間的轉(zhuǎn)換 (android轉(zhuǎn))

          android在處理一寫圖片資源的時候,會進行一些類型的轉(zhuǎn)換,現(xiàn)在有空整理一下:

          1、Drawable → Bitmap 的簡單方法
          ((BitmapDrawable)res.getDrawable(R.drawable.youricon)).getBitmap();


          2、Drawable → Bitmap
          Java代碼
          public static Bitmap drawableToBitmap(Drawable drawable) {  
                   
                  Bitmap bitmap = Bitmap  
                                  .createBitmap(  
                                                  drawable.getIntrinsicWidth(),  
                                                  drawable.getIntrinsicHeight(),  
                                                  drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888  
                                                                  : Bitmap.Config.RGB_565);  
                  Canvas canvas = new Canvas(bitmap);  
                  //canvas.setBitmap(bitmap);  
                  drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());  
                  drawable.draw(canvas);  
                  return bitmap;  
          }  


          3.Bitmap→Drawable   的簡單方法
          BitmapDrawable bitmapDrawable = (BitmapDrawable)bitmap;     
          Drawable drawable = (Drawable)bitmapDrawable;     
             
             
          Bitmap bitmap = new Bitmap (...);     
          Drawable drawable = new BitmapDrawable(bitmap);   


          3、從資源中獲取Bitmap
          Java代碼
          Resources res=getResources();  
            
          Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic);  





          4、Bitmap → byte[]
          Java代碼
          private byte[] Bitmap2Bytes(Bitmap bm){  
              ByteArrayOutputStream baos = new ByteArrayOutputStream();&nbsp;   
              bm.compress(Bitmap.CompressFormat.PNG, 100, baos);&nbsp;   
              return baos.toByteArray();  
          }  


          5、 byte[] → Bitmap
          Java代碼
          private Bitmap Bytes2Bimap(byte[] b){  
                      if(b.length!=0){  
                          return BitmapFactory.decodeByteArray(b, 0, b.length);  
                      }  
                      else {  
                          return null;  
                      }  
                }

          posted @ 2011-02-17 09:35 鴻雁 閱讀(2246) | 評論 (0)編輯 收藏

          修改TabHost默認樣式(文字居中)

          修改TabHost默認樣式

          TabHost是Android提供的一個容器組件,利用它可以輕松地實現(xiàn)TAB界面,如下圖所示:

          但很多時候,默認的TAB樣式并不符合軟件的整體風格,這時候該怎么辦呢?其實,我們可以編寫XML對其樣式進行修改。下面修改后的效果圖:

          1. TabHost布局文件 main.xml

          	<TabHost
          	    android:id="@+id/tabhost"  
          	    android:layout_width="fill_parent"
          	    android:layout_height="fill_parent">
          <LinearLayout  
          	        android:orientation="vertical"
          	        android:layout_width="fill_parent"  
          	        android:layout_height="fill_parent">
          <TabWidget  
          	            android:id="@android:id/tabs"
          	            android:layout_width="fill_parent" 
          	            android:layout_height="30dip"
          	            android:background="#a0a0a0"
          	            android:layout_weight="0" />
          <FrameLayout  
          	            android:id="@android:id/tabcontent"  
          	            android:layout_width="fill_parent"  
          	            android:layout_height="fill_parent"
          	            android:layout_weight="1">
          <ListView 
          				    android:id="@+id/user_list"
          				    android:layout_width="fill_parent"
          				    android:layout_height="fill_parent" 
          				    android:divider="@drawable/divider_line"
          				    android:cacheColorHint="#00000000" />
          <ListView 
          				    android:id="@+id/article_list"
          				    android:layout_width="fill_parent"
          				    android:layout_height="fill_parent" 
          				    android:divider="@drawable/divider_line"
          				    android:cacheColorHint="#00000000" />
          <ListView 
          				    android:id="@+id/feed_list"
          				    android:layout_width="fill_parent"
          				    android:layout_height="fill_parent" 
          				    android:divider="@drawable/divider_line"
          				    android:cacheColorHint="#00000000" />
          <ListView 
          				    android:id="@+id/book_list"
          				    android:layout_width="fill_parent"
          				    android:layout_height="fill_parent" 
          				    android:divider="@drawable/divider_line"
          				    android:cacheColorHint="#00000000" />
          </FrameLayout>
          </LinearLayout>
          </TabHost>


          FrameLayout里有四個ListView 分別對應用戶、文章、頻道、圖書。
          TabWidget和FrameLayout的ID不能自己定義修改。




          2. Activity后臺代碼
          RelativeLayout articleTab = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.minitab, null);
                      TextView articleTabLabel = (TextView) articleTab.findViewById(R.id.tab_label);
                      articleTabLabel.setText("文章");
                       
                      RelativeLayout feedTab = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.minitab, null);
                      TextView feedTabLabel = (TextView) feedTab.findViewById(R.id.tab_label);
                      feedTabLabel.setText("頻道");
                       
                      RelativeLayout bookTab = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.minitab, null);
                      TextView bookTabLabel = (TextView) bookTab.findViewById(R.id.tab_label);
                      bookTabLabel.setText("圖書");
                       
                      TabHost tabHost = (TabHost) findViewById(R.id.tabhost);
                      tabHost.setup();
                       
                      tabHost.addTab(tabHost.newTabSpec("user").setIndicator(userTab).setContent(R.id.user_list));
                      tabHost.addTab(tabHost.newTabSpec("article").setIndicator(articleTab).setContent(R.id.article_list));
                      tabHost.addTab(tabHost.newTabSpec("feed").setIndicator(feedTab).setContent(R.id.feed_list));
                      tabHost.addTab(tabHost.newTabSpec("book").setIndicator(bookTab).setContent(R.id.book_list));


          TabHost創(chuàng)建出來以后,必須先setup一下,tabHost.setup();

          setIndicator方法設置的View其實就對應了TAB中的一個個選項卡,它們都是通過一個叫minitab的布局文件inflate出來的
          3. 選項卡布局文件minitab.xml

          <?xml version="1.0" encoding="utf-8"?>
          <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:paddingLeft="5dip"
              android:paddingRight="5dip">
           
          <TextView android:id="@+id/tab_label"  
                  android:layout_width="fill_parent"
                  android:layout_height="fill_parent"
                  android:gravity="center"
                  android:textColor="#000000"
                  android:textStyle="bold"
                  android:background="@drawable/minitab" />
          </RelativeLayout>

          drawable/minitab是一個selector,指定了Tab選項卡的背景顏色。

          4. selector文件 minitab.xml

          <?xml version="1.0" encoding="utf-8"?>
          <selector
          	xmlns:android="http://schemas.android.com/apk/res/android"
          	>
          <item
          		android:state_selected="false"
          		android:drawable="@drawable/minitab_unselected"
          		>
          </item>
          <item
          		android:state_selected="true"
          		android:drawable="@drawable/minitab_default"
          		>
          </item>
          </selector>


          minitab_unselected是一淺藍色背景圖片
          minitab_default是一白色背景圖片




          posted @ 2011-02-16 11:28 鴻雁 閱讀(5994) | 評論 (0)編輯 收藏

          java規(guī)則引擎的原理

          摘 要 Java規(guī)則引擎是一種嵌入在Java程序中的組件,它的任務是把當前提交給引擎的Java數(shù)據(jù)對象與加載在引擎中的業(yè)務規(guī)則進行測試和比對,激活那些符合當前數(shù)據(jù)狀態(tài)下的業(yè)務規(guī)則,根據(jù)業(yè)務規(guī)則中聲明的執(zhí)行邏輯,觸發(fā)應用程序中對應的操作。

          引言

          目前,Java社區(qū)推動并發(fā)展了一種引人注目的新技術(shù)——Java規(guī)則引擎(Rule Engine)。利用它就可以在應用系統(tǒng)中分離商業(yè)決策者的商業(yè)決策邏輯和應用開發(fā)者的技術(shù)決策,并把這些商業(yè)決策放在中心數(shù)據(jù)庫或其他統(tǒng)一的地方,讓它們能在運行時可以動態(tài)地管理和修改,從而為企業(yè)保持靈活性和競爭力提供有效的技術(shù)支持。

          規(guī)則引擎的原理

          1、基于規(guī)則的專家系統(tǒng)(RBES)簡介

          Java規(guī)則引擎起源于基于規(guī)則的專家系統(tǒng),而基于規(guī)則的專家系統(tǒng)又是專家系統(tǒng)的其中一個分支。專家系統(tǒng)屬于人工智能的范疇,它模仿人類的推理方式,使用試探性的方法進行推理,并使用人類能理解的術(shù)語解釋和證明它的推理結(jié)論。為了更深入地了解Java規(guī)則引擎,下面簡要地介紹基于規(guī)則的專家系統(tǒng)。RBES包括三部分:Rule Base(knowledge base)、Working Memory(fact base)和Inference Engine。它們的結(jié)構(gòu)如下系統(tǒng)所示:





          圖1 基于規(guī)則的專家系統(tǒng)構(gòu)成

          如圖1所示,推理引擎包括三部分:模式匹配器(Pattern Matcher)、議程(Agenda)和執(zhí)行引擎(Execution Engine)。推理引擎通過決定哪些規(guī)則滿足事實或目標,并授予規(guī)則優(yōu)先級,滿足事實或目標的規(guī)則被加入議程。模式匹配器決定選擇執(zhí)行哪個規(guī)則,何時執(zhí)行規(guī)則;議程管理模式匹配器挑選出來的規(guī)則的執(zhí)行次序;執(zhí)行引擎負責執(zhí)行規(guī)則和其他動作。

          和人類的思維相對應,推理引擎存在兩者推理方式:演繹法(Forward-Chaining)和歸納法(Backward-Chaining)。演繹法從一個初始的事實出發(fā),不斷地應用規(guī)則得出結(jié)論(或執(zhí)行指定的動作)。而歸納法則是根據(jù)假設,不斷地尋找符合假設的事實。Rete算法是目前效率最高的一個Forward-Chaining推理算法,許多Java規(guī)則引擎都是基于Rete算法來進行推理計算的。

          推理引擎的推理步驟如下:

          (1)將初始數(shù)據(jù)(fact)輸入Working Memory。

          (2)使用Pattern Matcher比較規(guī)則庫(rule base)中的規(guī)則(rule)和數(shù)據(jù)(fact)。

          (3)如果執(zhí)行規(guī)則存在沖突(conflict),即同時激活了多個規(guī)則,將沖突的規(guī)則放入沖突集合。

          (4)解決沖突,將激活的規(guī)則按順序放入Agenda。

          (5)使用執(zhí)行引擎執(zhí)行Agenda中的規(guī)則。重復步驟2至5,直到執(zhí)行完畢所有Agenda中的規(guī)則。

          上述即是規(guī)則引擎的原始架構(gòu),Java規(guī)則引擎就是從這一原始架構(gòu)演變而來的。

          2、規(guī)則引擎相關構(gòu)件

          規(guī)則引擎是一種根據(jù)規(guī)則中包含的指定過濾條件,判斷其能否匹配運行時刻的實時條件來執(zhí)行規(guī)則中所規(guī)定的動作的引擎。與規(guī)則引擎相關的有四個基本概念,為更好地理解規(guī)則引擎的工作原理,下面將對這些概念進行逐一介紹。

          1)信息元(Information Unit)

          信息元是規(guī)則引擎的基本建筑塊,它是一個包含了特定事件的所有信息的對象。這些信息包括:消息、產(chǎn)生事件的應用程序標識、事件產(chǎn)生事件、信息元類型、相關規(guī)則集、通用方法、通用屬性以及一些系統(tǒng)相關信息等等。

          2)信息服務(Information Services)

          信息服務產(chǎn)生信息元對象。每個信息服務產(chǎn)生它自己類型相對應的信息元對象。即特定信息服務根據(jù)信息元所產(chǎn)生每個信息元對象有相同的格式,但可以有不同的屬性和規(guī)則集。需要注意的是,在一臺機器上可以運行許多不同的信息服務,還可以運行同一信息服務的不同實例。但無論如何,每個信息服務只產(chǎn)生它自己類型相對應的信息元。

          3)規(guī)則集(Rule Set)

          顧名思義,規(guī)則集就是許多規(guī)則的集合。每條規(guī)則包含一個條件過濾器和多個動作。一個條件過濾器可以包含多個過濾條件。條件過濾器是多個布爾表達式的組合,其組合結(jié)果仍然是一個布爾類型的。在程序運行時,動作將會在條件過濾器值為真的情況下執(zhí)行。除了一般的執(zhí)行動作,還有三類比較特別的動作,它們分別是:放棄動作(Discard Action)、包含動作(Include Action)和使信息元對象內(nèi)容持久化的動作。前兩種動作類型的區(qū)別將在2.3規(guī)則引擎工作機制小節(jié)介紹。

          4)隊列管理器(Queue Manager)

          隊列管理器用來管理來自不同信息服務的信息元對象的隊列。

          下面將研究規(guī)則引擎的這些相關構(gòu)件是如何協(xié)同工作的。

          如圖2所示,處理過程分為四個階段進行:信息服務接受事件并將其轉(zhuǎn)化為信息元,然后這些信息元被傳給隊列管理器,最后規(guī)則引擎接收這些信息元并應用它們自身攜帶的規(guī)則加以執(zhí)行,直到隊列管理器中不再有信息元。





          圖2 處理過程協(xié)作圖

          3、規(guī)則引擎的工作機制

          下面專門研究規(guī)則引擎的內(nèi)部處理過程。如圖3所示,規(guī)則引擎從隊列管理器中依次接收信息元,然后依規(guī)則的定義順序檢查信息元所帶規(guī)則集中的規(guī)則。如圖所示,規(guī)則引擎檢查第一個規(guī)則并對其條件過濾器求值,如果值為假,所有與此規(guī)則相關的動作皆被忽略并繼續(xù)執(zhí)行下一條規(guī)則。如果第二條規(guī)則的過濾器值為真,所有與此規(guī)則相關的動作皆依定義順序執(zhí)行,執(zhí)行完畢繼續(xù)下一條規(guī)則。該信息元中的所有規(guī)則執(zhí)行完畢后,信息元將被銷毀,然后從隊列管理器接收下一個信息元。在這個過程中并未考慮兩個特殊動作:放棄動作(Discard Action)和包含動作(Include Action)。放棄動作如果被執(zhí)行,將會跳過其所在信息元中接下來的所有規(guī)則,并銷毀所在信息元,規(guī)則引擎繼續(xù)接收隊列管理器中的下一個信息元。包含動作其實就是動作中包含其它現(xiàn)存規(guī)則集的動作。包含動作如果被執(zhí)行,規(guī)則引擎將暫停并進入被包含的規(guī)則集,執(zhí)行完畢后,規(guī)則引擎還會返回原來暫停的地方繼續(xù)執(zhí)行。這一過程將遞歸進行。





          圖3 規(guī)則引擎工作機制

          Java規(guī)則引擎的工作機制與上述規(guī)則引擎機制十分類似,只不過對上述概念進行了重新包裝組合。Java規(guī)則引擎對提交給引擎的Java數(shù)據(jù)對象進行檢索,根據(jù)這些對象的當前屬性值和它們之間的關系,從加載到引擎的規(guī)則集中發(fā)現(xiàn)符合條件的規(guī)則,創(chuàng)建這些規(guī)則的執(zhí)行實例。這些實例將在引擎接到執(zhí)行指令時、依照某種優(yōu)先序依次執(zhí)行。一般來講,Java規(guī)則引擎內(nèi)部由下面幾個部分構(gòu)成:工作內(nèi)存(Working Memory)即工作區(qū),用于存放被引擎引用的數(shù)據(jù)對象集合;規(guī)則執(zhí)行隊列,用于存放被激活的規(guī)則執(zhí)行實例;靜態(tài)規(guī)則區(qū),用于存放所有被加載的業(yè)務規(guī)則,這些規(guī)則將按照某種數(shù)據(jù)結(jié)構(gòu)組織,當工作區(qū)中的數(shù)據(jù)發(fā)生改變后,引擎需要迅速根據(jù)工作區(qū)中的對象現(xiàn)狀,調(diào)整規(guī)則執(zhí)行隊列中的規(guī)則執(zhí)行實例。Java規(guī)則引擎的結(jié)構(gòu)示意圖如圖4所示。





          圖4 Java規(guī)則引擎工作機制

          當引擎執(zhí)行時,會根據(jù)規(guī)則執(zhí)行隊列中的優(yōu)先順序逐條執(zhí)行規(guī)則執(zhí)行實例,由于規(guī)則的執(zhí)行部分可能會改變工作區(qū)的數(shù)據(jù)對象,從而會使隊列中的某些規(guī)則執(zhí)行實例因為條件改變而失效,必須從隊列中撤銷,也可能會激活原來不滿足條件的規(guī)則,生成新的規(guī)則執(zhí)行實例進入隊列。于是就產(chǎn)生了一種“動態(tài)”的規(guī)則執(zhí)行鏈,形成規(guī)則的推理機制。這種規(guī)則的“鏈式”反應完全是由工作區(qū)中的數(shù)據(jù)驅(qū)動的。

          任何一個規(guī)則引擎都需要很好地解決規(guī)則的推理機制和規(guī)則條件匹配的效率問題。規(guī)則條件匹配的效率決定了引擎的性能,引擎需要迅速測試工作區(qū)中的數(shù)據(jù)對象,從加載的規(guī)則集中發(fā)現(xiàn)符合條件的規(guī)則,生成規(guī)則執(zhí)行實例。1982年美國卡耐基·梅隆大學的Charles L. Forgy發(fā)明了一種叫Rete算法,很好地解決了這方面的問題。目前世界頂尖的商用業(yè)務規(guī)則引擎產(chǎn)品基本上都使用Rete算法。

          Java規(guī)則引擎API——JSR-94

          為了使規(guī)則引擎技術(shù)標準化,Java社區(qū)制定了Java規(guī)則引擎API(JSR94)規(guī)范。它為Java平臺訪問規(guī)則引擎定義了一些簡單的API。

          Java規(guī)則引擎API在javax.rules包中定義,是訪問規(guī)則引擎的標準企業(yè)級API。Java規(guī)則引擎API允許客戶程序使用統(tǒng)一的方式和不同廠商的規(guī)則引擎產(chǎn)品交互,就如同使用JDBC編寫獨立于廠商訪問不同的數(shù)據(jù)庫產(chǎn)品一樣。Java規(guī)則引擎API包括創(chuàng)建和管理規(guī)則集合的機制,在工作區(qū)中添加,刪除和修改對象的機制,以及初始化,重置和執(zhí)行規(guī)則引擎的機制。

          1、Java規(guī)則引擎API體系結(jié)構(gòu)

          Java規(guī)則引擎API主要由兩大類API組成: 規(guī)則管理API(The Rules Administrator API)和運行時客戶API(The Runtime Client API)。

          1)規(guī)則管理API

          規(guī)則管理API在javax.rules.admin中定義,包含裝載規(guī)則以及與規(guī)則對應的動作(執(zhí)行集 execution sets)以及實例化規(guī)則引擎。規(guī)則可以從外部資源中裝載,比如URI,Input streams, XML streams和readers等等。同時規(guī)則管理API還提供了注冊和取消注冊執(zhí)行集以及對執(zhí)行集進行維護的機制。使用admin包定義規(guī)則有助于對客戶訪問運行規(guī)則進行控制管理,它通過在執(zhí)行集上定義許可權(quán)使得未經(jīng)授權(quán)的用戶無法訪問受控規(guī)則。

          規(guī)則管理API使用類RuleServiceProvider來獲得規(guī)則管理器(RuleAdministrator)接口的實例。該接口提供方法注冊和取消注冊執(zhí)行集。規(guī)則管理器提供了本地和遠程的RuleExecutionSetProvider,它負責創(chuàng)建規(guī)則執(zhí)行集(RuleExecutionSet)。規(guī)則執(zhí)行集可以從如XML streams, binary streams等來源中創(chuàng)建。這些數(shù)據(jù)來源及其內(nèi)容經(jīng)匯集和序列化后傳送到遠程的運行規(guī)則引擎的服務器上。在大多數(shù)應用程序中,遠程規(guī)則引擎或遠程規(guī)則數(shù)據(jù)來源的情況并不多。為了避免這些情況中的網(wǎng)絡開銷,API規(guī)定了可以從運行在同一JVM中規(guī)則庫中讀取數(shù)據(jù)的本地RuleExecutionSetProvider。規(guī)則執(zhí)行集接口除了擁有能夠獲得有關規(guī)則執(zhí)行集的方法,還有能夠檢索在規(guī)則執(zhí)行集中定義的所有規(guī)則對象。這使得客戶能夠知道規(guī)則集中的規(guī)則對象并且按照自己需要來使用它們。

          2)運行時客戶API

          運行時API在javax.rules包中定義,為規(guī)則引擎用戶運行規(guī)則獲得結(jié)果提供了類和方法。運行時客戶只能訪問那些使用規(guī)則管理API注冊過的規(guī)則,運行時API幫助用戶獲得規(guī)則會話,并在這個會話中執(zhí)行規(guī)則。

          運行時API提供了對廠商規(guī)則引擎API的訪問方法,這類似于JDBC。類RuleServiceProvider提供了對具體規(guī)則引擎實現(xiàn)的運行時和管理API的訪問,規(guī)則引擎廠商通過該類將其規(guī)則引擎實現(xiàn)提供給客戶,并獲得RuleServiceProvider唯一標識規(guī)則引擎的URL。此URL的標準用法是使用類似于“com.mycompany.myrulesengine.rules.RuleServiceProvider”這樣的Internet域名空間,這保證了訪問URL的唯一性。類RuleServiceProvider內(nèi)部實現(xiàn)了規(guī)則管理和運行時訪問所需的接口。所有的RuleServiceProvider要想被客戶所訪問都必須用RuleServiceProviderManager進行注冊,注冊方式類似于JDBC API的DriverManager和Driver。

          運行時接口是運行時API的關鍵部分。運行時接口提供了用于創(chuàng)建規(guī)則會話(RuleSession)的方法,規(guī)則會話是用來運行規(guī)則的。運行時API同時也提供了訪問在service provider注冊過的所有規(guī)則執(zhí)行集(RuleExecutionSets)。規(guī)則會話接口定義了客戶使用的會話的類型,客戶根據(jù)自己運行規(guī)則的方式可以選擇使用有狀態(tài)會話或者無狀態(tài)會話。無狀態(tài)會話的工作方式就像一個無狀態(tài)會話bean。客戶可以發(fā)送單個輸入對象或一列對象來獲得輸出對象。當客戶需要一個與規(guī)則引擎間的專用會話時,有狀態(tài)會話就很有用。輸入的對象通過addObject() 方法可以加入到會話當中。同一個會話當中可以加入多個對象。對話中已有對象可以通過使用updateObject()方法得到更新。只要客戶與規(guī)則引擎間的會話依然存在,會話中的對象就不會丟失。

          RuleExecutionSetMetaData接口提供給客戶讓其查找規(guī)則執(zhí)行集的元數(shù)據(jù)(metadata)。元數(shù)據(jù)通過規(guī)則會話接口(RuleSession Interface)提供給用戶。

          2、Java規(guī)則引擎API安全問題

          規(guī)則引擎API將管理API和運行時API加以分開,從而為這些包提供了較好粒度的安全控制。規(guī)則引擎API并沒有提供明顯的安全機制,它可以和J2EE規(guī)范中定義的標準安全API聯(lián)合使用。安全可以由以下機制提供,如Java 認證和授權(quán)服務 (JAAS),Java加密擴展(JCE),Java安全套接字擴展(JSSE),或者其它定制的安全API。使用JAAS可以定義規(guī)則執(zhí)行集的許可權(quán)限,從而只有授權(quán)用戶才能訪問。

          3、異常與日志

          規(guī)則引擎API定義了javax.rules.RuleException作為規(guī)則引擎異常層次的根類。所有其它異常都繼承于這個根類。規(guī)則引擎中定義的異常都是受控制的異常(checked exceptions),所以捕獲異常的任務就交給了規(guī)則引擎。規(guī)則引擎API沒有提供明確的日志機制,但是它建議將Java Logging API用于規(guī)則引擎API。

          JSR 94 為規(guī)則引擎提供了公用標準API,僅僅為實現(xiàn)規(guī)則管理API和運行時API提供了指導規(guī)范,并沒有提供規(guī)則和動作該如何定義以及該用什么語言定義規(guī)則,也沒有為規(guī)則引擎如何讀和評價規(guī)則提供技術(shù)性指導。

          結(jié)束語

          規(guī)則引擎技術(shù)為管理多變的業(yè)務邏輯提供了一種解決方案。規(guī)則引擎既可以管理應用層的業(yè)務邏輯又可以使表示層的頁面流程可訂制。這就給軟件架構(gòu)師設計大型信息系統(tǒng)提供了一項新的選擇。而Java規(guī)則引擎在Java社區(qū)制定標準規(guī)范以后必將獲得更大發(fā)展。

          Drools規(guī)則引擎初學入門實例HelloWorld
          (1)下載eclipse(www.eclipse.org),如果是一般的java開發(fā),下載Eclipse IDE for Java Developers就行了,解壓后即可使用;

          (2)下載Drools(http://jboss.org/drools/downloads.html),目前最新版本是Drools 4.0.7 Binaries,下載后解壓即可;

          (3)之后下載eclipse的Drools插件,版本跟eclipse對應,目前有Drools 4.0.7 Eclipse 3.2 Workbench和Drools 4.0.7 Eclipse Europa 3.3 Workbench兩種。

                   Drools插件解壓后,將里面的org.drools.eclipse_4.0.7.jar文件copy到eclipse的plugins目錄中,重啟eclipse,在工具欄可以看到一個  圖標,這就是Drools的工作臺,之后就可通過這個按鈕創(chuàng)建Drools resource文件了。

          (4)開始Hello World

          Java文件:DroolsTest.java

          package com.sample;

          import java.io.InputStreamReader;
          import java.io.Reader;

          import org.drools.RuleBase;
          import org.drools.RuleBaseFactory;
          import org.drools.WorkingMemory;
          import org.drools.compiler.PackageBuilder;
          import org.drools.rule.Package;

          /**
          * This is a sample file to launch a rule package from a rule source file.
          */
          public class DroolsTest {

              public static final void main(String[] args) {
                  try {
                   
                   //load up the rulebase
                       RuleBase ruleBase = readRule();
                       WorkingMemory workingMemory = ruleBase.newStatefulSession();
                      
                      //go !
                       Message message = new Message();
                       message.setMessage(  "Hello World" );
                       message.setStatus( Message.HELLO );
                       workingMemory.insert( message );
                       workingMemory.fireAllRules();   
                      
                      
                   } catch (Throwable t) {
                       t.printStackTrace();
                   }
               }

              /**
                * Please note that this is the "low level" rule assembly API.
                */
          private static RuleBase readRule() throws Exception {
            //read in the source
             Reader source = new InputStreamReader( DroolsTest.class.getResourceAsStream( "/Sample.drl" ) );
            
            //optionally read in the DSL (if you are using it).
            //Reader dsl = new InputStreamReader( DroolsTest.class.getResourceAsStream( "/mylang.dsl" ) );

            //Use package builder to build up a rule package.
            //An alternative lower level class called "DrlParser" can also be used...
            
             PackageBuilder builder = new PackageBuilder();

            //this wil parse and compile in one step
            //NOTE: There are 2 methods here, the one argument one is for normal DRL.
             builder.addPackageFromDrl( source );

            //Use the following instead of above if you are using a DSL:
            //builder.addPackageFromDrl( source, dsl );
            
            //get the compiled package (which is serializable)
             Package pkg = builder.getPackage();
            
            //add the package to a rulebase (deploy the rule package).
             RuleBase ruleBase = RuleBaseFactory.newRuleBase();
             ruleBase.addPackage( pkg );
            return ruleBase;
          }

          public static class Message {
            public static final int HELLO = 0;
            public static final int GOODBYE = 1;
            public static final int GAME_OVER = 2;
            
            private String message;
            
            private int status;
            
            public String getMessage() {
             return this.message;
             }
            
            public void setMessage(String message) {
             this.message = message;
             }
            
            public int getStatus() {
             return this.status;
             }
            
            public void setStatus( int status ) {
             this.status = status;
             }
          }
              
          }
          選擇插件Drools按鈕里的"New Rule resource",建立規(guī)則(rule)文件:Sample.drl

          package com.sample

          import com.sample.DroolsTest.Message;

          rule "Hello World"
          when
             m : Message( status == Message.HELLO, message : message )
          then
             System.out.println( message );
             m.setMessage( "Goodbye cruel world" );
             m.setStatus( Message.GOODBYE );
             update( m );
          end

          rule "GoodBye"
          no-loop true
          when
             m : Message( status == Message.GOODBYE, message : message )
          then
             System.out.println( message );
             m.setStatus(Message.GAME_OVER);
             m.setMessage("game over now!");
             update( m );
          end

          rule "game over"
          when
             m : Message( status == Message.GAME_OVER)
          then
             System.out.println( m.getMessage() );   
          end
          注意:文件要放在相應的包里,然后編譯—執(zhí)行,當時出現(xiàn)了錯誤,查找資料,還需要加載包,包括:

          <1> Drools 4.0.7目錄下的drools-core-4.0.7.jar,drools-compiler-4.0.7.jar

          <2> Drools 4.0.7\lib目錄下的antlr-runtime-3.0.jar,mvel-1.3.1-java1.4.jar

          <3>以及eclipse\plugins目錄下的org.eclipse.jdt.core_3.2.3.v_686_R32x.jar(不同版本,包名會稍有不同)。

          重新運行,應該就不會有錯了。執(zhí)行結(jié)果如下:

          Hello World
          Goodbye cruel world
          game over now!

          posted @ 2011-01-25 23:27 鴻雁 閱讀(310) | 評論 (0)編輯 收藏

          特殊字符的轉(zhuǎn)義


        1. 特殊字符轉(zhuǎn)義   
        2.   
        3. 由于 Web 應用程序需要聯(lián)合使用到多種語言,每種語言都包含一些特殊的字符,對于動態(tài)語言或標簽式的語言而言,如果需要動態(tài)構(gòu)造語言的內(nèi)容時,一個我們經(jīng)常會碰到的問題就是特殊字符轉(zhuǎn)義的問題。下面是 Web 開發(fā)者最常面對需要轉(zhuǎn)義的特殊字符類型:   
        4.   
        5. HTML 特殊字符;    
        6. JavaScript 特殊字符;    
        7. SQL 特殊字符;    
        8. 如果不對這些特殊字符進行轉(zhuǎn)義處理,則不但可能破壞文檔結(jié)構(gòu),還可以引發(fā)潛在的安全問題。Spring 為 HTML 和 JavaScript 特殊字符提供了轉(zhuǎn)義操作工具類,它們分別是 HtmlUtils 和 JavaScriptUtils。   
        9.   
        10. HTML 特殊字符轉(zhuǎn)義   
        11.   
        12. HTML 中 <,>,& 等字符有特殊含義,它們是 HTML 語言的保留字,因此不能直接使用。使用這些個字符時,應使用它們的轉(zhuǎn)義序列:   
        13.   
        14. &:&    
        15. " :"    
        16. < :<    
        17. > :>    
        18. 由于 HTML 網(wǎng)頁本身就是一個文本型結(jié)構(gòu)化文檔,如果直接將這些包含了 HTML 特殊字符的內(nèi)容輸出到網(wǎng)頁中,極有可能破壞整個 HTML 文檔的結(jié)構(gòu)。所以,一般情況下需要對動態(tài)數(shù)據(jù)進行轉(zhuǎn)義處理,使用轉(zhuǎn)義序列表示 HTML 特殊字符。下面的 JSP 網(wǎng)頁將一些變量動態(tài)輸出到 HTML 網(wǎng)頁中:   
        19.   
        20.   
        21. 清單 1. 未進行 HTML 特殊字符轉(zhuǎn)義處理網(wǎng)頁   
        22.                    
        23. <%@ page language="java" contentType="text/html; charset=utf-8"%>   
        24. <%!   
        25.    String userName = "</td><tr></table>";   
        26.    String address = " \" type=\"button";   
        27.  %>   
        28. <table border="1">   
        29.    <tr>   
        30.      <td>姓名:</td><td><%=userName%></td> ①   
        31.    </tr>   
        32.    <tr>   
        33.      <td>年齡:</td><td>28</td>   
        34.    </tr>   
        35. </table>   
        36.  <input value="<%=address%>"  type="text" /> ②   
        37.     
        38.   
        39.   
        40. 在 ① 和 ② 處,我們未經(jīng)任何轉(zhuǎn)義處理就直接將變量輸出到 HTML 網(wǎng)頁中,由于這些變量可能包含一些特殊的 HTML 的字符,它們將可能破壞整個 HTML 文檔的結(jié)構(gòu)。我們可以從以上 JSP 頁面的一個具體輸出中了解這一問題:   
        41.   
        42. <table border="1">   
        43.    <tr>   
        44.      <td>姓名:</td><td></td><tr></table></td>    
        45.      ① 破壞了 <table> 的結(jié)構(gòu)   
        46.    </tr>   
        47.    <tr>   
        48.      <td>年齡:</td><td>28</td>   
        49.    </tr>   
        50. </table>   
        51.  <input value=" " type="button"  type="text" />    
        52.  ② 將本來是輸入框組件偷梁換柱為按鈕組件   
        53.     
        54.   
        55.   
        56. 融合動態(tài)數(shù)據(jù)后的 HTML 網(wǎng)頁已經(jīng)面目全非,首先 ① 處的 <table> 結(jié)構(gòu)被包含 HTML 特殊字符的 userName 變量截斷了,造成其后的 <table> 代碼變成無效的內(nèi)容;其次,② 處 <input> 被動態(tài)數(shù)據(jù)改換為按鈕類型的組件(type="button")。為了避免這一問題,我們需要事先對可能破壞 HTML 文檔結(jié)構(gòu)的動態(tài)數(shù)據(jù)進行轉(zhuǎn)義處理。Spring 為我們提供了一個簡單適用的 HTML 特殊字符轉(zhuǎn)義工具類,它就是 HtmlUtils。下面,我們通過一個簡單的例子了解 HtmlUtils 的具體用法:   
        57.   
        58.   
        59. 清單 2. HtmpEscapeExample   
        60.                    
        61. package com.baobaotao.escape;   
        62. import org.springframework.web.util.HtmlUtils;   
        63. public class HtmpEscapeExample {   
        64.     public static void main(String[] args) {   
        65.         String specialStr = "<div id=\"testDiv\">test1;test2</div>";   
        66.         String str1 = HtmlUtils.htmlEscape(specialStr); ①轉(zhuǎn)換為HTML轉(zhuǎn)義字符表示   
        67.         System.out.println(str1);   
        68.           
        69.         String str2 = HtmlUtils.htmlEscapeDecimal(specialStr); ②轉(zhuǎn)換為數(shù)據(jù)轉(zhuǎn)義表示   
        70.         System.out.println(str2);   
        71.           
        72.         String str3 = HtmlUtils.htmlEscapeHex(specialStr); ③轉(zhuǎn)換為十六進制數(shù)據(jù)轉(zhuǎn)義表示   
        73.         System.out.println(str3);   
        74.           
        75.         ④下面對轉(zhuǎn)義后字符串進行反向操作   
        76.         System.out.println(HtmlUtils.htmlUnescape(str1));   
        77.         System.out.println(HtmlUtils.htmlUnescape(str2));   
        78.         System.out.println(HtmlUtils.htmlUnescape(str3));   
        79.     }   
        80. }   
        81.     
        82.   
        83.   
        84. HTML 不但可以使用通用的轉(zhuǎn)義序列表示 HTML 特殊字符,還可以使用以 # 為前綴的數(shù)字序列表示 HTML 特殊字符,它們在最終的顯示效果上是一樣的。HtmlUtils 提供了三個轉(zhuǎn)義方法:   
        85.   
        86. 方法 說明    
        87. static String htmlEscape(String input)  將 HTML 特殊字符轉(zhuǎn)義為 HTML 通用轉(zhuǎn)義序列;    
        88. static String htmlEscapeDecimal(String input)  將 HTML 特殊字符轉(zhuǎn)義為帶 # 的十進制數(shù)據(jù)轉(zhuǎn)義序列;    
        89. static String htmlEscapeHex(String input)  將 HTML 特殊字符轉(zhuǎn)義為帶 # 的十六進制數(shù)據(jù)轉(zhuǎn)義序列;    
        90.   
        91. 此外,HtmlUtils 還提供了一個能夠?qū)⒔?jīng)過轉(zhuǎn)義內(nèi)容還原的方法:htmlUnescape(String input),它可以還原以上三種轉(zhuǎn)義序列的內(nèi)容。運行以上代碼,您將可以看到以下的輸出:   
        92.   
        93. str1:<div id="testDiv">test1;test2</div>   
        94. str2:<div id="testDiv">test1;test2</div>   
        95. str3:<div id="testDiv">test1;test2</div>   
        96. <div id="testDiv">test1;test2</div>   
        97. <div id="testDiv">test1;test2</div>   
        98. <div id="testDiv">test1;test2</div>   
        99.     
        100.   
        101.   
        102. 您只要使用 HtmlUtils 對代碼 清單 1 的 userName 和 address 進行轉(zhuǎn)義處理,最終輸出的 HTML 頁面就不會遭受破壞了。   
        103.   
        104. JavaScript 特殊字符轉(zhuǎn)義   
        105.   
        106. JavaScript 中也有一些需要特殊處理的字符,如果直接將它們嵌入 JavaScript 代碼中,JavaScript 程序結(jié)構(gòu)將會遭受破壞,甚至被嵌入一些惡意的程序。下面列出了需要轉(zhuǎn)義的特殊 JavaScript 字符:   
        107.   
        108. ' :\'    
        109. " :\"    
        110. \ :\\    
        111. 走紙換頁: \f    
        112. 換行:\n    
        113. 換欄符:\t    
        114. 回車:\r    
        115. 回退符:\b    
        116. ?    
        117. 我們通過一個具體例子演示動態(tài)變量是如何對 JavaScript 程序進行破壞的。假設我們有一個 JavaScript 數(shù)組變量,其元素值通過一個 Java List 對象提供,下面是完成這一操作的 JSP 代碼片斷:   
        118.   
        119.   
        120. 清單 3. jsTest.jsp:未對 JavaScript 特殊字符進行處理   
        121.                    
        122. <%@ page language="java" contentType="text/html; charset=utf-8"%>   
        123. <jsp:directive.page import="java.util.*"/>   
        124. <%   
        125.   List textList = new ArrayList();   
        126.   textList.add("\";alert();j=\"");   
        127. %>   
        128. <script>   
        129.   var txtList = new Array();   
        130.    <% for ( int i = 0 ; i < textList.size() ; i++) { %>   
        131.      txtList[<%=i%>] = "<%=textList.get(i)%>";    
        132.      ① 未對可能包含特殊 JavaScript 字符的變量進行處理   
        133.    <% } %>   
        134. </script>   
        135.     
        136.   
        137.   
        138. 當客戶端調(diào)用這個 JSP 頁面后,將得到以下的 HTML 輸出頁面:   
        139.   
        140. <script>   
        141.   var txtList = new Array();   
        142.    txtList[0] = "";alert();j=""; ① 本來是希望接受一個字符串,結(jié)果被植入了一段JavaScript代碼   
        143. </script>   
        144.     
        145.   
        146.   
        147. 由于包含 JavaScript 特殊字符的 Java 變量直接合并到 JavaScript 代碼中,我們本來期望 ① 處所示部分是一個普通的字符串,但結(jié)果變成了一段 JavaScript 代碼,網(wǎng)頁將彈出一個 alert 窗口。想像一下如果粗體部分的字符串是“";while(true)alert();j="”時會產(chǎn)生什么后果呢?   
        148.   
        149. 因此,如果網(wǎng)頁中的 JavaScript 代碼需要通過拼接 Java 變量動態(tài)產(chǎn)生時,一般需要對變量的內(nèi)容進行轉(zhuǎn)義處理,可以通過 Spring 的 JavaScriptUtils 完成這件工作。下面,我們使用 JavaScriptUtils 對以上代碼進行改造:   
        150.   
        151. <%@ page language="java" contentType="text/html; charset=utf-8"%>   
        152. <jsp:directive.page import="java.util.*"/>   
        153. <jsp:directive.page import="org.springframework.web.util.JavaScriptUtils"/>   
        154. <%   
        155.   List textList = new ArrayList();   
        156.   textList.add("\";alert();j=\"");   
        157. %>   
        158. <script>   
        159.    var txtList = new Array();   
        160.    <% for ( int i = 0 ; i < textList.size() ; i++) { %>   
        161.    ① 在輸出動態(tài)內(nèi)容前事先進行轉(zhuǎn)義處理   
        162.    txtList[<%=i%>] = "<%=JavaScriptUtils.javaScriptEscape(""+textList.get(i))%>";   
        163.    <% } %>   
        164. </script>   
        165.     
        166.   
        167.   
        168. 通過轉(zhuǎn)義處理后,這個 JSP 頁面輸出的結(jié)果網(wǎng)頁的 JavaScript 代碼就不會產(chǎn)生問題了:   
        169.   
        170. <script>   
        171.    var txtList = new Array();   
        172.    txtList[0] = "\";alert();j=\"";   
        173.    ① 粗體部分僅是一個普通的字符串,而非一段 JavaScript 的語句了   
        174. </script>   
        175.     
        176.   
        177.   
        178. SQL特殊字符轉(zhuǎn)義   
        179.   
        180. 應該說,您即使沒有處理 HTML 或 JavaScript 的特殊字符,也不會帶來災難性的后果,但是如果不在動態(tài)構(gòu)造 SQL 語句時對變量中特殊字符進行處理,將可能導致程序漏洞、數(shù)據(jù)盜取、數(shù)據(jù)破壞等嚴重的安全問題。網(wǎng)絡中有大量講解 SQL 注入的文章,感興趣的讀者可以搜索相關的資料深入研究。   
        181.   
        182. 雖然 SQL 注入的后果很嚴重,但是只要對動態(tài)構(gòu)造的 SQL 語句的變量進行特殊字符轉(zhuǎn)義處理,就可以避免這一問題的發(fā)生了。來看一個存在安全漏洞的經(jīng)典例子:   
        183.   
        184. SELECT COUNT(userId)    
        185. FROM t_user    
        186. WHERE userName='"+userName+"' AND password ='"+password+"';   
        187.     
        188.   
        189.   
        190. 以上 SQL 語句根據(jù)返回的結(jié)果數(shù)判斷用戶提供的登錄信息是否正確,如果 userName 變量不經(jīng)過特殊字符轉(zhuǎn)義處理就直接合并到 SQL 語句中,黑客就可以通過將 userName 設置為 “1' or '1'='1”繞過用戶名/密碼的檢查直接進入系統(tǒng)了。   
        191.   
        192. 所以除非必要,一般建議通過 PreparedStatement 參數(shù)綁定的方式構(gòu)造動態(tài) SQL 語句,因為這種方式可以避免 SQL 注入的潛在安全問題。但是往往很難在應用中完全避免通過拼接字符串構(gòu)造動態(tài) SQL 語句的方式。為了防止他人使用特殊 SQL 字符破壞 SQL 的語句結(jié)構(gòu)或植入惡意操作,必須在變量拼接到 SQL 語句之前對其中的特殊字符進行轉(zhuǎn)義處理。Spring 并沒有提供相應的工具類,您可以通過 jakarta commons lang 通用類包中(spring/lib/jakarta-commons/commons-lang.jar)的 StringEscapeUtils 完成這一工作:   
        193.   
        194.   
        195. 清單 4. SqlEscapeExample   
        196.                    
        197. package com.baobaotao.escape;   
        198. import org.apache.commons.lang.StringEscapeUtils;   
        199. public class SqlEscapeExample {   
        200.     public static void main(String[] args) {   
        201.         String userName = "1' or '1'='1";   
        202.         String password = "123456";   
        203.         userName = StringEscapeUtils.escapeSql(userName);   
        204.         password = StringEscapeUtils.escapeSql(password);   
        205.         String sql = "SELECT COUNT(userId) FROM t_user WHERE userName='"  
        206.             + userName + "' AND password ='" + password + "'";   
        207.         System.out.println(sql);   
        208.     }   
        209. }   
        210.     
        211.   
        212.   
        213. 事實上,StringEscapeUtils 不但提供了 SQL 特殊字符轉(zhuǎn)義處理的功能,還提供了 HTML、XML、JavaScript、Java 特殊字符的轉(zhuǎn)義和還原的方法。如果您不介意引入 jakarta commons lang 類包,我們更推薦您使用 StringEscapeUtils 工具類完成特殊字符轉(zhuǎn)義處理的工作。   

        214. posted @ 2011-01-24 14:16 鴻雁 閱讀(3857) | 評論 (0)編輯 收藏

          Oracle JOB間隔時間詳解

          Oracle JOB 間隔時間詳解
          INTERVAL參數(shù)設置:
          每天運行一次                        'SYSDATE + 1'
          每小時運行一次                     'SYSDATE + 1/24'
          每10分鐘運行一次                 'SYSDATE + 10/(60*24)'
          每30秒運行一次                    'SYSDATE + 30/(60*24*60)'
          每隔一星期運行一次               'SYSDATE + 7'
          每個月最后一天運行一次         'TRUNC(LAST_DAY(ADD_MONTHS(SYSDATE,1))) + 23/24'
          每年1月1號零時                    'TRUNC(LAST_DAY(TO_DATE(EXTRACT(YEAR from SYSDATE)||'12'||'01','YYYY-MM-DD'))+1)'
          每天午夜12點                       'TRUNC(SYSDATE + 1)'
          每天早上8點30分                  'TRUNC(SYSDATE + 1) + (8*60+30)/(24*60)'
          每星期二中午12點                 'NEXT_DAY(TRUNC(SYSDATE ), ''TUESDAY'' ) + 12/24'
          每個月第一天的午夜12點        'TRUNC(LAST_DAY(SYSDATE ) + 1)'
          每個月最后一天的23點           'TRUNC (LAST_DAY (SYSDATE)) + 23 / 24'
          每個季度最后一天的晚上11點  'TRUNC(ADD_MONTHS(SYSDATE + 2/24, 3 ), 'Q' ) -1/24'
          每星期六和日早上6點10分      'TRUNC(LEAST(NEXT_DAY(SYSDATE, ''S ......

          posted @ 2010-12-24 10:18 鴻雁 閱讀(1008) | 評論 (0)編輯 收藏

          JVM調(diào)優(yōu)總結(jié) -Xms -Xmx -Xmn -Xss(轉(zhuǎn))

          1. 堆大小設置
            JVM 中最大堆大小有三方面限制:相關操作系統(tǒng)的數(shù)據(jù)模型(32-bt還是64-bit)限制;系統(tǒng)的可用虛擬內(nèi)存限制;系統(tǒng)的可用物理內(nèi)存限制。32位系統(tǒng)下,一般限制在1.5G~2G;64為操作系統(tǒng)對內(nèi)存無限制。我在Windows Server 2003 系統(tǒng),3.5G物理內(nèi)存,JDK5.0下測試,最大可設置為1478m。
            典型設置:
            • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
              -
              Xmx3550m:設置JVM最大可用內(nèi)存為3550M。
              -Xms3550m
              :設置JVM促使內(nèi)存為3550m。此值可以設置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內(nèi)存。
              -Xmn2g
              :設置年輕代大小為2G。整個JVM內(nèi)存大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,所以增大年輕代后,將會減小年老代大小。此值對系統(tǒng)性能影響較大,Sun官方推薦配置為整個堆的3/8。
              -Xss128k
              :設置每個線程的堆棧大小。JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。更具應用的線程所需內(nèi)存大小進行調(diào)整。在相同物理內(nèi)存下,減小這個值能生成更多的線程。但是操作系統(tǒng)對一個進程內(nèi)的線程數(shù)還是有限制的,不能無限生成,經(jīng)驗值在3000~5000左右。
            • java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
              -XX:NewRatio=4
              :設置年輕代(包括Eden和兩個Survivor區(qū))與年老代的比值(除去持久代)。設置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5
              -XX:SurvivorRatio=4
              :設置年輕代中Eden區(qū)與Survivor區(qū)的大小比值。設置為4,則兩個Survivor區(qū)與一個Eden區(qū)的比值為2:4,一個Survivor區(qū)占整個年輕代的1/6
              -XX:MaxPermSize=16m:設置持久代大小為16m。
              -XX:MaxTenuringThreshold=0:設置垃圾最大年齡。如果設置為0的話,則年輕代對象不經(jīng)過Survivor區(qū),直接進入年老代。對于年老代比較多的應用,可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區(qū)進行多次復制,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概論。
          2. 回收器選擇
            JVM給了三種選擇:串行收集器、并行收集器、并發(fā)收集器,但是串行收集器只適用于小數(shù)據(jù)量的情況,所以這里的選擇主要針對并行收集器和并發(fā)收集器。默認情況下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動時加入相應參數(shù)。JDK5.0以后,JVM會根據(jù)當前系統(tǒng)配置進行判斷。
            1. 吞吐量優(yōu)先的并行收集器
              如上文所述,并行收集器主要以到達一定的吞吐量為目標,適用于科學技術(shù)和后臺處理等。
              典型配置
              • java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
                -XX:+UseParallelGC
                :選擇垃圾收集器為并行收集器。此配置僅對年輕代有效。即上述配置下,年輕代使用并發(fā)收集,而年老代仍舊使用串行收集。
                -XX:ParallelGCThreads=20:配置并行收集器的線程數(shù),即:同時多少個線程一起進行垃圾回收。此值最好配置與處理器數(shù)目相等。
              • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
                -XX:+UseParallelOldGC:配置年老代垃圾收集方式為并行收集。JDK6.0支持對年老代并行收集。
              • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100
                -XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間,如果無法滿足此時間,JVM會自動調(diào)整年輕代大小,以滿足此值。
              • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy
                -XX:+UseAdaptiveSizePolicy
                :設置此選項后,并行收集器會自動選擇年輕代區(qū)大小和相應的Survivor區(qū)比例,以達到目標系統(tǒng)規(guī)定的最低相應時間或者收集頻率等,此值建議使用并行收集器時,一直打開。
            2. 響應時間優(yōu)先的并發(fā)收集器
              如上文所述,并發(fā)收集器主要是保證系統(tǒng)的響應時間,減少垃圾收集時的停頓時間。適用于應用服務器、電信領域等。
              典型配置
              • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
                -XX:+UseConcMarkSweepGC:設置年老代為并發(fā)收集。測試中配置這個以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此時年輕代大小最好用-Xmn設置。
                -XX:+UseParNewGC:設置年輕代為并行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據(jù)系統(tǒng)配置自行設置,所以無需再設置此值。
              • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
                -XX:CMSFullGCsBeforeCompaction:由于并發(fā)收集器不對內(nèi)存空間進行壓縮、整理,所以運行一段時間以后會產(chǎn)生“碎片”,使得運行效率降低。此值設置運行多少次GC以后對內(nèi)存空間進行壓縮、整理。
                -XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,但是可以消除碎片
          3. 輔助信息
            JVM提供了大量命令行參數(shù),打印信息,供調(diào)試使用。主要有以下一些:
            • -XX:+PrintGC
              輸出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]

                              [Full GC 121376K->10414K(130112K), 0.0650971 secs]

            • -XX:+PrintGCDetails
              輸出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]

                              [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

            • -XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可與上面兩個混合使用
              輸出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
            • -XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中斷的執(zhí)行時間。可與上面混合使用
              輸出形式:Application time: 0.5291524 seconds
            • -XX:+PrintGCApplicationStoppedTime:打印垃圾回收期間程序暫停的時間。可與上面混合使用
              輸出形式:Total time for which application threads were stopped: 0.0468229 seconds
            • -XX:PrintHeapAtGC:打印GC前后的詳細堆棧信息
              輸出形式:
              34.702: [GC {Heap before gc invocations=7:
               def new generation   total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)
              eden space 49152K,  99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)
              from space 6144K,  55% used [0x221d0000, 0x22527e10, 0x227d0000)
                to   space 6144K,   0% used [0x21bd0000, 0x21bd0000, 0x221d0000)
               tenured generation   total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)
              the space 69632K,   3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)
               compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
                 the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
                  ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
                  rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
              34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:
               def new generation   total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)
              eden space 49152K,   0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)
                from space 6144K,  55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)
                to   space 6144K,   0% used [0x221d0000, 0x221d0000, 0x227d0000)
               tenured generation   total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)
              the space 69632K,   4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)
               compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
                 the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
                  ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
                  rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
              }
              , 0.0757599 secs]
            • -Xloggc:filename:與上面幾個配合使用,把相關日志信息記錄到文件以便分析。
          4. 常見配置匯總
            1. 堆設置
              • -Xms:初始堆大小
              • -Xmx:最大堆大小
              • -XX:NewSize=n:設置年輕代大小
              • -XX:NewRatio=n:設置年輕代和年老代的比值。如:為3,表示年輕代與年老代比值為1:3,年輕代占整個年輕代年老代和的1/4
              • -XX:SurvivorRatio=n:年輕代中Eden區(qū)與兩個Survivor區(qū)的比值。注意Survivor區(qū)有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區(qū)占整個年輕代的1/5
              • -XX:MaxPermSize=n:設置持久代大小
            2. 收集器設置
              • -XX:+UseSerialGC:設置串行收集器
              • -XX:+UseParallelGC:設置并行收集器
              • -XX:+UseParalledlOldGC:設置并行年老代收集器
              • -XX:+UseConcMarkSweepGC:設置并發(fā)收集器
            3. 垃圾回收統(tǒng)計信息
              • -XX:+PrintGC
              • -XX:+PrintGCDetails
              • -XX:+PrintGCTimeStamps
              • -Xloggc:filename
            4. 并行收集器設置
              • -XX:ParallelGCThreads=n:設置并行收集器收集時使用的CPU數(shù)。并行收集線程數(shù)。
              • -XX:MaxGCPauseMillis=n:設置并行收集最大暫停時間
              • -XX:GCTimeRatio=n:設置垃圾回收時間占程序運行時間的百分比。公式為1/(1+n)
            5. 并發(fā)收集器設置
              • -XX:+CMSIncrementalMode:設置為增量模式。適用于單CPU情況。
              • -XX:ParallelGCThreads=n:設置并發(fā)收集器年輕代收集方式為并行收集時,使用的CPU數(shù)。并行收集線程數(shù)。


          四、調(diào)優(yōu)總結(jié)

          1. 年輕代大小選擇
            • 響應時間優(yōu)先的應用盡可能設大,直到接近系統(tǒng)的最低響應時間限制(根據(jù)實際情況選擇)。在此種情況下,年輕代收集發(fā)生的頻率也是最小的。同時,減少到達年老代的對象。
            • 吞吐量優(yōu)先的應用:盡可能的設置大,可能到達Gbit的程度。因為對響應時間沒有要求,垃圾收集可以并行進行,一般適合8CPU以上的應用。
          2. 年老代大小選擇
            • 響應時間優(yōu)先的應用:年老代使用并發(fā)收集器,所以其大小需要小心設置,一般要考慮并發(fā)會話率會話持續(xù)時間等一些參數(shù)。如果堆設置小了,可以會造成內(nèi)存碎片、高回收頻率以及應用暫停而使用傳統(tǒng)的標記清除方式;如果堆大了,則需要較長的收集時間。最優(yōu)化的方案,一般需要參考以下數(shù)據(jù)獲得:
              • 并發(fā)垃圾收集信息
              • 持久代并發(fā)收集次數(shù)
              • 傳統(tǒng)GC信息
              • 花在年輕代和年老代回收上的時間比例
              減少年輕代和年老代花費的時間,一般會提高應用的效率
            • 吞吐量優(yōu)先的應用:一般吞吐量優(yōu)先的應用都有一個很大的年輕代和一個較小的年老代。原因是,這樣可以盡可能回收掉大部分短期對象,減少中期的對象,而年老代盡存放長期存活對象。
          3. 較小堆引起的碎片問題
            因為年老代的并發(fā)收集器使用標記、清除算法,所以不會對堆進行壓縮。當收集器回收時,他會把相鄰的空間進行合并,這樣可以分配給較大的對象。但是,當堆空間較小時,運行一段時間以后,就會出現(xiàn)“碎片”,如果并發(fā)收集器找不到足夠的空間,那么并發(fā)收集器將會停止,然后使用傳統(tǒng)的標記、清除方式進行回收。如果出現(xiàn)“碎片”,可能需要進行如下配置:
            • -XX:+UseCMSCompactAtFullCollection:使用并發(fā)收集器時,開啟對年老代的壓縮。
            • -XX:CMSFullGCsBeforeCompaction=0:上面配置開啟的情況下,這里設置多少次Full GC后,對年老代進行壓縮

          posted @ 2010-08-23 10:27 鴻雁 閱讀(293) | 評論 (0)編輯 收藏

          JVM調(diào)優(yōu)總結(jié)(一)-- 一些概念(轉(zhuǎn))

           

          數(shù)據(jù)類型

              Java虛擬機中,數(shù)據(jù)類型可以分為兩類:基本類型引用類型。基本類型的變量保存原始值,即:他代表的值就是數(shù)值本身;而引用類型的變量保存引用值。“引用值”代表了某個對象的引用,而不是對象本身,對象本身存放在這個引用值所表示的地址的位置。

          基本類型包括:byte,short,int,long,char,float,double,Boolean,returnAddress

          引用類型包括:類類型接口類型數(shù)組

          堆與棧

              堆和棧是程序運行的關鍵,很有必要把他們的關系說清楚。

           

             

              棧是運行時的單位,而堆是存儲的單位

              棧解決程序的運行問題,即程序如何執(zhí)行,或者說如何處理數(shù)據(jù);堆解決的是數(shù)據(jù)存儲的問題,即數(shù)據(jù)怎么放、放在哪兒。

              在Java中一個線程就會相應有一個線程棧與之對應,這點很容易理解,因為不同的線程執(zhí)行邏輯有所不同,因此需要一個獨立的線程棧。而堆則是所有線程共享的。棧因為是運行單位,因此里面存儲的信息都是跟當前線程(或程序)相關信息的。包括局部變量、程序運行狀態(tài)、方法返回值等等;而堆只負責存儲對象信息。

              為什么要把堆和棧區(qū)分出來呢?棧中不是也可以存儲數(shù)據(jù)嗎

              第一,從軟件設計的角度看,棧代表了處理邏輯,而堆代表了數(shù)據(jù)。這樣分開,使得處理邏輯更為清晰。分而治之的思想。這種隔離、模塊化的思想在軟件設計的方方面面都有體現(xiàn)。

              第二,堆與棧的分離,使得堆中的內(nèi)容可以被多個棧共享(也可以理解為多個線程訪問同一個對象)。這種共享的收益是很多的。一方面這種共享提供了一種有效的數(shù)據(jù)交互方式(如:共享內(nèi)存),另一方面,堆中的共享常量和緩存可以被所有棧訪問,節(jié)省了空間。

              第三,棧因為運行時的需要,比如保存系統(tǒng)運行的上下文,需要進行地址段的劃分。由于棧只能向上增長,因此就會限制住棧存儲內(nèi)容的能力。而堆不同,堆中的對象是可以根據(jù)需要動態(tài)增長的,因此棧和堆的拆分,使得動態(tài)增長成為可能,相應棧中只需記錄堆中的一個地址即可。

              第四,面向?qū)ο缶褪嵌押蜅5耐昝澜Y(jié)合。其實,面向?qū)ο蠓绞降某绦蚺c以前結(jié)構(gòu)化的程序在執(zhí)行上沒有任何區(qū)別。但是,面向?qū)ο蟮囊耄沟脤Υ龁栴}的思考方式發(fā)生了改變,而更接近于自然方式的思考。當我們把對象拆開,你會發(fā)現(xiàn),對象的屬性其實就是數(shù)據(jù),存放在堆中;而對象的行為(方法),就是運行邏輯,放在棧中。我們在編寫對象的時候,其實即編寫了數(shù)據(jù)結(jié)構(gòu),也編寫的處理數(shù)據(jù)的邏輯。不得不承認,面向?qū)ο蟮脑O計,確實很美。

              在Java中,Main函數(shù)就是棧的起始點,也是程序的起始點

              程序要運行總是有一個起點的。同C語言一樣,java中的Main就是那個起點。無論什么java程序,找到main就找到了程序執(zhí)行的入口:)

              堆中存什么?棧中存什么

              堆中存的是對象。棧中存的是基本數(shù)據(jù)類型堆中對象的引用。一個對象的大小是不可估計的,或者說是可以動態(tài)變化的,但是在棧中,一個對象只對應了一個4btye的引用(堆棧分離的好處:))。

              為什么不把基本類型放堆中呢?因為其占用的空間一般是1~8個字節(jié)——需要空間比較少,而且因為是基本類型,所以不會出現(xiàn)動態(tài)增長的情況——長度固定,因此棧中存儲就夠了,如果把他存在堆中是沒有什么意義的(還會浪費空間,后面說明)。可以這么說,基本類型和對象的引用都是存放在棧中,而且都是幾個字節(jié)的一個數(shù),因此在程序運行時,他們的處理方式是統(tǒng)一的。但是基本類型、對象引用和對象本身就有所區(qū)別了,因為一個是棧中的數(shù)據(jù)一個是堆中的數(shù)據(jù)。最常見的一個問題就是,Java中參數(shù)傳遞時的問題。

              Java中的參數(shù)傳遞時傳值呢?還是傳引用

              要說明這個問題,先要明確兩點:

                   1. 不要試圖與C進行類比,Java中沒有指針的概念

                   2. 程序運行永遠都是在棧中進行的,因而參數(shù)傳遞時,只存在傳遞基本類型和對象引用的問題。不會直接傳對象本身。

              明確以上兩點后。Java在方法調(diào)用傳遞參數(shù)時,因為沒有指針,所以它都是進行傳值調(diào)用(這點可以參考C的傳值調(diào)用)。因此,很多書里面都說Java是進行傳值調(diào)用,這點沒有問題,而且也簡化的C中復雜性。

              但是傳引用的錯覺是如何造成的呢?在運行棧中,基本類型和引用的處理是一樣的,都是傳值,所以,如果是傳引用的方法調(diào)用,也同時可以理解為“傳引用值”的傳值調(diào)用,即引用的處理跟基本類型是完全一樣的。但是當進入被調(diào)用方法時,被傳遞的這個引用的值,被程序解釋(或者查找)到堆中的對象,這個時候才對應到真正的對象。如果此時進行修改,修改的是引用對應的對象,而不是引用本身,即:修改的是堆中的數(shù)據(jù)。所以這個修改是可以保持的了。

              對象,從某種意義上說,是由基本類型組成的。可以把一個對象看作為一棵樹,對象的屬性如果還是對象,則還是一顆樹(即非葉子節(jié)點),基本類型則為樹的葉子節(jié)點。程序參數(shù)傳遞時,被傳遞的值本身都是不能進行修改的,但是,如果這個值是一個非葉子節(jié)點(即一個對象引用),則可以修改這個節(jié)點下面的所有內(nèi)容。

           

              堆和棧中,棧是程序運行最根本的東西。程序運行可以沒有堆,但是不能沒有棧。而堆是為棧進行數(shù)據(jù)存儲服務,說白了堆就是一塊共享的內(nèi)存。不過,正是因為堆和棧的分離的思想,才使得Java的垃圾回收成為可能。

               Java中,棧的大小通過-Xss來設置,當棧中存儲數(shù)據(jù)比較多時,需要適當調(diào)大這個值,否則會出現(xiàn)java.lang.StackOverflowError異常。常見的出現(xiàn)這個異常的是無法返回的遞歸,因為此時棧中保存的信息都是方法返回的記錄點。

          posted @ 2010-08-23 10:25 鴻雁 閱讀(199) | 評論 (0)編輯 收藏

          百萬數(shù)據(jù)查詢優(yōu)化技巧三十則

                  1.對查詢進行優(yōu)化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。

            2.應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:

            select id from t where num is null

            可以在num上設置默認值0,確保表中num列沒有null值,然后這樣查詢:

            select id from t where num=0

            3.應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。

            4.應盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:

            select id from t where num=10 or num=20

            可以這樣查詢:

            select id from t where num=10

            union all

            select id from t where num=20

            5.in 和 not in 也要慎用,否則會導致全表掃描,如:

            select id from t where num in(1,2,3)

            對于連續(xù)的數(shù)值,能用 between 就不要用 in 了:

            select id from t where num between 1 and 3

            6.下面的查詢也將導致全表掃描:

            select id from t where name like '%abc%'

            若要提高效率,可以考慮全文檢索。

            7.如果在 where 子句中使用參數(shù),也會導致全表掃描。因為SQL只有在運行時才會解析局部變量,但優(yōu)化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:

            select id from t where num=@num

            可以改為強制查詢使用索引:

            select id from t with(index(索引名)) where num=@num

            8.應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:

            select id from t where num/2=100

            應改為:

            select id from t where num=100*2

            9.應盡量避免在where子句中對字段進行函數(shù)操作,這將導致引擎放棄使用索引而進行全表掃描。如:

            select id from t where substring(name,1,3)='abc'--name以abc開頭的id

            select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id

            應改為:

            select id from t where name like 'abc%'

            select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'

            10.不要在 where 子句中的“=”左邊進行函數(shù)、算術(shù)運算或其他表達式運算,否則系統(tǒng)將可能無法正確使用索引。

            11.在使用索引字段作為條件時,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統(tǒng)使用該索引,否則該索引將不會被使用,并且應盡可能的讓字段順序與索引順序相一致。

            12.不要寫一些沒有意義的查詢,如需要生成一個空表結(jié)構(gòu):

            select col1,col2 into #t from t where 1=0

            這類代碼不會返回任何結(jié)果集,但是會消耗系統(tǒng)資源的,應改成這樣:

            create table #t(...)

            13.很多時候用 exists 代替 in 是一個好的選擇:

            select num from a where num in(select num from b)

            用下面的語句替換:

            select num from a where exists(select 1 from b where num=a.num)

            14.并不是所有索引對查詢都有效,SQL是根據(jù)表中數(shù)據(jù)來進行查詢優(yōu)化的,當索引列有大量數(shù)據(jù)重復時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那么即使在sex上建了索引也對查詢效率起不了作用。

            15.索引并不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數(shù)最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

            16.應盡可能的避免更新 clustered 索引數(shù)據(jù)列,因為 clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調(diào)整,會耗費相當大的資源。若應用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列,那么需要考慮是否應將該索引建為 clustered 索引。

            17.盡量使用數(shù)字型字段,若只含數(shù)值信息的字段盡量不要設計為字符型,這會降低查詢和連接的性能,并會增加存儲開銷。這是因為引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對于數(shù)字型而言只需要比較一次就夠了。

            18.盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長字段存儲空間小,可以節(jié)省存儲空間,其次對于查詢來說,在一個相對較小的字段內(nèi)搜索效率顯然要高些。

            19.任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。

            20.盡量使用表變量來代替臨時表。如果表變量包含大量數(shù)據(jù),請注意索引非常有限(只有主鍵索引)。

            21.避免頻繁創(chuàng)建和刪除臨時表,以減少系統(tǒng)表資源的消耗。

            22.臨時表并不是不可使用,適當?shù)厥褂盟鼈兛梢允鼓承├谈行В纾斝枰貜鸵么笮捅砘虺S帽碇械哪硞€數(shù)據(jù)集時。但是,對于一次性事件,最好使用導出表。

            23.在新建臨時表時,如果一次性插入數(shù)據(jù)量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數(shù)據(jù)量不大,為了緩和系統(tǒng)表的資源,應先create table,然后insert。

            24.如果使用到了臨時表,在存儲過程的最后務必將所有的臨時表顯式刪除,先 truncate table ,然后 drop table ,這樣可以避免系統(tǒng)表的較長時間鎖定。

            25.盡量避免使用游標,因為游標的效率較差,如果游標操作的數(shù)據(jù)超過1萬行,那么就應該考慮改寫。

            26.使用基于游標的方法或臨時表方法之前,應先尋找基于集的解決方案來解決問題,基于集的方法通常更有效。

            27.與臨時表一樣,游標并不是不可使用。對小型數(shù)據(jù)集使用 FAST_FORWARD 游標通常要優(yōu)于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數(shù)據(jù)時。在結(jié)果集中包括“合計”的例程通常要比使用游標執(zhí)行的速度快。如果開發(fā)時間允許,基于游標的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。

            28.在所有的存儲過程和觸發(fā)器的開始處設置 SET NOCOUNT ON ,在結(jié)束時設置 SET NOCOUNT OFF 。無需在執(zhí)行存儲過程和觸發(fā)器的每個語句后向客戶端發(fā)送 DONE_IN_PROC 消息。

            29.盡量避免大事務操作,提高系統(tǒng)并發(fā)能力。

            30.盡量避免向客戶端返回大數(shù)據(jù)量,若數(shù)據(jù)量過大,應該考慮相應需求是否合理。

          posted @ 2010-07-16 16:04 鴻雁 閱讀(254) | 評論 (0)編輯 收藏

          僅列出標題
          共18頁: First 上一頁 3 4 5 6 7 8 9 10 11 下一頁 Last 
          主站蜘蛛池模板: 梅州市| 金坛市| 漳浦县| 桓台县| 屯留县| 武隆县| 荥经县| 察隅县| 石景山区| 南靖县| 原阳县| 通许县| 崇仁县| 望奎县| 南京市| 海原县| 达尔| 华阴市| 安塞县| 沅江市| 汾西县| 安阳县| 舟曲县| 衡东县| 乌苏市| 普定县| 沁阳市| 尼勒克县| 莱州市| 揭西县| 正定县| 阆中市| 白城市| 长寿区| 泸西县| 卓尼县| 绥阳县| 津市市| 黎川县| 进贤县| 杭锦旗|