posts - 33, comments - 0, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          2011年10月6日

                在第一篇時就說過框架要在URL上作文章,是的,本文就框架怎樣充分利用url上作盡可能詳細的說明。

                做web開發的不可能對url陌生,早在web1.0時代,url作為統一資源定位符,在對web中資源的如何獲得上起到巨大作用。不論用戶請求的時靜態頁面或者是各種圖片、腳本文件,通過url總能從web網站獲取要訪問的資源。Web2.0更是常常使用url作為get請求時參數的傳遞,如http://xxx.xxx.xxx/xxx.jsp?user=admin。以及近幾年很火restful web service 摒棄soap而使用url傳遞請求參數 都說明合理利用url的可行和流行。

                當然不止是使用了url就算好的實踐,而是能夠做到優雅的使用,保證層次分明和整體的簡潔,這才算是好的方式,這也正是本框架對使用url 所追求的目標。

                首先來看幾個例子:

          http://www.cnblogs.com/p2
          http://www.xxx.com/index.do?page=2

          http://www.xxx.com/product/
          http://www.xxx.com/channel.do?channel=product

          http://www.xxx.com/product/mobile
          http://www.xxx.com/channel.do?channel=product&&subChannel=mobile

                相信各位看官不用我說也能明白,這幾組的實現肯定第一種實現的方式更佳。拋開它能屏蔽服務器端使用的技術這一特性不說,它還能夠更好地說明動態網站的層次結構,讓用戶在訪問時能明確知道在網站的什么位置,而不會覺得是陷入了一個迷宮。

                當然上面列舉的例子是網站前端所使用的url表現方式,因為表現方式可以多種多樣,個人喜好不同,本框架在設計時沒有給指定前端url的表現方式,而是定義接口,把這個權利留給使用的用戶。框架將考慮更多 通用性的東西而不是 個性 自由的東西。

           

                下面對框架里默認使用的url Router AMPPathRouter做詳細的介紹,包括設計的思想和實現的方式。首先AMPPathRouter的用途定位為后臺使用。為了理解快速的理解它的工作原理,先來和struts做一下對比。

                Struts關于請求的配置:

           

          <action name="login" class="com.lscmjx.action.LoginAction" method="login">
          <result name="success">
          main.jsp
          </result>
          <result name="failure">
          login.jsp
          </result>
          </action>

              它提交的url會是http://xxx.xxx.xxx/login,訪問web服務器時會把此url傳遞到struts框架交給它處理,之后struts會在struts.xml中尋找login的相關的配置,像上面例子,struts會找到LoginAction的類,并且調用其login的方法。

                寫到這里,我請問這是最好的方式嗎?當然不是,至少我在使用struts時就認為這是相當撇腳的設計。上面例子只是列舉一個login方法,假如一個系統中要對后臺調用的方法是100個,那豈不是就需要在struts.xml中寫100個與之類似的配置。想想都頭大,這樣繁瑣的工作,應該是由框架自己去處理,而不是人工給配置。

           

                再來看實現相同功能的Unicorn web框架的配置。

           

          <action class="com.mh.action.UserAction"></action>

                當然提交的url肯定需要包含多一些的信息,來保證能通過url正確調用框架Action里的方法。這里提交的url方式:http://xxx.xxx.xxx/UserAction/login/

                通過在url里附加調用的Action類的信息,可以省略為不同的方法都在xml里配置的麻煩。假如UserAction里有100個方法,框架也只需這一行的配置。

                有了大體的認識之后,來看框架的核心部分AMPPathRouter的具體實現。

           

            /**
          * 檢查url是否是此Router類要處理的,/Action/Method/Param 格式的將會被檢查合格,返回true
          *
          @param relativeUri
          *
          @param actionMap
          *
          @return
          */
          public boolean checkUrl(String relativeUri, Map<String, ActionSupport> actionMap) {
          Pattern pattern = Pattern.compile("^/\\w+/\\w+/\\S*");
          Matcher matcher = pattern.matcher(relativeUri);
          if(matcher.matches()) {
          String actionName = relativeUri.split("/")[1];
          ActionSupport actionSupport = actionMap.get(actionName);
          if(null != actionSupport) {
          String actionMethodName = relativeUri.split("/")[2];
          Class<?> actionClass = actionSupport.getClass();
          Method[] methods = actionClass.getMethods();
          for(int i = 0; i < methods.length; i++) {
          Method method = methods[i];
          String methodName = method.getName();
          if(methodName.equals(actionMethodName)) {
          return true;
          }
          }
          } else {
          return false;
          }
          }
          return false;
          }
          /**
          * 匹配規則為:
          * 1、符合/Action/method/param格式,
          * 2、并且Action在actionMap中的確存在
          * 3、method在此Action中存在
          */
          @Override
          public boolean route(String relativeUri, UrlFilter urlFilter) {
          Map<String, ActionSupport> actionMap = urlFilter.getActionMap();
          if(!this.checkUrl(relativeUri, actionMap)) {
          return false;
          }
          // 攔截Action/Method/Param方式的請求,并構建ActionSupport類的屬性
          String[] params = relativeUri.split("/");
          try {
          ActionSupport actionSupport = actionMap.get(params[1]);
          Class<?> action = actionSupport.getClass();
          Method method = action.getMethod(params[2], new Class[] {});
          if(params.length > 3) {
          this.boxingRequest(urlFilter.getRequest(), params[3]);
          }
          // 只要找到ActionSupport的子類,則初始化其所具有的屬性
          Object newInstance = action.newInstance();
          this.initActionSupport(newInstance, urlFilter);
          String result = (String) method.invoke(newInstance, new Object[] {});
          if (null == result || ActionSupport.AJAX.equals(result) || ActionSupport.FORWARD.equals(result) || ActionSupport.WEB_SERVICE.equals(result)) {
          return true;
          }
          if(ActionSupport.REDIRECT.equals(result)) {
          urlFilter.getResponse().sendRedirect(result);
          return true;
          }
          } catch (NoSuchMethodException e) {
          e.printStackTrace();
          } catch (SecurityException e) {
          e.printStackTrace();
          } catch (IllegalAccessException e) {
          e.printStackTrace();
          } catch (IllegalArgumentException e) {
          e.printStackTrace();
          } catch (InvocationTargetException e) {
          e.printStackTrace();
          } catch (InstantiationException e) {
          e.printStackTrace();
          } catch (Exception e) {
          e.printStackTrace();
          }
          return false;
          }
          /**
          * 把url中得param加入到request的attribute里
          *
          @param request
          *
          @param parameter
          */
          private void boxingRequest(HttpServletRequest request, String parameter) {
          String[] parameters = parameter.split("&");
          for (int i = 0; i < parameters.length; i++) {
          String param = parameters[i];
          String[] key_value = param.split("=");
          if(key_value.length == 2) {
          request.setAttribute(key_value[0], key_value[1]);
          }
          }
          }
          /**
          * 初始化ActionSupport類中所需的request、response、session、application等對象
          *
          @param obj
          *
          @param urlFilter
          */
          private void initActionSupport(Object obj, UrlFilter urlFilter) {
          ActionSupport action = (ActionSupport) obj;
          action.setRequest(urlFilter.getRequest());
          action.setResponse(urlFilter.getResponse());
          action.setSession(urlFilter.getSession());
          action.setApplication(urlFilter.getApplication());
          }

           

                這便是AMPPathRouter的全部內容,其中在把請求分發到ActionSupport的子類 并調用相關方法時 是通過反射實現,其他地方地方都是相當容易理解的。

                空說無憑,把框架應用到實戰中才是硬道理:

           

                好了,下一篇介紹Action 和 json。

          posted @ 2011-11-25 18:18 馬航 閱讀(269) | 評論 (0)編輯 收藏

                上篇說過,所有提交到web程序的url都被此UrlFilter攔截。攔截到請求后,UrlFilter則召集它的好多個得力干將Router 們, 詢問他們:“誰能處理此URL啊 ?”

          這時一位叫做AMPRouter 首當其沖 說:“這個url交給我了”。這時filter就會把此url全權交給AMPRouter來辦,至于如何去處理,filter也不再過問,它覺得:“我把任務都交給你了,怎么解決是你的事”。

                根據單一職責的原則,UrlFilter就負責上面情景中的分發urlRouter中的差事,url如何分發交給Router處理。并且Router實際是一個接口,使用框架的用戶完全可以自己實現Router,這樣用戶可以自主定義的url分發的策略。另外呢,框架初始化的一些操作它也是 推脫不掉的,像根據unicorn-config.xml初始化系統中的RouterAction'。下面是具體的代碼:

           

          @Override
          public void init(FilterConfig config) throws ServletException {
          	application = config.getServletContext();
          	String loadPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
          	String classPath = loadPath.substring(1, loadPath.length());
          	ArrayList<String> actions = XMLReader.getNodeValues(classPath + "unicorn-config.xml", "actions");
          	this.initActions(actions);
          	ArrayList<String> routers = XMLReader.getNodeValues(classPath + "unicorn-config.xml", "routers");
          	this.initRouters(routers);
          }

           

          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
          			throws IOException, ServletException {
          	HttpServletRequest request = (HttpServletRequest) servletRequest;
          	String path = request.getContextPath();
          	String uri = request.getRequestURI();
          	String relativeUri = uri.substring(path.length(), uri.length());
          	this.request = request;
          	this.session = request.getSession();
          	this.response = (HttpServletResponse) servletResponse;
          	// 用戶自定義的Router優先級最高,url先通過用戶定義的
          	Iterator<IPathRouter> iterator = routerList.iterator();
          	while(iterator.hasNext()) {
          		IPathRouter router = iterator.next();
          		if(router.route(relativeUri, this)) {
          			return ;
          		}
          	}
          	// 攔截不到的繼續訪問
          	filterChain.doFilter(servletRequest, servletResponse);
          }
          

                  其中Router類的初始化,Action類的初始化于這個類似:

          private void initRouters(ArrayList<String> routers) {
          	routerList = new ArrayList<IPathRouter>();
          	for (int i = 0; i < routers.size(); i++) {
          		String routerName = routers.get(i);
          		try {
          			Class<?> clz = Class.forName(routers.get(i));
          			// 單例模式通過方法獲取對象實例
          			IPathRouter router = (IPathRouter) clz.newInstance();
          			routerList.add(router);
          		} catch (ClassNotFoundException e) {
          			e.printStackTrace();
          		} catch (IllegalAccessException e) {
          			e.printStackTrace();
          		} catch (SecurityException e) {
          			e.printStackTrace();
          		} catch (IllegalArgumentException e) {
          			e.printStackTrace();
          		} catch (InstantiationException e) {
          			e.printStackTrace();
          		}
          	}
          	// 最后把框架默認的Router加入進來
          	routerList.add(new AMPPathRouter());
          }
          

                  其中unicorn-config.xml文件的編寫,拿其中我一個項目里的這個文件來舉例:

          <?xml version="1.0" encoding="UTF-8" ?>
          <config>
          <routers>
          <router class="com.mh.router.MySessionCheckRouter"></router>
          </routers>
          <actions>
          <action class="com.mh.action.UserAction"></action>
          <action class="com.mh.action.InformationAction"></action>
          <action class="com.mh.action.UploadInformationIconAction"></action>
          <action class="com.mh.action.TempPicAction"></action>
          <action class="com.mh.action.MobileAction"></action>
          </actions>
          </config>

                這里即定義了Action,也定義了自己的Router,并且從名稱上可以看出,這個SessionCheckRouter是要判斷所有提交到服務器的指定url的請求 是否已經登錄過,沒有登錄,可能會把此請求遣送會登錄頁。以及初始化所有的Action,在Router處理完請求,分發給action時,可以從filter里面去取。

           


          posted @ 2011-11-25 12:35 馬航 閱讀(273) | 評論 (0)編輯 收藏

                承接上篇的簡單介紹,下面詳細介紹整個框架的大致結構。

                先來看一下整個框架包的結構:

           

                可以看出框架包含的包很少,包的結構也超簡單。這里 涉及FilterActionSupportRouter等三個概念,他們之間的關系,通過下圖來表示:

           

                圖也不規范,說不上來是哪個UML圖,不過通過它也能看出一個請求到達時,框架基本的處理流程。首先由Filter攔截到所有請求,然后把請求交給所有注冊的Router類,如果請求的Url正好是一個Router要攔截的,則把此請求交給這個Router,框架不再把請求向下傳遞。Router得到請求后,分析Url,通過Url里的信息把請求交給對應的ActionSupport的子類來處理。

                這里攔截采用Filter來處理,這跟多數的web框架一樣,使用FilterServlet有更多的能力進行請求的分發。首先在一個web工程的web.xml文件中配置框架的UrlFilter類來攔截所有的請求。需要注意的一點是dispatcher 要設置為request,如果設置了forward的話,由框架內部進行的forward又會被框架攔截,從而造成無限的循環。Url-pattern設置為/*,表示所有的請求都會攔截,從而把對url分發的權利交由框架本身,而不是采用jsp規范里的url分發策略。框架在處理所有請求的url 時,依次交給各個Router類來處理,如果Router類判斷是符合自己的url格式,則分發給 action 處理。如果不能處理再交給下一級的Router,最后url經由所有Router處理完,剩下的資源文件的url,如http://xxx.xxx.xxx.jpg,則框架調用filterdoChain()方法,通過filter的過濾去訪問web里的資源。

          <filter>

                <filter-name>unicornWeb</filter-name>

                <filter-class>com.mh.mvc.filter.UrlFilter</filter-class>

          </filter>

          <filter-mapping>

              <filter-name>unicornWeb</filter-name>

              <url-pattern>/*</url-pattern>

              <dispatcher>REQUEST</dispatcher>

          </filter-mapping>

                大致的原理就是這樣,在下篇介紹框架的詳細實現。

          posted @ 2011-11-25 11:43 馬航 閱讀(350) | 評論 (0)編輯 收藏

                我承認有點標題黨了,不過題目中所說的幾項技術確實有其相似之處,欲知事情原委,且聽我詳細道來。

                項目一開始只是不滿 struts 龐大的體積,于是想自己根據其原理實現一個tiny 版。后來的開發中覺得,完全可以把上述的ajax、Restuful web service的一些思想加入進來。經過幾周的努力,便開發出了一個基本成型的web 框架,暫且起名為unicorn(獨角獸,吼吼)。下文開始便對這個自編寫的框架做一些列的介紹,并且初步打算是將其開源,希望能一起交流和完善它。

                首先,為了能快速了解它是什么,先來看一下配置文件:

          <?xml version="1.0" encoding="UTF-8" ?>
          <config>
          <routers>
          <router class="com.mh.router.MySessionCheckRouter"></router>
          </routers>
          <actions>
          <action class="com.mh.action.UserAction"></action>
          <action class="com.mh.action.InformationAction"></action>
          <action class="com.mh.action.MobileAction"></action>
          </actions>
          </config>

                上面就是整個工程的配置文件,可以看出需要配置的東西非常少,只需要制定action類 和 router類有哪些就Ok。框架奉行約定大于配置的思想,至于請求如何分發,這個不需要人工配置,框架自動解決。這里要介紹兩個概念Action 和 Router,熟悉Struts的肯定都知道Action,Action替代Servlet、JSP時代的Servlet,所有提交的請求由struts分發給不同的Action來處理。這里道理也是一樣的,Action就是經過框架處理后的請求接受者。再來說一下Router,字面意思路由器,學過計算機網絡的都知道,ip數據包在網絡上之所以能夠順利到達,就是因為路由器根據路由表來來確定出來傳輸的途徑。這里Router也是這個作用,根據訪問服務器的URL來制定分發策略。Router是完全可以自定義的,用戶可以定義自己的Router來制定URL分發的策略,并且用戶自定義的Router比系統默認的Router有更高的優先權。

          二、URL上做文章

          /UserAction/login/username=admin&&password=admin

                先來介紹系統MethodRouter的處理方式。上面的url根據"/"分為三個部分,第一部分是請求的Action類,第二部分是類中的方法Method,第三部分是提交的參數Param。這一點受上篇文章優酷的架構里URL設計的啟發。

                經過這樣的設計,就明白在上述配置文件中為何可以如此簡單了。

                當然也可以不以這樣的方式,框架提供自定義Router的支持。比如你想這樣處理URL:/前臺頁面/子欄目/子欄目

                想實現上面的方式,就可以自己定義Router,在Router里面獲取上述的URL,然后做處理、forward到相應的jsp頁面。

          三、使用Json傳輸數據

                Ajax請求很容易處理json數據,ajax可以與系統輕松交互。

                當初Web Service使用SOAP的xml格式傳輸數據,如今也有人指責這是大費周折。Restful方式提倡遵循HTTP語義,完全使用URL結合GET、POST、PUT、DELETE來傳輸請求,結果在roil陣營里廣泛使用,認為是web service更優雅的方式。所以本框架也吸取他們的優點,也完全可以通過url傳輸請求的數據,如上述URL中的Param部分。不過沒有遵循Restful強調的Http語義,全部使用Get和POST的請求方式,當然也可以制定為其他,這完全看你的心情,因為這對功能實現無關緊要。而且我覺得統一使用一種,更避免了需要指定請求方式的麻煩。

                數據的返回使用json格式,比SOAP更為輕量簡潔和優雅,而且有更多的平臺直接支持。如在android平臺,本身就支持json格式的處理, 如果使用web service 的SOAP,你可能還要導入KSOAP的第三方庫。

                在非瀏覽器的客戶端,可以借助編寫的工具類,來完成web service方式的操作,

          public interface IWebService {
          	public List<LiteInformationDTO> getInformationsOfOwnerApp(String ownerApp, int start, int limit) throws SocketTimeoutException;
          }
          

                經過這樣的封裝,已經與使用web service毫無差別,而且還會更加高效,因為處理json總比處理SOAP的xml要容易。

           

                先簡單寫這么多,之后的續篇詳細介紹。

          posted @ 2011-11-22 16:22 馬航 閱讀(284) | 評論 (0)編輯 收藏

          記得以前給大家介紹過視頻網站龍頭老大YouTube的技術架構,相信大家看了都會有不少的感觸,互聯網就是這么一個神奇的東西。今天我突然想到,優酷網在國內也算是視頻網站的老大了,不知道他的架構相對于YouTube是怎么樣的,于是帶著這個好奇心去網上找了優酷網架構的各方面資料,雖然談得沒有YouTube那么詳細,但多少還是挖掘了一點,現在總結一下,希望對喜歡架構的朋友有所幫助。

          一、網站基本數據概覽

          • 據2010年統計,優酷網日均獨立訪問人數(uv)達到了8900萬,日均訪問量(pv)更是達到了17億,優酷憑借這一數據成為google榜單中國內視頻網站排名最高的廠商。
          • 硬件方面,優酷網引進的戴爾服務器主要以 PowerEdge 1950與PowerEdge 860為主,存儲陣列以戴爾MD1000為主,2007的數據表明,優酷網已有1000多臺服務器遍布在全國各大省市,現在應該更多了吧。

          二、網站前端框架

          從一開始,優酷網就自建了一套CMS來解決前端的頁面顯示,各個模塊之間分離得比較恰當,前端可擴展性很好,UI的分離,讓開發與維護變得十分簡單和靈活,下圖是優酷前端的模塊調用關系:

          這樣,就根據module、method及params來確定調用相對獨立的模塊,顯得非常簡潔。下面附一張優酷的前端局部架構圖:

           

          三、數據庫架構

          應該說優酷的數據庫架構也是經歷了許多波折,從一開始的單臺MySQL服務器(Just Running)到簡單的MySQL主從復制、SSD優化、垂直分庫、水平sharding分庫,這一系列過程只有經歷過才會有更深的體會吧,就像MySpace的架構經歷一樣,架構也是一步步慢慢成長和成熟的。

          1、簡單的MySQL主從復制:

          MySQL的主從復制解決了數據庫的讀寫分離,并很好的提升了讀的性能,其原來圖如下:

          其主從復制的過程如下圖所示:

          但是,主從復制也帶來其他一系列性能瓶頸問題:

          1. 寫入無法擴展
          2. 寫入無法緩存
          3. 復制延時
          4. 鎖表率上升
          5. 表變大,緩存率下降

          那問題產生總得解決的,這就產生下面的優化方案,一起來看看。

           

          2、MySQL垂直分區

          如果把業務切割得足夠獨立,那把不同業務的數據放到不同的數據庫服務器將是一個不錯的方案,而且萬一其中一個業務崩潰了也不會影響其他業務的正常進行,并且也起到了負載分流的作用,大大提升了數據庫的吞吐能力。經過垂直分區后的數據庫架構圖如下:

          然而,盡管業務之間已經足夠獨立了,但是有些業務之間或多或少總會有點聯系,如用戶,基本上都會和每個業務相關聯,況且這種分區方式,也不能解決單張表數據量暴漲的問題,因此為何不試試水平sharding呢?

           

          3、MySQL水平分片(Sharding)

          這是一個非常好的思路,將用戶按一定規則(按id哈希)分組,并把該組用戶的數據存儲到一個數據庫分片中,即一個sharding,這樣隨著用戶數量的增加,只要簡單地配置一臺服務器即可,原理圖如下:

          如何來確定某個用戶所在的shard呢,可以建一張用戶和shard對應的數據表,每次請求先從這張表找用戶的shard id,再從對應shard中查詢相關數據,如下圖所示:

          但是,優酷是如何解決跨shard的查詢呢,這個是個難點,據介紹優酷是盡量不跨shard查詢,實在不行通過多維分片索引、分布式搜索引擎,下策是分布式數據庫查詢(這個非常麻煩而且耗性能)

           

          四、緩存策略

          貌似大的系統都對“緩存”情有獨鐘,從http緩存到memcached內存數據緩存,但優酷表示沒有用內存緩存,理由如下:

          1. 避免內存拷貝,避免內存鎖
          2. 如接到老大哥通知要把某個視頻撤下來,如果在緩存里是比較麻煩的

          而且Squid 的 write() 用戶進程空間有消耗,Lighttpd 1.5 的 AIO(異步I/O) 讀取文件到用戶內存導致效率也比較低下。

          但為何我們訪問優酷會如此流暢,與土豆相比優酷的視頻加載速度略勝一籌?這個要歸功于優酷建立的比較完善的內容分發網絡(CDN),它通過多種方式保證分布在全國各地的用戶進行就近訪問——用戶點擊視頻請求后,優酷網將根據用戶所處地區位置,將離用戶最近、服務狀況最好的視頻服務器地址傳送給用戶,從而保證用戶可以得到快速的視頻體驗。這就是CDN帶來的優勢,就近訪問,有關CDN的更多內容,請大家Google一下。

          好了,就總結這么多了,有興趣的同學接著補充,雖然很多資料圖片都來自網絡,但整理也不容易,歡迎轉載,轉載留個出處:青藤屋 原文鏈接

          posted @ 2011-11-02 11:23 馬航 閱讀(288) | 評論 (0)編輯 收藏

          windows系統使我們經常使用的操作系統怎么才能使用我們現在經常使用的操作系統不變的情況下繼續我們的SVN之旅,我們在綜合了好動種方法的同時感覺這些內容非常貼近我們SVN在Windows種的應用與配置.

          1.下載文件,

          下載最新版本subversion,我這里選擇svn-1.4.5-setup.exe

          下載 "Subversion Windows Service" 軟件包

          下載 TortoiseSVN shell integration utility

          2.安裝Subversion 服務器

          由于我下載的是setup.exe版本,安裝程序安裝后會自動設置系統變量.如果你下載的是zip版就需要手動設置系統變量.

          setup.exe版直接安裝就可以了.安裝到D:/Program Files/Subversion

          首先創建SVN儲存庫(repository)

          svnadmin create F:/svn/

          repository創建完畢后會在目錄下生成若干個文件和文件夾,dav目錄是提供給Apache與mod_dav_svn使用的目錄,讓它們存儲內部數據;db目錄就是所有版本控制的數據文件;hooks目錄放置hook腳本文件的目錄;locks用來放置Subversion文件庫鎖定數據的目錄,用來追蹤存取文件庫的客戶端;format文件是一個文本文件,里面只放了一個整數,表示當前文件庫配置的版本號;

          3.配置SVN服務器

          (這個位置就是在你建儲存庫的地方F:/svn)

          打開/conf/目錄,打開svnserve.conf找到一下兩句:

          # [general]

          # password-db = passwd

          # anon-access = none

          # auth-access = write

          去之每行開頭的#,其中第二行是指定身份驗證的文件名,即passwd文件.anon-access = none 是匿名用戶不能訪問,必須要有用戶名和密碼。(注意:問題就出在這,一定要注意格式去掉注釋后要頂格不能有空)

          同樣打開passwd文件,將

          # [users]

          # harry = harryssecret

          # sally = sallyssecret

                 格式為“用戶名 = 密碼”,如可插入一行:admin = admin888,即為系統添加一個用戶名為admin,密碼為admin888的用戶

          4.運行SVN服務器

          運行SVN服務

          在命令行執行

          svnserve --daemon --root F:/svn

          服務啟動,--daemon可簡寫為-d,--root可簡寫為-r,可以建立一個批處理文件并放在windows啟動組中便于開機就運行SVN服務(注意:這是臨時打開的服務,命令執行后不能關閉窗口)

          也可以制定subversion工作的端口:svnserve -d -r f:/svn --listen-port 9999

          用后臺服務的方式可以設置開機自動執行。

          D:/Program Files/Subversion/bin>sc create svnservice binpath= "C:/Program Files/Subversion/bin/svnserve.exe --service -r f:/svn  --listen-port 9999"

          就可以用net svnservice stop 或者start來啟動服務了 也可以在Sevices.msc來啟動了。

          5、用客戶端訪問

          格式:svn://服務器IP

           

          ---------------------------------------------------------------------------------------------------------

           

          基于svnserve的服務器,權限文件authz配置的常見問題及解答  
           最近在我用Subversion論壇(http://www.iUseSVN.com/bbs)經常有人提到這樣的問題: 
          為什么我的客戶端沒有寫權限? 
          為什么我的權限沒有起作用?

          總結他們的配置,發現 
          都是用svnserve作為服務器, 
          都在svnserve.conf中使用了authz-db選項

          原因可能如下:

          1,配置authz時,沒有注意svnserve啟動參數-r所指定的目錄。 
          這里有兩種情況: 
          A:-r直接指定到版本庫(稱之為單庫svnserve方式) 
          比如,有一個庫project1,位于D:/svn/project1 
          使用以下命令啟動svnserve

          [Copy to clipboard] [ - ]CODE: 
          svnserve -d -r D:/svn/project1 
          在這種情況下,一個svnserve只能為一個版本庫工作 
          authz文件如果配置成下面這樣就是錯的,

          [Copy to clipboard] [ - ]CODE: 
          [groups] 
          admin=user1 
          dev=user2 
          [project1:/] 
          @admin=rw 
          @doc=r 
          應該配置成

          [Copy to clipboard] [ - ]CODE: 
          [groups] 
          admin=user1 
          dev=user2 
          [/] 
          @admin=rw 
          @doc=r 
          因為[project1:/]表示庫project1的根目錄,而按上面的啟動參數,是沒有庫的概念的。 
          使用類似這樣的URL:svn://192.168.0.1/ 即可訪問project1

          B:-r指定到版本庫的上級目錄(稱之為多庫svnserve方式) 
          同樣,有一個庫project1,位于D:/svn/project1 
          如果使用以下命令啟動svnserve

          [Copy to clipboard] [ - ]CODE: 
          svnserve -d -r D:/svn 
          這種情況,一個svnserve可以為多個版本庫工作, 
          這時如果想限制指定庫的指定目錄,就應該指定具體的庫,像這樣

          [Copy to clipboard] [ - ]CODE: 
          [groups] 
          admin=user1 
          dev=user2 
          [project1:/] 
          @admin=rw 
          @doc=r 
          如果此時你還用[/],則表示所有庫的根目錄,同理,[/src]表示所有庫的根目錄下的src目錄 
          使用類似這樣的URL:svn://192.168.0.1/project1 即可訪問project1 
          這樣的URL:svn://192.168.0.1/project2 即可訪問project2

          2,對中文目錄進行權限控制時,沒有將權限文件authz改為utf-8格式。

          svn對于非英文文件名和目錄名使用utf-8格式編碼處理,要對中文目錄進行正確控制, 
          應該使用無BOM的utf-8格式,如何將默認的文件轉為utf-8, 
          我使用的是UltraEdit的菜單"ASCII to UTF-8 (Unicode Editing)"。在UltraEdit的配置中,可以設置有無BOM  

          posted @ 2011-10-12 17:26 馬航 閱讀(423) | 評論 (0)編輯 收藏

          SIP協議

           

          SIP協議過程概念及分析

           

          SIP入門開發之路(含SIP開發需要學習的資源及網址)

           

          SIP揭密(中文版)

           

          使用Java的SIP Servlet進行SIP開發

           

           

          Asterisk:

           

          Asterisk安裝及測試

           

          Asterisk十問十答

           

          Asterisk入門教程

           

          Asterisk介紹-Asterisk RealTime SIP

          asterisk配置文件列表及常用指令

           

          asterisk 官方文檔

           

          asterisk目錄及配置說明

           

          Asterisk功能整理

           

          Asterisk使用ODBC實現語音信箱

           

          使用Asterisk實現可視的語音交換

           

           

          OpenSIPS

           

          開源SIP服務器OpenSIPS應用介紹

           

          Opensips 安裝

           

          Opensips 配置文件

           

          Mediaproxy的安裝及其在OpenSIPS中的配置

           

          Opensips文檔之MediaProxy模塊

           

          使用OpenSIPS構建電話通信系統-8媒體服務整合

           

          使用OpenSIPS構建電話通信系統-4腳本及路由基礎

           

          Opensips文檔之TM模塊

           

          Opensips文檔之RR模塊

           

          Opensips文檔之TEXTOPS 模塊

           

          Opensips文檔之AVPOPS模塊

           

           

          NAT穿透(即SIP打洞)

           

          使用OpenSIPS構建電話通信系統-SIP穿透NAT

           

          NAT穿透問題探討

           

          完美的NAT穿透技術ICE介紹

           

          ICE-SIP穿透NAT問題的終極解決方案

           

          NAT穿透技術ICE基礎教程

          posted @ 2011-10-07 20:31 馬航 閱讀(451) | 評論 (0)編輯 收藏

               摘要: Android中有一控件是ExpandableListView,比ListView更高級,ExpandableListView的效果很實用,比如因為需要查看一堆文件的目錄結構或者開發像QQ好友那樣的界面,就應該使用Expandablelistview。 本文最終效果如下: 首先是Activity代碼,實際開發中數據(包括父item,子item及圖片,Expandablelistview...  閱讀全文

          posted @ 2011-10-06 09:37 馬航 閱讀(5469) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 黎平县| 金阳县| 海口市| 六枝特区| 丰原市| 长乐市| 泗水县| 达尔| 宝丰县| 泽库县| 灵石县| 资溪县| 德格县| 沙湾县| 南丰县| 尉氏县| 怀集县| 广安市| 永寿县| 盐源县| 仙桃市| 上高县| 新巴尔虎右旗| 北安市| 绥芬河市| 阿拉善左旗| 卓资县| 平乐县| 阜南县| 北川| 双峰县| 石楼县| 西宁市| 郎溪县| 凤山市| 通化县| 仁寿县| 罗平县| 扎兰屯市| 新宁县| 广河县|