聶永的博客

          記錄工作/學習的點點滴滴。

          Servlet 3.0筆記之Redis操作示范Retwis JAVA版

          最近學習Redis,一個目前風頭正勁的KEY-VALUE NOSQL,支持LIST、SET、MAP等格式存儲,某些操作竟然是原子級別,比如incr命令,相當驚艷。官方提供的一個Retwis(simple clone of Twitter)示范,證明了完全可以使用redis替代SQL數據庫存儲數據,真是一大開創性創新,挑戰人們的傳統思維模式,為之驚嘆!

          不過那是一個PHP版本,我下載到本地PHP但無法部署成功;還好有一個Retwis-RB示范,還提供一個在線版,試用之后,再下載源代碼,RUBY + Sinatra MVC結構,看的流口水,代碼那般簡潔。

          為體驗Redis,同時也為對比Ruby代碼,因此誕生了Retwis-JAVA,Retwis-RB的山寨版,因此其邏輯和HTML資源一致,再次表示對原作者Dan Lucraft的感謝。

          Retwis-JAVA,基于Servlet 3.0 + UrlRewrite + Freemarker + Jedis。示范運行在Tomcat 7中,redis為最新的2.22版本,jedis為redis的java客戶端操作框架。在Servlet 3.0規范中,對Url映射處理依然沒有進步,因此只能使用UrlRewrite框架讓部分url看起來友好一些。另外,項目沒有使用IOC容器框架,沒有使用MVC框架,代碼量稍微多些,代碼相對耦合一些。若使用Struts2 + Spring 代碼量會少一些。

          對涉及到的redis存儲結構,大致如下:


          涉及到的兩個對象很簡單:


          序列化后以二進制數據保存到redis中:

              private byte[] object2Bytes(V value) {
                  
          if (value == null)
                      
          return null;

                  ByteArrayOutputStream arrayOutputStream 
          = new ByteArrayOutputStream();
                  ObjectOutputStream outputStream;
                  
          try {
                      outputStream 
          = new ObjectOutputStream(arrayOutputStream);

                      outputStream.writeObject(value);
                  } 
          catch (IOException e) {
                      e.printStackTrace();
                  } 
          finally {
                      
          try {
                          arrayOutputStream.close();
                      } 
          catch (IOException e) {
                          e.printStackTrace();
                      }
                  }

                  
          return arrayOutputStream.toByteArray();
              }


              
          public void save(String key, V value) {
                  jedis.set(getKey(key), object2Bytes(value));
              }

          獲取用戶的timeline時,redis的LRANGE命令提供對list類型數據提供分頁操作:

              private List<Status> timeline(String targetId, int page) {
                  
          if (page < 1)
                      page 
          = 1;

                  
          int startIndex = (page - 1* 10;
                  
          int endIndex = page * 10;

                  List
          <String> idList = super.jedis
                          .lrange(targetId, startIndex, endIndex);

                  
          if (idList.isEmpty())
                      
          return new ArrayList<Status>(0);

                  List
          <Status> statusList = new ArrayList<Status>(idList.size());
                  
          for (String id : idList) {
                      Status status 
          = load(Long.valueOf(id));

                      
          if (status == null)
                          
          continue;

                      status.setUser(userService.load(status.getUid()));

                      statusList.add(status);
                  }

                  
          return statusList;
              }

          很顯然,LRANGE取出了Status對象的ID,然后我們需要再次根據ID獲取對應的Status對象二進制數據,然后反序列化:

              public Status load(long id) {
                  
          return super.get(getFormatId(id));
              }

              
          private String getFormatId(long id) {
                  
          return String.format(STATUS_ID_FORMAT, id);
              }

              
          private static final String STATUS_ID_FORMAT = "status:id:%d";

              
          public V get(String key) {
                  
          return byte2Object(jedis.get(getKey(key)));
              }

              @SuppressWarnings(
          "unchecked")
              
          private V byte2Object(byte[] bytes) {
                  
          if (bytes == null || bytes.length == 0)
                      
          return null;

                  
          try {
                      ObjectInputStream inputStream;
                      inputStream 
          = new ObjectInputStream(new ByteArrayInputStream(bytes));
                      Object obj 
          = inputStream.readObject();

                      
          return (V) obj;
                  } 
          catch (IOException e) {
                      e.printStackTrace();
                  } 
          catch (ClassNotFoundException e) {
                      e.printStackTrace();
                  }

                  
          return null;
              }

          以上使用JDK內置的序列化支持;更多序列化,可參考hessian、google protobuf等序列化框架,后者提供業界更為成熟的跨平臺、更為高效的序列化方案。更多代碼請參見附件。

          一些總結和思考:

          1. 不僅僅是緩存,替代SQL數據庫已完全成為可能,更高效,更經濟;雖然只是打開了一扇小的窗戶,但說不準以后人們會把大門打開。
          2. 實際環境中,可能最佳方式為SQL + NOSQL配合使用,互相彌補不足;還好,redis指令不算多,可速查,簡單易記。
          3. JAVA和RUBY代碼相比,有些重

          另:

          在線版,請參考 http://retwisrb.danlucraft.com/。那個誰誰,要運行范例,保證redis運行才行。

          Retwis-JAVA下載

          參考資料:

          Writing a simple Twitter clone with PHP and Redis

          https://github.com/xetorthio/jedis

          http://redis.io/commands

          posted on 2011-04-06 09:42 nieyong 閱讀(6127) 評論(0)  編輯  收藏 所屬分類: Servlet3

          公告

          所有文章皆為原創,若轉載請標明出處,謝謝~

          新浪微博,歡迎關注:

          導航

          <2011年4月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          1234567

          統計

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 普兰店市| 吉安县| 湖口县| 垣曲县| 中西区| 拉萨市| 含山县| 黑水县| 会宁县| 漳平市| 平远县| 广平县| 普陀区| 宁海县| 慈溪市| 柘荣县| 武宁县| 五寨县| 浑源县| 中卫市| 类乌齐县| 淳化县| 长沙县| 黄骅市| 乐亭县| 习水县| 夏津县| 林西县| 观塘区| 临安市| 宾川县| 甘南县| 贵州省| 大连市| 玉山县| 林周县| 许昌市| 宕昌县| 竹北市| 玉龙| 寿阳县|