空山雪林通用模塊工作室

           

          2010年10月7日

          高可用性服務(wù)端的設(shè)計(jì)與實(shí)現(xiàn)

          本文的客戶(hù)端基于我們的GQT開(kāi)源項(xiàng)目:http://cxlh.iteye.com/blog/2021463

           

          本人拙見(jiàn),如有不同意見(jiàn),歡迎拍磚,同時(shí)獻(xiàn)給特別有對(duì)服務(wù)端跨語(yǔ)言需求的程序猿們!

          客戶(hù)端(GQT Demo V3(服務(wù)端配套版).rar)太大請(qǐng)挪步到QQ群下載(群號(hào):101189702),注明:GQT或Java,C++等;

          Java工程代碼請(qǐng)挪步下載:http://cxlh.iteye.com/blog/2074307 


          總體設(shè)計(jì)思路: 

           

          1. 高可用性:每個(gè)業(yè)務(wù)服務(wù)端都是獨(dú)立的個(gè)體,任何一個(gè)業(yè)務(wù)服務(wù)器Crash時(shí),都不會(huì)影響服務(wù),并且業(yè)務(wù)服務(wù)器可以按需增加,業(yè)務(wù)服務(wù)端使用Java代碼實(shí)現(xiàn),主要是為了考慮更好的數(shù)據(jù)庫(kù)操作,更好的事務(wù)支持,更好的社區(qū)支持以及可用更多的開(kāi)源服務(wù),提高開(kāi)發(fā)效率;
          2. 自動(dòng)負(fù)載均衡:當(dāng)某個(gè)接口頻繁調(diào)用,增加的響應(yīng)服務(wù)器自動(dòng)分流接口調(diào)用壓力,也就說(shuō)我們的異步的請(qǐng)求/響應(yīng)模式下,有一個(gè)Broker仲裁程序決定客戶(hù)端發(fā)來(lái)的請(qǐng)求交由哪個(gè)Java業(yè)務(wù)服務(wù)器來(lái)處理,這個(gè)Broker程序我們采用C++代碼實(shí)現(xiàn);
          3. 客戶(hù)端(包括桌面或移動(dòng)端)網(wǎng)關(guān):根據(jù)策略選取遠(yuǎn)程Socket服務(wù)器連接,Socket服務(wù)器與服務(wù)器之間的消息通訊通過(guò)地址尋址遠(yuǎn)程路由,以便連在不同Socket服務(wù)器上的用戶(hù)之間可以相互通訊,典型的應(yīng)用就是聊天程序;
          4. Sub/Pub類(lèi)消息:客戶(hù)端訂閱服務(wù)端的服務(wù),服務(wù)端有多個(gè)Pub服務(wù)器(Java編寫(xiě)),交由Socket服務(wù)器將客戶(hù)端Sub的消息Push給Client,此時(shí)Socket服務(wù)只作為Proxy用;

            好了,其實(shí)這里只有2個(gè)C++核心組件(Socket服務(wù)器,Broker仲裁程序)和N個(gè)Java業(yè)務(wù)服務(wù)器(請(qǐng)求處理服務(wù)器,Pub服務(wù)器),以下介紹用法(需要一定的客戶(hù)端編程知識(shí)和一些腳本只是):

          客戶(hù)端發(fā)送請(qǐng)求,異步收取,編寫(xiě)業(yè)務(wù)代碼步驟:

           

          編寫(xiě)Java服務(wù)端,比如我們編寫(xiě)一個(gè)按關(guān)鍵字讀取股票列表等接口(依賴(lài)注入用的Spring):

          @Repository @CacheNamespace(implementation = org.mybatis.caches.ehcache.EhcacheCache.class, readWrite = true) public interface StockDao { 	@Select("select SYMBOL,SHORT_NAME from `master`") 	public List<Map> listStock(); 	 	@Select("select SYMBOL,SHORT_NAME from `master` where SHORT_NAME like '%${k}%' or SPELL_NAME like '%${k}%' or SYMBOL like '%${k}%' limit 0,10") 	public List<Map> listStockByKeyword(Map<String,String> map); 	 	@Select("select SYMBOL,SHORT_NAME from `master` where exch_id=8") 	public List<Map> listAllSh(Map<String,String> map); 	 	@Select("select SYMBOL,SHORT_NAME from `master` where trade_date=#dt#") 	public List<Map> listStockByDate(Map<String,String> map); }

          編寫(xiě)Java服務(wù)就這么簡(jiǎn)單:

          package com.gqt.demo;

          import java.io.File;
          import java.io.FileInputStream;
          import java.io.InputStream;
          import java.util.Properties;
          import java.util.ResourceBundle;

          import org.apache.logging.log4j.LogManager;
          import org.apache.logging.log4j.Logger;

          import com.gqt.server.BaseReqServer;
          import com.gqt.server.ReqCallBack;

          public class StockServer extends BaseReqServer {
              private static Logger logger = LogManager.getLogger(StockServer.class.getName());
              
              final static Properties prop = new Properties();
              static{
                  InputStream is = null
                  try {
                      String c_path = StockServer.class.getResource("/").getPath();
                      logger.info("c_path:{}",c_path);
                      is = new FileInputStream(new File(c_path+"config.properties"));
                      prop.load(is);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }

              }
              public StockServer(String ip, String port, ReqCallBack callback) {
                  super(ip, port, callback);
              }
              public static void main(String[] args) {
                  logger.info("=============StockServer========");
                  new Thread(){
                      @Override
                      public void run() {
                          ReqCallBack callback = new StockCallBack();
                          String ip = prop.getProperty("gqt-reqserver-ip");
                          String port = prop.getProperty("gqt-reqserver-port");
                          final StockServer ss = new StockServer(ip,port,callback);
                          ss.startServer();
                      }
                  }.start();
              }
          }

          編寫(xiě)客戶(hù)端C++或腳本:

          gw.req("命令代碼"."命令代碼對(duì)應(yīng)的參數(shù)列表"); //回調(diào)函數(shù) gw.s_cb_gw.connect(function(trcode,msg){ 	log("-----------------------------------------------------------------"); 	log("<p>gqt server異步方式獲取數(shù)據(jù),回調(diào)信息:trcode=" + trcode + ",msg=" + msg+"</p>"); });

           

          效果如下:

           

           

           

           

          這樣做的好處:

           

          1. 桌面或移動(dòng)的客戶(hù)端再也不用編寫(xiě)繁瑣的網(wǎng)絡(luò)通信相關(guān)的程序,只需要發(fā)送請(qǐng)求/訂閱,處理響應(yīng)/訂閱數(shù)據(jù)包即可,也不用關(guān)心底層的數(shù)據(jù)分包合包,客戶(hù)端代理,網(wǎng)絡(luò)傳輸?shù)募咏饷芎蛪嚎s等;
          2. 客戶(hù)端開(kāi)發(fā)人員也幾乎不用和服務(wù)端程序員溝通,通過(guò)JSON解析請(qǐng)求透?jìng)鹘oJava,Java響應(yīng)的數(shù)據(jù)帶上包頭和響應(yīng)給客戶(hù)端,客戶(hù)端解析JSON即可;
          3. 跨語(yǔ)言的服務(wù)端雖然會(huì)損失一定的性能,但似乎這點(diǎn)ZeroMQ已做的足夠好;

           

          Demo程序部署步驟:

           

          1. 創(chuàng)建一個(gè)MySQL Demo數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)名:stock,utf-8編碼,導(dǎo)入output下的stock.sql;
          2. 配置output下的config.properties文件,指定機(jī)器IP,配置jdbc.properties,確保數(shù)據(jù)庫(kù)配置正確;
          3. 點(diǎn)擊runStockServer.bat,運(yùn)行stock的Java Demo服務(wù)器,可以開(kāi)任意個(gè);
          4. 配置gqt-server-communicator(cpp)下的config_ims.ini文件,IP地址全部換成機(jī)器IP;
          5. 啟動(dòng)ss_server.exe,ss_zserver.exe;
          6. 點(diǎn)擊客戶(hù)端GQT程序,配置config_ims.ini中的IP地址,切換到gateway的標(biāo)簽頁(yè),enjoy it!


           

          posted @ 2014-05-30 16:15 徐靈 閱讀(1235) | 評(píng)論 (0)編輯 收藏

          也談基于Web的含工作流項(xiàng)目的一般開(kāi)發(fā)流程

          該項(xiàng)目包含的通用模塊代碼等我有時(shí)間一并剝離貢獻(xiàn)出來(lái)(基于WebSocket的通知引擎,工作流整合模塊,自定義表單(詳見(jiàn)這里),基于RBAC權(quán)限設(shè)計(jì)),最近太忙了,Web項(xiàng)目有一段時(shí)間沒(méi)碰,有點(diǎn)生疏的感覺(jué),主要在忙GQT項(xiàng)目,一套基于桌面開(kāi)發(fā)的框架,詳見(jiàn)這里,寫(xiě)代碼寫(xiě)的有點(diǎn)手酸的感覺(jué)。

           

          基于Web的含工作流的項(xiàng)目看起來(lái)并不如想象的那么簡(jiǎn)單,主要需求:

          1. 靈活定制工作流,并跟蹤流程進(jìn)度;
          2. 每個(gè)Order含有歷史軌跡記錄,可在歷史中查看;
          3. 工作流的Action靈活,認(rèn)領(lǐng)任務(wù)不一定非要先提取表單,因?yàn)楹芏喙?jié)點(diǎn)都只有幾個(gè)動(dòng)作,直接按鈕操作即可;
          4. 待辦事宜列表在不刷新頁(yè)面情況下也能變動(dòng);

          項(xiàng)目要求:

          1. 操作簡(jiǎn)單高效;
          2. 權(quán)限細(xì)節(jié)到按鈕級(jí)別;
          3. 并發(fā)數(shù)少,不超過(guò)3000個(gè)在線用戶(hù);

          主要可能使用到技術(shù):

          1. 工作流引擎,我這里選用Activiti5,很靈活好用;
          2. 權(quán)限使用Spring Security,基于標(biāo)簽式管理權(quán)限很方便;
          3. 通知引擎使用WebSocket,基于Flash實(shí)時(shí)通信,基于socket.io;
          4. 權(quán)限粒度基于經(jīng)典的RBAC;
          5. 總體框架Spring MVC+Mybatis;

          實(shí)現(xiàn)的WebSocket的總體思路:

          1. WebSocket Server獨(dú)立于Web項(xiàng)目,Web Server與WebSocket Server之間的局域網(wǎng)通信基于簡(jiǎn)單的Socket通信,這樣這個(gè)組件可以完全解耦和通用;
          2. 當(dāng)Web項(xiàng)目要Push消息到Client時(shí),通過(guò)Web Server的Socket Client向WebSocket Server的Socker Server發(fā)送消息,然后WebSocket Server收到消息后解碼,廣播到所有瀏覽器;

          我們實(shí)現(xiàn)的事件通知非常簡(jiǎn)單,設(shè)定全局變量并讓瀏覽器偵聽(tīng):

          var G_WebSocket=false; 
          var EVENT_ORDER_CHANGE_STATUS = "orderChange";
          var EVENT_ORDER_CHANGE_AMOUNT = "amountChange";
          var EVENT_ORDER_CHANGE_REFUND = "refundChange";
          WebSocket.init = function(callbackFunc){
          socket = io.connect(connUrl, connOptions);
          socket.on('connect', function() {
          G_WebSocket=true;
          callbackFunc("connect",null);
          });
          socket.on('disconnect', function() {
          G_WebSocket=false;
          callbackFunc("disconnect",null);
          });
          socket.on('clientQuit', function(obj){
          G_WebSocket=false;
          callbackFunc("clientQuit",obj);
          });
          socket.on('broadcast', function(obj) {
          callbackFunc("broadcast",obj);
          });
          };

           

          在需要偵聽(tīng)WebSocket接受Web Server推送消息的地方加上一個(gè)函數(shù)即可:

          	WebSocket.init(function(command,jsonObj){ 		
          if(command=="broadcast"){
          if(jsonObj.e == EVENT_ORDER_CHANGE_STATUS){
          //TODO:write your code here
          }else if(jsonObj.e == EVENT_ORDER_CHANGE_AMOUNT){
          //TODO:write your code here
          }else if(jsonObj.e == EVENT_ORDER_CHANGE_REFUND){
          //TODO:write your code here
          }
          }
          });

           這樣的結(jié)構(gòu)要擴(kuò)展推送服務(wù)很簡(jiǎn)單,比如按頻道推送等,都可以很容易的擴(kuò)展。

          再看看看工作流,我們實(shí)現(xiàn)了activiti通用的申請(qǐng)?zhí)峤蝗蝿?wù)流程和自定義表單功能,提取跟蹤流程圖功能等,這樣你要設(shè)計(jì)一個(gè)新流程也變得非常簡(jiǎn)單,只需要在eclipse里劃上工作流圖,在后臺(tái)發(fā)布,然后通過(guò)SpringMVC的RestAPI啟動(dòng)實(shí)例流程,申領(lǐng)完成任務(wù)等,如下圖:



           流程走到了分支的兩個(gè)節(jié)點(diǎn)上,這樣對(duì)后續(xù)新增的工作流提供了極大的遍歷。

          最后說(shuō)說(shuō)Spring Security,基于RBAC的權(quán)限體系搭建好后(可以用在任何管理系統(tǒng)中),要在頁(yè)面中訪問(wèn)一個(gè)資源,首先判斷一下是否有權(quán)限,如下HTML:

          <sec:authorize ifAllGranted="r_pd"> 
          <a href="#">resource access here</a>
          </sec:authorize>

           

          <sec:authorize url="/XXX/XXX/XXX.html"> 	
          <a href="XXX/XXX/XXX.html'">
          <span>XXX功能</span>
          </a>
          </sec:authorize>

            

          前臺(tái)由于項(xiàng)目比較小,沒(méi)有用到j(luò)s的MVC框架,如backbone等,這里就不再記錄了。

           

           

          posted @ 2014-03-20 12:48 徐靈 閱讀(1677) | 評(píng)論 (6)編輯 收藏

          Openfire 3.7.0提供的優(yōu)秀開(kāi)源在線客服系統(tǒng)

          Openfire 3.7.0開(kāi)始官方為我們提供了一套優(yōu)秀的在線客服系統(tǒng),你可以按如下步驟安裝使用這套在線客服系統(tǒng):
          1. 安裝和部署Openfire 3.7.0
          2. 安裝fastpath插件
          3. 從openfire的svn上下載webchat源碼,webchat導(dǎo)入到eclipse中的工程如下圖:
           
          這套系統(tǒng)為我們提供了嵌入到客戶(hù)端代碼的源碼,客戶(hù)端只需要簡(jiǎn)單的嵌入如下代碼,即可顯示在線客服圖標(biāo):<html>
          <head>
          <title>Fastpath Web Chat</title>
          <script language="JavaScript" type="text/javascript" src="http://www.faqee.com:7080/webchat2/jivelive.jsp"></script>
          </head>
          <body>
          <script>
                showChatButton('demo@workgroup.nb.faqee.com');
           
          </script>
          </body>
          </html>
          但點(diǎn)擊在線客服圖標(biāo)時(shí),如下圖:

          客服端使用Spark接受來(lái)自訪客的請(qǐng)求的,點(diǎn)擊Accept,如下圖:


          我們?yōu)槟峁┝?個(gè)客服賬號(hào),請(qǐng)用Spark登陸,嵌入的訪客代碼見(jiàn)博客,有興趣的朋友可以測(cè)試一下:)
          服務(wù)器填入:nb.faqee.com
          客服賬號(hào):vms01/vms01,vms02/vms02,……,vms09/vms09
          訪客嵌入代碼測(cè)試地址:
          http://code.faqee.com/interface/fastpath.jsp

          posted @ 2011-03-09 12:51 徐靈 閱讀(8206) | 評(píng)論 (12)編輯 收藏

          Flash開(kāi)發(fā)開(kāi)源工具推薦—FlashDevelop

          偶玩的版本是3.3.1,簡(jiǎn)單而華麗的外表,界面截圖如下:


          ?

          安裝FD,你首先需要安裝:

          1. JDK 1.6以上版本
          2. .Net運(yùn)行環(huán)境
          繼而你就可以一路Next完成安裝了……

          安裝完成后,你必須去adobe官方網(wǎng)站下載開(kāi)源的Flex SDK,我這邊下載的版本是:flex_sdk_3.5.0.12683_mpl

          下載完成后,解壓到某個(gè)目錄下即可,如:F:\flex_sdk_3.5.0.12683_mpl

          然后在FD中設(shè)置Flex SDK,如下圖:



          ?
          我們這邊以O(shè)pen Flash Chart為例,導(dǎo)入項(xiàng)目工程,點(diǎn)擊運(yùn)行,如下圖:



          ?
          你可以設(shè)置斷點(diǎn),運(yùn)行表達(dá)式,使用起來(lái)非常方便,強(qiáng)烈推薦給大家使用!


          已有 1 人發(fā)表留言,猛擊->>這里<<-參與討論


          JavaEye推薦



          posted @ 2010-10-12 08:43 徐靈 閱讀(257) | 評(píng)論 (0)編輯 收藏

          國(guó)慶度假小記

          10月5,6日去寧波附近的度假村旅游了一下,一家3人出發(fā)了,由于兒子還比較小,怕他累著,所以這次就去了九龍湖開(kāi)元名都大酒店度假村,5星級(jí)的酒店,住了一晚,有點(diǎn)小貴,協(xié)議價(jià)后也得880元一晚,不過(guò)是無(wú)敵湖景房,超大陽(yáng)臺(tái),陽(yáng)臺(tái)上就可以看到整個(gè)九龍湖,甚是壯觀,兒子玩的開(kāi)心的不得了,在酒店的花園里滑滑板車(chē),5日早上出發(fā),6日中午回來(lái),在回來(lái)的路上,順便小逛了一下慈城古鎮(zhèn),到寧波吃了點(diǎn)飯,到家就該兒子的睡覺(jué)時(shí)間了……這個(gè)國(guó)慶節(jié)3日就簡(jiǎn)單的陪兒子在東錢(qián)湖上座了下船,他很喜歡坐船的……其實(shí)時(shí)間都在不知不覺(jué)中度過(guò),國(guó)慶長(zhǎng)假結(jié)束了……

          ?

          PS:附上酒店小圖一張,以之紀(jì)念……

          ?





          已有 0 人發(fā)表留言,猛擊->>這里<<-參與討論


          JavaEye推薦



          posted @ 2010-10-07 20:29 徐靈 閱讀(130) | 評(píng)論 (0)編輯 收藏

          導(dǎo)航

          友情鏈接

          最新評(píng)論

          主站蜘蛛池模板: 多伦县| 图片| 龙江县| 华池县| 高安市| 凌云县| 周宁县| 徐水县| 郸城县| 宝鸡市| 双城市| 桦南县| 西安市| 阿勒泰市| 西畴县| 肥乡县| 昌邑市| 尚志市| 盘山县| 巨鹿县| 惠安县| 杨浦区| 浑源县| 蓝田县| 龙门县| 天长市| 新巴尔虎右旗| 桦甸市| 荔波县| 罗甸县| 静海县| 三亚市| 大安市| 石棉县| 顺昌县| 台前县| 道孚县| 和田县| 陵川县| 横峰县| 桂林市|