1. 程式人生 > >iOS路由設計(四)路由設計思路分析

iOS路由設計(四)路由設計思路分析

前言

隨著使用者的需求越來越多,對App的使用者體驗也變的要求越來越高。為了更好的應對各種需求,開發人員從軟體工程的角度,將App架構由原來簡單的MVC變成MVVM,VIPER等複雜架構。更換適合業務的架構,是為了後期能更好的維護專案。

但是使用者依舊不滿意,繼續對開發人員提出了更多更高的要求,不僅需要高質量的使用者體驗,還要求快速迭代,最好一天出一個新功能,而且使用者還要求不更新就能體驗到新功能。為了滿足使用者需求,於是開發人員就用H5,ReactNative,Weex等技術對已有的專案進行改造。專案架構也變得更加的複雜,縱向的會進行分層,網路層,UI層,資料持久層。每一層橫向的也會根據業務進行元件化。儘管這樣做了以後會讓開發更加有效率,更加好維護,但是如何解耦各層,解耦各個介面和各個元件,降低各個元件之間的耦合度,如何能讓整個系統不管多麼複雜的情況下都能保持“高內聚,低耦合”的特點?這一系列的問題都擺在開發人員面前,亟待解決。今天就來談談解決這個問題的一些思路。

目錄

  • 1.引子
  • 2.App路由能解決哪些問題
  • 3.App之間跳轉實現
  • 4.App內元件間路由設計
  • 5.各個方案優缺點
  • 6.最好的方案

一. 引子

大前端發展這麼多年了,相信也一定會遇到相似的問題。近兩年SPA發展極其迅猛,React 和 Vue一直處於風口浪尖,那我們就看看他們是如何處理好這一問題的。


在SPA單頁面應用,路由起到了很關鍵的作用。路由的作用主要是保證檢視和 URL 的同步。在前端的眼裡看來,檢視是被看成是資源的一種表現。當用戶在頁面中進行操作時,應用會在若干個互動狀態中切換,路由則可以記錄下某些重要的狀態,比如使用者檢視一個網站,使用者是否登入、在訪問網站的哪一個頁面。而這些變化同樣會被記錄在瀏覽器的歷史中,使用者可以通過瀏覽器的前進、後退按鈕切換狀態。總的來說,使用者可以通過手動輸入或者與頁面進行互動來改變 URL,然後通過同步或者非同步的方式向服務端傳送請求獲取資源,成功後重新繪製 UI,原理如下圖所示:


react-router通過傳入的location到最終渲染新的UI,流程如下:


location的來源有2種,一種是瀏覽器的回退和前進,另外一種是直接點了一個連結。新的 location 物件後,路由內部的 matchRoutes 方法會匹配出 Route 元件樹中與當前 location 物件匹配的一個子集,並且得到了 nextState,在this.setState(nextState) 時就可以實現重新渲染 Router 元件。

大前端的做法大概是這樣的,我們可以把這些思想借鑑到iOS這邊來。上圖中的Back / Forward 在iOS這邊很多情況下都可以被UINavgation所管理。所以iOS的Router主要處理綠色的那一塊。

二. App路由能解決哪些問題


既然前端能在SPA上解決URL和UI的同步問題,那這種思想可以在App上解決哪些問題呢?

思考如下的問題,平時我們開發中是如何優雅的解決的:

1.3D-Touch功能或者點選推送訊息,要求外部跳轉到App內部一個很深層次的一個介面。

比如微信的3D-Touch可以直接跳轉到“我的二維碼”。“我的二維碼”介面在我的裡面的第三級介面。或者再極端一點,產品需求給了更加變態的需求,要求跳轉到App內部第十層的介面,怎麼處理?

2.自家的一系列App之間如何相互跳轉?

如果自己App有幾個,相互之間還想相互跳轉,怎麼處理?

3.如何解除App元件之間和App頁面之間的耦合性?

隨著專案越來越複雜,各個元件,各個頁面之間的跳轉邏輯關聯性越來越多,如何能優雅的解除各個元件和頁面之間的耦合性?

4.如何能統一iOS和Android兩端的頁面跳轉邏輯?甚至如何能統一三端的請求資源的方式?

專案裡面某些模組會混合ReactNative,Weex,H5介面,這些介面還會呼叫Native的介面,以及Native的元件。那麼,如何能統一Web端和Native端請求資源的方式?

5.如果使用了動態下發配置檔案來配置App的跳轉邏輯,那麼如果做到iOS和Android兩邊只要共用一套配置檔案?

6.如果App出現bug了,如何不用JSPatch,就能做到簡單的熱修復功能?

比如App上線突然遇到了緊急bug,能否把頁面動態降級成H5,ReactNative,Weex?或者是直接換成一個本地的錯誤介面?

7.如何在每個元件間呼叫和頁面跳轉時都進行埋點統計?每個跳轉的地方都手寫程式碼埋點?利用Runtime AOP ?

8.如何在每個元件間呼叫的過程中,加入呼叫的邏輯檢查,令牌機制,配合灰度進行風控邏輯?

9.如何在App任何介面都可以呼叫同一個介面或者同一個元件?只能在AppDelegate裡面註冊單例來實現?

比如App出現問題了,使用者可能在任何介面,如何隨時隨地的讓使用者強制登出?或者強制都跳轉到同一個本地的error介面?或者跳轉到相應的H5,ReactNative,Weex介面?如何讓使用者在任何介面,隨時隨地的彈出一個View ?

以上這些問題其實都可以通過在App端設計一個路由來解決。那麼我們怎麼設計一個路由呢?

三. App之間跳轉實現

在談App內部的路由之前,先來談談在iOS系統間,不同App之間是怎麼實現跳轉的。

1. URL Scheme方式

iOS系統是預設支援URL Scheme的,具體見官方文件

比如說,在iPhone的Safari瀏覽器上面輸入如下的命令,會自動開啟一些App:

// 開啟郵箱
mailto://

// 給110撥打電話
tel://110

在iOS 9 之前只要在App的info.plist裡面新增URL types - URL Schemes,如下圖:


這裡就添加了一個com.ios.Qhomer的Scheme。這樣就可以在iPhone的Safari瀏覽器上面輸入:

com.ios.Qhomer://

就可以直接開啟這個App了。

關於其他一些常見的App,可以從iTunes裡面下載到它的ipa檔案,解壓,顯示包內容裡面可以找到info.plist檔案,開啟它,在裡面就可以相應的URL Scheme。

// 手機QQ
mqq://

// 微信
weixin://

// 新浪微博
sinaweibo://

// 餓了麼
eleme://

當然了,某些App對於呼叫URL Scheme比較敏感,它們不希望其他的App隨意的就呼叫自己。

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation
{
    NSLog(@"sourceApplication: %@", sourceApplication);
    NSLog(@"URL scheme:%@", [url scheme]);
    NSLog(@"URL query: %@", [url query]);

    if ([sourceApplication isEqualToString:@"com.tencent.weixin"]){
        // 允許開啟
        return YES;
    }else{
        return NO;
    }
}

如果待呼叫的App已經運行了,那麼它的生命週期如下:


如果待呼叫的App在後臺,那麼它的生命週期如下:




如上圖,餓了麼App允許通過URL Scheme呼叫,那麼我們可以在Safari裡面呼叫到餓了麼App。手機QQ不允許呼叫,我們在Safari裡面也就沒法跳轉過去。

App也是可以直接跳轉到系統設定的。比如有些需求要求檢測使用者有沒有開啟某些系統許可權,如果沒有開啟就彈框提示,點選彈框的按鈕直接跳轉到系統設定裡面對應的設定介面。

2. Universal Links方式

雖然在微信內部開網頁會禁止所有的Scheme,但是iOS 9.0新增加了一項功能是Universal Links,使用這個功能可以使我們的App通過HTTP連結來啟動App。
1.如果安裝過App,不管在微信裡面http連結還是在Safari瀏覽器,還是其他第三方瀏覽器,都可以開啟App。
2.如果沒有安裝過App,就會開啟網頁。

具體設定需要3步:

1.App需要開啟Associated Domains服務,並設定Domains,注意必須要applinks:開頭。


2.域名必須要支援HTTPS。

3.上傳內容是Json格式的檔案,檔名為apple-app-site-association到自己域名的根目錄下,或者.well-known目錄下。iOS自動會去讀取這個檔案。具體的檔案內容請檢視官方文件


如果App支援了Universal Links方式,那麼可以在其他App裡面直接跳轉到我們自己的App裡面。如下圖,點選連結,由於該連結會Matcher到我們設定的連結,所以選單裡面會顯示用我們的App開啟。


在瀏覽器裡面也是一樣的效果,如果是支援了Universal Links方式,訪問相應的URL,會有不同的效果。如下圖:


以上就是iOS系統中App間跳轉的二種方式。

從iOS 系統裡面支援的URL Scheme方式,我們可以看出,對於一個資源的訪問,蘋果也是用URI的方式來訪問的。

統一資源識別符號(英語:Uniform Resource Identifier,或URI)是一個用於標識某一網際網路資源名稱的字串。 該種標識允許使用者對網路中(一般指全球資訊網)的資源通過特定的協議進行互動操作。URI的最常見的形式是統一資源定位符(URL)。

舉個例子:


這是一段URI,每一段都代表了對應的含義。對方接收到了這樣一串字串,按照規則解析出來,就能獲取到所有的有用資訊。

這個能給我們設計App元件間的路由帶來一些思路麼?如果我們想要定義一個三端(iOS,Android,H5)的統一訪問資源的方式,能用URI的這種方式實現麼?

四. App內元件間路由設計

上一章節中我們介紹了iOS系統中,系統是如何幫我們處理App間跳轉邏輯的。這一章節我們著重討論一下,App內部,各個元件之間的路由應該怎麼設計。關於App內部的路由設計,主要需要解決2個問題:

1.各個頁面和元件之間的跳轉問題。
2.各個元件之間相互呼叫。

先來分析一下這兩個問題。

1. 關於頁面跳轉


在iOS開發的過程中,經常會遇到以下的場景,點選按鈕跳轉Push到另外一個介面,或者點選一個cell Present一個新的ViewController。在MVC模式中,一般都是新建一個VC,然後Push / Present到下一個VC。但是在MVVM中,會有一些不合適的情況。


眾所周知,MVVM把MVC拆成了上圖演示的樣子,原來View對應的與資料相關的程式碼都移到ViewModel中,相應的C也變瘦了,演變成了M-VM-C-V的結構。這裡的C裡面的程式碼可以只剩下頁面跳轉相關的邏輯。如果用程式碼表示就是下面這樣子:

假設一個按鈕的執行邏輯都封裝成了command。

    @weakify(self);
    [[[_viewModel.someCommand executionSignals] flatten] subscribeNext:^(id x) {
        @strongify(self);
        // 跳轉邏輯
        [self.navigationController pushViewController:targetViewController animated:YES];
  }];

上述的程式碼本身沒啥問題,但是可能會弱化MVVM框架的一個重要作用。

MVVM框架的目的除去解耦以外,還有2個很重要的目的:

  1. 程式碼高複用率
  2. 方便進行單元測試

如果需要測試一個業務是否正確,我們只要對ViewModel進行單元測試即可。前提是假定我們使用ReactiveCocoa進行UI繫結的過程是準確無誤的。目前繫結是正確的。所以我們只需要單元測試到ViewModel即可完成業務邏輯的測試。

頁面跳轉也屬於業務邏輯,所以應該放在ViewModel中一起單元測試,保證業務邏輯測試的覆蓋率。

把頁面跳轉放到ViewModel中,有2種做法,第一種就是用路由來實現,第二種由於和路由沒有關係,所以這裡就不多闡述,有興趣的可以看lpd-mvvm-kit這個庫關於頁面跳轉的具體實現。

頁面跳轉相互的耦合性也就體現出來了:

1.由於pushViewController或者presentViewController,後面都需要帶一個待操作的ViewController,那麼就必須要引入該類,import標頭檔案也就引入了耦合性。
2.由於跳轉這裡寫死了跳轉操作,如果線上一旦出現了bug,這裡是不受我們控制的。
3.推送訊息或者是3D-Touch需求,要求直接跳轉到內部第10級介面,那麼就需要寫一個入口跳轉到指定介面。

2. 關於元件間呼叫


關於元件間的呼叫,也需要解耦。隨著業務越來越複雜,我們封裝的元件越來越多,要是封裝的粒度拿捏不準,就會出現大量元件之間耦合度高的問題。元件的粒度可以隨著業務的調整,不斷的調整元件職責的劃分。但是元件之間的呼叫依舊不可避免,相互呼叫對方元件暴露的介面。如何減少各個元件之間的耦合度,是一個設計優秀的路由的職責所在。

3. 如何設計一個路由

如何設計一個能完美解決上述2個問題的路由,讓我們先來看看GitHub上優秀開源庫的設計思路。以下是我從Github上面找的一些路由方案,按照Star從高到低排列。依次來分析一下它們各自的設計思路。

JLRoutes在整個Github上面Star最多,那就來從它來分析分析它的具體設計思路。

首先JLRoutes是受URL Scheme思路的影響。它把所有對資源的請求看成是一個URI。

首先來熟悉一下NSURLComponent的各個欄位:


Note
The URLs employed by the NSURL
class are described in RFC 1808RFC 1738, and RFC 2732.

JLRoutes會傳入每個字串,都按照上面的樣子進行切分處理,分別根據RFC的標準定義,取到各個NSURLComponent。


JLRoutes全域性會儲存一個Map,這個Map會以scheme為Key,JLRoutes為Value。所以在routeControllerMap裡面每個scheme都是唯一的。

至於為何有這麼多條路由,筆者認為,如果路由按照業務線進行劃分的話,每個業務線可能會有不相同的邏輯,即使每個業務裡面的元件名字可能相同,但是由於業務線不同,會有不同的路由規則。

舉個例子:如果滴滴按照每個城市的打車業務進行元件化拆分,那麼每個城市就對應著這裡的每個scheme。每個城市的打車業務都有叫車,付款……等業務,但是由於每個城市的地方法規不相同,所以這些元件即使名字相同,但是裡面的功能也許千差萬別。所以這裡劃分出了多個route,也可以理解為不同的名稱空間。

在每個JLRoutes裡面都儲存了一個數組,這個數組裡面儲存了每個路由規則JLRRouteDefinition裡面會儲存外部傳進來的block閉包,pattern,和拆分之後的pattern。

在每個JLRoutes的數組裡面,會按照路由的優先順序進行排列,優先順序高的排列在前面。

- (void)_registerRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock
{
    JLRRouteDefinition *route = [[JLRRouteDefinition alloc] initWithScheme:self.scheme pattern:routePattern priority:priority handlerBlock:handlerBlock];

    if (priority == 0 || self.routes.count == 0) {
        [self.routes addObject:route];
    } else {
        NSUInteger index = 0;
        BOOL addedRoute = NO;

        // 找到當前已經存在的一條優先順序比當前待插入的路由低的路由
        for (JLRRouteDefinition *existingRoute in [self.routes copy]) {
            if (existingRoute.priority < priority) {
                // 如果找到,就插入陣列
                [self.routes insertObject:route atIndex:index];
                addedRoute = YES;
                break;
            }
            index++;
        }

        // 如果沒有找到任何一條路由比當前待插入的路由低的路由,或者最後一條路由優先順序和當前路由一樣,那麼就只能插入到最後。
        if (!addedRoute) {
            [self.routes addObject:route];
        }
    }
}

由於這個數組裡面的路由是一個單調佇列,所以查詢優先順序的時候只用從高往低遍歷即可。

具體查詢路由的過程如下:


首先根據外部傳進來的URL初始化一個JLRRouteRequest,然後用這個JLRRouteRequest在當前的路由數組裡面依次request,每個規則都會生成一個response,但是隻有符合條件的response才會match,最後取出匹配的JLRRouteResponse拿出其字典parameters裡面對應的引數就可以了。查詢和匹配過程中重要的程式碼如下:

- (BOOL)_routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters executeRouteBlock:(BOOL)executeRouteBlock
{
    if (!URL) {
        return NO;
    }

    [self _verboseLog:@"Trying to route URL %@", URL];

    BOOL didRoute = NO;
    JLRRouteRequest *request = [[JLRRouteRequest alloc] initWithURL:URL];

    for (JLRRouteDefinition *route in [self.routes copy]) {
        // 檢查每一個route,生成對應的response
        JLRRouteResponse *response = [route routeResponseForRequest:request decodePlusSymbols:shouldDecodePlusSymbols];
        if (!response.isMatch) {
            continue;
        }

        [self _verboseLog:@"Successfully matched %@", route];

        if (!executeRouteBlock) {
            // 如果我們被要求不允許執行,但是又找了匹配的路由response。
            return YES;
        }

        // 裝配最後的引數
        NSMutableDictionary *finalParameters = [NSMutableDictionary dictionary];
        [finalParameters addEntriesFromDictionary:response.parameters];
        [finalParameters addEntriesFromDictionary:parameters];
        [self _verboseLog:@"Final parameters are %@", finalParameters];

        didRoute = [route callHandlerBlockWithParameters:finalParameters];

        if (didRoute) {
            // 呼叫Handler成功
            break;
        }
    }

    if (!didRoute) {
        [self _verboseLog:@"Could not find a matching route"];
    }

    // 如果在當前路由規則裡面沒有找到匹配的路由,當前路由不是global 的,並且允許降級到global裡面去查詢,那麼我們繼續在global的路由規則裡面去查詢。
    if (!didRoute && self.shouldFallbackToGlobalRoutes && ![self _isGlobalRoutesController]) {
        [self _verboseLog:@"Falling back to global routes..."];
        didRoute = [[JLRoutes globalRoutes] _routeURL:URL withParameters:parameters executeRouteBlock:executeRouteBlock];
    }

    // 最後,依舊沒有找到任何能匹配的,如果有unmatched URL handler,呼叫這個閉包進行最後的處理。

if, after everything, we did not route anything and we have an unmatched URL handler, then call it
    if (!didRoute && executeRouteBlock && self.unmatchedURLHandler) {
        [self _verboseLog:@"Falling back to the unmatched URL handler"];
        self.unmatchedURLHandler(self, URL, parameters);
    }

    return didRoute;
}

舉個例子:

我們先註冊一個Router,規則如下:

[[JLRoutes globalRoutes] addRoute:@"/:object/:primaryKey" handler:^BOOL(NSDictionary *parameters) {
  NSString *object = parameters[@"object"];
  NSString *primaryKey = parameters[@"primaryKey"];
  // stuff
  return YES;
}];

我們傳入一個URL,讓Router進行處理。

NSURL *editPost = [NSURL URLWithString:@"ele://post/halfrost?debug=true&foo=bar"];
[[UIApplication sharedApplication] openURL:editPost];

匹配成功之後,我們會得到下面這樣一個字典:

{
  "object": "post",
  "action": "halfrost",
  "debug": "true",
  "foo": "bar",
  "JLRouteURL": "ele://post/halfrost?debug=true&foo=bar",
  "JLRoutePattern": "/:object/:action",
  "JLRouteScheme": "JLRoutesGlobalRoutesScheme"
}

把上述過程圖解出來,見下圖:


JLRoutes還可以支援Optional的路由規則,假如定義一條路由規則:

/the(/foo/:a)(/bar/:b)

JLRoutes 會幫我們預設註冊如下4條路由規則:

/the/foo/:a/bar/:b
/the/foo/:a
/the/bar/:b
/the

Routable路由是用在in-app native端的 URL router, 它可以用在iOS上也可以用在Android上。


UPRouter裡面儲存了2個字典。routes字典裡面儲存的Key是路由規則,Value儲存的是UPRouterOptions。cachedRoutes裡面儲存的Key是最終的URL,帶傳參的,Value儲存的是RouterParams。RouterParams裡面會包含在routes匹配的到的UPRouterOptions,還有額外的開啟引數openParams和一些額外引數extraParams。

- (RouterParams *)routerParamsForUrl:(NSString *)url extraParams: (NSDictionary *)extraParams {
    if (!url) {
        //if we wait, caching this as key would throw an exception
        if (_ignoresExceptions) {
            return nil;
        }
        @throw [NSException exceptionWithName:@"RouteNotFoundException"
                                       reason:[NSString stringWithFormat:ROUTE_NOT_FOUND_FORMAT, url]
                                     userInfo:nil];
    }

    if ([self.cachedRoutes objectForKey:url] && !extraParams) {
        return [self.cachedRoutes objectForKey:url];
    }

   // 比對url通過/分割之後的引數個數和pathComponents的個數是否一樣
    NSArray *givenParts = url.pathComponents;
    NSArray *legacyParts = [url componentsSeparatedByString:@"/"];
    if ([legacyParts count] != [givenParts count]) {
        NSLog(@"Routable Warning - your URL %@ has empty path components - this will throw an error in an upcoming release", url);
        givenParts = legacyParts;
    }

    __block RouterParams *openParams = nil;
    [self.routes enumerateKeysAndObjectsUsingBlock:
     ^(NSString *routerUrl, UPRouterOptions *routerOptions, BOOL *stop) {

         NSArray *routerParts = [routerUrl pathComponents];
         if ([routerParts count] == [givenParts count]) {

             NSDictionary *givenParams = [self paramsForUrlComponents:givenParts routerUrlComponents:routerParts];
             if (givenParams) {
                 openParams = [[RouterParams alloc] initWithRouterOptions:routerOptions openParams:givenParams extraParams: extraParams];
                 *stop = YES;
             }
         }
     }];

    if (!openParams) {
        if (_ignoresExceptions) {
            return nil;
        }
        @throw [NSException exceptionWithName:@"RouteNotFoundException"
                                       reason:[NSString stringWithFormat:ROUTE_NOT_FOUND_FORMAT, url]
                                     userInfo:nil];
    }
    [self.cachedRoutes setObject:openParams forKey:url];
    return openParams;
}

這一段程式碼裡面重點在幹一件事情,遍歷routes字典,然後找到引數匹配的字串,封裝成RouterParams返回。

- (NSDictionary *)paramsForUrlComponents:(NSArray *)givenUrlComponents routerUrlComponents:(NSArray *)routerUrlComponents {

    __block NSMutableDictionary *params = [NSMutableDictionary dictionary];
    [routerUrlComponents enumerateObjectsUsingBlock:
     ^(NSString *routerComponent, NSUInteger idx, BOOL *stop) {

         NSString *givenComponent = givenUrlComponents[idx];
         if ([routerComponent hasPrefix:@":"]) {
             NSString *key = [routerComponent substringFromIndex:1];
             [params setObject:givenComponent forKey:key];
         }
         else if (![routerComponent isEqualToString:givenComponent]) {
             params = nil;
             *stop = YES;
         }
     }];
    return params;
}

上面這段函式,第一個引數是外部傳進來URL帶有各個入參的分割陣列。第二個引數是路由規則分割開的陣列。routerComponent由於規定:號後面才是引數,所以routerComponent的第1個位置就是對應的引數名。params字典裡面以引數名為Key,引數為Value。

 NSDictionary *givenParams = [self paramsForUrlComponents:givenParts routerUrlComponents:routerParts];
if (givenParams) {
       openParams = [[RouterParams alloc] initWithRouterOptions:routerOptions openParams:givenParams extraParams: extraParams];
       *stop = YES;
}

最後通過RouterParams的初始化方法,把路由規則對應的UPRouterOptions,上一步封裝好的引數字典givenParams,還有
routerParamsForUrl: extraParams: 方法的第二個入參,這3個引數作為初始化引數,生成了一個RouterParams。

            
           

相關推薦

iOS路由設計路由設計思路分析

前言 隨著使用者的需求越來越多,對App的使用者體驗也變的要求越來越高。為了更好的應對各種需求,開發人員從軟體工程的角度,將App架構由原來簡單的MVC變成MVVM,VIPER等複雜架構。更換適合業務的架構,是為了後期能更好的維護專案。 但是使用者依舊不滿意,繼續對開發人員提出了更多更高的要求,不

Hi3559AV100外接UVC/MJPEG相機實時採圖設計:VDEC_Send_Stream執行緒分析

  下面隨筆將對Hi3559AV100外接UVC/MJPEG相機實現實時採圖設計的關鍵點-VDEC_Send_Stream執行緒進行分析,一兩個星期前我寫了有三篇系列隨筆,已經實現了專案功能,大家可以參考下面隨筆: Hi3559AV100外接UVC/MJPEG相機實時採圖設計(一):Linux USB攝像頭驅

學習yaf路由

protect 根據 route 構造 bar mod enc ble function 使用路由 使用路由既可以讓之很復雜,同時也能讓它很簡單,這是歸於你的應用。然而使用一個路由是很簡單的,你可以添加你的路由協議給路由器,這樣就OK了! 不同的路

MVC實戰之排球計分—— View設計與實現

service family 角色 元素 需要 rom 之前 con xsl (view)視圖 視圖是用戶看到並與之交互的界面。對老式的Web應用程序來說,視圖就是由HTML元素組成的界面,在新式的Web應用程序中,HTML依舊在視圖中扮演著重要的角色,但一些新的技術已層出

RabbitMQ ——路由

路由 key eve lar enter align queue bit 機會 RabbitMQ(四) ——路由 (轉載請附上本文鏈接——linhxx) 一、概述 路由模式(routing)是交換機不將消息廣播到全部的隊列中,而是采用交換機的另一種模式——direc

原創小程序設計一起來聊天吧!

www tco 了解 代碼 oid ole com value etc   解決上述問題之後,作為“客戶”的我,又覺得啟動的時候啟動的是若幹個客戶端窗口文件和一個服務器文件,服務器的輸出都是在控制臺輸出的,有了之前解決問題的經驗,考慮能不能寫一個服務器窗口文件,將服務器的輸

PL/SQL程序設計—— 遊標

查詢 多行 記錄 設計 col acl 形式 一個 程序 在PL/SQL程序中,對於處理多行記錄的事務經常使用遊標來實現。 (一)遊標概念   為了處理SQL語句,ORACLE必須分配一片叫上下文的區域來處理所必需的信息,其中包括要處理的行的數目,一個指向語句被分析以後的表

基於中臺思想的物流系統設計:物流服務與物流詳情

一、概述 在物流系統中,中臺只負責物流訂單的流轉,具體的物流履行往往需要對接第三方快遞公司。由於第三方快遞公司的技術標準不一樣,因此我們需要對第三方快遞公司的介面進行封裝,這裡涉及到兩大類封裝,一個是下發請求的封裝,一個是接收回傳的物流詳情的封裝。對於下發快遞公司,我們不僅僅是介面層面的封裝,而是抽象出了

計算機組成與設計—— 加法和減法的實現

二進位制加法 半加器(Half Adder) 半加器的功能是將兩個1位二進位制數相加。輸入埠A、B,輸出埠S(輸出),C(進位)。 其有一個很明顯的缺點:不能將低位的進位參與運算。   全加器(Full Adder) 全加器由兩個半加器構成。輸入埠A、B、Cin,輸出埠S(和)、Co

大資料之hbase --- rowkey設計原則模擬通話日誌,BloomFilter,phonix環境部署,hive-hbase整合

一、rowkey設計 -- 模擬通話日誌 -------------------------------------------------- 1.建表 $hbase> create 'ns1:calllogs' , 'f1' 2.編寫

深入理解並行程式設計-分割和同步設計

原文連結    作者:paul    譯者:謝寶友,魯陽,陳渝 圖1.1:設計模式與鎖粒度 圖1.1是不同程度同步粒度的圖形表示。每一種同步粒度都用一節內容來描述。下面幾節主要關注鎖,不過其他幾種同步方式也有類似的粒度問題。 1.1. 序列程式 圖1.2:Intel處理器的MIPS/時鐘

學習 Laravel 那些坑 路由

位置 5.4的時候,路由還在 app/Http/routes.php 5.6的時候就挪到 app 目錄外的 routes/web.php 對於一個 MVC 框架,如何解析路由是非常重要的問題,這樣變來變去真得好嗎?

JavaWeb開發設計高併發方案設計

高併發時要求系統對高QPS併發請求快速處理,並且有足夠的系統容量處理這些資料。 簡單總結一下高併發系統的技術點: 1、請求排程 1)使用CDN CDN即內容分發網路,CDN系統能夠實時地根據網路流量和各節點的連線、負載狀況以及到使用者的距離和響應時間等綜合資訊將使用者的請求重新導向離

深入淺出微服務框架dubbo設計

四、 設計篇本篇是《深入淺出微服務框架dubbo》的終篇4.1執行緒模型netty+zookeeper+curator+dubboProtocol+hession2seralization組合4.2協議資料格式這裡引用官網的一張圖:第三行代表了協議頭,Magic,serial

RabbitMQ 路由選擇 Routing

                上一篇部落格我們建立了一個簡單的日誌系統,我們能夠廣播日誌訊息給所有你的接收者,如果你不瞭解,請檢視:RabbitMQ (三) 釋出/訂閱。本篇部落格我們準備給日誌系統新增新的特性,讓日誌接收者能夠訂閱部分訊息。例如,我們可以僅僅將致命的錯誤寫入日誌檔案,然而仍然在控制面板上打

spring cloud快速入門教程路由閘道器Zuul

現在服務也統一註冊管理了,配置也統一管理了,我們就可以瘋狂的開發各項微服務了,是不是還覺得少了點什麼?前端怎麼訪問到相應服務?這就用到路由網關了。 路由閘道器就是整個微服務的統一入口,看看第一張的架構圖,專案的前端做成了動靜分離,靜態檔案、html頁面、css檔案和js檔案

【Material Design視覺設計語言】UI元件設計:表格

【Material Design視覺設計語言】章節列表 表格是一個用來展示原始資料集,使用者可操作的視覺化的網格結構,並且通常出現於桌面企業產品中。 一 表格的佈局 1.1 表格型別 1.1.1 基本表格 基本的表格佈局如下

一種分散式框架設計

我們設計的分散式系統,在正常工作時呈現出網狀。服務有層次性,客戶的請求會逐步經歷各層服務進行處理,當遍歷完所有服務後才會完成一次請求。每層服務會有若干臺機器,上游節點的機器可以把輸出結果傳遞到下游節點的任意一臺機器上。 當服務所依賴的資料需要更新時,我們需要做好同步工作,

演算法分析設計動態規劃

動態規劃的概念複習 每次決策依賴於當前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。 動態規劃的思想和策略 將待求解的問題分解為若干個子問題,按順序求解子階段,前一子問題的解,為後

開始一個React專案路由例項v4

前言 在開始一個React專案(三)路由基礎(v4)中我大概總結了一下web應用的路由,這一篇我會接著上一篇分享一些例子。 簡單的路由示例 一個最簡單的網站結構是首頁和幾個獨立的二級頁面,假如我們有三個獨立的二級頁面分別為:新聞頁、課程頁、加入我們,路