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

          2011年10月7日

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

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

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

                首先來看幾個例子:

          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

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

                當(dāng)然上面列舉的例子是網(wǎng)站前端所使用的url表現(xiàn)方式,因?yàn)楸憩F(xiàn)方式可以多種多樣,個人喜好不同,本框架在設(shè)計(jì)時沒有給指定前端url的表現(xiàn)方式,而是定義接口,把這個權(quán)利留給使用的用戶??蚣軐⒖紤]更多 通用性的東西而不是 個性 自由的東西。

           

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

                Struts關(guān)于請求的配置:

           

          <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服務(wù)器時會把此url傳遞到struts框架交給它處理,之后struts會在struts.xml中尋找login的相關(guān)的配置,像上面例子,struts會找到LoginAction的類,并且調(diào)用其login的方法。

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

           

                再來看實(shí)現(xiàn)相同功能的Unicorn web框架的配置。

           

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

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

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

                有了大體的認(rèn)識之后,來看框架的核心部分AMPPathRouter的具體實(shí)現(xiàn)。

           

            /**
          * 檢查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;
          }
          /**
          * 匹配規(guī)則為:
          * 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方式的請求,并構(gòu)建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的全部內(nèi)容,其中在把請求分發(fā)到ActionSupport的子類 并調(diào)用相關(guān)方法時 是通過反射實(shí)現(xiàn),其他地方地方都是相當(dāng)容易理解的。

                空說無憑,把框架應(yīng)用到實(shí)戰(zhàn)中才是硬道理:

           

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

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

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

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

                根據(jù)單一職責(zé)的原則,UrlFilter就負(fù)責(zé)上面情景中的分發(fā)urlRouter中的差事,url如何分發(fā)交給Router處理。并且Router實(shí)際是一個接口,使用框架的用戶完全可以自己實(shí)現(xiàn)Router,這樣用戶可以自主定義的url分發(fā)的策略。另外呢,框架初始化的一些操作它也是 推脫不掉的,像根據(jù)unicorn-config.xml初始化系統(tǒng)中的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優(yōu)先級最高,url先通過用戶定義的
          	Iterator<IPathRouter> iterator = routerList.iterator();
          	while(iterator.hasNext()) {
          		IPathRouter router = iterator.next();
          		if(router.route(relativeUri, this)) {
          			return ;
          		}
          	}
          	// 攔截不到的繼續(xù)訪問
          	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));
          			// 單例模式通過方法獲取對象實(shí)例
          			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();
          		}
          	}
          	// 最后把框架默認(rèn)的Router加入進(jìn)來
          	routerList.add(new AMPPathRouter());
          }
          

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

          <?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是要判斷所有提交到服務(wù)器的指定url的請求 是否已經(jīng)登錄過,沒有登錄,可能會把此請求遣送會登錄頁。以及初始化所有的Action,在Router處理完請求,分發(fā)給action時,可以從filter里面去取。

           


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

                承接上篇的簡單介紹,下面詳細(xì)介紹整個框架的大致結(jié)構(gòu)。

                先來看一下整個框架包的結(jié)構(gòu):

           

                可以看出框架包含的包很少,包的結(jié)構(gòu)也超簡單。這里 涉及Filter、ActionSupport、Router等三個概念,他們之間的關(guān)系,通過下圖來表示:

           

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

                這里攔截采用Filter來處理,這跟多數(shù)的web框架一樣,使用FilterServlet有更多的能力進(jìn)行請求的分發(fā)。首先在一個web工程的web.xml文件中配置框架的UrlFilter類來攔截所有的請求。需要注意的一點(diǎn)是dispatcher 要設(shè)置為request,如果設(shè)置了forward的話,由框架內(nèi)部進(jìn)行的forward又會被框架攔截,從而造成無限的循環(huán)。Url-pattern設(shè)置為/*,表示所有的請求都會攔截,從而把對url分發(fā)的權(quán)利交由框架本身,而不是采用jsp規(guī)范里的url分發(fā)策略??蚣茉谔幚硭姓埱蟮?/span>url 時,依次交給各個Router類來處理,如果Router類判斷是符合自己的url格式,則分發(fā)給 action 處理。如果不能處理再交給下一級的Router,最后url經(jīng)由所有Router處理完,剩下的資源文件的url,如http://xxx.xxx.xxx.jpg,則框架調(diào)用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>

                大致的原理就是這樣,在下篇介紹框架的詳細(xì)實(shí)現(xiàn)。

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

                我承認(rèn)有點(diǎn)標(biāo)題黨了,不過題目中所說的幾項(xiàng)技術(shù)確實(shí)有其相似之處,欲知事情原委,且聽我詳細(xì)道來。

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

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

          <?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。框架奉行約定大于配置的思想,至于請求如何分發(fā),這個不需要人工配置,框架自動解決。這里要介紹兩個概念A(yù)ction 和 Router,熟悉Struts的肯定都知道Action,Action替代Servlet、JSP時代的Servlet,所有提交的請求由struts分發(fā)給不同的Action來處理。這里道理也是一樣的,Action就是經(jīng)過框架處理后的請求接受者。再來說一下Router,字面意思路由器,學(xué)過計(jì)算機(jī)網(wǎng)絡(luò)的都知道,ip數(shù)據(jù)包在網(wǎng)絡(luò)上之所以能夠順利到達(dá),就是因?yàn)槁酚善鞲鶕?jù)路由表來來確定出來傳輸?shù)耐緩?。這里Router也是這個作用,根據(jù)訪問服務(wù)器的URL來制定分發(fā)策略。Router是完全可以自定義的,用戶可以定義自己的Router來制定URL分發(fā)的策略,并且用戶自定義的Router比系統(tǒng)默認(rèn)的Router有更高的優(yōu)先權(quán)。

          二、URL上做文章

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

                先來介紹系統(tǒng)MethodRouter的處理方式。上面的url根據(jù)"/"分為三個部分,第一部分是請求的Action類,第二部分是類中的方法Method,第三部分是提交的參數(shù)Param。這一點(diǎn)受上篇文章優(yōu)酷的架構(gòu)里URL設(shè)計(jì)的啟發(fā)。

                經(jīng)過這樣的設(shè)計(jì),就明白在上述配置文件中為何可以如此簡單了。

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

                想實(shí)現(xiàn)上面的方式,就可以自己定義Router,在Router里面獲取上述的URL,然后做處理、forward到相應(yīng)的jsp頁面。

          三、使用Json傳輸數(shù)據(jù)

                Ajax請求很容易處理json數(shù)據(jù),ajax可以與系統(tǒng)輕松交互。

                當(dāng)初Web Service使用SOAP的xml格式傳輸數(shù)據(jù),如今也有人指責(zé)這是大費(fèi)周折。Restful方式提倡遵循HTTP語義,完全使用URL結(jié)合GET、POST、PUT、DELETE來傳輸請求,結(jié)果在roil陣營里廣泛使用,認(rèn)為是web service更優(yōu)雅的方式。所以本框架也吸取他們的優(yōu)點(diǎn),也完全可以通過url傳輸請求的數(shù)據(jù),如上述URL中的Param部分。不過沒有遵循Restful強(qiáng)調(diào)的Http語義,全部使用Get和POST的請求方式,當(dāng)然也可以制定為其他,這完全看你的心情,因?yàn)檫@對功能實(shí)現(xiàn)無關(guān)緊要。而且我覺得統(tǒng)一使用一種,更避免了需要指定請求方式的麻煩。

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

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

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

                經(jīng)過這樣的封裝,已經(jīng)與使用web service毫無差別,而且還會更加高效,因?yàn)樘幚韏son總比處理SOAP的xml要容易。

           

                先簡單寫這么多,之后的續(xù)篇詳細(xì)介紹。

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

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

          一、網(wǎng)站基本數(shù)據(jù)概覽

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

          二、網(wǎng)站前端框架

          從一開始,優(yōu)酷網(wǎng)就自建了一套CMS來解決前端的頁面顯示,各個模塊之間分離得比較恰當(dāng),前端可擴(kuò)展性很好,UI的分離,讓開發(fā)與維護(hù)變得十分簡單和靈活,下圖是優(yōu)酷前端的模塊調(diào)用關(guān)系:

          這樣,就根據(jù)module、method及params來確定調(diào)用相對獨(dú)立的模塊,顯得非常簡潔。下面附一張優(yōu)酷的前端局部架構(gòu)圖:

           

          三、數(shù)據(jù)庫架構(gòu)

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

          1、簡單的MySQL主從復(fù)制:

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

          其主從復(fù)制的過程如下圖所示:

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

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

          那問題產(chǎn)生總得解決的,這就產(chǎn)生下面的優(yōu)化方案,一起來看看。

           

          2、MySQL垂直分區(qū)

          如果把業(yè)務(wù)切割得足夠獨(dú)立,那把不同業(yè)務(wù)的數(shù)據(jù)放到不同的數(shù)據(jù)庫服務(wù)器將是一個不錯的方案,而且萬一其中一個業(yè)務(wù)崩潰了也不會影響其他業(yè)務(wù)的正常進(jìn)行,并且也起到了負(fù)載分流的作用,大大提升了數(shù)據(jù)庫的吞吐能力。經(jīng)過垂直分區(qū)后的數(shù)據(jù)庫架構(gòu)圖如下:

          然而,盡管業(yè)務(wù)之間已經(jīng)足夠獨(dú)立了,但是有些業(yè)務(wù)之間或多或少總會有點(diǎn)聯(lián)系,如用戶,基本上都會和每個業(yè)務(wù)相關(guān)聯(lián),況且這種分區(qū)方式,也不能解決單張表數(shù)據(jù)量暴漲的問題,因此為何不試試水平sharding呢?

           

          3、MySQL水平分片(Sharding)

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

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

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

           

          四、緩存策略

          貌似大的系統(tǒng)都對“緩存”情有獨(dú)鐘,從http緩存到memcached內(nèi)存數(shù)據(jù)緩存,但優(yōu)酷表示沒有用內(nèi)存緩存,理由如下:

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

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

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

          好了,就總結(jié)這么多了,有興趣的同學(xué)接著補(bǔ)充,雖然很多資料圖片都來自網(wǎng)絡(luò),但整理也不容易,歡迎轉(zhuǎn)載,轉(zhuǎn)載留個出處:青藤屋 原文鏈接

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

          windows系統(tǒng)使我們經(jīng)常使用的操作系統(tǒng)怎么才能使用我們現(xiàn)在經(jīng)常使用的操作系統(tǒng)不變的情況下繼續(xù)我們的SVN之旅,我們在綜合了好動種方法的同時感覺這些內(nèi)容非常貼近我們SVN在Windows種的應(yīng)用與配置.

          1.下載文件,

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

          下載 "Subversion Windows Service" 軟件包

          下載 TortoiseSVN shell integration utility

          2.安裝Subversion 服務(wù)器

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

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

          首先創(chuàng)建SVN儲存庫(repository)

          svnadmin create F:/svn/

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

          3.配置SVN服務(wù)器

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

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

          # [general]

          # password-db = passwd

          # anon-access = none

          # auth-access = write

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

          同樣打開passwd文件,將

          # [users]

          # harry = harryssecret

          # sally = sallyssecret

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

          4.運(yùn)行SVN服務(wù)器

          運(yùn)行SVN服務(wù)

          在命令行執(zhí)行

          svnserve --daemon --root F:/svn

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

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

          用后臺服務(wù)的方式可以設(shè)置開機(jī)自動執(zhí)行。

          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來啟動服務(wù)了 也可以在Sevices.msc來啟動了。

          5、用客戶端訪問

          格式:svn://服務(wù)器IP

           

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

           

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

          總結(jié)他們的配置,發(fā)現(xiàn) 
          都是用svnserve作為服務(wù)器, 
          都在svnserve.conf中使用了authz-db選項(xiàng)

          原因可能如下:

          1,配置authz時,沒有注意svnserve啟動參數(shù)-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 
          應(yīng)該配置成

          [Copy to clipboard] [ - ]CODE: 
          [groups] 
          admin=user1 
          dev=user2 
          [/] 
          @admin=rw 
          @doc=r 
          因?yàn)閇project1:/]表示庫project1的根目錄,而按上面的啟動參數(shù),是沒有庫的概念的。 
          使用類似這樣的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可以為多個版本庫工作, 
          這時如果想限制指定庫的指定目錄,就應(yīng)該指定具體的庫,像這樣

          [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,對中文目錄進(jìn)行權(quán)限控制時,沒有將權(quán)限文件authz改為utf-8格式。

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

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

          SIP協(xié)議

           

          SIP協(xié)議過程概念及分析

           

          SIP入門開發(fā)之路(含SIP開發(fā)需要學(xué)習(xí)的資源及網(wǎng)址)

           

          SIP揭密(中文版)

           

          使用Java的SIP Servlet進(jìn)行SIP開發(fā)

           

           

          Asterisk:

           

          Asterisk安裝及測試

           

          Asterisk十問十答

           

          Asterisk入門教程

           

          Asterisk介紹-Asterisk RealTime SIP

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

           

          asterisk 官方文檔

           

          asterisk目錄及配置說明

           

          Asterisk功能整理

           

          Asterisk使用ODBC實(shí)現(xiàn)語音信箱

           

          使用Asterisk實(shí)現(xiàn)可視的語音交換

           

           

          OpenSIPS

           

          開源SIP服務(wù)器OpenSIPS應(yīng)用介紹

           

          Opensips 安裝

           

          Opensips 配置文件

           

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

           

          Opensips文檔之MediaProxy模塊

           

          使用OpenSIPS構(gòu)建電話通信系統(tǒng)-8媒體服務(wù)整合

           

          使用OpenSIPS構(gòu)建電話通信系統(tǒng)-4腳本及路由基礎(chǔ)

           

          Opensips文檔之TM模塊

           

          Opensips文檔之RR模塊

           

          Opensips文檔之TEXTOPS 模塊

           

          Opensips文檔之AVPOPS模塊

           

           

          NAT穿透(即SIP打洞)

           

          使用OpenSIPS構(gòu)建電話通信系統(tǒng)-SIP穿透NAT

           

          NAT穿透問題探討

           

          完美的NAT穿透技術(shù)ICE介紹

           

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

           

          NAT穿透技術(shù)ICE基礎(chǔ)教程

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

          主站蜘蛛池模板: 黑水县| 齐河县| 治县。| 泉州市| 永和县| 金寨县| 康保县| 太仆寺旗| 延庆县| 谷城县| 天祝| 定兴县| 岑巩县| 济宁市| 博湖县| 鞍山市| 青浦区| 宿州市| 彭州市| 镇安县| 资阳市| 临沭县| 容城县| 临泽县| 巩留县| 成都市| 多伦县| 高邑县| 德钦县| 三亚市| 公主岭市| 昌吉市| 潜山县| 务川| 宣汉县| 台东县| 北票市| 萨迦县| 临安市| 云浮市| 永宁县|