小菜毛毛技術分享

          與大家共同成長

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            164 Posts :: 141 Stories :: 94 Comments :: 0 Trackbacks

          #

          從零開始。

          >1 下載Android SDK ,然后帶著不求甚解的態度去閱讀 SDK 里的官方文檔。
             沒有手機軟件開發經驗、Java 以及 eclipse 使用經驗的朋友不必擔心,我也是一樣。
             剛開始接觸不可能一下就看明白,先大體掃一下官方文檔,了解一下基本概念,不需要完全理解,
             有點印象就行了。

          >2 去 http://www.eoeandroid.com/viewthread.php?tid=772&extra=page%3D1
             下載 eoe 特刊~ 寫的很不錯,你首先需要看完第一期,就可以把開發環境搭建好了。
             其他幾期可以稍后再看。
           
          >3 查閱官方文檔,按照里邊的 hello world 教程一步一步做,很快你的第一個 android 項目就可以
             成功運行了!

          >4 接下來你應該學習官方文檔里的記事本了,這個要稍微花點時間,做的過程可能有一些疑問,沒關系,
             大膽忽略,只要可以成功運行即可。

          >5 看了一大堆英文的東西,也有不少疑問了,應該看一些中文的文檔來加深理解了。
             強烈推薦  深入淺出 Android -- Google 手持設備應用程式設計入門      
             http://code.google.com/p/androidbmi/wiki/DiveIntoAndroid
             雖然是繁體的,但是文章的質量相當高,看完之后基本上就可以算是入門了!

          >6 接下來,建議把 eoe 特刊的其它幾期都看完,相信你對一些常見的開發需求就很有把握了。

          >7 請加入hong老大建的群,這里有很多對android 感興趣 的兄弟,大家可以一起探討。
              http://groups.google.com/group/china-android-dev
           
          >8 推薦幾個不錯的博客,
             http://haric.javaeye.com/ 
             http://rayleung.javaeye.com/
                
          >9 可以經常去 http://www.eoeandroid.com/forumdisplay.php?fid=27 這里逛逛。

          >10 想開始做app賺錢了? 呵呵,把 javaeye 里移動編程板塊的精華良好貼都翻一遍吧~ 祝福你!

          轉自:http://www.javaeye.com/topic/473980

          posted @ 2010-07-07 11:00 小菜毛毛 閱讀(1146) | 評論 (0)編輯 收藏

          在使用<c:out value="${map[key]}" />  求對應的KEY中的,value
          必須注意:key的類型必須和map中存放的key的類型一致,否則不能得到預期的結果
          posted @ 2010-07-05 15:07 小菜毛毛 閱讀(492) | 評論 (0)編輯 收藏

          1、迭代

                當forEach 的items屬性中的表達式的值是java.util.Map時,則var中命名的變量的類型就是java.util.Map.Entry。這時 var=entry的話,用表達式${entry.key}取得鍵名。用表達式${entry.value}得到每個entry的值。這是因為 java.util.Map.Entry對象有getKey和getValue方法,表達式語言遵守JavaBean的命名約定。

               例:

          Html代碼
          1. <c:forEach items="${map}" var="entry">  
          2.        <c:out value="${entry.key}" />  
          3.         <c:out value="${entry.value}" />  
          4. </c:forEach>  

          2、根據key求值

                如果事先知道key那么很容易根據${map.key值}就可以得到值對象,但是如果key是一個變量呢?有一個問題,如果給定一個key的變量如何使用 EL得到對象呢,這里需要使用EL表達式中的[]來解決。

                 例:

          Html代碼
          1. <c:out value="${map[key]}" />  
          2. <!-- 這里的map就是 java.util.Map對像,key是這個map里的一個 key -->  
          1、迭代
          Map的每個對象以key=value的形式給出
          當forEach tag的item屬性中的表達式的值是java.util.Map時,在var中命名的變量被設置為類型是java.util.Map.Entry的 item。這時,迭代變量被稱為entry,因此,用表達式${entry.key}取得鍵名。
          在下面的例子中你會看到,可以用表達 式${entry.value}得到每個entry的值。這是因為java.util.Map.Entry對象有getKey和getValue方法,表 達式語言遵守JavaBean的命名約定。
          通常,JSP EL表達式${a.b.c.d}是用代碼a.getB().getC().getD()來計算的。這種表達式是對JavaBean屬性的相繼調用的簡化。
          示例:
          Html代碼
          1. <%@ page language="java" pageEncoding="utf-8"%>  
          2. <jsp:directive.page import="com.xaccp.vo.BookAdapter"/>  
          3. <%@ taglib prefix="c" uri="/WEB-INF/c.tld"  %>  
          4. <jsp:directive.page import="java.util.Hashtable"/>  
          5. <jsp:directive.page import="com.xaccp.vo.Book"/>  
          6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
          7. <html:html locale="true">  
          8.   <head>  
          9.    <%  
          10.      Hashtable table=new Hashtable();  
          11.      BookAdapter ba=new BookAdapter();  
          12.      table.put("aaa",new Book(1,"abc"));  
          13.      table.put("bbb",new Book(2,"bbb"));  
          14.      table.put("ccc",new Book(3,"cccc"));  
          15.      pageContext.setAttribute("table",table);  
          16.     %>  
          17.   </head>  
          18.    
          19.   <body>  
          20.   
          21.     <hr>  
          22.     <c:forEach items="${table}" var="aaa">  
          23.      <c:set var="key" value="${aaa.key}" ></c:set>  
          24.      <c:set var="book" value="${aaa.value}"></c:set>  
          25.       ${key }=${book }111  name:${book.bookID}<br>  
          26.   
          27.     </c:forEach>  
          28.   </body>  
          29. </html:html>  
          30.   
          31.    
           2、根據key變量求值
          如果事先知道key那么很容易根據${map.key值}就可以得到值對象,但是如果key是一個變量呢?
          有一 個問題,如果給定一個key的變量如何使用EL得到對象呢,這里需要使用EL表達式中的[]來解決,解決方法如示例:
          Html代碼
          1. <%@ page language="java" pageEncoding="utf-8"%>     
          2. <jsp:directive.page import="com.xaccp.vo.BookAdapter"/>     
          3. <%@ taglib prefix="c" uri="/WEB-INF/c.tld"  %>     
          4. <jsp:directive.page import="java.util.Hashtable"/>     
          5. <jsp:directive.page import="com.xaccp.vo.Book"/>     
          6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">     
          7. <html:html locale="true">     
          8.   <head>     
          9.    <%     
          10.      Hashtable table=new Hashtable();     
          11.      table.put("aaa",new Book(1,"abc"));     
          12.      table.put("bbb",new Book(2,"bbb"));     
          13.      table.put("ccc",new Book(3,"cccc"));     
          14.      pageContext.setAttribute("table",table);     
          15.     %>     
          16.   </head>     
          17.       
          18.   <body>     
          19.     This a struts page. <br>     
          20.     <c:set var="keys" value="bbb" ></c:set>     
          21.     <c:set var="book" value="${pageScope.table[pageScope.keys]}"></c:set>     
          22.  ${keys }=${book}     
          23. <!-- 這里的${book就是對應于key值得book對象} -->     
          24.     <br>     
          25.   </body>     
          26. </html:html>   
          posted @ 2010-07-05 15:05 小菜毛毛 閱讀(21144) | 評論 (2)編輯 收藏

          google app engin Text字段可保存任意長的字符(可能有人說用String,不好意思engin不支持500字符以上的String)
          持久化字段需這樣才能保存
          @Persistent(defaultFetchGroup="true")
              private Text text;
          可用text.getValue()獲取其保存的字符串

          注意:用jstl標簽<c:out value="${text}" />會進行轉義
          如果不想轉義,即是原封不動的將字符串輸出,用${text}即可


          posted @ 2010-06-22 14:55 小菜毛毛 閱讀(203) | 評論 (0)編輯 收藏


          1.對于JSTL先了解下:
          sevlet2.4 jsp2.0規范   需使用jstl1.1 tomcat5.X
          :使用標簽需在jsp頭部引用<%@ taglib prefix="c" uri="
          sevlet2.5              需使用jstl1.2 tomcat6.X
          <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" import="java.util.*"%>
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          isELIgnored="false"表示使用EL表達式
          EL表達式可參見:http://www.cnblogs.com/hya1109/archive/2007/10/02/912947.html
          2.在google app engine中使用JSTL,需在jsp中加入:
          <%@ isELIgnored="false" %>
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>(這句代碼在myeclipse中會報錯不用管)
          因為在web.xml中其實sevlet2.5
          ${article.title}====article.getTile();







          posted @ 2010-06-13 14:58 小菜毛毛 閱讀(416) | 評論 (0)編輯 收藏

          這個可是我從各處搜集來的資料,上傳到了我的資源里,供大家下載學習,希望大家支持一下
          1、【eoeAndroid特刊】第一期 Andriod 1.5 SDK簡介 http://download.csdn.net/source/2399809

          2、 【eoeAndroid特刊】第二期 圖像處理篇(1) http://download.csdn.net/source/2399814

          3、 【eoeAndroid特刊】第三期 Android Market及應用發布 http://download.csdn.net/source/2399818

          4、 【eoeAndroid特刊】第五期 Android widget http://download.csdn.net/source/2399825

          5、 【eoeAndroid特刊】第六期 數據通信-成為Android數據流大師 http://download.csdn.net/source/2399830

          6、 【eoeAndroid特刊】第七期 NDK http://download.csdn.net/source/2400070

          7、 【eoeAndroid特刊】第八期 數據存儲操作 http://download.csdn.net/source/2399834

          8、 【eoeAndroid特刊】第八期+ 開發技巧 http://download.csdn.net/source/2399841

          9、 【eoeAndroid特刊】第九期 http://download.csdn.net/source/2400075

          10、 android開發教程 http://download.csdn.net/source/2400084

          11、 android基礎教程 http://download.csdn.net/source/2425923

          12、 Google手機開發《Android.A.Programmers.Guide》 http://download.csdn.net/source/2425883

          13、 Professional.Android.Application.Development.2009 http://download.csdn.net/source/2425899

          14、 Apress.Android.Essentials.Jul.2008 http://download.csdn.net/source/2425905

          15、 android開發入門與實踐 http://download.csdn.net/source/2425931

          16、 android開發入門與實踐源代碼 http://download.csdn.net/source/2425935

          17、 Android應用框架原理和程序設計 http://download.csdn.net/source/2399790

          18、 android JNI 簡介與實現 http://download.csdn.net/source/2425864

          19、 19、Android開發指南中文版 添加此鏈接:http://download.csdn.net/source/2426094
          20、android ADT-0.9.6 http://download.csdn.net/source/2399804

          21、 android-sdk2.2 http://developer.android.com/sdk/android-2.2.html 


          另外附視頻資源網址
          《Google Android開發入門與實戰隨書視頻》WMV[壓縮包] http://www.verycd.com/topics/2779532/
          上傳最新資源:android中文幫助文檔 http://download.csdn.net/source/2447570
          posted @ 2010-06-11 13:29 小菜毛毛 閱讀(783) | 評論 (0)編輯 收藏

          http://homelink.javaeye.com/blog/293328#comments
          參考文檔 http://www.ibm.com/developerworks/cn/web/wa-lo-comet/

                  comet是HTTP長連接,就是在HTTP發送請求時,服務器不立刻發送響應信息給客戶端, 而是保持著連接,等待一定情況發生后才把數據發送回去給客戶端。所以用comet可以實現服務器端的數據實時地發送給客戶端。

                  本文主要是用java和js來簡單地實現comet,最后附上源碼和使用例子。

                  在客戶端用XMLRequest發送請求到服務器,在服務器端用一個servlet來接收XMLRequest的請求,當接收到請 求時,并不立刻響應客戶端,而是把該servlet線程阻塞,等到一定事件發生后,再響應客戶端。當客戶端接收到服務端的響應后,調用自定義的回調函數來 處理服務器發送回來的數據,處理完成后,再發送一個XMLRequest請求到服務端,這樣循環下去,就可以實現數據的實時更新,又不必要在客戶端不斷地 輪循(polling)。

                   利用該comet的實現(以后簡稱為keeper)時,只要在客戶端注冊事件和寫一個處理返回數據的回調函數,然后在服務端實現 keeper中的EventListener接口,調用Controller.action(eventListener,eventType)就可以 了。

                  keeper分成兩大部分,第一部分為客戶端的javascript,第二部分是服務端的servlet和事件處理。

                  一.客戶端

                  建立一個XMLRequest對象池,每發送一次請求,從對象池中取一個XMLRequest對象,如果沒有可用的對象,則創建一 個,把它加入到對象池中。這部分的代碼來自于網絡。

                  為了使用方便,再添加一些方法,用來注冊事件。這樣只要調用注冊函數來注冊事件,并且把回調函數傳給注冊事件函數就行了,處理數據 的事情,交給回調函數,并由用戶來實現。

                  keeper為了方便使用,把客戶端的javascript代碼集成在servlet中,當配置好keeper的servlet, 啟動HTTP服務器時,keeper會根據用戶的配置,在相應的目錄下生成客戶端的javascript代碼。

              二.服務端

                  服務端的servlet初始化時,根據配置來生成相應的客戶端javascript代碼。

                  servlet的入口由keeper.servlet.Keeper.java中的doGet進入。在Keeper的doGet 中,從請求中獲取用戶注冊事件的名稱(字符串類型),然后根據事件的名稱,構造一個事件(Event類型),再把它注冊到NameRegister中,注 冊完成后,該servlet線程調用wait(),把自已停止。等待該servlet線程被喚醒后,從Event中調用事件的EventListener 接口的process(request,response)來處理客戶端的請求。

          1.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          2.         String eventName = request.getParameter("event");
          3.         NameRegister reg = NameRegister.getInstance();
          4.         Event event = null;
          5.         try {
          6.             event = reg.getEvent(eventName);
          7.             if(event == null) {
          8.                 event = new Event(eventName,this);
          9.                 reg.registeEvent(eventName, event);
          10.             }
          11.             if(event.getServlet() == null) {
          12.                 event.setServlet(this);
          13.             }
          14.             
          15.         } catch (RegistException e1) {
          16.             e1.printStackTrace();
          17.         }
          18.         synchronized(this) {
          19.             while(!event.isProcess()) {
          20.                 try {
          21.                     wait();
          22.                 } catch (InterruptedException e) {
          23.                     e.printStackTrace();
          24.                 }
          25.             }
          26.         }
          27.         EventListener listener = event.getListener();
          28.         if(listener != null) {
          29.            listener.process(request,response);
          30.         }   
          31.     }

                  在服務端處理事件時,調用了keeper.control.Controller中的靜態方法 action(EventListener listener,String eventName)來處理。如下所示。

          1.     public static boolean action(EventListener listener,String eventName){
          2.         NameRegister reg = NameRegister.getInstance();
          3.         HttpServlet servlet = null;
          4.         Event e = null;
          5.         try {
          6.             e = reg.getEvent(eventName,true);
          7.             if(e == null) {
          8.                 return false;
          9.             }
          10.             e.setListener(listener);
          11.             servlet = e.getServlet();
          12.             e.setProcess(true);
          13.             synchronized(servlet) {
          14.                 servlet.notifyAll();
          15.             }
          16.         } catch (RegistException ex) {
          17.             ex.printStackTrace();
          18.             }
          19.         if(servlet != null && e != null) {
          20.             e = null;
          21.             return true;
          22.         } else {
          23.             return false;
          24.         }
          25.     }

          下面開始用keeper來寫一個簡單的網頁聊天程序和基于服務端的時間。

              1.客戶端設置

                  注冊兩個事件,一個用于是時間事件,一個是消息事件。同時還要寫兩個回調函數,用于處理服務 端返回的時間和聊天消息。如下所于:

          1. <script type="text/javascript">
          2.     Keeper.addListener('timer',showTime);//注冊時間事件
          3.     function showTime(obj){ //時間處理回調函數
          4.         var sp = document.getElementById("dateTime");
          5.         if(sp){
          6.             sp.innerHTML = obj.responseText;
          7.         }
          8.     }
          9.     function startOrStop(obj){
          10.         var btn = document.getElementById("controlBtn")
          11.         btn.value=obj.responseText;
          12.     }
          13.     Keeper.addListener('msg',showMsg,"GBK");//注冊消息事 件,最后一個參數是
          14. //字符串編碼
          15.     function showMsg(obj){//處理消息的回調函數
          16.         var msg = document.getElementById("msg");
          17.         if(msg){
          18.             
          19.                 msg.value = obj.responseText+""n"+msg.value;
          20.             
          21.         }
          22.     }
          23.     function sendMsg() {
          24.         var msg = document.getElementById("sendMsg");
          25.         if(msg){
          26.             var d = "msg="+msg.value;
          27.             sendReq('POST','./demo',d,startOrStop);
          28.             msg.value = "";
          29.         }
          30.     }
          31.     
          32. </script>

              2.配置服務端

              服務端的配置在 web.xml文件中,如下所示

          1.   <servlet>
          2.     <servlet-name>keeper</servlet-name>
          3.     <servlet-class>keeper.servlet.Keeper</servlet-class>
          4.     <init-param>
          5. <!--可選項,設置生成客戶端的JavaScript路徑和名字,默認置為 /keeper.js-->
          6.       <param-name>ScriptName</param-name>
          7.       <param-value>/keeperScript.js</param-value>
          8.     </init-param>
          9. <!--這個一定要設置,否則不能生成客戶端代碼-->
          10.     <load-on-startup>1</load-on-startup>
          11.   </servlet>
          12.   <servlet-mapping>
          13.     <servlet-name>keeper</servlet-name>
          14.     <url-pattern>/keeper</url-pattern>
          15.   </servlet-mapping>

                  用<script type="text/javascript" src="./keeperScript.js"></script>在頁面包含JavaScript時,這里的src一定要和上面配 置的一至。上面的設置除了<init-param></init-param>為可選的設置外,其他的都是必要的,而且不能改 變。

                  3.編寫事件處理代碼,消息的處理代碼如下:
          1.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          2.         System.out.println("Post..");
          3.         String msg = request.getParameter("msg");
          4.         Controller.action(new SendMsg(msg),"msg");
          5.     }
          1. class SendMsg implements EventListener{
          2.     private String msg;
          3.     public SendMsg(String msg) {
          4.         this.msg = msg;
          5.     }
          6.     @Override
          7.     public void process(HttpServletRequest request, HttpServletResponse response) {
          8.         response.setCharacterEncoding("UTF-8");
          9.         PrintWriter out = null;
          10.         try {
          11.             out = response.getWriter();
          12.             if(msg!=null){
          13.                 out.write(msg);
          14.             }
          15.         } catch (IOException e) {   
          16.             e.printStackTrace();
          17.         }
          18.         finally{
          19.             if(out != null) {
          20.                 out.close();
          21.             }
          22.         }       
          23.     }
          24. }

           到這時,一個基本的keeper應用就完成了。其它部分請參考附件中的例子源碼。


          posted @ 2010-06-02 15:31 小菜毛毛 閱讀(3057) | 評論 (1)編輯 收藏

          互聯網服務的特點就是面向海量級的用戶,面向海量級的用戶如何提供穩定的服務呢?這里,對這幾年的一些經驗積累和平時接觸的一些理念做一個總結。
                一、原則
                1.Web服務的CAP原理
                CAP指的是三個要素:一致性(Consistency)、可用性(Availability)、分區容忍性(Partition tolerance)。CAP原理指的是這三個要素最多只能同時實現兩點,不可能三者兼顧,對于海量級服務,一般這是一條常記心中的基準準則。
                如下是《Web服務的CAP 》關于CAP的定義:
          • 一致性:可以參考數據庫的一致性。每次信息的讀取都需要反映最新更新后的數據。
          • 可用性:高可用性意味著每一次請求都可以成功完成并受到響應數據
          • 分區寬容度:這個是容錯機制的要求。一個服務需要在局部出錯的情況下,沒有出錯的那部分被復制的數據分區仍然可以支持部分服務的操作,可以簡單的 理解為可以很容易的在線增減機器以達到更高的擴展性,即所謂的橫向擴展能力。

                面向海量級的分布式服務設計,基本上分區容忍性(Partition tolerance)是第一要素,因此根據業務的情況,我們需要在一致性(Consistency)和可用性(Availability)之間做取舍。對 于一些業務,譬如支付寶或財付通,一致性會是第一考慮要素,即使是延遲的不一致也是不可接受的,這種時候只能犧牲可用性以保證一致性。對于一些應用,譬如 淘寶或拍拍交易中的評價信息,一般用戶是可以接受延遲的一致性,這種時候可以優先考慮可用性,而用最終一致性來保證數據一致,譬如通過某種對帳機制。對于 一些應用,甚至一致性都并非要求,只需要保證差不多一致性即可,譬如Q-zone中的農場游戲中的偷菜。
                根據我們應用的業務需求,選擇合適的一致性級別,以更好地保證系統的分區容忍性和可用性。
                2.柔性可用
                面向海量級的分布式服務設計,我們要意識到,一切都是不可靠的,在不可靠的環境的環境中構建可靠的應用,其中最重要的一點就是保持系統的柔性。
                1)不可靠的環境
                我們可能已經見慣一個遠程服務提供不了服務了,運行一段時間后WebServer突然不響應了,數據庫隨著負載的不斷增加再放上一條SQL語句就會垮掉。 但是,硬盤壞掉、電源斷掉、光纖中斷,聽起來似乎多么不可思議,然而,當一個海量服務需要成千上萬臺服務器、需要部署全國各地的數十個數據中心、需要橫跨 電信網通教育網三大網絡的時候,一切聽起來不可思議的事情會變成常態。一切都是不可靠的,唯一可靠的就是不可靠本身。
                2)劃分服務級別
                我們應該意識到,在這種不可靠的環境中提供完美的服務,本身就是一個神話,即使不是說完全不可能,但至少是代價高昂的,因此,當某些問題發生(環境變地不 可靠的時候),我們必須做出取舍,選擇為用戶提供用戶最關心的服務,這種服務雖然聽起來是有損的(至少是不完美的),但卻能在一定程度上滿足用戶大部分的 需求。譬如,當網絡帶寬無法為用戶提供最好的體驗而擴容又不是短期可以達到的時候,選擇降低一些非重要服務的體驗是一個比較好的選擇。
                在面向海量互聯網的設計當中,對服務進行分級,當系統變地不可靠的時候,優先提供重要優先級的服務。
                3)盡快響應
                互聯網用戶的耐心是非常有限的,如果一個頁面需要3秒以上才能看到,也許大部分用戶的第一選擇就是關掉瀏覽器。在構建柔性可用的互聯網服務的時候,響應時 間大部分情況下都是需要最優先考慮。還是一句話,環境是不可靠的,當我們無法盡快從遠程服務獲得數據、當數據庫已經慢如蝸牛,也許當后臺還在吭哧吭哧干活 的時候,用戶老早已經關閉了頁面,處理返回的數據也只是在浪費表情,面向互聯網用戶,響應就是生命。
                 二、策略
                 如何讓我們的應用提供更高質量的服務呢,這里是一些在日常開發使用到或者觀察到的一些策略的總結:

                 1.數據sharding
                海量服務相應也意味著海量的用戶和海量的用戶數據,大家都知道,即使是再強大的數據庫、再強大服務器,在單表上億規模的數據足夠讓一條簡單的SQL語句慢 如蝸牛(甚至于在百萬、千萬級別上,如果沒有采取合適的策略,都無法滿足服務要求),一般處理這種千萬上億級數據的大家基本上都會想到的就是數據 sharding,將數據切割成多個數據集,分散到多個數據庫的多個表中(譬如將用戶數據按用戶ID切割成4個數據庫每個數據庫100個表共400個 表),由于每個表數據足夠小可以讓我們的SQL語句快速地執行。而至于如何切割,實際上跟具體的業務策略有關系。
                 當然,我們要認識到,這種數據sharding并非全無代價的,這也意味著我們需要做出一些折中,譬如可能很難進行跨表數據集的查詢、聯表和排序也變地非 常困難、同時數據庫client程序編寫也會變地更加復雜、保證數據一致性在某些情況下會變地困難重重。sharding并非萬能藥,選擇是否 sharding、如何sharding、為sharding如何換用一個近似的業務描述方式,這是業務設計需要仔細考慮的問題。
                 2.Cache
                 經驗會告訴我們,基本上大部分系統的瓶頸會集中在IO/數據庫上,常識也告訴我們,網絡和內存的速度比IO/數據庫會提升甚至不止一個數量級。面向海量服 務,Cache基本上是一個必選項,分布式Cache更是一個不二選擇,根據我們的需要,我們可以選擇memcached(非持久化)、 memcachedb/Tokyo Tyrant(持久化),甚至構建自己的cache平臺。
                 在使用Cache上,下面是需要仔細考慮的點:

          • 選擇合適的Cache分布算法,基本上我們會發現使用取模方式決定Cache位置是不可靠的,因為壞節點的摘除或者節點擴量會讓我們的Cache 命中率在短時間內下降到冰點,甚至會導致系統在短期內的負載迅速增長甚至于崩潰,選擇一種合適的分布算法非常重要,譬如穩定的一致性哈希
          • Cache管理:為每個應用配置獨立的Cache通常不是一個好主意,我們可以選擇在大量的機器上,只要有空閑內存,則運行Cache實例,將 Cache實例分成多個組,每個組就是一個完整的Cache池,而多個應用共享一個Cache池
          • 合理的序列化格式:使用緊湊的序列化方案存儲Cache數據,盡量少存儲冗余數據,一方面可以最大能力地榨取Cache的存儲利用率,另一方面, 可以更方便地進行容量預估。此外,不可避免地,隨著業務的升級,存儲的數據的格式有可能會變更,序列化也需要注意向上兼容的問題,讓新格式的客戶端能夠支 持舊的數據格式。
          • 容量估算:在開始運行之前,先為自己的應用可能使用到的容量做一個容量預估,以合理地分配合適的Cache池,同時為可能的容量擴充提供參考。
          • 容量監控:Cache命中率怎么樣,Cache的存儲飽和度怎么樣,Client的Socket連接數等等,對這些數據的采集和監控,將為業務的 調整和容量的擴充提供了數據支持。
          • 選擇在哪層上進行Cache,譬如數據層Cache、應用層Cache和Web層Cache,越靠近數據,Cache的通用性越高,越容易保持 Cache數據的一致性,但相應的處理流程也越長,而越靠近用戶,Cache的通用性越差,越難保證Cache數據的一致性,但是響應也越快,根據業務的 特點,選擇合適的Cache層是非常重要的。一般而言,我們會選擇將粗粒度、極少變更、數據對用戶不敏感(即可以一定程度上接受誤差)、并且非針對用戶級 的數據,在最靠近用戶的層面上Cache,譬如圖片Cache、TOP X等數據;而將一些細粒度、變更相對頻繁、用戶相對敏感的數據或者是針對用戶級的數據放在靠近數據的一段,譬如用戶的Profile、關系鏈等。

                 3.服務集群
                 面向海量服務,系統的橫向擴展基本上是第一要素,在我的經驗和經歷中,服務集群需要考慮如下因素:

          • 分層:合理地對系統進行分層,將系統資源要求不同的部分進行合理地邏輯/物理分層,一般對于簡單業務,Client層、WebServer層和 DB層已經足夠,對于更復雜業務,可能要切分成Client層、WebServer層、業務層、數據訪問層(業務層和數據訪問層一般傾向于在物理上處于同 一層)、數據存儲層(DB),太多的分層會導致處理流程變長,但相應系統地靈活性和部署性會更強。
          • 功能細粒度化:將功能進行細粒度的劃分,并使用獨立的進程部署,一方面能更有利于錯誤的隔離,另一方面在功能變更的時候避免一個功能對其他功能產 生影響
          • 按數據集部署:如果每一層都允許對下一層所有的服務接口進行訪問,將存在幾個嚴重的缺陷,一是隨著部署服務的增長,會發現下一層必須允許數量非常 龐大的Socket連接進來,二是我們可能必須把不同的服務部署在不同的數據中心(DC)的不同機房上,即便是上G的光纖專線,機房間的穿梭流量也會變地 不可接受,三是每個服務節點都是全數據容量接入,并不利于做一些有效的內部優化機制,四是只能采用代碼級控制的灰度發布和部署。當部署規模達到一定數量級 的時候,按數據集橫向切割成多組服務集合,每組服務集合只為特定的數據集服務,在部署上,每組服務集合可以部署在獨立的相同的數據中心(DC)上。
          • 無狀態:狀態將為系統的橫向擴容帶來無窮盡的煩惱。對于狀態信息比少的情況,可以選擇將全部狀態信息放在請求發器端,對于狀態信息比較多的情況, 可以考慮維持一個統一的Session中心。
          • 選擇合適的負載均衡器和負載均衡策略:譬如在L4上負載均衡的LVS、L7上負載均衡的Nginx、甚至是專用的負載均衡硬件F5(L4),對于 在L7上工作的負載均衡器,選擇合適的負載均衡策略也非常重要,一般讓用戶總是負載均衡到同一臺后端Server是一個很好的方式

                4.灰度發布
                當系統的用戶增長到一定的規模,一個小小功能的發布也會產生非常大的影響,這個時候,將功能先對一小部分用戶開放,并慢慢擴展到全量用戶是一個穩妥的做 法,使用灰度化的發布將避免功能的BUG產生大面積的錯誤。如下是一些常見的灰度控制策略:

          • 白名單控制:只有白名單上的用戶才允許訪問,一般用于全新功能Alpha階段,只向被邀請的用戶開放功能
          • 準入門檻控制:常見的譬如gmail出來之初的邀請碼、QQ農場開始階段的X級的黃鉆準入,同樣一般用于新功能的Beta階段,慢慢通過一步一步 降低門檻,避免在開始之處由于系統可能潛在的問題或者容量無法支撐導致整個系統的崩潰。
          • 按數據集開放:一般常用于成熟的功能的新功能開發,避免新功能的錯誤產生大面積的影響

                5.設計自己的通信協議:二進制協議、向上/下兼容
                隨著系統的穩定運行訪問量的上漲,慢慢會發現,一些看起來工作良好的協議性能變地不可接受,譬如基于xml的協議xml-rpc,將會發現xml解析和包 體的增大變地不可接受,即便是接近于二進制的hessian協議,多出來的字段描述信息(按我的理解,hessian協議是類似于map結構的,包含字段 的名稱信息)和基于文本的http頭將會使協議效率變地低下。也許,在開始合適的時候而不是到最后不得已的時候,去設計一個良好的基于二進制的高效的內部 通信協議是一個好的方式。按我的經驗,設計自己的通信協議需要注意如下幾點:

          • 協議緊湊性,否則早晚你會為你浪費的空間痛心疾首
          • 協議擴展性,早晚會發現舊的協議格式不能適應新的業務需求,而在早期預留變地非常地重要,基本上,參見一些常用的規范,魔術數(對于無效果的請求 可以迅速丟棄)、協議版本信息、協議頭、協議Body、每個部分(包括結構體信息中的對象)的長度這些信息是不能省的
          • 向下兼容和向上兼容:但功能被大規模地調用的時候,發布一個新的版本,讓所有的client同時升級基本上是不可接受的,因此在設計之處就需要考 慮好兼容性的問題

                6.設計自己的Application Server
                事情進行到需要自己設計通信協議,自己構建Application Server也變地順理成章,下面是在自己開發Application Server的時候需要處理的常見的問題:

          • 過載保護:當系統的某個部件出現問題的時候,最常見的情況是整個系統的負載出現爆炸性的增長而導致雪崩效應,在設計application server的時候,必須注意進行系統的過載保護,當請求可以預期無法處理的時候(譬如排隊滿載或者排隊時間過長),丟棄是一個明智的選擇,TCP的 backlog參數是一個典型的范例。
          • 頻率控制:即便是同一系統中的其他應用在調用,一個糟糕的程序可能會將服務的所有資源占完,因此,應用端必須對此做防范措施,頻率控制是其中比較 重要的一個
          • 異步化/無響應返回:對于一些業務,只需要保證請求會被處理即可,客戶端并不關心什么時候處理完,只要最終保證處理就行,甚至最終沒有處理也不是 很嚴重的事情,譬如郵件,對于這種應用,應快速響應避免占著寶貴的連接資源,而將請求進入異步處理隊列慢慢處理。
          • 自我監控:Application Server本身應該具備自我監控的功能,譬如性能數據采集、為外部提供內部狀態的查詢(譬如排隊情況、處理線程、等待線程)等
          • 預警:譬如當處理變慢、排隊太多、發生請求丟棄的情況、并發請求太多的時候,Application Server應該具備預警的能力以快速地對問題進行處理
          • 模塊化、模塊間松耦合、機制和策略分離:如果不想一下子面對所有的復雜性或不希望在修改小部分而不得不對所有的測試進行回歸的話,模塊化是一個很 好的選擇,將問題進行模塊切割,每個模塊保持合理的復雜度,譬如對于這里的Application Server,可以切分成請求接收/管理/響應、協議解析、業務處理、數據采集、監控和預警等等模塊。這里同時要注意塊間使用松耦合的方式交互,譬如,請 求接收和業務處理之間則可以使用阻塞隊列通信的方式降低耦合。另外還需要注意的是機制和策略的分離,譬如協議可能會變更、性能采集和告警的方式可能會變化 等等,事先的機制和策略分離,策略變更的處理將變地更加簡單。

                 7.Client
                很多應用會作為服務的Client,去調用其他的服務,如下是在做為Client應該注意的一些問題:

          • 服務不可靠:作為Client永遠要記住的一點就是,遠程服務永遠是不可靠的,因此作為Client自己要注意做自我保護,當遠程服務如果無法訪 問時,做折中處理
          • 超時保護:還是上面所說的,遠程服務永遠都是不可靠的,永遠也無法預測到遠程什么時候會響應,甚至可能不會響應(譬如遠程主機宕機),請求方要做 好超時保護,譬如對于主機不可達的情況,在linux環境下,有時會讓客戶端等上幾分鐘TCP層才會最終告訴你服務不可到達。
          • 并發/異步:為了提速響應,對于很多可以并行獲取的數據,我們總是應該并行地去獲取,對于一些我們無法控制的同步接口——譬如讀數據庫或同步讀 cache——雖然不是很完美,但多線程并行去獲取是一個可用的選擇,而對于服務端都是使用自構建的Application Server,使用異步Client接口至關重要,將請求全部發送出去,使用異步IO設置超時等待返回即可,甚至于更進一步異步anywhere,在將 client與application server整合到一塊的時候,請求發送出去之后立即返回,將線程/進程資源歸還,而在請求響應回來符合條件的時候,觸發回調做后續處理。

                8.監控和預警
                基本上我們會見慣了各種網絡設備或服務器的監控,譬如網絡流量、IO、CPU、內存等監控數據,然而除了這些總體的運行數據,應用的細粒度化的數據也需要 被監控,服務的訪問壓力怎么樣、處理速度怎么樣、性能瓶頸在哪里、帶寬主要是被什么應用占、Java虛擬機的CPU占用情況怎么樣、各內存區的內存占用情 況如何,這些數據將有利于我們更好的了解系統的運行情況,并對系統的優化和擴容提供數據指導。

                除了應用總體監控,特定業務的監控也是一個可選項,譬如定時檢查每個業務的每個具體功能點(url)訪問是否正常、訪問速度如何、頁面訪問速度如何(用戶 角度,包括服務響應時間、頁面渲染時間等,即網頁測速)、每個頁面的PV、每個頁面(特別是圖片)每天占用的總帶寬等等。這些數據將為系統預警和優化提供 數據上的支持,例如對于圖片,如果我們知道哪些圖片占用的帶寬非常大(不一定是圖片本身比較大,而可能是訪問比較大),則一個小小的優化會節省大量的網絡 帶寬開銷,當然,這些事情對于小規模的訪問是沒有意義的,網絡帶寬開銷節省的成本可能都沒有人力成本高。
                除了監控,有效的預警機制也是必不可少,應用是否在很好地提供服務、響應時間是否能夠達到要求、系統容量是否達到一個閥值。有效的預警機制將讓我們盡快地 對問題進行處理。
                9.配置中心化
                當系統錯誤的時候,我們如何盡快地恢復呢,當新增服務節點的時候,如何盡快地讓真個系統感知到呢?當系統膨脹之后,如果每次摘除服務節點或者新增節點都需 要修改每臺應用配置,那么配置和系統的維護將變地越來越困難。
               配置中心化是一個很好的處理這個問題的方案,將所有配置進行統一地存儲,而當發生變更的時候(摘除問題節點或者擴量增加服務節點或新增服務),使用一些通 知機制讓各應用刷新配置。甚至于,我們可以自動地檢測出問題節點并進行智能化的切換。
                三、最后
                構建面向海量用戶的服務,可以說是困難重重挑戰重重,一些原則和前人的設計思路可以讓我們獲得一些幫助,但是更大的挑戰會來源于細節部分,按我們技術老大 的說法,原則和思路只要看幾本書是個技術人員都會,但決定一個系統架構師能力的,往往卻是對細節的處理能力。因此,在掌握原則和前人的設計思路的基礎上, 更深入地挖掘技術的細節,才是面向海量用戶的服務的制勝之道。

          posted @ 2010-05-28 18:20 小菜毛毛 閱讀(613) | 評論 (1)編輯 收藏

          剛換了項目組,接觸到了htmlunit,就把官方示例翻譯一下,作為入門:
          先下載依賴的相關JAR包:http://sourceforge.net/projects/htmlunit/files/
          示例1:獲取javaeye網站的title
          import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
          import com.gargoylesoftware.htmlunit.WebClient;
          import com.gargoylesoftware.htmlunit.html.HtmlPage;

              
          public static void getHomeTile() throws FailingHttpStatusCodeException, MalformedURLException, IOException{
                  
          final WebClient webClient = new WebClient();
                  
          final HtmlPage htmlPage = webClient.getPage("http://www.javaeye.com");
                  System.out.println(htmlPage.getTitleText());
                  System.out.println(htmlPage.getTextContent());

              }
          可以看見打印出:JavaEye Java編程 Spring框架 AJAX技術 Agile敏捷軟件開發 ruby on rails實踐 - JavaEye做最棒的軟件開發交流社區
          和javaeye首頁的title屬性一樣
          示例二:獲取一個特定的瀏覽器版本
          public void homePage_Firefox() throws Exception {
              
          final WebClient webClient = new WebClient(BrowserVersion.FIREFOX_2);
              
          final HtmlPage page = webClient.getPage("http://htmlunit.sourceforge.net");
              assertEquals(
          "HtmlUnit - Welcome to HtmlUnit", page.getTitleText());
          }
          示例三:獲取特定ID的DIV或者錨鏈接
          public void getElements() throws Exception {
              
          final WebClient webClient = new WebClient();
              
          final HtmlPage page = webClient.getPage("http://some_url");
              
          final HtmlDivision div = page.getHtmlElementById("some_div_id");
              
          final HtmlAnchor anchor = page.getAnchorByName("anchor_name");
          }
          實例四:模擬頁面提交一個form
          public void submittingForm() throws Exception {
              
          final WebClient webClient = new WebClient();

              
          // Get the first page
              final HtmlPage page1 = webClient.getPage("http://some_url");

              
          // Get the form that we are dealing with and within that form, 
              
          // find the submit button and the field that we want to change.
              final HtmlForm form = page1.getFormByName("myform");

              
          final HtmlSubmitInput button = form.getInputByName("submitbutton");
              
          final HtmlTextInput textField = form.getInputByName("userid");

              
          // Change the value of the text field
              textField.setValueAttribute("root");

              
          // Now submit the form by clicking the button and get back the second page.
              final HtmlPage page2 = button.click();
          }


          posted @ 2010-05-28 15:32 小菜毛毛 閱讀(16697) | 評論 (2)編輯 收藏

          Eclipse 快捷鍵(1)
          Ctrl+1 快速修復(最經典的快捷鍵,就不用多說了)
          Ctrl+D: 刪除當前行  
          Ctrl+Alt+↓ 復制當前行到下一行(復制增加)
          Ctrl+Alt+↑ 復制當前行到上一行(復制增加)
          Alt+↓ 當前行和下面一行交互位置(特別實用,可以省去先剪切,再粘貼了)
          Alt+↑ 當前行和上面一行交互位置(同上)
          Alt+← 前一個編輯的頁面
          Alt+→ 下一個編輯的頁面(當然是針對上面那條來說了)
          Alt+Enter 顯示當前選擇資源(工程,or 文件 or文件)的屬性
          Shift+Enter 在當前行的下一行插入空行(這時鼠標可以在當前行的任一位置,不一定是最后)
          Shift+Ctrl+Enter 在當前行插入空行(原理同上條)
          Ctrl+Q 定位到最后編輯的地方
          Ctrl+L 定位在某行 (對于程序超過100的人就有福音了)
          Ctrl+M 最大化當前的Edit或View (再按則反之)
          Ctrl+/ 注釋當前行,再按則取消注釋
          Ctrl+O 快速顯示 OutLine
          Ctrl+T 快速顯示當前類的繼承結構
          Ctrl+W 關閉當前Editer
          Ctrl+K 參照選中的Word快速定位到下一個
          Ctrl+E 快速顯示當前Editer的下拉列表(如果當前頁面沒有顯示的用黑體表示)
          Ctrl+/(小鍵盤) 折疊當前類中的所有代碼
          Ctrl+×(小鍵盤) 展開當前類中的所有代碼
          Ctrl+Space 代碼助手完成一些代碼的插入(但一般和輸入法有沖突,可以修改輸入法的熱鍵,也可以暫用Alt+/來代替)
          Ctrl+Shift+E 顯示管理當前打開的所有的View的管理器(可以選擇關閉,激活等操作)
          Ctrl+J 正向增量查找(按下Ctrl+J后,你所輸入的每個字母編輯器都提供快速匹配定位到某個單詞,如果沒有,則在stutes line中顯示沒有找到了,查一個單詞時,特別實用,這個功能Idea兩年前就有了)
          Ctrl+Shift+J 反向增量查找(和上條相同,只不過是從后往前查)
          Ctrl+Shift+F4 關閉所有打開的Editer
          Ctrl+Shift+X 把當前選中的文本全部變味小寫
          Ctrl+Shift+Y 把當前選中的文本全部變為小寫
          Ctrl+Shift+F 格式化當前代碼
          Ctrl+Shift+P 定位到對于的匹配符(譬如{}) (從前面定位后面時,光標要在匹配符里面,后面到前面,則反之)
          下面的快捷鍵是重構里面常用的,本人就自己喜歡且常用的整理一下(注:一般重構的快捷鍵都是Alt+Shift開頭的了)
          Alt+Shift+R 重命名 (是我自己最愛用的一個了,尤其是變量和類的Rename,比手工方法能節省很多勞動力)
          Alt+Shift+M 抽取方法 (這是重構里面最常用的方法之一了,尤其是對一大堆泥團代碼有用)
          Alt+Shift+C 修改函數結構(比較實用,有N個函數調用了這個方法,修改一次搞定)
          Alt+Shift+L 抽取本地變量( 可以直接把一些魔法數字和字符串抽取成一個變量,尤其是多處調用的時候)
          Alt+Shift+F 把Class中的local變量變為field變量 (比較實用的功能)
          Alt+Shift+I 合并變量(可能這樣說有點不妥Inline)
          Alt+Shift+V 移動函數和變量(不怎么常用)
          Alt+Shift+Z 重構的后悔藥(Undo)
          Ctrl + Z 返回到修改前的狀態(編寫代碼時的后悔藥)
          Ctrl + Y 與上面的操作相反 (即剛后悔完又后悔)
          Shift + / 自動導入類包 (在你寫好的類名的右邊用這個可導入包)
          Ctrl + Shif + / 自動注釋代碼
          Ctrl + Shif + "自動取消已經注釋的代碼
          Ctrl + Shif +O 自動引導類包

          MyEclipse 快捷鍵(2)[b][/b][color=#FF0000][/color]
          (1)Ctrl+M切換窗口的大小
          (2)Ctrl+Q跳到最后一次的編輯處
          (3)F2當鼠標放在一個標記處出現Tooltip時候按F2則把鼠標移開時Tooltip還會顯示即Show Tooltip Description。
            F3跳到聲明或定義的地方。
            F5單步調試進入函數內部。
            F6單步調試不進入函數內部,如果裝了金山詞霸2006則要把“取詞開關”的快捷鍵改成其他的。
            F7由函數內部返回到調用處。
            F8一直執行到下一個斷點。
          (4)Ctrl+Pg~對于XML文件是切換代碼和圖示窗口
          (5)Ctrl+Alt+I看Java文件中變量的相關信息
          (6)Ctrl+PgUp對于代碼窗口是打開“Show List”下拉框,在此下拉框里顯示有最近曾打開的文件
          (7)Ctrl+/ 在代碼窗口中是這種//~注釋。
            Ctrl+Shift+/ 在代碼窗口中是這種/*~*/注釋,在JSP文件窗口中是<!--~-->。
          (8)Alt+Shift+O(或點擊工具欄中的Toggle Mark Occurrences按鈕) 當點擊某個標記時可使本頁面中其他地方的此標記黃色凸顯,并且窗口的右邊框會出現白色的方塊,點擊此方塊會跳到此標記處。
          (9)右擊窗口的左邊框即加斷點的地方選Show Line Numbers可以加行號。
          (10)Ctrl+I格式化激活的元素Format Active Elements。
            Ctrl+Shift+F格式化文件Format Document。
          (11)Ctrl+S保存當前文件。
            Ctrl+Shift+S保存所有未保存的文件。
          (12)Ctrl+Shift+M(先把光標放在需導入包的類名上) 作用是加Import語句。
            Ctrl+Shift+O作用是缺少的Import語句被加入,多余的Import語句被刪除。
          (13)Ctrl+Space提示鍵入內容即Content Assist,此時要將輸入法中Chinese(Simplified)IME-Ime/Nonlme Toggle的快捷鍵(用于切換英文和其他文字)改成其他的。
            Ctrl+Shift+Space提示信息即Context Information。
          (14)雙擊窗口的左邊框可以加斷點。
          (15)Ctrl+D刪除當前行。
          posted @ 2010-05-25 16:35 小菜毛毛 閱讀(333) | 評論 (0)編輯 收藏

          僅列出標題
          共17頁: First 上一頁 2 3 4 5 6 7 8 9 10 下一頁 Last 
          主站蜘蛛池模板: 永平县| 黑龙江省| 沭阳县| 九江县| 蕉岭县| 云南省| 丰镇市| 滨州市| 依兰县| 皋兰县| 巴青县| 八宿县| 曲靖市| 宜良县| 九寨沟县| 广汉市| 渝北区| 滕州市| 巩义市| 沙雅县| 内乡县| 酒泉市| 象山县| 丰镇市| 鹰潭市| 普兰县| 明溪县| 乌兰浩特市| 龙泉市| 南澳县| 科技| 明光市| 大悟县| 青阳县| 兰州市| 华宁县| 亚东县| 秭归县| 理塘县| 辽中县| 张家界市|