byte[]轉(zhuǎn)16進(jìn)制bug總結(jié)

          (一)問題

                   項(xiàng)目中需要對文件做md5sum,分兩步走:1、對文件流的每個(gè)字節(jié)用md5實(shí)例進(jìn)行update,然后進(jìn)行digest2digest返回長度為16byte數(shù)組,一般我們需要把byte數(shù)組轉(zhuǎn)成16進(jìn)制字符串(很多開源的md5加密算法如此實(shí)現(xiàn),真正的原因還不是很理解,可能是便于查看和傳輸)。具體的實(shí)現(xiàn)代碼如下:

                   /**

              * 對文件進(jìn)行md5 sum操作

              * @param checkFile 要進(jìn)行做md5 sum的文件

              * @return

              */

              public static String md5sum(File checkFile){

                 String md5sumResult = "";

                 if(checkFile == null || (!checkFile.exists())){

                     return md5sumResult;

                 }

                 MessageDigest digest = MessageDigest.getInstance("MD5");

                 InputStream is = new FileInputStream(checkFile);

                 byte[] buffer = new byte[8192];

                 int read = 0;

                 try {

                     while( (read = is.read(buffer)) > 0) {

                        digest.update(buffer, 0, read);

                     }

                     byte[] md5sum = digest.digest();

                     BigInteger bigInt = new BigInteger(1, md5sum);

                     md5sumResult = bigInt.toString(16);

                 }

                 catch(IOException e) {

                     throw new RuntimeException("Unable to process file for MD5", e);

                 }

                 finally {

                     try {

                        is.close();

                     }

                     catch(IOException e) {

                        throw new RuntimeException("Unable to close input stream for MD5 calculation", e);

                     }

                 }

                

                 return md5sumResult;

              }

           

              其中黃色背景色的轉(zhuǎn)換方式是有問題的。為什么用bigint轉(zhuǎn)16進(jìn)制會(huì)有問題呢,原因是bigint進(jìn)行16進(jìn)制轉(zhuǎn)換的時(shí)候第一個(gè)0被自動(dòng)去掉了.

             

           

          (二)正確解決方式

          那正確的方式是怎么樣的呢?下面有兩種不同的轉(zhuǎn)換方式,但是原理其實(shí)是一致的。

              第一種正確的方式(由王建提供):

              /**

              * 將字節(jié)數(shù)組轉(zhuǎn)換為16進(jìn)制字符串

              *

              * @param buffer

              * @return

              */

             public static String toHex(byte[] buffer) {

                StringBuffer sb = new StringBuffer(buffer.length * 2);

                for (int i = 0; i < buffer.length; i++) {

                 sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16));

                 sb.append(Character.forDigit(buffer[i] & 15, 16));

                }

           

                return sb.toString();

             }

           

              第二種正確的方式:

              public static String bytes2HexString(byte[] b) {

                 String ret = "";

                 for (int i = 0; i < b.length; i++) {

                     String hex = Integer.toHexString(b[i] & 0xFF);

                     if (hex.length() == 1) {

                        hex = '0' + hex;

                     }

                     ret += hex;

                 }

                 return ret;

              }

           

          (三)問題分析

              Md5算法對任何長度的字符串進(jìn)行編碼最后輸出是128位長整數(shù),也就是長度為16byte數(shù)組。我們項(xiàng)目調(diào)用的是jdk實(shí)現(xiàn)的md5算法,所以一般是沒問題的。

              接下來我們要處理的事情,分別循環(huán)數(shù)組,把每個(gè)字節(jié)轉(zhuǎn)換成2個(gè)16進(jìn)制字符,也就是說每4位轉(zhuǎn)成一個(gè)16進(jìn)制字符。

             

              上面正確的兩種方式也就是做了這樣的事情。

              第一種方式:

                 Character.forDigit((buffer[i] & 240) >> 4, 16)把字節(jié)的高4位取出右移4位換算成int,然后通過forDigit轉(zhuǎn)換成16進(jìn)制字符

                 Character.forDigit(buffer[i] & 15, 16)把字節(jié)的低4位取出換算成int,然后通過forDigit轉(zhuǎn)換成16進(jìn)制字符

             

              第二種方式:

                 Integer.toHexString(b[i] & 0xFF)把整個(gè)字節(jié)轉(zhuǎn)成int,然后toHexString也就是做高4位和低4位的運(yùn)算。但是這個(gè)方法如果高四位是0的話就不輸出任何東西,

          所以在輸出的字符前加0即可。

                 b[i] & 0xFF就是把byte轉(zhuǎn)成int,為什么用與oxff做與運(yùn)算,是因?yàn)槿绻?/span>b[i]是負(fù)數(shù)的話,從8位變成32位會(huì)補(bǔ)1,所以需要與0xff做與運(yùn)算,可以把前面的24位全部清零,又可以表示成原來的字節(jié)了。

             

           

          附:

          盡量使用開源提供的工具包,比如:

          org.apache.commons.codec.digest.DigestUtils.md5Hex(InputStream data)來對文件流進(jìn)行md5即可,更加方便,可靠。

          posted @ 2012-01-10 14:39 yangpingyu 閱讀(8071) | 評論 (2)編輯 收藏

          Java類加載體系

          早期的java程序員可能只要懂基本語法,還有少數(shù)的項(xiàng)目經(jīng)驗(yàn)就可以找到一份比較好的工作。Java和java社區(qū)的發(fā)展,更多的人了解它,深入它。現(xiàn)在java程序員了解一些語法我看還遠(yuǎn)遠(yuǎn)不夠了,對于jvm的了解和深入是非常重要的。網(wǎng)民的增多,網(wǎng)站的剛性需求,很多網(wǎng)站面臨高性能,高并發(fā)等等一系列的問題。沒有深入jvm的java程序員是很難寫出高質(zhì)量高并發(fā)的代碼(也許一棒子打死所有人了,但我想絕大部分是肯定的)。

           

          Osgi也許你并不陌生,但是他底層的實(shí)現(xiàn)機(jī)制你可能沒去了解過。如果你是個(gè)打破砂鍋問到底的人,你肯定會(huì)想知道osgi是如何做到的。但是你沒有了解jvm的類加載體系,你肯定很難理解osgi是如果做到類隔離等一系列的問題。不過想完整理解osgi還需要其他很多方面的知識,但是它基本的機(jī)制還是的了解jvm的類加載機(jī)制。

           

          Java類庫有些包只是定義了一個(gè)標(biāo)準(zhǔn),具體的實(shí)現(xiàn)都是由具體的供應(yīng)商來提供。Java與數(shù)據(jù)庫連接就是一個(gè)很好的例子。Java.sql類庫只是定義了java與數(shù)據(jù)庫連接的標(biāo)準(zhǔn),那么與mysql就需要msyql的驅(qū)動(dòng),oracle就需要oracle的驅(qū)動(dòng),而java.sql類庫是由bootstrap classloader加載,驅(qū)動(dòng)包中的類是由system classloader來加載,不同類加載器加載的類是無法相互認(rèn)識,所以自然也無法正常提供功能了。jvm又是提供了什么機(jī)制讓他們交互呢?如果你確實(shí)對這些問題毫無頭緒的話,那么我覺得你真的要好好理解下jvm類加載體系。

           

          這篇文章主要是介紹下jvm類加載的機(jī)制基礎(chǔ)知識。關(guān)于其他相關(guān)涉及,有時(shí)間的話,我會(huì)單獨(dú)寫文章來介紹。

           

          1 java類加載器

          http://www.aygfsteel.com/images/blogjava_net/yangpingyu/classloader-1.jpg

          1.1 Bootstrap classloader:sun jdk是用c++實(shí)現(xiàn),所以在代碼中你是無法獲取此加載器的實(shí)例。此加載器主要負(fù)責(zé)加載$JAVA_HOME/jre/lib/rt.jar。java類中獲取結(jié)果為null,這里可以用一個(gè)例子跑下證明:

          public class Test {

                   public static void main(String[] arg) throws Exception{

                             ClassLoader classloader = Test.class.getClassLoader();

                             System.out.println(classloader);

                             System.out.println(classloader.getParent());

                             System.out.println(classloader.getParent().getParent());

                   }

          }

          輸出結(jié)果:

          sun.misc.Launcher$AppClassLoader@19821f

          sun.misc.Launcher$ExtClassLoader@addbf1

          null

          最后輸出的null就是代表bootstrap classloader。

           

          1.2 Extension classloader:主要加載擴(kuò)展功能的jar,$JAVA_HOME/jre/lib/ext/*.jar。

          1.3 System classloader:加載claspath中的jar包。

          1.4自定義 classloader:主要加載你指定的目錄中的包和類。

           

          2 雙親委派模型

          系統(tǒng)運(yùn)行時(shí),我們請求加載String類,此時(shí)System Classloader自己不找classpath中的包,把請求轉(zhuǎn)發(fā)給Extension Classloader,但它也不做查找,又轉(zhuǎn)發(fā)給Bootstrap Classloader,但是它發(fā)現(xiàn)自己沒有parent了。于是他在rt.jar包中找到String類并加載到j(luò)vm中提供使用。Jvm為什么要這么實(shí)現(xiàn)呢?其實(shí)和java安全體系有關(guān)。假設(shè)jvm不是這么實(shí)現(xiàn),我們自定義一個(gè)String類,做一些破壞,那么運(yùn)行jvm的機(jī)器肯定要受到損壞。具體例子:

          public class String {

                   public static void main(String[] args) {

                             System.out.println("hello world");

                   }

          }

          我們運(yùn)行自定義String類的時(shí)候報(bào)錯(cuò)了,說沒有main方法,可我們定義的明明有的,嘿嘿,委派機(jī)制的緣故最后加載到的是由bootstrap classloader在rt.jar包中的String,那個(gè)類是沒有main方法,因此報(bào)錯(cuò)了。

          http://www.aygfsteel.com/images/blogjava_net/yangpingyu/classloader-2.jpg

          3 類隔離

          jvm中的類是:類加載器+包名+類名。比如:URLClassLoader1,URLClassLoader2分別加載com.test.Test的時(shí)候會(huì)加載兩次,因?yàn)槊總€(gè)classloader中的類對于其他classloader來說是隔離的,不認(rèn)識的。例子:

          import java.net.URL;
          import java.net.URLClassLoader;

          public class CustomClassloaderTest {
              
          public static void main(String[] args) throws Exception {
                  URL url = 
          new URL("file:/g:/");
                  URLClassLoader ucl = 
          new URLClassLoader(new URL[]{url});
                  Class c = ucl.loadClass("Yang");
                  c.newInstance();
                  System.out.println(c.getClassLoader());

                  URLClassLoader ucl2 = 
          new URLClassLoader(new URL[]{url});
                  Class c2 = ucl2.loadClass("Yang");
                  c2.newInstance();
                  System.out.println(c2.getClassLoader());
              }
          }

          大家把Yang類存在g盤下。

          public class Yang {
              
          static {
                  System.out.println("Yang");
              }
          }

          運(yùn)行結(jié)果:

          Yang

          java.net.URLClassLoader@c17164

          Yang

          java.net.URLClassLoader@61de33

          看到每次加載Yang類的時(shí)候都輸出Yang,說明Yang類被加載了兩次。

          如果你不確信,可以修改下代碼,讓同一classloader加載Yang類兩次

           

          import java.net.URL;
          import java.net.URLClassLoader;

          public class CustomClassloaderTest {
              
          public static void main(String[] args) throws Exception {
                  URL url = 
          new URL("file:/g:/");
                  URLClassLoader ucl = 
          new URLClassLoader(new URL[]{url});
                  Class c = ucl.loadClass("Yang");
                  c.newInstance();
                  System.out.println(c.getClassLoader());

                  Class c2 = ucl.loadClass("Yang");
                  c2.newInstance();
                  System.out.println(c2.getClassLoader());
              }
          }

          看看輸出結(jié)果:

          Yang

          java.net.URLClassLoader@c17164

          java.net.URLClassLoader@c17164

          結(jié)果中只輸出了一次Yang。因此可以證明我們最開始說的類隔離。

           

          4 線程上下文類加載器

          我們理解了雙親委派模型,那么目前只有由下向上單向?qū)ふ翌悾╯ystem->extension->bootstrap)

          。我們在最開始的時(shí)候說過,java.sql包中的類由bootstrap或extension classloader加載,而mysql驅(qū)動(dòng)包是在classpath中由system來加載,但bootstrap中的類是無法找到system classloader中的類,此時(shí)靠線程上下文類加載器來解決。線程上下文類加載器主要就是能讓jvm類加載模型具有了向下尋找的可能,bootstrap->extension->system,如果不做任何設(shè)置,線程上下文類加載器默認(rèn)是system classloader。本來這里想寫一個(gè)例子的,可是有點(diǎn)麻煩,所以下次單獨(dú)寫一篇關(guān)于這方面的知識。

           

          posted @ 2011-05-14 21:41 yangpingyu 閱讀(1026) | 評論 (2)編輯 收藏

          grails學(xué)習(xí)(二)grails單元測試

          以前我在小公司,完成項(xiàng)目功能是終極目標(biāo)。開發(fā)人員很害怕需求變化,因?yàn)樗麄兏呐铝恕D菃栴}出在哪里呢?后來我仔細(xì)想想,是沒有做測試造成。那開發(fā)人員為什么如此害怕需求變化,我舉個(gè)例子,a服務(wù)給b服務(wù)和c服務(wù)調(diào)用,后來需求改變,導(dǎo)致a服務(wù)無法滿足b服務(wù),能完成自身的功能是天大的事,于是沒有和別人溝通把a(bǔ)服務(wù)直接改了。項(xiàng)目上線,突然有一天客戶打電話說你們網(wǎng)站這里出問題,那里出問題,以前都不會(huì)的啊。你們怎么弄的。于是根據(jù)頁面錯(cuò)誤信息,開發(fā)人員很快找到錯(cuò)誤根源,原來a服務(wù)改動(dòng),導(dǎo)致b服務(wù)不正常。而d,e,f服務(wù)依賴于b,那么導(dǎo)致d,e,f相關(guān)功能都出錯(cuò)了。立馬動(dòng)手改,改完上線,能知道的問題都沒了,哈哈,真高興,可是不能高興太早哇,也許還有潛在bug。

          軟件的bug是無法避免,但是我們可以盡量減少bug,不斷提升代碼質(zhì)量。剛我也說過,上述問題造成的原因是沒有做測試。測試包括很多了,單元測試、集成測試和功能測試等等。既然測試如此重要,每完成一個(gè)類都能進(jìn)行測試。

          以前也許你比較糾結(jié),沒有好的工具,現(xiàn)在java社區(qū)非常活躍,我們可以選擇的太多太多了:junit4,jmock,mockito,easymock,TestNg等等。如果你用過grails,那么你更清楚,此類快速開發(fā)框架已經(jīng)幫我們集成好了。使用起來非常簡單。所以今天我主要講述下grails的單元測試。

          假設(shè)需求:我們給每個(gè)用戶分配工作,每個(gè)人都要完成兩件事情,第一件事情:根據(jù)自己的用戶名返回歡迎信息;第二件事情:根據(jù)自己的地址返回國家地區(qū)。

          詳細(xì)設(shè)計(jì)

          用戶信息類:
          package com.test.domian

          class User {

              
          int id
              String name
              String address
              
              
          static constraints = {
              }
          }

          工作服務(wù)接口:
          package com.test.services

          class WorkService {

              
          /**
               * 根據(jù)用戶名返回歡迎字符
               * 
          @param userName
               * 
          @return
               
          */
              def processWorkOne(String userName) {
              }
              
              
          /**
               * 根據(jù)地址返回地區(qū)
               * 
          @param address
               * 
          @return
               
          */
              def processWorkTwo(String address){
              }
              
          }

          用戶工作服務(wù):
          package com.test.services

          import com.test.domian.User

          class UserService {

              def workService
              
              def doWork() {
                  
                  def userList 
          = User.list()
                  userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                      it.address 
          = workService.processWorkTwo(it.address)
                      
                  }
              }
          }

          我們重點(diǎn)來看下測試類:
          package com.test.services

          import grails.test.*

          import com.test.domian.User

          class UserServiceTests extends GrailsUnitTestCase {
              
          protected void setUp() {
                  
          super.setUp()
              }

              
          protected void tearDown() {
                  
          super.tearDown()
              }

              
          void testDoWork() {
                  
                  
          //構(gòu)造數(shù)據(jù),類似于數(shù)據(jù)庫存在三條記錄
                  def user1 = new User(id:1, name:"lucy", address:"hangzhou")
                  def user2 
          = new User(id:2, name:"lily", address:"wenzhou")
                  def user3 
          = new User(id:3, name:"lilei", address:"beijing")
                  mockDomain User, [user1, user2, user3]
                  
                  
          //mock WorkService接口的processWorkOne方法和processWorkTwo方法
                  def workControl = mockFor(WorkService)
                  def userCount 
          = User.count()
                  
          while(userCount-- > 0){
                      workControl.demand.processWorkOne(
          1..1){String userName ->
                          
          return "hello world, " << userName
                      }
                      workControl.demand.processWorkTwo(
          1..1){String address ->
                          
          return "location in " << address
                      }
                  }
                  def workService 
          = workControl.createMock()
                  
                  
          //把構(gòu)造好的workservice傳給userservice
                  UserService userService = new UserService()
                  userService.workService 
          = workService
                  
                  userService.doWork()
                  
                  def user4 
          = User.findById(1)
                  assertEquals 
          "hello world, lucy", user4.name
                  assertEquals 
          "location in hangzhou", user4.address
              }
          }

          以下著重來具體說明:
          1、mockDomain方法就是構(gòu)造數(shù)據(jù),包括domain類的動(dòng)態(tài)方法都可以使用,比如:save(),list(),findby*()等。代碼中的User.count(); User.list();就是因?yàn)檎{(diào)用了mockDomain方法才可以正常使用。如果是集成測試的話,grails會(huì)幫我們構(gòu)造好,可以直接使用。但這里是單元測試,所以需要自己mock。

          2、mockFor方法就是給WorkService構(gòu)造一個(gè)對象,然后給
          workControl對象的demand代理創(chuàng)建兩個(gè)UserService中用的processWorkOne和processWorkTwo方法,代碼中用到了1..1,表示mock對象只能調(diào)用這個(gè)方法一次,為什么要循環(huán)三次設(shè)置processWorkOne和processWorkTwo方法呢?因?yàn)槲覀冊赨serService是對三個(gè)對象分別進(jìn)行調(diào)用處理這兩件事情。也許你會(huì)想,干嘛不直接把1..3(最少調(diào)用一次,最多調(diào)用三次)。是的,我最開始也是這么來處理,可是單元測試就是同不過。
          如果把
          UserService類中的
          workControl.demand.processWorkOne(1..1){String userName ->
                
          return "hello world, " << userName
           }

          改成
          workControl.demand.processWorkOne(1..3){String userName ->
                
          return "hello world, " << userName
           }

          然后把UserServiceTests類中的:
          userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                      it.address 
          = workService.processWorkTwo(it.address)
          }
          改成
          userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                     
          it.name = workService.processWorkOne(it.name)
                      it.name = workService.processWorkOne(it.name)
                      it.address = workService.processWorkTwo(it.address)
          }

          單元測試可以通過,但是改成這樣
          userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                     
          it.name = workService.processWorkOne(it.name)
                      it.address = workService.processWorkTwo(it.address)
                     
          it.name = workService.processWorkOne(it.name)
          }
          單元測試通不過。
          以上就是表明1..3的含義:這個(gè)方法要連續(xù)被調(diào)用至少一次,至多三次。
          但是有的人說我在UserService中就要這么寫
          userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                     
          it.name = workService.processWorkOne(it.name)
                      it.address = workService.processWorkTwo(it.address)
                     
          it.name = workService.processWorkOne(it.name)
          }
          那我要怎么改單元測試才能通過?
          我們把
          UserServiceTests的demand這段代碼
          workControl.demand.processWorkOne(1..1){String userName ->
               
          return "hello world, " << userName
          }
          workControl.demand.processWorkTwo(
          1..1){String address ->
                
          return "location in " << address
          }

          改成
          workControl.demand.processWorkOne(1..2){String userName ->
                
          return "hello world, " << userName
          }
          workControl.demand.processWorkTwo(
          1..1){String address ->
                 
          return "location in " << address
           }

          workControl.demand.processWorkOne(1..1){String address ->
                 
          return "location in " << address
           }
          這樣就通過了。
          以上就是說明構(gòu)造出來的函數(shù)只能按照構(gòu)造的順序調(diào)用。今天就是因?yàn)檫@個(gè)花了我好長時(shí)間啊,希望我理解是正確的。如有不對,請留言糾正。



          posted @ 2011-05-13 21:40 yangpingyu 閱讀(1886) | 評論 (0)編輯 收藏

          grails學(xué)習(xí)(一)Grails工程與maven集成

           

          Grails工程與maven集成

          Grails其實(shí)也有自己的一些項(xiàng)目管理命令,如:grails package,grails test-app,grails war等。但是公司現(xiàn)在基本上都是用maven來管理項(xiàng)目,所以從管理上進(jìn)行統(tǒng)一的目的,我們也讓grails工程由maven來管理。

          Grails與maven集成是靠maven插件機(jī)制。

          接下來描述下集成的步驟:

          1、在$home/.m2/settings.xml中配置plugin group

          <settings>

           

           <pluginGroups>

              <pluginGroup>org.grails</pluginGroup>

           </pluginGroups>

          </settings>

          沒有配置之前,要運(yùn)行g(shù)rails:help命令要這樣寫:mvn org.grails:help,有了配置之后我們就可以這么寫:mvn grails:help。

          2、我們創(chuàng)建一個(gè)maven管理的grails工程

          Mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-4:generate
              -DarchetypeGroupId=org.grails 
              -DarchetypeArtifactId=grails-maven-archetype 
              -DarchetypeVersion=1.2.0 
          -DgroupId=example -DartifactId=my-app
          grails-maven-archetype這里用的是1.2.0版本,好像maven3集成的版本比這個(gè)要新。
           
          3、進(jìn)入my-app當(dāng)前目錄,運(yùn)行mvn initialize
          在運(yùn)行過程中可能會(huì)出現(xiàn)如下問題:
          Resolving plugin JAR dependencies 
          :: problems summary ::
          :::: WARNINGS
                         module not found: org.hibernate#hibernate-core;3.3.1.GA
          那么你在application.properties文件中添加plugins.hibernate=1.3.2
          plugins.tomcat=1.3.2兩個(gè)插件。最后運(yùn)行mvn compile重新編譯工程。
           
          4、我們是用springsource tool suite開發(fā),導(dǎo)入工程。項(xiàng)目中需要對excel操作,我們采用jxl.jar開源包。在pom文件中配置如下內(nèi)容:

          <dependency>

                <groupId>jxl</groupId>

                <artifactId>jxl</artifactId>

                <version>2.4.2</version>

          </dependency>
          重新編譯下工程,但是引用jxl包中類的文件還是報(bào)錯(cuò),說找不到類。這怎么回事呢?我也很納悶,一般maven工程都是這樣就可以。
           
          后來在官方文檔上看到一句話:pom=true。只要把這句話加到conf/BuildConfig.groovy文件中的grails.project.dependency.resolution方法中。如圖:
           
          重新編譯,但是還是報(bào)錯(cuò)。Ide還是無法引用jxl包中的類。后來發(fā)現(xiàn)在grails tools中找到了一個(gè)命令。
           
          執(zhí)行過后,已經(jīng)加入到了grails dependencies中。
           
          為什么會(huì)這樣呢,是因?yàn)橛袃商讬C(jī)制造成的。
          第一套機(jī)制:maven自身管理項(xiàng)目的機(jī)制。
          第二套機(jī)制:grails也有自己的一套管理機(jī)制。
           
          在絕大多數(shù)情況下maven通過grails的集成插件可以對工程進(jìn)行打包,部署,運(yùn)行測試等管理。
          Grails可以通過自己的grails war等命令進(jìn)行打包,部署,運(yùn)行測試等管理。
          Grails也可以通過配置pom=true讓grails那套管理機(jī)制用pom中的配置,不使用自己的管理機(jī)制。此時(shí)你就不用在conf/BuildConfig.groovy中管理依賴和資源庫等配置。
           

          posted @ 2011-05-10 10:34 yangpingyu 閱讀(3490) | 評論 (0)編輯 收藏

          成長

          大學(xué)期間,我熱愛觀看央視“贏在中國”節(jié)目。學(xué)到了很多做事的方法和做人的方式,雖然對于什么是創(chuàng)業(yè),在創(chuàng)業(yè)中會(huì)遇到什么問題等等一些都未知,但那份年輕人的沖動(dòng)和對于創(chuàng)業(yè)的興奮已經(jīng)被激發(fā)到極點(diǎn)。畢業(yè)找工作,我毅然選擇了一家從事旅游行業(yè)的創(chuàng)業(yè)型互聯(lián)網(wǎng)小公司。

           

          面試階段,一個(gè)相互選擇的階段。在大學(xué)期間,我運(yùn)用J2EE技術(shù)給某朋友成功建立一家從事游戲虛擬物品交易平臺,無形中已經(jīng)培養(yǎng)了一定的需求溝通,需求挖掘,設(shè)計(jì),項(xiàng)目管理等能力。加上我是一張“白紙”(價(jià)值觀等都未受到任何公司的影響)和對于創(chuàng)業(yè)的那份沖勁。老板理所當(dāng)然的選擇了我,由我來負(fù)責(zé)一個(gè)項(xiàng)目的全部工作。公司給我提供我認(rèn)為過得去的工資,也是我所喜好的創(chuàng)業(yè)型小公司,我認(rèn)為很有前途(年少無知,不知后路的艱辛)。我也選擇了公司。這就是我第一家的公司。

           

          工作階段,充當(dāng)孫悟空的階段。最簡單的開發(fā)模式,最簡單的項(xiàng)目管理方式,最簡單的上線過程,最簡單的線上故障處理。一切都是那么簡單。簡單的讓我換第二份工作的時(shí)候讓人覺得這幾年的工作都毫無進(jìn)步可言。

           

          簡單的開發(fā)模式,老板一個(gè)概念的產(chǎn)生,沒有產(chǎn)品,直接拋到開發(fā),開發(fā)要么模仿別人的網(wǎng)站要么自己捉摸該怎么實(shí)現(xiàn),然后進(jìn)入編碼,沒有代碼review,沒有單元測試,沒有回歸測試,不關(guān)注代碼風(fēng)格,不注重代碼質(zhì)量。功能實(shí)現(xiàn)了就算完了。后來改進(jìn),有產(chǎn)品分析設(shè)計(jì)產(chǎn)生prd,開發(fā)人員按照prd進(jìn)行開發(fā),有時(shí)進(jìn)行部分重構(gòu),代碼質(zhì)量也沒有太多的提升。

           

          簡單的項(xiàng)目管理方式,概念出來,開發(fā)人員大致揣測出老板的意圖之后,開始分模塊,估算時(shí)間,分配人員功能模塊。老板看到項(xiàng)目時(shí)間需要3個(gè)月,老板對我們說,不可能需要這么長時(shí)間吧,給你們一個(gè)月半的時(shí)間給我完成吧,最后經(jīng)過討價(jià)還價(jià),老板說再加半個(gè)月,你們不能再說了,最后我們被老板“強(qiáng)奸”了。項(xiàng)目也有版本控制,但是沒有分支,只有主干,多個(gè)不同時(shí)間點(diǎn)的需求上線都在同一個(gè)主干上開發(fā),導(dǎo)致有時(shí)候因?yàn)楹竺鏁r(shí)間點(diǎn)的需求影響了前面時(shí)間點(diǎn)需求的上線。

           

          最簡單的上線過程,開發(fā)人員自學(xué)linux系統(tǒng)管理,自己通過ftp把主干代碼上傳部署,而且都是老板訪問不了網(wǎng)站我們才知道出故障了,接著開發(fā)人員在主干上改bug,改完bug重新部署,發(fā)現(xiàn)之前的bug沒了,出現(xiàn)了新的4個(gè)bug。繼續(xù)修改bug,部署無限循環(huán)。

           

          最簡單的線上故障處理,數(shù)據(jù)庫負(fù)載過高、web服務(wù)器負(fù)載過高、服務(wù)器硬件壞了、網(wǎng)絡(luò)線路,機(jī)房斷電等等問題出現(xiàn)之后,唯一的辦法就是停止網(wǎng)站服務(wù)進(jìn)行修復(fù)。

           

          每周五下午例會(huì),討論的問題都沒有積累下來,沒有被分享給其他團(tuán)隊(duì)成員,更不可能分享給新員工。

           

          上面的流程基本上都是由開發(fā)人員負(fù)責(zé),開發(fā)人員此時(shí)就是多角色,類似孫悟空,需要不斷轉(zhuǎn)換角色。更重要的是,上述所有流程和項(xiàng)目管理都只是關(guān)注項(xiàng)目自身。沒有關(guān)注團(tuán)隊(duì)知識的積累,人員的培訓(xùn)。本質(zhì)上就是不關(guān)注“成長”。我作為項(xiàng)目經(jīng)理,我承認(rèn)自己之前的不足,但值得慶幸的是我在離開之前做了一些比較有意義的事情,我讓部門開發(fā)人員每個(gè)星期輪流做分享,讓整個(gè)部門的人能夠?qū)W到更多的知識。

           

           

          后來來到淘寶,在這里能學(xué)到很多東西,因?yàn)檫@里有完善的培訓(xùn)體系,注重員工的個(gè)人成長。一個(gè)小插曲,我在新員工手冊里看到sprint這個(gè)單詞,我想在業(yè)內(nèi)算比較牛的公司竟然還會(huì)把spring寫成sprint,后來接觸到了scrum敏捷開發(fā),我才知道原來sprintscrum的一個(gè)迭代周期。那個(gè)羞啊。哈哈。

           

          來到淘寶,猛然有一種柳暗花明又一村的感覺,以前我做的事情都有涉及到,但在腦海中沒有一個(gè)成形的框架,零零散散。

           

          下面來講講跟我之前不一樣的地方。

           

          開發(fā)模式:

          l  周五的雙周pk,產(chǎn)品排好需求優(yōu)先級,項(xiàng)目經(jīng)理根據(jù)團(tuán)隊(duì)的人力資源pk需求,哪些可以完成,哪些人力不夠。這里要著重強(qiáng)調(diào)工時(shí),每個(gè)人每天都是按照4小時(shí)來算,其他4小時(shí)主要學(xué)習(xí),更好的完成工作。4個(gè)小時(shí)是根據(jù)團(tuán)隊(duì)平時(shí)工作效率來計(jì)算的,也有可能是56個(gè)小時(shí)。不同時(shí)期不同項(xiàng)目每個(gè)人都有不同的變化曲線。

          l  Pk下來的需求,技術(shù)團(tuán)隊(duì)在周一進(jìn)行任務(wù)拆分,然后大家領(lǐng)取各自的任務(wù)。

          l  每個(gè)團(tuán)隊(duì)有自己的任務(wù)墻(故事墻),主要就是讓大家在每天的15分鐘晨會(huì)上列出各自的每天要完成的任務(wù)。

          l  每天早上15分鐘的晨會(huì),一個(gè)是讓大家都能在某個(gè)時(shí)間點(diǎn)之前趕到公司開會(huì),另一個(gè)就是讓大家清楚自己今天要做什么。晨會(huì)主要描述:1 我昨天做了什么 2 我遇到了哪些問題,自己解決了可以簡單分享給同事,解決了不了可以讓團(tuán)隊(duì)來幫忙一起解決。 3 今天我要做什么

          l  每雙周要做回顧,看看出現(xiàn)了什么問題,哪些地方可以再改進(jìn)。

          l  最后就是分享,有技術(shù)分享,業(yè)務(wù)分享。

           

          這個(gè)開發(fā)模式對比以前有以下幾個(gè)優(yōu)點(diǎn):

          l  團(tuán)隊(duì)資源的合理利用,不會(huì)出現(xiàn)老板說幾個(gè)月完成然后底下的人拼命的加班,讓大家對技術(shù)的興趣越來越高,工作的越來越快樂。成長也越來越快。

          l  讓大家明確知道自己今天要做什么。

          l  分享,不但自己是分享的參與者也會(huì)是分享的發(fā)起人。不管是哪種角色,你都能學(xué)到很多很多。通過分享,團(tuán)隊(duì)的進(jìn)步會(huì)非常快。

           

          開發(fā)流程:

          l  編碼、單元測試

          l  Findbugs

          l  Mvn test

          l  Code review(重構(gòu),然后從頭開始)

          l  提交代碼到svn

           

          這個(gè)開發(fā)流程主要關(guān)注的是代碼的提升,保證代碼的質(zhì)量,通過代碼審查讓盡早發(fā)現(xiàn)不合理的地方。

           

          上線流程:

          l  提前一個(gè)星期申請上線

          l  單元測試

          l  提測給測試團(tuán)隊(duì)

          l  打包

          l  發(fā)上線計(jì)劃,預(yù)發(fā)冒煙,發(fā)布生產(chǎn)環(huán)境

           

          這個(gè)流程我不是很熟悉,所以不作評論了。

           

          有了這些比較優(yōu)秀的模式和流程,也需要工具的配合。

          代碼版本控制:svn,并發(fā)開發(fā)的需求需要用到分支,主干代碼盡量保證隨時(shí)可以上線。

          項(xiàng)目的管理:maven,開發(fā)模式,測試模式,生產(chǎn)模式配置的切換,也可以和hudson進(jìn)行持續(xù)集成。

           

          讓我最有感觸的就是淘寶非常注重知識的積累和員工的成長。在這里我感覺我真的成長了,有踏實(shí)的感覺,少了浮躁。這篇文章并不是說小公司不好,也是因?yàn)樵谥澳羌夜窘佑|的面廣,所以來淘寶知道自己哪些對于自己更重要,但也并不鼓勵(lì)你畢業(yè)就去小公司,因?yàn)閷δ愕某砷L不好。每個(gè)人的路都是唯一的,大家喜歡怎么走就看大家自己的了,每個(gè)選擇都是獨(dú)一無二的,這樣才能活出獨(dú)一無二的生活,絢爛的生活。

          posted @ 2011-05-03 18:46 yangpingyu 閱讀(381) | 評論 (4)編輯 收藏

          DiffieHellman協(xié)議實(shí)踐

          開發(fā)過程中遇到了一些數(shù)據(jù)傳輸安全性問題,一個(gè)很重要實(shí)際需求,客戶端加密的數(shù)據(jù)在服務(wù)端要解密回來還要進(jìn)行一些處理。

          腦中立馬跳出幾種解決方法:

          1、直接使用MD5進(jìn)行加密好了,可是MD5是不可逆的算法,而某些數(shù)據(jù)到達(dá)服務(wù)器端需要解密出來進(jìn)行一些處理。看來不滿足實(shí)際需求。

          2、那可以嘗試下DES,3DES或者AES等一些對稱算法加密處理,想想挺好的,對稱算法的效率也挺快。可是密鑰該怎么從服務(wù)端安全的傳遞到客戶端呢,這個(gè)問題不解決,加密還是如同虛設(shè)。

          3、最后一種方案那就是使用RSA非對稱算法,這個(gè)算法的好處就是服務(wù)器端自己維護(hù)私鑰,把公鑰開放給客戶端,有人在網(wǎng)絡(luò)上監(jiān)聽到公鑰和加密后的數(shù) 據(jù)也沒關(guān)系,因?yàn)榧用艿臄?shù)據(jù)需要私鑰才能解的開。也許大家都熟悉https協(xié)議,其實(shí)這種協(xié)議就是用RSA非對稱算法來實(shí)現(xiàn),但是大家肯定也有感受,用 https的時(shí)候網(wǎng)頁打開的速度會(huì)比http要慢很多。我也考慮到這點(diǎn),于是做了一個(gè)基準(zhǔn)測試,在服務(wù)端寫了個(gè)測試類,結(jié)果讓我大吃一驚,2g的cpu循 環(huán)100次用私鑰去解密竟然花了我50000多毫秒,那我循環(huán)10000次呢,靠,竟然花了幾分鐘。那如果采用這種方案去實(shí)現(xiàn)的話,應(yīng)用程序的性能會(huì)被這 些解密動(dòng)作所拉下。沒辦法哦,又只能放棄此類方案。

          4、山重水復(fù)疑無路,柳暗花明又一村。突然腦中又蹦出另外一種想法,還是使用對稱算法,關(guān)于密鑰的傳遞可以采用DiffieHellman協(xié)議。 于是乎,上網(wǎng)查了一些資料,發(fā)現(xiàn)java類似的算法還是可以查的到,但是單有java也不行,我要在客戶端加密,java端進(jìn)行解密,所以還需要有 JavaScript的類似算法。最后在enano-1.1.7這個(gè)php開源的電子商務(wù)網(wǎng)站內(nèi)找到了相關(guān)信息。無意間發(fā)現(xiàn)這個(gè)開源程序的一篇wiki, 詳細(xì)介紹了一套安全解決方案(http://enanocms.org/News:Article/2008/02/20/Diffie_Hellman_key_exchange_implemented)。

          以上只是我的思路,但還沒有寫個(gè)應(yīng)用程序測試過。那么光有理論沒有實(shí)踐也不行,那就建個(gè)工程實(shí)現(xiàn)一下唄。嘿嘿。

          case:用戶注冊。

          case描述:客戶在客戶端填寫一些信息,提交之前通過密鑰把用戶名和密碼進(jìn)行加密,服務(wù)端需要把用戶名和密碼解密回來進(jìn)行進(jìn)行處理,一個(gè)很重要的 處理就是,給密碼加鹽值,然后進(jìn)行MD5加密,也許你會(huì)問,為什么這個(gè)動(dòng)作不能在客戶端做呢,其實(shí)也是可以的,但是為了不想讓黑客知道我密碼加密的體制所 以放到服務(wù)端進(jìn)行。

          case UML:


          設(shè)計(jì)到的一些類和文件:




          其中用到了base64,主要就是解決了中文亂碼問題。

          實(shí)現(xiàn)過程中的一些總結(jié):

          1、對于安全算法等一些總結(jié),我用到一些相關(guān)算法類都是可以單獨(dú)拿來用的。而jdk中也有支持的相關(guān)類,可以看看jca和jce。這兩個(gè)擴(kuò)展包其實(shí)并沒有真正的實(shí)現(xiàn),他們只是對這些安全問題的抽象。真正的實(shí)現(xiàn)有sunjce和Bouncy Castle。

          2、對于算法本身定義的理解很重要,AES支持128,192,256位的密鑰,但每次被加密的一定要是128位的內(nèi)容,一般我們被加密的都是超過此長度的,那可以這么來處理:


          把要加密的內(nèi)容進(jìn)行分組處理。解密也是類似。

          最后的總結(jié):此次實(shí)踐只是模擬了一個(gè)場景,還可以運(yùn)用很多場景中,OpenSSL,OpenID都可以運(yùn)用。不是我說的,我也是看了找了相關(guān)的材 料,OpenSSL java的項(xiàng)目還在建設(shè)中,php是有的,大家可以去找下,openid也是有用到的,這里推薦一個(gè)開源的項(xiàng)目openid4java,也可以去查查看, 有時(shí)間去看看里面的源碼還是不錯(cuò)的。如果你也啥地方還不明白或者需要里面用到的一些代碼,可以和我溝通交 流,msn:yangpingyu@gmail.com.

          posted @ 2011-02-21 16:00 yangpingyu 閱讀(506) | 評論 (0)編輯 收藏

          fastdfs-apache-module模塊在centos下的使用說明

          fastdfs-apache-module主要作用就是配合sotrage存儲(chǔ)器以http方式下載文件,更重要的是解決了storage同步帶來的延遲。也許熟悉fastdfs的朋友們知道,以前通過tracker來跳轉(zhuǎn)也可以解決或其他方式也可以解決,舍取就要看大家的應(yīng)用了。但是fishman(fastdfs作者)做過測試,性能絕對是fastdfs-apache-module要高。

          生產(chǎn)環(huán)境中我肯定要用一些性能比較高的軟件嘍。因此把下載方式改成用fast-apache-module。以下是我的使用步驟和遇到的一些問題。前提條件:1、已經(jīng)安裝好fastdfs,而且版本是FastDFS_v2.02.tar.gz或以上。2、已經(jīng)安裝好apache2.0或以上。

          第一步,下載fastdfs-apache-module

          第二步,編譯fastdfs-apache-module。分以下幾種情況。
          1 、如果apache是你自己安裝的,并且apache文件目錄在/usr/local/apache2。那么你可以直接運(yùn)行make,然后make install。
          2、 如果apache是你自己安裝的,apache目錄在/opt/apache2,那么首先你得把Makefile文件的以下幾處地方修改。
                第七行改成:top_srcdir=/opt/apache2
                第八行改成:top_builddir=/opt/apache2
                第九行改成:include /opt/apache2/build/special.mk
                第十二行改成:APXS=/opt/apache2/bin/apxs
                第十三行改成:APACHECTL=/opt/apache2/bin/apachectl
                改完后,然后執(zhí)行make和make install命令。
          3、 如果是系統(tǒng)自帶的httpd,那么你就比較麻煩了。
                3.1 首先安裝httpd-devel包。建議使用centos的yum進(jìn)行安裝,souhu的源比較好用,速度挺快的。
                3.2 類似的也要改Makefile文件了。
                     第七行改成:top_srcdir=/etc/httpd
                     第八行改成:top_builddir=/etc/httpd
                     第九行改成:include /etc/httpd/build/special.mk
                     第十二行改成:APXS=/usr/sbin/apxs
                     第十三行改成:APACHECTL=/usr/sbin/apachectl
                     保存。
                3.3 ln -s /etc/httpd/build /usr/lib64/httpd/build(為了讓第九行找到special.mk)
                3.4 make
                3.5 make install
          如果正常編譯的話,在/etc/fdfs目錄下多了一個(gè)mod_fastdfs.conf文件,在${apache安裝根目錄}/modules目錄下生成
          mod_fastdfs.so文件。
          第三步:修改httpd的配置文件httpd.conf。
                     1、LoadModule fastdfs_module modules/mod_fastdfs.so
                     2、<Location /M00>
                               
          sethandler  fastdfs
                           </
          Location>
                     3、設(shè)置
          DocumentRoot為:${fastdfs_base_path}/data

          第四步:ln -s ${fastdfs_base_path}/data  ${fastdfs_base_path}/data/M00
          第五步:修改/etc/fdfs/mod_fastdfs.conf配置文件,文件中對每個(gè)設(shè)置字段都有注釋
          第七步:重啟apache。

          以上就是fastdfs-apache-module安裝的具體過程。僅供參考。

          posted @ 2010-11-04 14:26 yangpingyu 閱讀(2116) | 評論 (0)編輯 收藏

          fastdfs分布式文件系統(tǒng)部署

          在介紹本文之前,我向大家介紹下一個(gè)非常棒的分布式文件系統(tǒng)fastdfs,關(guān)于她的具體介紹和優(yōu)點(diǎn)我不做詳細(xì)介紹了,有關(guān)資料可以訪問:http://linux.chinaunix.net/bbs/forum-75-1.html

          web2.0海量小文件的存儲(chǔ)是所有系統(tǒng)架構(gòu)師必須要面對的一個(gè)問題。

          這幾天一直在忙著給公司部署分布式文件系統(tǒng)。也看了幾個(gè)大型公司的分布式文件系統(tǒng)的架構(gòu):flicker,taobao,拍拍網(wǎng)。深入理解各自應(yīng)用的場景,弄明白了很多個(gè)為什么之后,自己將要為公司部署的分布式文件系統(tǒng)架構(gòu)也慢慢浮出水面了。

          架構(gòu)隨著業(yè)務(wù)發(fā)展而逐步改變的,比如類似淘寶這樣的訪問量,可能需要cdn來加速靜態(tài)資源(js,css)和用戶上傳的業(yè)務(wù)相關(guān)的圖片而一般訪問量不是特別大的網(wǎng)站可以不用部署cdn,原因肯定很多,硬件成本和維護(hù)成本等等。

          那么接下來我就分別介紹部署cdn的分布式文件系統(tǒng)架構(gòu)和普通分布式文件系統(tǒng)架構(gòu)。

          (一)部署cdn的分布式文件系統(tǒng)架構(gòu):

          涉及到的技術(shù):lvs,haproxy(或nagix),squid,nagix(或apache),fastdfs。
          有圖有真相,先畫個(gè)圖。


          接下來我對每層設(shè)置的意義進(jìn)行解釋下。
          lvs7層代理:主要通過ip來找到自己合適的下層服務(wù)器。
          haproxy或nginx4層代理:主要通過url hash找到合適的一臺squid。最主要功能就是為了提高squid緩存的命中率。
          squid集群:緩存所有用戶訪問的對象。
          文件服務(wù)器集群:1、tracker服務(wù)器,主要管理文件存儲(chǔ)的源storage等一些信息。 2、storage服務(wù)器就是實(shí)際文件的存儲(chǔ)位置。最近fishman(fastdfs的作者)開發(fā)了一個(gè)apache module解決了延遲問題,那么我們可以不用啟用tracker內(nèi)嵌的服務(wù)器來跳轉(zhuǎn)了,直接把http服務(wù)器配置在storage服務(wù)器上。給我們帶來了很多方便。

          (二)普通分布式文件系統(tǒng)架構(gòu):


          (圖片引自:http://linux.chinaunix.net/bbs/thread-1062461-1-1.html

          這個(gè)架構(gòu)沒有涉及到cdn的部署,相比起來應(yīng)該更容易理解了。對于tracker和storage的作用類似于在部署cdn的分布式文件系統(tǒng)架構(gòu)部分介紹的一樣。其中client我覺得有必要要解釋下,這里的client有兩層含義:1、相對于tracker和storage服務(wù)器來說,client是訪問這些服務(wù)器的客戶端。 2、應(yīng)用程序的服務(wù)端。實(shí)際開發(fā)中,我們可以部署一臺圖片上傳服務(wù)器來作為應(yīng)用程序的服務(wù)端,也可以把圖片上傳服務(wù)直接寫到應(yīng)用程序中。

          兩種架構(gòu)都是基于fastdfs分布式文件系統(tǒng)。所以你們了解fastdfs軟件本身之后再看這篇文章也許會(huì)更有益。

          posted @ 2010-10-26 17:27 yangpingyu 閱讀(3738) | 評論 (2)編輯 收藏

          spring security引發(fā)的若干思考

          兩個(gè)項(xiàng)目中都使用了spring security安全框架,很多資料都是介紹spring security具體使用。今天我如果還是寫這些東西就顯得多余了,那么我從不同的角度來總結(jié)自己對這個(gè)框架的一些認(rèn)識。

          首先看看兩個(gè)疑惑,然后我會(huì)逐步解釋這兩個(gè)疑惑。

          第一個(gè)疑惑,spring security框架是spring的子框架,我就非常好奇spring security和spring是如何融合起來,確切的說,spring security定義的對象如何納入spring ioc 容器中管理。研究到最后其實(shí)都是spring自身的一些知識,比如:自定義擴(kuò)展xml schema,spring ioc啟動(dòng)。

          第二個(gè)疑惑,spring security如何攔截用戶的請求。這部分可以解讀spring security源碼可以得到答案。

          徹底搞明白第一個(gè)疑惑之后,也許你以后自己寫一個(gè)框架,就可以很方便的整合到spring中去了。對于框架開發(fā)工程師來說,開發(fā)新的框架之后能整合spring是必須的事情了,畢竟spring給我們所帶來的好處是可想而知的。這也是我要徹底了解清楚原理的動(dòng)力所在。廢話一堆,進(jìn)入主題吧~~~~

          第一個(gè)疑惑最后涉及到兩個(gè)方面的知識,spring ioc啟動(dòng)和spring可擴(kuò)展xml schema。spring ioc有兩個(gè)非常重要的概念,beanfactory和applicationContext,后者提供了更多更強(qiáng)的功能。為了避免過多的細(xì)節(jié)直接解讀beanfactory的讀取過程,xmlbeanfactory讀取xml文件會(huì)經(jīng)歷如下兩個(gè)過程:1、通過resource接口讀取xml文件,轉(zhuǎn)換成document。 2、從document中解析出bean的配置。具體詳細(xì)過程請參照文章:spring讀取xml配置源代碼分析(這篇文章一定要先看懂,不然后面很難繼續(xù))。看過我介紹大家看的那篇文章之后,其實(shí)也有所了解spring擴(kuò)展xml schema機(jī)制了。如果還不是很清楚再結(jié)合這篇文章:基于Spring可擴(kuò)展Schema提供自定義配置支持。感覺有點(diǎn)東拼西湊的,呵呵,主要怕以后自己忘記了,所以才寫篇blog。

          第二個(gè)疑惑我們就看源代碼吧。
          <filter-mapping>
             
          <filter-name>jcaptchaFilter</filter-name>
             
          <url-pattern>/j_spring_security_check</url-pattern>
          </filter-mapping>
              
          <filter>
             
          <filter-name>springSecurityFilterChain</filter-name>
             
          <filter-class>com.busyCity.web.filter.DelegatingFilterProxy</filter-class>
          </filter>
          <filter-mapping>
             
          <filter-name>springSecurityFilterChain</filter-name>
             
          <url-pattern>/*</url-pattern>
              <dispatcher>FORWARD</dispatcher>  
              <dispatcher>REQUEST</dispatcher>
          </filter-mapping>
          web.xml中配置了DelegatingFilterProxyDelegatingFilterProxy調(diào)用FilterChainProxy的doFilter
                public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException
                {
                    
          if(currentPosition == additionalFilters.size())
                     {
                          
          if(FilterChainProxy.logger.isDebugEnabled())
                              FilterChainProxy.logger.debug((
          new StringBuilder()).append(fi.getRequestUrl()).append(" reached end of additional filter chain; proceeding with original chain").toString());
                          fi.getChain().doFilter(request, response);
                      } 
          else
                      {
                          currentPosition
          ++;
                          Filter nextFilter 
          = (Filter)additionalFilters.get(currentPosition - 1);
                          
          if(FilterChainProxy.logger.isDebugEnabled())
                              FilterChainProxy.logger.debug((
          new StringBuilder()).append(fi.getRequestUrl()).append(" at position ").append(currentPosition).append(" of ").append(additionalFilters.size()).append(" in additional filter chain; firing Filter: '").append(nextFilter).append("'").toString());
                          nextFilter.doFilter(request, response, 
          this);
                      }
                  }
          這個(gè)方法就是循環(huán)調(diào)用我們用http命名空間配置的那些過濾器。然后根據(jù)不同的過濾器處理不同的內(nèi)容。


          我描述的都很簡單,主要原因是做個(gè)記錄,以后忘記了可以根據(jù)這個(gè)思路重新找到答案。不需要重新開始研究。呵呵。

          posted @ 2010-10-08 16:05 yangpingyu 閱讀(741) | 評論 (0)編輯 收藏

          支付寶soa實(shí)踐總結(jié)

          一直以來,看了很多東西自己知道就完了,而我覺得作為互聯(lián)網(wǎng)的一份子,應(yīng)該懂得分享。

          最近看了很多關(guān)于程立的一些演講,學(xué)習(xí)到了soa實(shí)踐中非常寶貴的經(jīng)驗(yàn)。

          最開始支付寶架構(gòu)是單應(yīng)用系統(tǒng),采用分層架構(gòu)模式(展現(xiàn)層+業(yè)務(wù)層+持久層),用過分層架構(gòu)模式的人都很清楚業(yè)務(wù)層是最關(guān)鍵的一層,也是最容易造成臃腫和龐大的一層,所以需要合理的分離。而最佳實(shí)踐應(yīng)該是把業(yè)務(wù)層分為:facade層和業(yè)務(wù)邏輯層,也符合現(xiàn)在比較熱的領(lǐng)域模型驅(qū)動(dòng),業(yè)務(wù)邏輯層主要負(fù)責(zé)業(yè)務(wù)本身的實(shí)現(xiàn),facade層區(qū)分產(chǎn)品功能和決策。對于中小型而且項(xiàng)目業(yè)務(wù)需求也不怎么變的應(yīng)用來說,是比較合理的架構(gòu)。無論從學(xué)習(xí)成本和人力成本來說,都相對較低。但是業(yè)務(wù)不斷擴(kuò)張的應(yīng)用來說,到最后開發(fā)成本和維護(hù)將會(huì)逐步提高。支付寶也是這個(gè)時(shí)候就開始著手對“對象”進(jìn)行“組件”化,也就是接下來的第二個(gè)過程了。

          從對象到組件化,我們首先要來看看組件這個(gè)概念。組件是功能職責(zé)相近類的集合。組件本身需要以下幾點(diǎn)功能:1、屬性 2、運(yùn)行級別 3、引用 4、擴(kuò)展 等。而那時(shí)比較符合支付寶這一思想的規(guī)范有osgi。具體關(guān)于osgi的相關(guān)內(nèi)容可以自行g(shù)oogle。如果用osgi會(huì)出現(xiàn)如下三個(gè)問題:
          1、osgi平臺如何在tomcat,jboss下運(yùn)行。(現(xiàn)在tomcat,jboss等應(yīng)用服務(wù)器都已經(jīng)支持了,以前需要自己來擴(kuò)展tomcat)
          2、osgi使用起來比較麻煩和復(fù)雜,如何跟spring整合起來使用也是需要解決的問題,不過spring就是spring,05左右好像就對這方面開始研究了,現(xiàn)在作為一個(gè)子項(xiàng)目(Spring Dynamic Modules)。
          3、osgi規(guī)范沒有擴(kuò)展這種功能,所以需要自己來對osgi的一些框架進(jìn)行擴(kuò)展,可以參照eclipse的一個(gè)osgi框架,或者也可以直接用此框架(Equinox)。
          這些問題都解決了,那么“對象”進(jìn)行“組件”過程就完成了。但是這個(gè)時(shí)候需要對企業(yè)內(nèi)部的系統(tǒng)能夠配合起來,那么可以對業(yè)務(wù)層進(jìn)行服務(wù)化。也就是第三個(gè)過程了。

          把組件都以服務(wù)方式部署出來,同時(shí)也需要管理好這些服務(wù),那么此時(shí)可以引入esb(企業(yè)服務(wù)總線)中間件了。商業(yè)的和開源的都有,支付寶用的是開源的mule,不過對于某些方面,支付寶進(jìn)行改造過,為了確保消息能夠百分百不丟失,畢竟跟錢打交道嘛需要嚴(yán)謹(jǐn)。

          其實(shí)這些基礎(chǔ)平臺搭建好后,還需要解決一個(gè)特別難的難題。功能服務(wù)化后,數(shù)據(jù)庫也不再是集中式了,本地事務(wù)也不能保證了。這個(gè)時(shí)候需要引入分布式事務(wù),數(shù)據(jù)庫軟件本身也提供分布式事務(wù),但是對于需要高訪問量和高性能的網(wǎng)站來說,可能需要換一種方式了。cap和base理論告訴我們可以適當(dāng)放棄強(qiáng)一致性來達(dá)到其他兩項(xiàng)性能。ws-transaction是分布式中間件,但是完成一個(gè)事務(wù)需要很多消息來溝通,至少大大增大了事務(wù)的中間過程會(huì)被中斷掉。支付寶公司研究出了一些分布式事務(wù)模式,比如冪等性模式,補(bǔ)償性模式,tcc模式等等。每個(gè)模式都有不同的應(yīng)用場景,大家可以根據(jù)自己的業(yè)務(wù)特點(diǎn)來進(jìn)行選取。

          以上即是我最近學(xué)習(xí)的一些總結(jié)。讓更多的人能學(xué)習(xí)好的技術(shù)和經(jīng)驗(yàn)。讓我們一起來分享吧。

          posted @ 2010-09-27 15:36 yangpingyu 閱讀(2846) | 評論 (0)編輯 收藏

          僅列出標(biāo)題
          共3頁: 上一頁 1 2 3 下一頁 
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          收藏夾

          linux

          產(chǎn)品交互

          分析,設(shè)計(jì),架構(gòu)

          安全

          技術(shù)牛人

          數(shù)據(jù)庫

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 长宁区| 新巴尔虎左旗| 绥芬河市| 南郑县| 丹江口市| 客服| 乐至县| 屏山县| 乌兰察布市| 天台县| 延寿县| 深圳市| 新绛县| 尚义县| 两当县| 南投市| 芮城县| 宁城县| 根河市| 壤塘县| 晴隆县| 海口市| 博乐市| 宽城| 海阳市| 鲁甸县| 浦北县| 临颍县| 兰考县| 环江| 从化市| 宜川县| 遂平县| 保亭| 安远县| 防城港市| 天津市| 临安市| 德格县| 墨江| 容城县|