• 再談I2C!結合項目經驗説説這項知識

    目錄 背景 硬件層 數據傳輸協議 實際上如何工作? 單個主設備連接多個從機 多個主設備連接多個從機 如何編程? 總結 背景 I2C(Inter-Integrated Circuit),中文應該叫集成電路總線,它是一種串行通信總線,使用多主從架構,是由飛利浦公司在1980年代初設計的,方便了主板、嵌入式系統或手機與周邊設備組件之間的通訊。由於其簡單性,它被廣泛用於微控制器與傳感器陣列,顯示器,IoT設備,EEPROM等之間的通信。 I2C最重要的功能包括: 只需要兩條總線; 沒有嚴格的波特率要求,例如使用RS232,主設備生成總線時鐘; 所有組件之間都存在簡單的主/從關係,連接到總線的每個設備均可通過唯一地址進行軟件尋址; I2C是真正的多主設備總線,可提供仲裁和衝突檢測; 傳輸速度; 標準模式:Standard Mode=100  Kbps 快速模式:Fast Mode=400  Kbps 高速模式:High speed mode=3.4 Mbps 超快速模式:Ultra fast mode=5 Mbps 最大主設備數:無限制; 最大從機數:理論上是127; 以上是I2C的一些重要特點,下面會進一步對I2C進行介紹。 硬件層 I2C協議僅需要一個SDA和SCL引腳。SDA是串行數據線的縮寫,而SCL是串行時鐘線的縮寫。這兩條數據線需要接上拉電阻。 設備間的連接如下所示: 使用I2C,可以將多個從機(Slave)連接到單個主設備(Master),並且還可以有多個主設備(Master)控制一個或多個從機(Slave)。 假如希望有多個微控制器(MCU)將數據記錄到單個存儲卡或將文本顯示到單個LCD時,這個功能就非常有用。 I2C總線(SDA,SCL)內部都使用漏極開路驅動器(開漏驅動),因此SDA和SCL 可以被拉低為低電平,但是不能被驅動為高電平,所以每條線上都要使用一個上拉電阻,默認情況下將其保持在高電平; 上拉電阻的值取決於許多因素。德州儀器TI 建議 使用以下公式來計算正確的上拉電阻值: 其中  是邏輯低電壓; 是邏輯低電流; 是信號的最大上升時間; 是總線(電線)電容; 具體如下所示: 根據上表,這裏不難發現需要在做電阻選擇需要滿足幾個條件; 灌電流 最大值為 ; 另外I2C總線規範和用户手冊還為低電平輸出電壓 設置了最大值為0.4V 所以根據上述公式可以計算,對於5V的電源,每個上拉電阻阻值至少1.53kΩ,而對於3.3V的電源,每個電阻阻值至少967Ω。 如果覺得計算電阻值比較麻煩,也可以使用典型值 4.7kΩ。 上述推導過程可以參考 TI的文檔《I2C Bus Pullup Resistor Calculation》 //www.ti.com/lit/an/slva689/slva689.pdf 最終在調試的時候,當我們測量SDA或SCL信號並且邏輯LOW上的電壓高於0.4V時,我們就知道可以知道灌電流太高了; 當然,這並不意味着每當灌電流超過3mA時,設備就會立即停止工作。但是,在操作超出其規格的設備時,應始終小心,因為它可能導致通信故障,縮短其使用壽命甚至甚至永久損壞設備。 數據傳輸協議 主設備和從設備進行數據傳輸時遵循以下協議格式。數據通過一條SDA數據線在主設備和從設備之間傳輸0和1的串行數據。串行數據序列的結構可以分為,開始條件,地址位,讀寫位,應答位,數據位,停止條件,具體如下所示; 開始條件 當主設備決定開始通訊時,需要發送開始信號,需要執行以下動作; 先將SDA線從高壓電平切換到低壓電平; 然後將 SCL從高電平切換到低電平; 在主設備發送開始條件信號之後,所有從機即使處於睡眠模式也將變為活動狀態,並等待接收地址位。 具體如下圖所示; 地址位 通常地址位佔7位數據,主設備如果需要向從機發送/接收數據,首先要發送對應從機的地址,然後會匹配總線上掛載的從機的地址; I2C還支持10位尋址; 讀寫位 該位指定數據傳輸的方向; 如果主設備需要將數據發送到從設備,則該位設置為  0; 如果主設備需要往從設備接收數據,則將其設置為  1 。 ACK / NACK 主機每次發送完數據之後會等待從設備的應答信號ACK; 在第9個時鐘信號,如果從設備發送應答信號 ACK,則 SDA會被拉低; 若沒有應答信號 NACK,則 SDA會輸出為高電平,這過程會引起主設備發生重啓或者停止; 數據塊 傳輸的數據總共有8位,由發送方設置,它需要將數據位傳輸到接收方。 發送之後會緊跟一個ACK / NACK位,如果接收器成功接收到數據,則設置為0。否則,它保持邏輯“ 1”。 重複發送,直到數據完全傳輸為止。 停止條件 當主設備決定結束通訊時,需要發送開始信號,需要執行以下動作; 先將SDA線從低電壓電平切換到高電壓電平; 再將SCL線從高電平拉到低電平; 具體如下圖所示; 實際上如何工作? 第一步:起始條件 主設備通過將SDA線從高電平切換到低電平,再將SCL線從高電平切換到低電平,來向每個連接的從機發送啓動條件 : 第二步:發送從設備地址 主設備向每個從機發送要與之通信的從機的7位或10位地址,以及相應的讀/寫位; 第三步:接收應答 每個從設備將主設備發送的地址與其自己的地址進行比較。如果地址匹配,則從設備通過將SDA線拉低一位以表示返回一個ACK位; 如果來自主設備的地址與從機自身的地址不匹配,則從設備將SDA線拉高,表示返回一個NACK位; 第四步:收發數據 主設備發送或接收數據到從設備; 第五步:接收應答 在傳輸完每個數據幀後,接收設備將另一個ACK位返回給發送方,以確認已成功接收到該幀: 第六步:停止通信 為了停止數據傳輸,主設備將SCL切換為高電平,然後再將SDA切換為高電平,從而向從機發送停止條件; 單個主設備連接多個從機 I2C總線上的主設備使用7位地址對從設備進行尋址,可以使用128( )個從機地址。 請使用4.7K上拉電阻將SDA和SCL線連接到Vcc; 多個主設備連接多個從機 多個主設備可以連接到一個或多個從機; 當兩個主設備試圖通過SDA線路同時發送或接收數據時,同一系統中的多個主設備就會出現問題。 為了解決這個問題,每個主設備都需要在發送消息之前檢測SDA線是低電平還是高電平; 如果SDA線為低電平,則意味着另一個主設備可以控制總線,並且主設備應等待發送消息。 如果SDA線為高電平,則可以安全地發送消息。 要將多個主設備連接到多個從機,請使用下圖,其中4.7K上拉電阻將SDA和SCL線連接到Vcc: 如何編程? Talk is cheap. Show me the code. 參考了STM32的HAL庫中I2C驅動,主設備發送函數HAL_I2C_Master_Transmit()具體如下: /**  * @brief  Transmits in master mode an amount of data in blocking mode.  * @param  hi2c Pointer to a I2C_HandleTypeDef structure that contains  *                the configuration information for the specified I2C.  * @param  DevAddress Target device address: The device 7 bits address value  *         in datasheet must be shifted to the left before calling the interface  * @param  pData Pointer to data buffer  * @param  Size Amount of data to be sent  * @param  Timeout Timeout duration  * @retval HAL status  */HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c,                                           uint16_t DevAddress,                                           uint8_t *pData,                                           uint16_t Size,                                           uint32_t Timeout){  uint32_t tickstart = 0x00U;  /* Init tickstart for timeout management*/  tickstart = HAL_GetTick();  if(hi2c->State == HAL_I2C_STATE_READY){    /* Wait until BUSY flag is reset */    if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG, tickstart) != HAL_OK){      return HAL_BUSY;    }    /* Process Locked */    __HAL_LOCK(hi2c);    /* Check if the I2C is already enabled */    if((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE){      /* Enable I2C peripheral */      __HAL_I2C_ENABLE(hi2c);    }    /* Disable Pos */    hi2c->Instance->CR1 &= ~I2C_CR1_POS;    hi2c->State     = HAL_I2C_STATE_BUSY_TX;    hi2c->Mode      = HAL_I2C_MODE_MASTER;    hi2c->ErrorCode = HAL_I2C_ERROR_NONE;    /* Prepare transfer parameters */    hi2c->pBuffPtr    = pData;    hi2c->XferCount   = Size;    hi2c->XferOptions = I2C_NO_OPTION_FRAME;    hi2c->XferSize    = hi2c->XferCount;    /* Send Slave Address */    if(I2C_MasterRequestWrite(hi2c, DevAddress, Timeout, tickstart) != HAL_OK){      if(hi2c->ErrorCode == HAL_I2C_ERROR_AF){        /* Process Unlocked */        __HAL_UNLOCK(hi2c);        return HAL_ERROR;      }else{        /* Process Unlocked */        __HAL_UNLOCK(hi2c);        return HAL_TIMEOUT;      }    }    /* Clear ADDR flag */    __HAL_I2C_CLEAR_ADDRFLAG(hi2c);    while(hi2c->XferSize > 0U){      /* Wait until TXE flag is set */      if(I2C_WaitOnTXEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK){        if(hi2c->ErrorCode == HAL_I2C_ERROR_AF){          /* Generate Stop */          hi2c->Instance->CR1 |= I2C_CR1_STOP;          return HAL_ERROR;        }else{          return HAL_TIMEOUT;        }      }      /* Write data to DR */      hi2c->Instance->DR = (*hi2c->pBuffPtr++);      hi2c->XferCount--;      hi2c->XferSize--;      if((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == SET)          && (hi2c->XferSize != 0U)){        /* Write data to DR */        hi2c->Instance->DR = (*hi2c->pBuffPtr++);        hi2c->XferCount--;        hi2c->XferSize--;      }      /* Wait until BTF flag is set */      if(I2C_WaitOnBTFFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK){                  if(hi2c->ErrorCode == HAL_I2C_ERROR_AF){          /* Generate Stop */          hi2c->Instance->CR1 |= I2C_CR1_STOP;          return HAL_ERROR;        }else{          return HAL_TIMEOUT;        }      }    }    /* Generate Stop */    hi2c->Instance->CR1 |= I2C_CR1_STOP;    hi2c->State = HAL_I2C_STATE_READY;    hi2c->Mode = HAL_I2C_MODE_NONE;        /* Process Unlocked */    __HAL_UNLOCK(hi2c);    return HAL_OK;  }else{    return HAL_BUSY;  }} 總結 本文主要介紹I2C的入門基礎知識,從I2C協議的硬件層,協議層進行了簡單介紹;作者能力有限,難免存在錯誤和紕漏,請大佬不吝賜教。 -END- 來源 | 小麥大叔 作者 | 菜刀和小麥 | 整理文章為傳播相關技術,版權歸原作者所有 | | 如有侵權,請聯繫刪除 | 【香港神州集運】用C實現:均值計算的兩種算法 【香港神州集運】單片機DSP必備概念:快速教會你傅立葉算法 【香港神州集運】幾種常見的校驗算法 【香港神州集運】C語言編程:九種必會查找算法(附完整代碼) 【香港神州集運】圖解機器學習:請不要再説看不懂算法! 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-10-25 關鍵詞: 嵌入式 通訊

  • 2020-1024=996:歸併排序!

    今天繼續給大家分享排序算法裏面的另外一種排序算法:歸併排序! 一、歸併排序: 1、歸併排序操作的核心思想: a、確定分界點:mid=(l+r)/2 b、遞歸排序左邊和右邊(排完左右兩邊的數,就會成為兩個有序的序列了) c、歸併(把上面的兩個有序序列合併成一個有序的序列,用一個簡單的詞來説,就是合二為一!) 2、舉例: 比如上圖我們有兩組已經排好的序列數字,我們要進行第三步合併,該如何進行呢?思路如下: a、這裏先定義一個空的數組res,它主要是為了臨時存放合併序列排序好的數字;我們從圖中可以看到,第一個序列指針i指向數字1,第二序列指針j指向2,這個時候我們要比較兩個數字的大小,小的數字就放到臨時數組res裏面去,這裏我們明顯知道數字1小於2,所以把1放到臨時數組res裏去 b、然後指針i往下移動,如下圖所示,再次進行比較,明顯發現指針j指向的數字2更小,把它放到res裏面去,然後指針j往下移動,指針i不動,後面依次類推 c、如下圖所示,兩個指針都指向了數字5,如果遇到兩個數字一樣的話,一般是把第一個序列的數字放到臨時數組res裏面去,這點稍微要注意一下 d、最後把臨時數組裏面的是數字放到原來的數組裏面去 注意:一個算法穩定,並不能説它的時間效率是穩定的;這裏的穩定是説兩個序列中有兩個數是相同的,如果在排完序之後,他們的位置還是沒有發生變化的話,那麼這個排序就是穩定的,反之亦然! 3、歸併排序的平均時間複雜度的計算推導: 注:圖片來源://visualgo.net/zh/sorting 從圖片的縱性來分析,當拆解到1的時候,這個時候什麼數等於n除於它等於1,通過計算,我們知道是logn,然後再從橫向分析,我們要最多比較n個數字,所以歸併排序的時間複雜度就是:nlogn 二、代碼示例: 代碼: #include using namespace std;const int N = 1e5 + 10;int n;int q[N], tmp[N];void merge_sort(int q[],int l, int r){ if(l>=r)return;//判斷序列中是否為空或者只有一個數字,如果是的話,我們就不用排序了 //確定分界點 int mid = l + r >> 1; //遞歸處理 merge_sort(q,l,mid); merge_sort(q,mid+1,r); //定義雙指針 int k =0,i = l, j= mid+1; //歸併處理 while(i 

    時間:2020-10-25 關鍵詞: 排序算法 嵌入式

  • 萬能算法PID最全總結

    PID的數學模型 在工業應用中PID及其衍生算法是應用最廣泛的算法之一,是當之無愧的萬能算法,如果能夠熟練掌握PID算法的設計與實現過程,對於一般的研發人員來講,應該是足夠應對一般研發問題了,而難能可貴的是,在很多控制算法當中,PID控制算法又是最簡單,最能體現反饋思想的控制算法,可謂經典中的經典。經典的未必是複雜的,經典的東西常常是簡單的,而且是最簡單的。PID算法的一般形式: PID算法通過誤差信號控制被控量,而控制器本身就是比例、積分、微分三個環節的加和。這裏我們規定(在t時刻): 1.輸入量為 2.輸出量為 3.偏差量為  PID算法的數字離散化 假設採樣間隔為T,則在第K個T時刻: 偏差=  積分環節用加和的形式表示,即  微分環節用斜率的形式表示,即 PID算法離散化後的式子:  則可表示成為:   其中式中: 比例參數 :控制器的輸出與輸入偏差值成比例關係。系統一旦出現偏差,比例調節立即產生調節作用以減少偏差。特點:過程簡單快速、比例作用大,可以加快調節,減小誤差;但是使系統穩定性下降,造成不穩定,有餘差。 積分參數 :積分環節主要是用來消除靜差,所謂靜差,就是系統穩定後輸出值和設定值之間的差值,積分環節實際上就是偏差累計的過程,把累計的誤差加到原有系統上以抵消系統造成的靜差。 微分參數 :微分信號則反應了偏差信號的變化規律,或者説是變化趨勢,根據偏差信號的變化趨勢來進行超前調節,從而增加了系統的快速性。 PID的基本離散表示形式如上。目前的這種表述形式屬於位置型PID,另外一種表述方式為增量式PID,由上述表達式可以輕易得到: 那麼: 上式就是離散化PID的增量式表示方式,由公式可以看出,增量式的表達結果和最近三次的偏差有關,這樣就大大提高了系統的穩定性。需要注意的是最終的輸出結果應該為: 輸出量 =  + 增量調節值 目的 PID 的重要性應該無需多説了,這個控制領域的應用最廣泛的算法了. 本篇文章的目的是希望通過一個例子展示算法過程,並解釋以下概念: (1)簡單描述何為PID, 為何需要PID,PID 能達到什麼作用。 (2)理解P(比例環節)作用:基礎比例環節。 缺點: 產生穩態誤差. 疑問: 何為穩態誤差 為什麼會產生穩態誤差. (3)理解I(積分環節)作用:消除穩態誤差. 缺點: 增加超調 疑問: 積分為何能消除穩態誤差? (4) 理解D(微分環節)作用:加大慣性響應速度,減弱超調趨勢 疑問: 為何能減弱超調 (5)理解各個比例係數的作用 何為PID以及為何需要PID? 以下即PID 控制的整體框圖,過程描述為:  設定一個輸出目標,反饋系統傳回輸出值,如與目標不一致,則存在一個誤差,PID 根據此誤差調整輸入值,直至輸出達到設定值. 疑問: 那麼我們為什麼需要PID 呢,比如我控制温度,我不能監控温度值,温度值一到就停止嗎? 這裏必須要先説下我們的目標,因為我們所有的控制無非就是想輸出能夠達到我們的設定,即如果我們設定了一個目標温度值,那麼我們想要一個什麼樣的温度變化呢. 比如設定目標温度為30度, 目標無非是希望達到圖1 希望其能夠快速而且沒有抖動的達到30度. 那這樣大家應該就明白,如果使用温度一到就停止的辦法,當然如果要求不高可能也行,當肯定達不到圖1 這樣的要求,因為温度到了後餘温也會讓温度繼續升高.而且温度自身也會通過空氣散熱的. 圖  系統輸出的響應目標 綜上所述,我們需要PID的原因無非就是普通控制手段沒有辦法使輸出快速穩定的到達設定值。 控制器的P,I,D項選擇 下面將常用的各種控制規律的控制特點簡單歸納一下: (1)、比例控制規律P:採用P控制規律能較快地克服擾動的影響,它的作用於輸出值較快,但不能很好穩定在一個理想的數值,不良的結果是雖較能有效的克服擾動的影響,但有餘差出現。它適用於控制通道滯後較小、負荷變化不大、控制要求不高、被控參數允許在一定範圍內有餘差的場合。如:金彪公用工程部下設的水泵房冷、熱水池水位控制;油泵房中間油罐油位控制等。 (2)、比例積分控制規律(PI):在工程中比例積分控制規律是應用最廣泛的一種控制規律。積分能在比例的基礎上消除餘差,它適用於控制通道滯後較小、負荷變化不大、被控參數不允許有餘差的場合。如:在主線窯頭重油換向室中F1401到F1419號槍的重油流量控制系統;油泵房供油管流量控制系統;退火窯各區温度調節系統等。 (3)、比例微分控制規律(PD):微分具有超前作用,對於具有容量滯後的控制通道,引入微分參與控制,在微分項設置得當的情況下,對於提高系統的動態性能指標,有着顯著效果。因此,對於控制通道的時間常數或容量滯後較大的場合,為了提高系統的穩定性,減小動態偏差等可選用比例微分控制規律。如:加熱型温度控制、成分控制。需要説明一點,對於那些純滯後較大的區域裏,微分項是無能為力,而在測量信號有噪聲或週期性振動的系統,則也不宜採用微分控制。如:大窯玻璃液位的控制。 (4)、例積分微分控制規律(PID):PID控制規律是一種較理想的控制規律,它在比例的基礎上引入積分,可以消除餘差,再加入微分作用,又能提高系統的穩定性。它適用於控制通道時間常數或容量滯後較大、控制要求較高的場合。如温度控制、成分控制等。 鑑於D規律的作用,我們還必須瞭解時間滯後的概念,時間滯後包括容量滯後與純滯後。其中容量滯後通常又包括:測量滯後和傳送滯後。測量滯後是檢測元件在檢測時需要建立一種平衡,如熱電偶、熱電阻、壓力等響應較慢產生的一種滯後。而傳送滯後則是在傳感器、變送器、執行機構等設備產生的一種控制滯後。純滯後是相對與測量滯後的,在工業上,大多的純滯後是由於物料傳輸所致,如:大窯玻璃液位,在投料機動作到核子液位儀檢測需要很長的一段時間。 總之,控制規律的選用要根據過程特性和工藝要求來選取,決不是説PID控制規律在任何情況下都具有較好的控制性能,不分場合都採用是不明智的。如果這樣做,只會給其它工作增加複雜性,並給參數整定帶來困難。當採用PID控制器還達不到工藝要求,則需要考慮其它的控制方案。如串級控制、前饋控制、大滯後控制等。 Kp,Ti,Td三個參數的設定是PID控制算法的關鍵問題。一般説來編程時只能設定他們的大概數值,並在系統運行時通過反覆調試來確定最佳值。因此調試階段程序須得能隨時修改和記憶這三個參數。 數字PID控制器 (1)模擬PID控制規律的離散化   (2)數字PID控制器的差分方程 參數的自整定 在某些應用場合,比如通用儀表行業,系統的工作對象是不確定的,不同的對象就得采用不同的參數值,沒法為用户設定參數,就引入參數自整定的概念。實質就是在首次使用時,通過N次測量為新的工作對象尋找一套參數,並記憶下來作為以後工作的依據。具體的整定方法有三種:臨界比例度法、衰減曲線法、經驗法。 1、臨界比例度法(Ziegler-Nichols) 1.1  在純比例作用下,逐漸增加增益至產生等副震盪,根據臨界增益和臨界週期參數得出PID控制器參數,步驟如下: (1)將純比例控制器接入到閉環控制系統中(設置控制器參數積分時間常數Ti =∞,實際微分時間常數Td =0)。 (2)控制器比例增益K設置為最小,加入階躍擾動(一般是改變控制器的給定值),觀察被調量的階躍響應曲線。 (3)由小到大改變比例增益K,直到閉環系統出現振盪。 (4)系統出現持續等幅振盪時,此時的增益為臨界增益(Ku),振盪週期(波峯間的時間)為臨界週期(Tu)。 (5) 由表1得出PID控制器參數。 表1 1.2  採用臨界比例度法整定時應注意以下幾點: (1)在採用這種方法獲取等幅振盪曲線時,應使控制系統工作在線性區,不要使控制閥出現開、關的極端狀態,否則得到的持續振盪曲線可能是“極限循環”,從線性系統概念上説系統早已處於發散振盪了。 (2)由於被控對象特性的不同,按上表求得的控制器參數不一定都能獲得滿意的結果。對於無自平衡特性的對象,用臨界比例度法求得的控制器參數往住使系統響應的衰減率偏大(ψ>0.75 )。而對於有自平衡特性的高階等容對象,用此法整定控制器參數時系統響應衰減率大多偏小(ψ<0.75 )。為此,上述求得的控制器參數,應針對具體系統在實際運行過程中進行在線校正。 (3) 臨界比例度法適用於臨界振幅不大、振盪週期較長的過程控制系統,但有些系統從安全性考慮不允許進行穩定邊界試驗,如鍋爐汽包水位控制系統。還有某些時間常數較大的單容對象,用純比例控制時系統始終是穩定的,對於這些系統也是無法用臨界比例度法來進行參數整定的。 (4)只適用於二階以上的高階對象,或一階加純滯後的對象,否則,在純比例控制情況下,系統不會出現等幅振盪。 1.3  若求出被控對象的靜態放大倍數KP=△y/△u ,則增益乘積KpKu可視為系統的最大開環增益。通常認為Ziegler-Nichols閉環試驗整定法的適用範圍為: (1) 當KpKu > 20時,應採用更為複雜的控制算法,以求較好的調節效果。 (2)當KpKu < 2時,應使用一些能補償傳輸遲延的控制策略。 (3)當1.5

    時間:2020-10-25 關鍵詞: C語言 pid

  • 紫光8GB DDR4內存條開賣!顆粒神祕未知

    國產——至少國產品牌——內存產品越來越豐富,紫光集團旗下西安紫光國芯的新款DDR4 SO-DIMM筆記本內存馬甲條也悄然上市了。 新條採用了綠色的PCB電路板,覆蓋紫色的散熱馬甲,基於熱傳導技術,上有紫光國芯的標識和電路圖案,但整體比較樸實,畢竟是裝在本子裏邊的。 規格方面,單條容量8GB、16GB, 還有兩條16GB套裝,頻率可選2666MHz、3200MHz,對應時序分別為CL19-19-19、CL22-22-22,電壓1.2V。 平台兼容Intel、AMD,並支持XMP一鍵超頻,BIOS中選擇配置文件即可獲得需要的頻率和時序,不過僅限2666MHz頻率版本。 此前的無馬甲普條有4GB、8GB、16GB三種容量,2400MHz、2666MHz兩種頻率,套上馬甲之後確實提升了一個檔次。 紫光沒有透露使用的具體DRAM顆粒,估計不是合肥長鑫的國產顆粒。 價格方面,8GB 2666MHz 219元,8GB 3200MHz 249元,提供三年換新、終身質保服務。 -END- 來源 | 快科技 | | 如有侵權,請聯繫刪除 | 【香港神州集運】SK海力士收購英特爾NAND閃存業務 【香港神州集運】首片國產6英寸碳化硅晶圓產品發佈!在上海 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-10-25 關鍵詞: 消費電子 內存

  • PNY XLR8 CS3040 頂級固態硬盤,為發燒而生

    PNY XLR8 CS3040 頂級固態硬盤,為發燒而生

    在下述內容中,本文將對PNY XLR8 CS3040 頂級固態硬盤的相關消息予以報道,一起來了解下它的具體情況吧。 該固態硬盤採用 M.2 2280 接口,它還具有與 NVMe 1.4 兼容的 PCIe 4.0 x4 接口。容量方面, XLR8 CS3040 提供了 500 GB、1 TB 和 2 TB 三種版本。 性能方面, XLR8 CS3040 2 TB 版本讀取 5600 MB / 秒,寫入 4300 MB / 秒;1 TB 版本讀取 5600 MB / 秒,寫入 4300 MB / 秒;500 GB 版本讀取速度為 5600 MB / 秒,寫入速度為 2600 MB / 秒。但官方並未公佈隨機讀取的數據。 其他方面,該固態硬盤壽命為2000000 小時,將於 11 月中旬上市,售價屆時方公佈。 上述便是小編這次為大家推薦的內容,希望大家喜歡,想了解更多內容,請關注我們網站哦。

    時間:2020-10-25 關鍵詞: 性能 固態硬盤 pny

  • 一加 Nord N10 5G 最新消息流露,搶先看

    一加 Nord N10 5G 最新消息流露,搶先看

    一加是大家的關注品牌之一,為此小編將帶來一加 Nord N10 5G 智能手機的最新介紹,一起來看看吧。 一加 Nord N10 5G 配備了 6.49 英寸 FHD+ 顯示屏,刷新率為 90Hz。這款手機搭載高通驍龍 690 芯片,配備 6GB RAM 和 128GB 存儲空間,內置 4300mAh 電池,但是還沒有關於快充的消息。 其他方面,一加 Nord N10 5G 的背面配有四攝像頭系統,包括一個 64MP 主攝像頭,一個 8MP 廣角鏡頭和兩個 2MP 鏡頭。 而一加 Nord N100 將 配備 6.52 英寸 HD+ LCD 顯示屏,60Hz 刷新率,搭載高通驍龍 460 處理器,並配備 4GB RAM 和 64GB 內部存儲。一加 Nord N100 內置 5000mAh 電池,但沒有提及快充。 一加 Nord N100 背面同樣將配備三攝像頭,包括一個 13MP 主攝,一個 2MP 微距鏡頭和一個 2MP 廣角鏡頭,還有一個 8MP 前置攝像頭,該手機不支持 5G,支持雙 SIM 卡和 USB-C 接口。一加 Nord N10 和 Nord N100 都將提供 3.5mm 耳機插孔。 最後,小編誠心感謝大家的閲讀。你們的每一次閲讀,都是對小編莫大的鼓勵。最後的最後,祝大家有個精彩的一天。

    時間:2020-10-25 關鍵詞: 智能手機 處理器 一加

  • 雷神 Master 便攜顯示器,顯示器,隨身帶

    雷神 Master 便攜顯示器,顯示器,隨身帶

    本文將對雷神即將推出的 Master 便攜顯示器予以介紹,如果你想對它的具體情況一探究竟,或者想要增進對它的認識,不妨請看以下內容哦。 雷神 Master 便攜顯示器擁有 6mm 輕薄機身,FHD 144Hz 屏。雷神 Master 便攜顯示器採用了 6mm 輕薄機身的設計,鋁合金打造,配備了一體式可翻轉支架,0-170 度調節。 雷神 Master 便攜顯示器採用了15.6 英寸 2.5D 曲面玻璃屏,支持交互式 10 點觸控,刷新率為 144Hz,支持 TNT、華為 EMUI 和三星 DEX。 雷神 Master 便攜顯示器擁有 micro USB、3.5mm 音頻接口、mini HDMI 和兩個 USB-C 接口。 經由小編的介紹,不知道你對它是否充滿了興趣?如果你想對它有更多的瞭解,不妨嘗試度娘更多信息或者在我們的網站裏進行搜索哦。

    時間:2020-10-25 關鍵詞: 顯示器 master 雷神

  • 新都智慧物流產業園區瞭解一下

    新都智慧物流產業園區瞭解一下

    沿物流大道進入新都智慧物流產業園區,物流業的國際巨頭和國內大咖鱗次櫛比,美國安博、德國郵政DHL、普洛斯、澳大利亞嘉民、阿里“菜鳥”、京東亞洲一號等行業領軍企業每時每刻都在吞吐着成噸的貨物。 每天上萬台次的貨車來回穿梭,數萬人在產業鏈條上忙碌,新都智慧物流產業園是成都市統籌佈局建設的66個產業功能區之一,已引進商貿物流平台企業49家、二次招商企業3500餘家,是名副其實的“流量”產業功能區。“高‘流量’為功能區發展增添活力,也給園區黨建工作帶來了挑戰。傳統物流企業亟待轉型升級,大量流動人口管理存在壓力。”新都智慧物流產業園管委會主任張謹坦言,推動產業園區高質量發展,抓好黨建也是一種生產力。 思路一變天地寬,把黨建植入服務企業的全過程,用黨建激發企業內生動力,助力企業“安家興業”。當黨建活動走近五湖四海的卡車司機,和諧園區營造的“家”更立體鮮活了。 載體 “卡車司機之家”歡迎你 未來打造物流特色產業社區 常年在四川湖南兩地跑運輸的老秦師傅從司機之家微信羣裏獲悉,傳化公路港裏的“卡車司機之家”全新升級亮相了。卸了貨停好車,老秦就直奔“卡車司機之家”。 “卡車司機之家”動感的裝飾畫、門前色彩絢麗的運動休閒區在忙碌的公路港裏格外醒目,年輕化的色調似乎要為長途奔襲的司機們提提神。室內不僅有司機們學習車輛維護知識的會議室,在司機休息室裏,按摩椅、手機充電器、書屋、健身室等設施設備配置齊全。 其實真正讓老秦心儀的並非“卡車司機之家”完善的配套設施,而是這裏時常會舉行的“卡友大講堂”活動,新都智慧物流產業園管委會藉助這一平台,經常會組織司機中的流動黨員開展黨建活動。老秦在部隊入黨,退伍後幹上運輸,沒想到在新都智慧物流產業園區重新過上了組織生活,“找到組織的感覺真好,每次來都像回家。” 園區管委會針對商貿物流企業流動性強帶來的“組織覆蓋難”“活動開展難”“作用發揮難”等黨建工作難題,堅持“經濟要流量、黨建要留量”理念,把黨建工作植入產業平台、服務平台,讓黨建走進各個羣體。 據瞭解,目前園區管委會依託相對穩定的49家平台企業建立黨組織,把二次招商企業全部納入平台黨組織管理,搭建“黨工委—綜合黨委—產業社區綜合黨委—‘兩新’黨組織”四級黨建工作格局。目前建立了以海寧皮革城、廣匯申蓉汽車園、貴達茶都等為代表的專業市場類黨組織;以傳化公路港、東皓物流為代表的物流運輸類黨組織;以雲集藥業、寶灣物流、中國物流為代表的物流倉儲類黨組織,企業黨組織應建必建率達100%。 據悉,新都智慧物流產業園正在以多功能黨羣服務陣地為切入口,依託傳化公路港市場載體,建設集物流樞紐、倉庫貨站、餐飲住宿、休閒娛樂等業態為一體的物流特色產業社區,打造出“一中心、一街區”的物流人共享空間。 據瞭解,園區以平台為依託,引導企業開展技能培訓、技能競賽、文化體育、維權普法等各項活動,形成陽光正氣的產業功能區發展氛圍。寶灣物流、傳化公路港、安仕吉物流等6家平台企業結合實際打造出各具特色的職工之家、職工小家,營造和諧“家”文化。 活動 豐富多樣活動進企業 黨建引領暖人心 9月23日,智慧物流產業園黨工委攜手新都區衞健局、新都區中醫院,在園區黨羣服務中心——“卡車司機之家”開展了“健康義診進企業、黨建引領暖人心”健康義診活動。 8名醫護人員為園區企業員工及卡車司機就如何預防及治療職業病、如何做好疫情自我防護等知識進行了講解和宣傳。醫護人員為現場諮詢的司機羣體開展測量血壓、檢測血糖、頸椎腰椎一般專項檢查,並根據檢查對象的血壓、血糖情況有針對性地介紹如何通過改善飲食習慣來提高身體素質、如何通過體育鍛煉來增強身體免疫力。 此次活動的開展不僅增強了企業員工及司機羣體的健康意識,也使黨員羣眾真切感受到園區黨組織對流動黨員及企業員工的關心和關愛,提升黨組織的凝聚力和向心力。 像這樣的活動每年都會舉辦多次。如8月舉行的“開學季·關愛行”——貨車司機子女關愛活動就讓許多司機深受感動。園區黨工委實現黨建工作與業務工作雙融合、雙促進,立足職能、服務民生,從思想上、生活上關心關懷園區企業和羣眾,既是幫助解決實際困難,又是良好營商環境的示範。 9月,“法治進園區·營商惠萬企”為主題的黨建結對活動由6名黨員律師組成法治專家團,為園區38家企業“烹飪”一道專業化的法治宣傳和服務“菜品”。這種企業帶着問題諮詢、律師專家團集體討論答疑,通過互動交流當場解答的方式深受企業歡迎。智慧物流產業園黨工委今後還將在走訪企業和與企業日常交流中收集各類涉法需求,並根據實際情況不定期開展此類法律沙龍活動,積極為企業紓困解惑,為企業發展營造良好的園區環境,努力推動智慧物流產業園健康有序發展。 今年7月1日,慶祝中國共產黨成立99週年,園區各支部進行相互學習和交流,就如何開展黨建工作、如何以黨建為引領帶動企業發展進行了交流學習。通過升國旗、唱紅歌、重温入黨誓詞,讓產業園區的黨員們過了一次有意義的聯合主題黨日活動。 機制 黨建指導員進企業 共建共創和諧園區 按照“一個平台企業配備一名黨建工作指導員”的原則,從黨員幹部、非公企業優秀黨務工作者中選派49名兼職黨建工作指導員,社會化選聘4名全職黨建工作指導員,建強益企服務“配送員”隊伍。 “過去企業黨員少,組織生活程序也不是很規範,黨員凝聚力亟待加強”。張榮斌是園區派駐成都市雲集藥業的黨建指導員,來到企業,從最基本的如何過好組織生活開始,幫助企業找到黨建的“感覺”。如今,雲集藥業的黨建已是有聲有色,企業會給每個黨員過政治生日,在這個特殊的生日上,黨員重温誓詞,分享學習心得,前往紅色教育基地,感受黨的文化和歷史的薰陶。 雲集藥業的質量總監曾莉説,找到“感覺”的黨員們在企業的各個崗位都在發揮先鋒模範作用。雲集藥業擔負着各類疫苗的全川配送,在此次新冠疫情期間,為了不耽誤市民的疫苗接種和防疫藥品物資的需求,大年初二,企業開始逐步復工,黨員帶頭放棄休假,堅守崗位,為其他員工起到了很好的示範作用。 曾莉介紹説,目前企業還借鑑公交車設立黨員先鋒車的經驗,在企業的配送貨車中,設立黨員先鋒崗,在藥品分揀倉庫設立黨員責任區,有力地傳播正能量、引導示範黨員的先鋒模範作用。 園區採用支部共建等方式,以黨員為骨幹成立“商友之家”“書記幫幫團”等爭議調解室,做到矛盾糾紛不出園。通過黨組織引領,推動95%以上的企業建立了勞動爭議調解委員會。傳化公路港黨支部成立“紅色老孃舅”,建立黨員聯繫商户、司機常態化制度和協調糾紛平台。 新都智慧物流產業園通過黨建這把金鑰匙,開啓了新都智慧物流園區高質量發展之門。正在朝着功能配套完善,人居環境、營商環境良好,“人城境業”和諧統一的智慧物流新城全力邁進。

    時間:2020-10-25 關鍵詞: 手機 汽車 智慧物流

  • 紅巖為智慧物流行業發展添光添彩

    紅巖為智慧物流行業發展添光添彩

    前段時間舉行了第22屆中國國際工業博覽會。會上,上汽依維柯紅巖商用車有限公司(以下簡稱“上汽紅巖”)的“5G+L4智能重卡”項目獲得了CIIF2020工博會大獎。 據瞭解,近年來,隨着大數據、雲計算、人工智能、區塊鏈等新技術的不斷創新和應用,以及5G網絡的快速建設和投入商用,“智慧物流”進入人們的視野。截至2017年,物流數據、物流雲、物流技術服務的市場規模已超過4000億。綜合國家經濟增長及物流行業發展趨勢等眾多因素,專家預計,2025 年智能物流的市場規模將超過萬億。在這一市場背景下,上汽紅巖5G智能重卡應運而生。 上汽紅巖5G智能卡車是上汽紅巖根據未來自動駕駛多個場景研發的一款L4級的自動駕駛重卡,目前已經在上海洋山港實現了複雜場景下的高階自動駕駛、自動轉向、變道、加減速、自動避讓行人等等。此外,在結合實際場景的基礎上,它還實現了在港口區域自動駕駛、精準停泊、和港口裝卸系統對接完成自動裝卸集裝箱的動作。這些出色表現,恰恰證明了其在智慧物流領域的無限潛力,無論對於物流安全保障還是物流效率提升,都有着深遠影響。 在行駛過程中,許多物流車輛之間由於沒有合理的規劃通行距離、速度,常出現交通事故、堵車,區域通行能力也大打折扣。而上汽紅巖5G智能重卡特有的隊列行駛功能可以有效改善這一交通痛點。基於5G和V2X技術,上汽紅巖5G智能重卡在隊列形式功能的支持下,能在20毫秒內建立車隊間的實時交互通訊,確保自動跟車、車道保持、繞道換行、緊急制動等隊列行駛功能即時實現。正因此,在今年東海大橋的示範運營中,上汽紅巖“5G+L4”智能重卡實現了5輛車的隊列行駛,車速達到60-80公里/小時,隊列行駛間距縮小到17米。不難看出,在未來真正投入商業化運營時,上汽紅巖5G智能重卡有望緩解交通壓力,提升物流運輸效率,保障物流運輸安全,為智慧物流添彩。 值得一提的是,上汽紅巖“5G+L4智能重卡”實車道路測試里程已超過18.5萬公里,台架虛擬仿真測試里程超過700萬公里,應用場景從深水港物流園區-東海大橋-洋山一期擴展到洋山四期。在今年洋山港準商業化運營的基礎上,上汽集團將與上港集團、中國移動等夥伴們攜手並進,下一步實現百輛級,並迅速向千輛級智能重卡大批量商業化運營推進。 從技術研發到示範運營,上汽紅巖在打造5G智能重卡方面始終保持鋭意進取的精神,緊跟上汽集團“新四化”發展戰略 ,以科技創新引領重卡行業智能化。上汽紅巖“5G+L4智能重卡”能夠脱穎而出,獲得CIIF2020工博會大獎,無疑是實至名歸。

    時間:2020-10-25 關鍵詞: 5G 自動駕駛 智慧物流

  • 智能物流概念股上漲

    智能物流概念股上漲

    A股行情在經過了兩天的強勢上漲之後,今天開始出現震盪,有人説,是行情結束了嗎?還是短期的回調?今天我也給大家一個定心丸,目前股指屬於良性調整,回調就是低吸機會!今天在題材上智能物流概念股強勢上漲,楚天科技強勢封板,智能物流概念股上漲的原因是什麼呢?後期的走勢又如何? 一.智能物流概念股上漲原因 1.新基建核心是對物流行業來講是數據價值釋放,這個是非常重要的一個點,特別對於打造新的智慧化物流運營體系,打造物流特色的數字快運有極大的促進作用。新基建的投資資智能物流,利好相關智能物流概念股。 2.國內電商規模不斷擴大,快遞包裹量不斷增加,也促使智能物流得到空前的發展,智能化的物流體系,為物流行業的發展添磚加瓦,智能物流未來幾年將會得到跨越式的發展。 3.到2020年我國智能物流市場規模將超過5000億元,至2025年將突破萬億元。2012年我國智能物流市場規模約為1200億元,至2018年我國智能物流市場規模增長突破4000億元,達到4070億元,年複合增長率達22.58%。 相關上市公司也迎來新的機會。 二.智能物流概念龍頭股 楚天科技:主營業務:製藥裝備的研發、設計、生產、銷售和服務。 誠益通:主營業務:從事製藥、生物製品生產過程中的自動化控制應用,以自主研發的核心技術和自主生產。 杭叉集團:主營業務:叉車等工業車輛的研發、生產和銷售。 海瀾之家:主營業務:高檔精紡呢絨、高檔西服、襯衫,職業服的生產和銷售,染整加工業務。 華鵬飛:主營業務:綜合物流服務、供應鏈商品銷售服務。 新寧物流:主營業務:以電子元器件保税倉儲為核心,併為電子信息產業供應鏈中的原料供應。 順豐控股:主營業務:綜合性快遞物流服務。 德邦股份:主營業務:公路快運與快遞及相關增值業務服務。 韻達快遞:主營業務:綜合快遞物流業務。 圓通快遞:主營業務:綜合性快遞物流服務。

    時間:2020-10-25 關鍵詞: 電子 信息 智能物流

  • 新基建背景下,這些姿勢架構師必須懂!

    今天,北極熊全程參與了“UCloud用户大會暨Think in Cloud 2020”,不知不覺TIC大會自2014年創辦以來已經連續六屆了,本次大會的主題是“構建•創見”,一如既往的保持了高水準。今年1月UCloud在科創板掛牌,一路披荊斬棘,成為中國第一家公有云科創板上市公司。通過本次大會可以感受到,上市之後UCloud中立的初心不改,依然秉持用户為先的理念令人信賴。 本次大會料很多,尤其是技術分論壇,揭祕了許多公有云背後的技術祕密。下面北極熊一一介紹下本次大會技術分論壇的內容,幫助沒有時間參會的朋友快速瞭解下這次大會都有哪些技術乾貨。 一、物聯網,UCloud準備好了! 在《新基建.新物聯》議題中,UCloud 物聯網產品負責人錢波提到,2020年全球活躍的物聯網設備達到100億,到2025年全球物聯網設備數量預計達到220億,這麼多設備必然會給產業互聯網帶來新的變革。 對於物聯網,UCloud早已開始進軍佈局,比如2019年7月發佈了第一版的UIoT Core平台,同時配帶了直連設備的SDK;今年的3月份和國內領先的操作系統商RT-Thread 深度合作推出了RT-Thread生態下的UIoT SDK;今年的5月份重磅發佈了一個面向非直連設備的UIoT Edge 產品,同時也發佈了針對傳感器的Modbus驅動;今年7月份和一應科技聯合發佈了智能社區的解決方案,同時也發佈了基於電力能耗能源的DLT645的設備驅動。 提到物聯網,必然要分為三個部分雲、邊、端,雲主要是解決設備的廣連接和消息的高併發問題;邊主要是解決那些不能直接上雲的設備的連接和本地計算的問題;而那些不能上雲的設備包括大量的傳感器、行業的表計甚至是工業的機牀,我們稱之為端。正是由於UIoT Core實現了設備和雲上面應用的一個連接的橋樑,所以我們通常認為雲端的功能是IoT產品家族的一個最基本的底座。 在物聯網通訊方面,UCloud推出了物聯網通信雲平台-UIoT Core,通過UIoT SDK,最快可實現10分鐘完成設備數據上雲。費用方面1.6元/百萬消息,1.0元/百萬分鐘(100萬分鍾=1.9年),在目前的主流公有云裏面,是性價比最高的。 同時,UCloud物聯網解決方案已經擁有智慧社區、電力能源、智能硬件、智能製藥AIoT合作伙伴和案例,形成了物聯網生態。對此,錢波重點分享了多個使用場景下的案例,像一應科技、晟能科技、奕客圍棋等均已藉助UIoT Core平台實現了雲端物聯。 二、SDN,解決異構網絡互聯互通! 在《異構網絡之SDN解決方案》議題中,UCloud 虛擬網絡平台產品負責人周健首先分享了UCloud SDN網絡的架構,其中UXR系統被設計用來解決各網關的流量轉發,公有云VPC/混合雲網關/物理雲網關主要解決的是異構網絡的互聯互通,UDPN/雲聯網主要解決異構網絡的跨域互聯互通,從而實現統一的網絡模型、高效互聯互通、安全隔離。 UCloud公有云VPC除了提供標準的VPC功能,還支持一系列高級特性,如分佈式路由器DVR,讓用户的東西向流量不成為瓶頸;支持子網跨可用區,滿足用户業務系統的高可用部署需求;支持自定義路由表,滿足用户複雜的自定義網絡架構。 對於有些用户業務來説,需要部署在不同的Region/AZ,以滿足業務級別的高可用。所以,跨域之間的互聯互通也就成為用户網絡的需求。這就可以通過UDPN和雲聯網兩個產品來滿足此類需求。UDPN構建了覆蓋國內外近30節點的專線網絡,支持即買即用,也支持從1M到數G帶寬的動態調整。雲聯網是UCloud正在開發中的一個產品,它把跨域網絡聯通及維護的細節對用户屏蔽,用户只需要將需要打通和斷開的地域向雲聯網中添加和刪除。 最後,周健還分享了一個第三方智能風險管理服務提供商的案例,該客户面臨着在國內分佈廣、組網困難,海外業務需求建設、運維困難,業務發展迅速無法滿足快速擴容的困境。通過UCloud SDN網絡實現了全球快速布點,滿足了業務高速增長的需求,同時在公有云+混合雲模式下,核心機密放在託管雲、其他業務部署在公有云,從而最大化資源利用,節約了成本。 三、出海抗DDoS,UCloud構築堅固防線! 在UCloud高級產品經理馮業浩分享的《UCloud海外DDOS防護》中,詳細介紹了DDoS攻擊的原理、黑客可能的攻擊方法,以及針對不同的攻擊流量,UCloud在防DDoS方面提供全球分級應對方案。 本地清洗產品的防護閾值上限為20G,可以抵禦小規模的DDoS攻擊,好處是一旦購買服務之後,用户賬户下所有的EIP都能夠享受到相關的DDoS防禦的服務,EIP的延時不會有任何的影響。 高防EIP則引入了專門的線路進行高防的清洗,線路的帶寬足夠將所有的攻擊流量和正常流量引入,同時線路還可以保證訪問效果和普通的IP地址相比沒有明顯的差別,高防EIP擁有70G的防護能力,延時等同於普通的EIP。 分佈式高防產品採用的思路,是用單獨的高防機房進行清洗之後通過公網進行回源。目前的高防機房位於香港,覆蓋東南亞的效果很不錯,最高延時不超過60毫秒。相對於普通的高防產品,分佈式高防產品沒有域名的限制,沒有端口的限制,也沒有QPS的數量的限制,因此非常適合用於大帶寬的DDoS防禦。 Anycast採用了分治法的思路,UCloud在全球八大入口點宣告了同樣的高防地址,這樣所有的攻擊流量和正常業務流量都會通過這八個入口點分別就近流入,而攻擊流量會在這8個入口點分別進行清洗,完成之後公網正常的業務流量,將會通過UCloud的全球骨幹網進行回源送到最終的源站。 介紹中還提到多個受到大流量攻擊的典型案例,比如某遊戲客户曾經受到高達226G的攻擊,UCloud高防方案幫助其有效抵禦了30多分鐘的攻擊。 四、直播時代,UCloud打造音視頻生產工具! UCloud邊緣計算產品負責人裴志偉分享的《URTC 實時音視頻,助力企業構建新一代生產工具》中,他講到,最近一年多,直播和視頻會議需求強烈,UCloud的音視頻解決方案URTC已支持實時語音通話、多人實時會議、萬人互動直播三大場景。 要實現URTC產品的落地支持,首先SDK的兼容性非常重要。目前URTC已經適配了超過4000款主流pc、手機、瀏覽器等等;其中web場景已經較好的支持了手機默認瀏覽器(最常見的是短信內嵌url直接發起在線視頻辦理業務),也支持了微信瀏覽器(常見的一些是羣管理、私域流量以及在線客服等等);而即將迎來爆發的視頻物聯網常用的linux終端,也已經支持了主流的嵌入式芯片、海思、樹莓派等等。 其次是用户體驗的改善,在RTC的定義中,實時音視頻需要延遲低於400ms。而URTC可以在國內提供100ms、東南亞200ms、中國大陸到歐美地區低於300ms的超低延時用户體驗。 用户體驗部分還有個核心環節是抵抗公共互聯網的不可靠,URTC通過一系列複雜的基於網絡實際狀況的自適應調優技術,目前提供了視頻最高抗40%丟包以及音頻最高抗70%丟包的能力。並且在網絡條件惡化的情況下,還有智能降低碼率、幀率以及最極端的只保留音頻等策略,從而最大可能性的保證會議/教學等諸多場景的業務連續性,當然這些策略都是可以根據用户業務特性進行靈活配置的。 除此之外,用户使用URTC SDK時還會有些相關性較高的產品能力需求,結合UCloud公有云平台,URTC將服務端大規模轉碼、混流、AI處理、加水印、錄製存儲、旁路推流等有機整體的結合在一起,給用户提供一站式直播、錄製音視頻服務。同時UFile對象存儲的接入也降低了一些用户需要走公網傳輸數據的成本,在一些限定場景下也能通過彈性和錯峯大幅降低成本。 五、歸檔存儲,UCloud做到了低成本、高可靠! 《海量數據雲歸檔最佳實踐》議題中,UCloud高級產品經理周恭元介紹到,UCloud最早在2017年就推出了第一代的歸檔存儲產品,2019年針對對象存儲產品的不同存儲類型做了統一,在對象存儲服務中提供了3種不同的存儲類型,單價上由熱至冷,存儲成本分別為標準存儲的一半與四分之一。用户可以實現在同一個存儲空間裏存儲數據,藉助生命週期策略自動對數據進行降冷處理,從而優化存儲成本。 UCloud在今年8月發佈了新一代歸檔存儲產品,成為了國內首先採用JBOD磁盤陣列與SMR疊瓦式磁盤介質提供公有云歸檔存儲服務的香港神州集運。在具備分鐘級別取回時效、11個9以上數據可靠性的歸檔存儲產品中,突破了傳統3分錢每GB每月的定價區間,達到了0.024元每GB每月。 UCloud通過採用西部數據高密度的疊瓦式磁盤存儲介質與磁盤陣列方案,單位機架的存儲容量相比較傳統36盤位的存儲服務器提升5.375倍,同時藉助磁盤休眠的技術,降低了90%的硬盤能耗。 在降低成本的同時,新一代歸檔存儲的全新架構也帶來了更高的可靠性保障,通過雙機頭的故障快速切換,能在數據取回快速的同時提供極高的可用性保障;通過採用Intel大比例糾刪碼冗餘策略,歸檔存儲提供了同時4塊硬盤故障情況下的數據可靠性保障,由於歸檔存儲讀取請求較小的緣故,新一代歸檔存儲還引入了定期的一致性校驗應對磁盤的靜默錯誤,以保障數據一致性。 歸檔存儲適用的是寫大於讀的數據存儲場景,核心場景彙總為三類,分別為多媒體數據歸檔、歷史數據合規性歸檔,和大數據、AI分析數據的歸檔。對於這些場景的存儲,UCloud新一代歸檔存儲做到了低成本、高可靠! 六、Cube,免服務器運維的容器服務! 針對議題《UCloud Cube容器技術解析》,UCloud 容器雲研發負責人張苗磊介紹了一款Serverless容器實例服務Cube。Cube產品的優勢是免服務器運維,用户無需關注基礎設施資源的運行,按秒計費,秒級啓動,並且有自愈功能,比K8S更輕量,更靈活。K8S比較複雜,用户在關心其應用的同時還要學習K8S知識,Cube理念是將K8S的最小運行單元pod,直接暴露出來,而將其他K8S繁瑣的概念統統封裝起來。 Cube對外僅暴露pod,用户所需要的鏡像運行命令,和其他資源關聯的關係都可以通過標準的K8S yaml提交給UCloud API。這樣pod就可以直接運行起來了,而用户實際所需要負責的僅僅是pod所需要的資源大小。 Cube背後的pod是如何實現的呢,我們知道原生的docker實現,由於不能很好的做到資源隔離和租户隔離,因此無法在雲香港神州集運上直接暴露給用户。因此Cube對docker運行時進行了大量的改造,在標準的虛擬機內實現的容器,QEMU提供了虛擬機的隔離能力,而用户在QEMU虛擬機內會部署一個完整的docker或者container。 Cube對外暴露的是標準的K8S CRI接口,但具體的實現是一個輕量級的虛擬機,而用户實際需要運行的容器是在輕量級的虛擬機內拉起的,這樣的帶來的好處是Cube融合了虛擬機資源隔離的優點和容器快速啓動的優點。 為了完全的比擬docker實現的容器快速啓動,Cube在性能上也做了很多優化,比如將QEMU虛擬機換成了Firecracker輕量級虛擬機,能夠進一步的降低虛擬化損耗,並且拉起速度能夠降低到100毫秒。 當然容器的快速啓動,也不僅包括容器啓動的時間,還包括了鏡像拉取的時間。為了解決這種問題,UCloud實現了鏡像緩存的功能,即用户的實際鏡像拉取在第一次拉取中會緩存,在鏡像緩存中心的鏡像加載是直接通過NBD的形式,直接掛載到Cube容器裏,這樣就可以實現Cube的快速啓動而跳過了鏡像拉取的時間。對於特別大的鏡像,用户也可以選擇預先加載的形式,直接加載到鏡像緩存中心從而進一步降低了啓動時間。 除了這些,張苗磊還針對Cube的網絡、存儲、監控/日誌等方面做了詳細的技術解析,並且最後講解了一個Cube典型的使用場景。上圖中我們可以看到所有的計算功能都可以通過Cube容器的實例來提供。而入向流量通過ULB來實現,後接的存儲、數據庫都可以通過雲上原生提供的Mysql或者UFS來實現,這樣架構可以很好的提供計算、存儲分離,並且能夠提供快速橫向擴展和彈性資源使用的能力。 總結:因為開放,所以信賴! 除了以上這些,TIC大會的技術分論壇直播還有UCloud 私有云研發負責人彭興宇的《UCloudStack 2.0技術方案分享》、UCloud 人工智能產品負責人王達侃的《人臉識別測温產品的設計與實踐》、UCloud 醫療產品負責人王彬的《UCloud智慧醫療雲解決方案發布》以及英特爾全球雲計算總監Santiago Durante的《構建智慧雲基石, 加速雲上創新》四個議題。本文限於篇幅就不一一詳細介紹了,感興趣的大家可以點擊“閲讀原文”鏈接查看完整直播視頻回放。 最後想説的是,通過今天TIC大會的技術分論壇直播,我有兩點體會: 第一,從頭到尾,一直能感受到UCloud在技術上開放的氣息,因為開放,所以信賴。作為最早專注雲計算的香港神州集運之一,UCloud在技術上積累的實力不容小覷。一直能夠保持開放中立的初心,讓用户用的放心。 第二,UCloud在技術上已經走的更深入,更有自信,今天的分享涉及基礎架構到物聯網、音視頻應用、醫療行業、安全等多個維度、多個層面的技術,並且UCloud的產品或者解決方案已經很成熟,在不同行業均有豐富的案例。 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-10-25 關鍵詞: 架構師 嵌入式

  • 某拼多多程序員:員工學歷比阿里強了幾條街

    請點擊上面  一鍵關注! 互聯網大廠對學歷的要求有多高?有的大廠要求211,985,有的大廠卻覺得普通一本二本都可以,只要能幹活就行,這之間的差別有多大呢? 一個拼多多程序員發帖説,拼多多的員工整體學歷比阿里強了好幾條街,難怪公司氛圍好很多。拼多多人均985,阿里人均才普通一本甚至二本,連211都沒到。 此番言論立即引起了網友們的激烈反彈,大家紛紛diss樓主,學歷高有什麼用?連拉屎自由都沒有,人均985卻活得那麼憋屈,也不知道樓主哪來的優越感,難道是膝蓋軟骨太脆弱了?看來拼多多的“拉屎梗”真是深入人心,一提到拼多多,大家立即想到了奇特的拉屎姿勢。 除了拉屎不便之外,拼多多的大小周和瘋狂加班也成了大家diss的重點,一進拼多多,壽命短十年,拼多多就是窮人孩子吃苦掙錢的地方。      阿里員工紛紛出來迴應,有的説自己是幼兒園結業,隔壁同事婦產科早產肄業,阿里員工就這麼差,怎麼了? 有的説阿里學歷人均985碩士,一半是浙大的,哪裏低了? 有的説十年前的一本會比現在的研究生差嗎?有的説在阿里985都是普通的,清北才能抬起頭,還有國外高校的呢!樓主一定是被拼多多洗腦了,可見拼多多的pua有多厲害。 還有許多網友説學歷高低也不説明什麼,馬雲學歷也一般,但也不妨礙人家當首富。大部分互聯網公司都不看學歷,因為英雄不問出處,何況有時候學歷高的人情商反而低。 拼多多員工學歷高又怎樣?還不是高學歷賣假貨?根本沒資格和阿里battle,阿里的p7能對標楊振寧,拼多多行嗎? 阿里的平均學歷低也是有原因的,畢竟成立20多年,老員工的學歷確實不高,新員工就不同了。拼多多成立時間晚,而且阿里和拼多多的發展階段不同步,一是分蛋糕,一個是做蛋糕,員工體量差別也很大,自然不能一起比較。 網友説,阿里員工學歷低,正説明阿里招人不注重學歷,不拘一格降人才,注重的是能力和情商,給更多的人就業機會,這才是一個大廠的胸襟和格局。 學歷固然能説明許多問題,比如一個人的學習能力,自律能力,努力程度等等。但衡量一個人的不僅是學歷,還有許多因素,比如能力,情商,德行,性格……能否做好一份工作,只看學歷絕對不行。學歷只能代表當學生時的水平,不能代表一個人的全部,畢業的那一天,這些榮譽和光環就都是過去式了。如果一個人總是沉湎於過去的輝煌不能自拔,那就太幼稚了。如果一個公司在招人的時候唯學歷論,可要小心錯過適合的人才,畢竟那張紙的重量不足以支撐一個人的全部。 特別推薦一個分享架構+算法的優質內容,還沒關注的小夥伴,可以長按關注一下: 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-10-25 關鍵詞: 互聯網 程序員

  • 一個技術總監的忠告:精通那麼多技術為何還是做不好一個項目?

    編寫高質量可維護的代碼既是程序員的基本修養,也是能決定項目成敗的關鍵因素,本文試圖總結出問題項目普遍存在的共性問題並給出相應的解決方案。 1. 程序員的宿命? 程序員的職業生涯中難免遇到爛項目,有些項目是你加入時已經爛了,有些是自己從頭開始親手做成了爛項目,有些是從裏到外的爛,有些是表面光鮮等你深入進去發現是個“焦油坑”,有些是此時還沒爛但是已經出現問題徵兆走在了腐爛的路上。 國內基本上是這樣,國外情況我瞭解不多,不過從英文社區和技術媒體上老外同行的抱怨程度看,應該是差不多的,雖然整體素質可能更高,但是也因更久的信息化而積累了更多問題。畢竟“焦油坑、Shit_Mountain 屎山”這些舶來的術語不是無緣無故被髮明出來的。 Any way,這大概就是我們這個行業的宿命——要麼改行,要麼就是與爛項目爛代碼長相伴。就像宇宙的“熵增加定律”一樣: 孤立系統的一切自發過程均向着令其狀態更無序的方向發展,如果要使系統恢復到原先的有序狀態是不可能的,除非外界對它做功。 面對這宿命的陰影,有些人認命了麻木了,逐漸對這個行業失去熱情。 那些不認命的選擇與之抗爭,但是地上並沒有路,當年軟件危機的陰雲也從未真正散去,人月神話仍然是神話,於是人們做出了各自不同的判斷和嘗試: 掀桌子另起爐灶派: 很多人把項目做爛的原因歸咎於項目前期的基礎沒打好、需求不穩定一路打補丁、前面的架構師和程序員留下的爛攤子難以收拾。 他們要麼沒有信心去收拾爛攤子,要麼覺得這是費力不討好,於是要放棄掉項目,寄希望於出現一個機會能重頭再來。 但是他們對於如何避免重蹈覆轍、做出另一個爛項目是沒有把握也沒有深入思考的,只是盲目樂觀的認為自己比前任更高明。 激進改革派: 這個派別把原因歸結於爛項目當初沒有采用正確的編程語言、最新最強大的技術棧或工具。 他們中一部分人也想着有機會另起爐灶,用上時下最流行最熱門的技術棧(spring boot、springcloud、redis、nosql、docker、vue)。 或者即便不另起爐灶,也認為現有技術棧太過時無法容忍了(其實可能並不算過時),不用微服務不用分佈式就不能接受,於是激進的引入新技術棧,魯莽的對項目做大手術。 這種對剛剛流行還不成熟技術的盲目跟風、技術選型不慎重的情況非常普遍,今天在他們眼中落伍的技術棧,其實也不過是幾年前另一批人趕的時髦。 我不反對技術上的追新,但是同樣的,這裏的問題是:他們對於大手術的風險和副作用,對如何避免重蹈覆轍用新技術架構做出另一個爛項目,沒有把握也沒有深入思考的,只是盲目樂觀的認為新技術能帶來成功。 也沒人能阻止這種簡歷驅動的技術選型浮躁風氣,畢竟花的是公司的資源,用新東西顯得自己很有追求,失敗了也不影響簡歷美化,簡歷上只會增加一段項目履歷和幾種精通技能,不會提到又做爛了一個項目,名利雙收穩賺不賠。 保守改良派: 還有一類人他們不願輕易放棄這個有問題但仍在創造效益的項目,因為他們看到了項目仍然有維護的價值,也看到了另起爐灶的難度(萬事開頭難,其實項目的冷啓動存在很多外部制約因素)、大手術對業務造成影響的代價、系統遷移的難度和風險。 同時他們嘗試用温和漸進的方式逐步改善項目質量,採用一系列工程實踐(主要包括重構熱點代碼、補自動化測試、補文檔)來清理“技術債”,消除制約項目開發效率和交付質量的瓶頸。 如果把一個問題項目比作病入膏肓的病人,那麼這三種做法分別相當於是放棄治療、截肢手術、保守治療。 2. 一個 35+ 程序員的反思 年輕時候我也是掀桌子派和激進派的,新工程新框架大開大合,一路走來經驗值技能樹蹭蹭的漲,跳槽加薪好不快活。 但是近幾年隨着年齡增長,一方面新東西學不動了,另一方面對經歷過的項目反思的多了觀念逐漸改變了。 對我觸動最大的一件事是那個我在 2016 年初開始從零搭建起的項目,在我 2018 年底離開的時候(僅從代碼質量角度)已經讓我很不滿意了。只是,這一次沒有任何藉口了: 從技術選型到架構設計到代碼規範,都是我自己做的,團隊不大,也是我自己組建和一手帶出來的; 最開始的半年進展非常順利,用着我最趁手的技術和工具一路狂奔,年底前替換掉了之前採購的那個垃圾產品(對的,有個前任在業務上做參照也算是個很大的有利因素); 做的過程我也算是全力以赴,用盡畢生所學——前面 13 年工作的經驗值和走過的彎路、教訓,使得公司只用其它同類公司同類項目 20% 的資源就把平台做起來了; 如果説多快好省是最高境界,那麼當時的我算是做到了多、快、省——交付的功能非常豐富且貼近業務需求、開發節奏快速、對公司開發資源很節省; 但是現在看來,“好”就遠遠沒有達到了,到了項目中期,簡單優先級高的需求都已經做完了,公司業務上出現了新的挑戰——接入另一個核心繫統以及外部平台,真正的考驗來了。 那個改造工程影響面比較大,需要對我們的系統做大面積修改,最麻煩的是這意味着從一個簡單的單體系統變成了一個分佈式的系統,而且業務涉及資金交易,可靠性要求較高,是難上加難。 於是問題開始出現了:我之前架構的優點——簡單直接——這個時候不再是優點了,簡單直接的架構在業務環境、技術環境都簡單的情況下可以做到多快好省,但是當業務、技術環境都陡然複雜起來時,就不行了; 具體的表現就是:架構和代碼層面的結構都快速的變得複雜、混亂起來了——熵急劇增加; 後面的事情就一發不可收拾:代碼改起來越來越吃力、測試問題變多、生產環境故障和問題變多、於是消耗在排查測試問題生產問題和修復數據方面的精力急劇增加、出現惡性循環。。。 到了這個境地,項目就算是做爛了!一個我從頭開始做起的沒有任何藉口的失敗! 於是我意識到一個非常淺顯的道理:擁有一張空白的畫卷、一支最高級的畫筆、一間專業的畫室,無法保證你可以畫出美麗的畫卷。如果你不善於畫畫,那麼一切都是空想和意淫。 然後我變成了一個“保守改良派”,因為我意識到掀桌子和激進的改革都是不負責任的,説不好聽的那樣其實是掩耳盜鈴、逃避困難,人不可能逃避一輩子,你總要面對。 即便掀了桌子另起爐灶了,你還是需要找到一種辦法把這個新的爐灶燒好,因為隨着項目發展之前的老問題還是會一個一個冒出來,還是需要面對現實、不逃避、找辦法。 面對問題不僅有助於你把當前項目做好,也同樣有助於將來有新的項目時更好的把握住機會。 無論是職業生涯還是自然年齡,人到了這個階段都開始喜歡回顧和總結,也變得比過去更在乎項目、產品乃至公司的商業成敗。 軟件開發作為一種商業活動,判斷其成敗的依據應該是:能否以可接受的成本、可預期的時間節奏、穩定的質量水平、持續交付滿足業務需要的功能市場需要的產品。 其實就是項目管理四要素——成本、進度、範圍、質量,傳統項目管理理論認為這四要素彼此制約難以兼得,項目管理的藝術在於四要素的平衡取捨。 關於軟件工程和項目管理的理論和著作已經很多很成熟,這裏我從程序員的視角提出一個新的觀點——質量不可妥協: 質量要素不是一個可以被犧牲和妥協的要素——犧牲質量會導致其它三要素全都受損,反之同理,追求質量會讓你在其它三個方面同時受益。 在保持一個質量水平的前提下,成本、進度、範圍三要素確確實實是互相制約關係——典型的比如犧牲成本(加班加點)來加快進度交付急需的功能。 正如著名的“破窗效應”所啓示的那樣:任何一種不良現象的存在,都在傳遞着一種信息,這種信息會導致不良現象的無限擴展,同時必須高度警覺那些看起來是偶然的、個別的、輕微的“過錯”,如果對這種行為不聞不問、熟視無睹、反應遲鈍或糾正不力,就會縱容更多的人“去打爛更多的窗户玻璃”,就極有可能演變成“千里之堤,潰於蟻穴”的惡果——質量不佳的代碼之於一個項目,正如一扇破了的窗之於一幢建築、一個螞蟻巢之於一座大堤。 好消息是,只要把質量提上去項目就會逐漸走上健康的軌道,其它三個方面也都會改善。管好了質量,你就很大程度上把握住了項目成敗的關鍵因素。 壞消息是,項目的質量很容易失控,現實中質量不佳、越做越臃腫混亂的項目比比皆是,質量改善越做越好的案例聞所未聞,以至於人們將其視為如同物理學中“熵增加定律”一樣的必然規律了。 當然任何事情都有一個度的問題,當質量低於某個水平時才會導致其它三要素同時受損。反之當質量高到某個水平以後,繼續追求質量不僅得不到明顯收益,而且也會損害其它三要素——邊際效用遞減定律。 這個度需要你為自己去評估和測量,如果目前的質量水平還在兩者之間,那麼就應該重點改進項目質量。當然,現實世界中很少看到哪個項目質量高到了不需要重視的程度。 3. 項目走向衰敗的最常見誘因——代碼質量不佳 一個項目的衰敗一如一個人健康狀況的惡化,當然可能有多種多樣的原因——比如需求失控、業務調整、人員變動流失。但是作為我們技術人,如果能做好自己分內的工作——編寫出可維護的代碼、減少技術債利息成本、交付一個健壯靈活的應用架構,那也絕對是功德無量的。 雖然很難估算出這究竟能挽救多少項目,但是在我十多年職業生涯中,經歷的和近距離觀察的幾十個項目,確實看到了大量的項目正是由於代碼質量不佳導致的失敗和遺憾,同時我也發現其實失敗項目的很多問題、癥結也確確實實都可以歸因到項目代碼的混亂和質量低下,比如一個常見的項目腐爛惡性循環:代碼亂》bug 多》排查問題耗時》複用度低》加班 996》士氣低落…… 所謂“千里之堤,毀於蟻穴”,代碼問題就是蟻穴。 接下來,讓我們從項目管理聚焦到項目代碼質量這個相對小的領域來深入剖析。編寫高質量可維護的代碼是程序員的基本修養,本文試圖在代碼層面找到一些失敗項目中普遍存在的癥結問題,同時基於個人十幾年開發經驗總結出的一些設計模式作為藥方分享出來。 關於代碼質量的話題其實很難通過一篇文章闡述明白,甚至需要一本書的篇幅,裏面涉及到的很多概念關注點之間存在複雜微妙關係。 推薦《設計模式之美》的第二章節《從哪些維度評判代碼質量的好壞?如何具備寫出高質量代碼的能力?》,這是我看到的關於代碼質量主題最精彩深刻的論述。 4. 一個失敗項目覆盤 先貼幾張代碼截圖,看一下這個重病纏身的項目的病灶和症狀: 這是該項目中一個最核心、最複雜也是最經常要被改動的 class,代碼行數 4881; 結果就是冗長的 API 列表(列表需要滾動 4 屏才能到底,公有私有 API 180 個); 還是那個 Class,頭部的 import 延綿到了 139 行,去掉第一行 package 聲明和少量空行總共 import 引入了 130 個 class! 還是那個坑爹的組件,從 156 行開始到 235 行聲明瞭 Spring 依賴注入的組件 40 個! 這裏先不去分析這個類的問題,只是初步展示一下病情嚴重程度。 我相信這應該不算是特別糟糕的情況,比這個嚴重的項目俯拾皆是,但是這也應該足夠拿來暴露問題、剖析成因了。 4.1 癥結 1:組件粒度過大、API 氾濫 分層的理念早已深入人心,尤其是業務邏輯層的獨立,徹底杜絕了之前(不分層的年代)業務邏輯與展現邏輯、持久化邏輯等混雜的問題。 但是好景不長,隨着業務的複雜和變更,在業務邏輯層的複雜性也急劇增加,成為了新的開發效率瓶頸, 問題就出在了業務邏輯組件的劃分方式——按領域模型劃分業務邏輯組件: 業界關於如何設計業務邏輯層 並沒有標準和最佳實踐,絕大多數項目(我自己經歷過的項目以及我有機會深入瞭解的項目)中大家都是想當然的按照業務領域對象來設計; 例如:領域實體對象有 Account、Order、Delivery、Campaign。於是業務邏輯層就設計出 AccountService、OrderService、DeliveryService、CampaignService 這種做法在項目簡單是沒什麼問題,事實上項目簡單時 你隨便怎麼設計都問題不大。 但是當項目變大和複雜以後,就會出現問題了: 組件臃腫:Service 組件的個數跟領域實體對象個數基本相當,必然造成個別 Service 組件變得非常臃腫——API 非常多,代碼行數達到幾千行; 職責模糊:業務邏輯往往跨多個領域實體,無論放在哪個 Service 都不合適,同樣的,要找一個功能的實現邏輯也無法確定在哪個 Service 中; 代碼重複 or 邏輯糾纏的兩難選擇:當遇到一個業務邏輯,其中的某個環節在另一個業務邏輯 API 中已經實現,這時如果不想忍受重複實現和代碼,就只能去調用那個 API。但這樣就造成了業務邏輯組件之間的耦合與依賴,這種耦合與依賴很快會擴散——新的 API 又會被其它業務邏輯依賴,最終形成蜘蛛網一樣的複雜依賴甚至循環依賴; 複用代碼、減少重複雖然是好的,但是複雜耦合依賴的害處也很大——趕走一隻狼引來了一隻虎。兩杯毒酒給你選! 前面截圖的那個問題組件 ContractService 就是一個典型案例,這樣的組件往往是熱點代碼以及整個項目的開發效率的瓶頸。 4.2 藥方 1:倒金字塔結構——業務邏輯組件職責單一、禁止層內依賴 問題根源的反面其實就藏着解決方案,只是需要我們有意識的去改變習慣、遵循新的設計風格,而不是憑直覺去設計: 業務邏輯層應該被設計成一個個功能非常單一的小組件,所謂小是指 API 數量少、代碼行數少; 由於職責單一因此必然組件數量多,每一個組件對應一個很具體的業務功能點(或者幾個相近的); 複用(調用、依賴)只應該發生在相鄰的兩層之間——上層調用下層的 API 來實現對下層功能的複用; 於是系統架構就自然呈現出倒立的金字塔形狀:越接近頂層的業務場景組件數量越多,越往下層的複用性高,於是組件數量越少。 4.3 癥結 2:低內聚、高耦合 經典面向對象理論告訴我們,好的代碼結構應該是“高內聚、低耦合”的: 高內聚:組件本身應該儘可能的包含其所實現功能的所有重要信息和細節,以便讓維護者無需跳轉到其它多個地方去了解必要的知識。 低耦合:組件之間的互相依賴和了解儘可能少,以便在一個組件需要改動時其它組件不受影響。 其實這兩者就是一體兩面,做到了高內聚基本也就做到了低耦合,相反如果內聚度很低,勢必存在大量高耦合的組件。 我觀察發現,很多項目都存在低內聚、高耦合的問題。根本原因在於很多程序員,甚至是很多經驗豐富的程序員也缺少這方面的意識——對“內聚性”概念不甚清楚,對內聚性被破壞的危害沒有意識,對如何避免更是無從談起。 很多人從一開始就憑直覺寫程序,有了一定經驗以後一般能認識到重複代碼的危害,對複用性有很強的認識,於是就會掉進一個陷阱——盲目追求複用,結果破壞了內聚性。 業界關於“複用性”的認識存在一個誤區—— 認為包括業務邏輯組件在內的任何層面的組件都應該追求最大限度的可複用性; 複用當然是好的,但那應該有個前提條件:不增加系統複雜度的情況下的複用,才是好的。 什麼樣的複用會增加系統複雜性、是不好的呢?前面提到的,一個業務邏輯 API 被另一個業務邏輯 API 複用——就是不好的: 損害了穩定性:因為業務邏輯本身是跟現實世界的業務掛鈎的,而業務會發生變化;當你複用一個會發生變化的 API,相當於在沙子上建高樓——地基是鬆動的; 增加了複雜性:這樣的依賴還造成代碼可讀性降低——在一個本就複雜的業務邏輯代碼中,包含了對另一個複雜業務邏輯的調用,複雜度會急劇增加,而且會不斷氾濫和傳遞; 內聚性被破壞:由於業務邏輯被打散在了多個組件的方法內,變得支離破碎,無法在一個地方看清整體邏輯脈絡和實現步驟——內聚性被破壞,同時也意味着,這個調用鏈條上涉及的所有組件之間存在高耦合。 4.4 藥方 2:複用的兩種正確姿勢——打造自己的 lib 和 framework 軟件架構中有兩種東西來實現複用——lib 和 framework, lib 庫是供你(應用程序)調用的,它幫你實現特定的能力(比如日誌、數據庫驅動、json 序列化、日期計算、http 請求)。 framework 框架是供你擴展的,它本身就是半個應用程序,定義好了組件劃分和交互機制,你需要按照其規則擴展出特定的實現並綁定集成到其中,來完成一個應用程序。 lib 就是組合方式的複用,framework 則是繼承式的複用,繼承的 Java 關鍵字是 extends,所以本質上是擴展。 過去有個説法:“組合優於繼承,能用組合解決的問題儘量不要繼承”。我不同意這個説法,這容易誤導初學者以為組合優於繼承,其實繼承才是面向對象最強大的地方,當然任何東西都不能亂用。 典型的繼承亂用就是為了獲得父類的某個 API 而去繼承,繼承一定是為了擴展,而不是為了直接獲得一個能力,獲得能力應該調用 lib,父類不應該去實現具體功能,那是 lib 該做的事。 也不應該為了使用 lib 而去繼承 lib 中的 Class。lib 就是用來被組合被調用的,framework 就是用來被繼承、擴展的。 再展開一下:lib 既可以是第三方的(log4j、httpclient、fastjson),也可是你自己工程的(比如你的持久層 Dao、你的 utils); framework 同理,既可以是第三方的(springmvc、jpa、springsecurity),也可以是你項目內封裝的面向具體業務領域的(比如 report、excel 導出、paging 或任何可複用的算法、流程)。 從這個意義上説, 一個項目中的代碼其實只有 3 種:自定義的 lib class、自定義的 framework 相關 class、擴展第三方或自定義 framework 的組件 class。 再擴展一下:相對於過去,現在我們已經有了足夠多的第三方 lib 和 framework 來複用,來幫助項目節省大量代碼,開發工作似乎變成了索然無味、沒技術含量的 CRUD。但是對於業務非常複雜的項目,則需要有經驗、有抽象思維、懂設計模式的人,去設計面向業務的 framework 和麪向業務的 lib,只有這樣才能交付可維護、可擴展、可複用的軟件架構——高質量架構,幫助項目或產品取得成功。 4.5 癥結 3:抽象不夠、邏輯糾纏——High Level 業務邏輯和 Low Level 實現邏輯糾纏 當我們説“代碼中包含的業務邏輯”的時候,我們到底在説什麼?業界並沒有一個標準,大家經常講的 CRUD 增刪改查其實屬於更底層的數據訪問邏輯。 我的觀點是:所謂代碼中的業務邏輯,是指這段代碼所表現出的所有輸入輸出規則、算法和行為,通常可以分為以下 5 類: 輸入合法性校驗; 業務規則校驗:典型的如檢查交易記錄狀態、金額、時限、權限等,通常包含數據庫或外部接口的查詢作為參考; 數據持久化行為:數據庫、緩存、文件、日誌等任何形式的數據寫入行為; 外部接口調用行為; 輸出/返回值準備。 當然具體到某一個組件實例,可能不會包括上述全部 5 類業務邏輯,但是也可能每一類業務邏輯存在多個。 單這樣看你可能覺得並不是特別複雜,但是現實中上述 5 類業務邏輯中的每一個通常還包含着一到多個底層實現邏輯,如 CRUD 數據訪問邏輯或第三方 API 的調用。 例如輸入合法性校驗,通常需要查詢對應記錄是否存在,外部接口調用前通常需要查詢相關記錄以獲得調用接口需要的參數,調用接口後還需要根據結果更新相關記錄狀態。 顯然這裏存在兩個 Level 的邏輯——High Level 的與業務需求對應且關聯緊密的邏輯、Low Level 的實現邏輯。 如果對兩個 Level 的邏輯不加以區分、混為一談,代碼質量立刻就會遭到嚴重損害: 可讀性變差:兩個維度的複雜性——業務複雜性和底層實現的技術複雜性——被摻雜在了一起,複雜度 1+1>2 劇增,給其他人閲讀代碼增加很大負擔; 可維護性差:可維護性通常指排查和解決問題所需花費的代價高低,當兩個 level 的邏輯糾纏在一起,會使排查問題變的更困難,修復問題時也更容易出錯; 可擴展性無從談起:擴展性通常指為系統增加一個特性所需花費的代價高低,代價越高擴展性越差;與排查修復問題類似,邏輯糾纏顯然也會使添加新特性變得困難、一不小心就破壞了已有功能。 下面這段代碼就是一個典型案例——High Level 的邏輯流程(參數獲取、反序列化、參數校驗、緩存寫入、數據庫持久化、更新相關交易記錄)完全淹沒在了 Low Level 的實現邏輯(字符串比較、Json 反序列化、redis 操作、dao 操作以及前後各種瑣碎的參數準備和返回值處理)。下一節我會針對這段問題代碼給出重構方案。 @Overridepublic void updateFromMQ(String compress) { try { JSONObject object = JSON.parseObject(compress); if (StringUtils.isBlank(object.getString("type")) || StringUtils.isBlank(object.getString("mobile")) || StringUtils.isBlank(object.getString("data"))){ throw new AppException("MQ返回參數異常"); } logger.info(object.getString("mobile")+""+object.getString("type")); Map map = new HashMap(); map.put("type",CrawlingTaskType.get(object.getInteger("type"))); map.put("mobile", object.getString("mobile")); List list = baseDAO.find("from crt c where c.phoneNumber=:mobile and c.taskType=:type", map); redisClientTemplate.set(object.getString("mobile") + "_" + object.getString("type"),CompressUtil.compress( object.getString("data"))); redisClientTemplate.expire(object.getString("mobile") + "_" + object.getString("type"), 2*24*60*60); //保存成功 存入redis 保存48小時 CrawlingTask crawlingTask = null; // providType:(0:新顏,1XX支付寶,2:ZZ淘寶,3:TT淘寶) if (CollectionUtils.isNotEmpty(list)){ crawlingTask = list.get(0); crawlingTask.setJsonStr(object.getString("data")); }else{ //新增 crawlingTask = new CrawlingTask(UUID.randomUUID().toString(), object.getString("data"), object.getString("mobile"), CrawlingTaskType.get(object.getInteger("type"))); crawlingTask.setNeedUpdate(true); } baseDAO.saveOrUpdate(crawlingTask); //保存芝麻分到xyz if ("3".equals(object.getString("type"))){ String data = object.getString("data"); Integer zmf = JSON.parseObject(data).getJSONObject("taobao_user_info").getInteger("zm_score"); Map param = new HashMap(); param.put("phoneNumber", object.getString("mobile")); List list1 = personBaseDaoI.find("from xyz where phoneNumber=:phoneNumber", param); if (list1 !=null){ for (Dperson dperson:list1){ dperson.setZmScore(zmf); personBaseDaoI.saveOrUpdate(dperson); AppFlowUtil.updateAppUserInfo(dperson.getToken(),null,null,zmf);//查詢多租户表 身份認證、淘寶認證 為0 置為1 } } } } catch (Exception e) { logger.error("更新my MQ授權信息失敗", e); throw new AppException(e.getMessage(),e); }} 4.6 藥方 3:控制邏輯分離——業務模板 Pattern of NestedBusinessTemplate 解決“邏輯糾纏”最關鍵是要找到一種隔離機制,把兩個 Level 的邏輯分開——控制邏輯分離,分離的好處很多: 根據經驗,當我們着手維護一段代碼時,一定是想先弄清楚它的整體流程、算法和行為,而不是一上來就去研究它的細枝末節; 控制邏輯分離後,只需要去看 High Level 部分就能瞭解到上述內容,閲讀代碼的負擔大幅度降低,代碼可讀性顯著增強; 讀懂代碼是後續一切維護、重構工作的前提,而且一份代碼被讀的次數遠遠高於被修改的次數(高一個數量級),因此代碼對人的可讀性再怎麼強調都不為過,可讀性增強可以大幅度提高系統可維護性,也是重構的最主要目標。 同時,根據我的經驗,High Level 業務邏輯的變更往往比 Low Level 實現邏輯變更要來的頻繁,畢竟前者跟業務直接對應。當然不同類型項目情況不一樣,另外它們發生變更的時間點往往也不同; 在這樣的背景下,控制邏輯分離的好處就更明顯了:每次維護、擴充系統功能只需改動一個 Levle 的代碼,另一個 Level 不受影響或影響很小,這會大幅降低修改成本和風險。 我在總結過去多個項目中的教訓和經驗後,總結出了一項最佳實踐或者説是設計模式——業務模板 Pattern of NestedBusinessTemplat,可以非常簡單、有效的分離兩類邏輯,先看代碼: public class XyzService {abstract class AbsUpdateFromMQ { public final void doProcess(String jsonStr) { try { JSONObject json = doParseAndValidate(jsonStr); cache2Redis(json); saveJsonStr2CrawingTask(json); updateZmScore4Dperson(json); } catch (Exception e) { logger.error("更新my MQ授權信息失敗", e); throw new AppException(e.getMessage(), e); } } protected abstract void updateZmScore4Dperson(JSONObject json); protected abstract void saveJsonStr2CrawingTask(JSONObject json); protected abstract void cache2Redis(JSONObject json); protected abstract JSONObject doParseAndValidate(String json) throws AppException;} @SuppressWarnings({ "unchecked", "rawtypes" })public void processAuthResultDataCallback(String compress) { new AbsUpdateFromMQ() {@Overrideprotected void updateZmScore4Dperson(JSONObject json) { //保存芝麻分到xyz if ("3".equals(json.getString("type"))){ String data = json.getString("data"); Integer zmf = JSON.parseObject(data).getJSONObject("taobao_user_info").getInteger("zm_score"); Map param = new HashMap(); param.put("phoneNumber", json.getString("mobile")); List list1 = personBaseDaoI.find("from xyz where phoneNumber=:phoneNumber", param); if (list1 !=null){ for (Dperson dperson:list1){ dperson.setZmScore(zmf); personBaseDaoI.saveOrUpdate(dperson); AppFlowUtil.updateAppUserInfo(dperson.getToken(),null,null,zmf); } } }} @Overrideprotected void saveJsonStr2CrawingTask(JSONObject json) { Map map = new HashMap(); map.put("type",CrawlingTaskType.get(json.getInteger("type"))); map.put("mobile", json.getString("mobile")); List list = baseDAO.find("from crt c where c.phoneNumber=:mobile and c.taskType=:type", map); CrawlingTask crawlingTask = null; // providType:(0:xx,1yy支付寶,2:zz淘寶,3:tt淘寶) if (CollectionUtils.isNotEmpty(list)){ crawlingTask = list.get(0); crawlingTask.setJsonStr(json.getString("data")); }else{ //新增 crawlingTask = new CrawlingTask(UUID.randomUUID().toString(), json.getString("data"), json.getString("mobile"), CrawlingTaskType.get(json.getInteger("type"))); crawlingTask.setNeedUpdate(true); } baseDAO.saveOrUpdate(crawlingTask);}@Overrideprotected void cache2Redis(JSONObject json) { redisClientTemplate.set(json.getString("mobile") + "_" + json.getString("type"),CompressUtil.compress( json.getString("data"))); redisClientTemplate.expire(json.getString("mobile") + "_" + json.getString("type"), 2*24*60*60);}@Overrideprotected JSONObject doParseAndValidate(String json) throws AppException { JSONObject object = JSON.parseObject(json); if (StringUtils.isBlank(object.getString("type")) || StringUtils.isBlank(object.getString("mobile")) || StringUtils.isBlank(object.getString("data"))){ throw new AppException("MQ返回參數異常"); } logger.info(object.getString("mobile")+""+object.getString("type")); return object; } }.doProcess(compress);} 如果你熟悉經典的 GOF23 種設計模式,很容易發現上面的代碼示例其實就是 Template Method 設計模式的運用,沒什麼新鮮的。 沒錯,我這個方案沒有提出和創造任何新東西,我只是在實踐中偶然發現 Template Method 設計模式真的非常適合解決廣泛存在的邏輯糾纏問題,而且也發現很少有程序員能主動運用這個設計模式;一部分原因可能是意識到“邏輯糾纏”問題的人本就不多,同時熟悉這個設計模式並能自如運用的人也不算多,兩者的交集自然就是少得可憐;不管是什麼原因,結果就是這個問題廣泛存在成了通病。 我看到一部分對代碼質量有追求的程序員 他們的解決辦法是通過"結構化編程"和“模塊化編程”: 把 Low Level 邏輯提取成 private function,被 High Level 代碼所在的 function 直接調用; 問題 1 硬連接不靈活:首先,這樣雖然起到了一定的隔離效果,但是兩個 level 之間是靜態的硬關聯,Low Level 無法被簡單的替換,替換時還是需要修改和影響到 High Level 部分; 問題 2 組件內可見性造成混亂:提取出來的 private function 在當前組件內是全局可見的——對其它無關的 High Level function 也是可見的,各個模塊之間仍然存在邏輯糾纏。這在很多項目中的熱點代碼中很常見,問題也很突出:試想一個包含幾十個 API 的組件,每個 API 的 function 存在一兩個關聯的 private function,那這個組件內部的混亂程度、維護難度是難以承受的。 把 Low Level 邏輯抽取到新的組件中,供 High Level 代碼所在的組件依賴和調用;更有經驗的程序員可能會增加一層接口並且藉助 Spring 依賴注入; 問題 1 API 氾濫:提取出新的組件似乎避免了“結構化編程”的侷限性,但是帶來了新的問題——API 氾濫:因為組件之間調用只能走 public 方法,而這個 API 其實沒有太多複用機會根本沒必要做成 public 這種最高可見性。 問題 2 同層組件依賴失控:組件和 API 氾濫後必然導致組件之間互相依賴成為常態,慢慢變得失控以後最終變成所有組件都依賴其它大部分組件,甚至出現循環依賴;比如那個擁有 130 個 import 和 40 個 Spring 依賴組件的 ContractService。 下面介紹一下 Template Method 設計模式的運用,簡單歸納就是: High Level邏輯封裝在抽象父類AbsUpdateFromMQ的一個final function中,形成一個業務邏輯的模板; final function保證了其中邏輯不會被子類有意或無意的篡改破壞,因此其中封裝的一定是業務邏輯中那些相對固定不變的東西。至於那些可變的部分以及暫時不確定的部分,以abstract protected function形式預留擴展點; 子類(一個匿名內部類)像“做填空題”一樣,填充模板實現Low Level邏輯——實現那些protected function擴展點;由於擴展點在父類中是abstract的,因此編譯器會提醒子類的程序員該擴展什麼。 那麼它是如何避免上面兩個方案的 4 個侷限性的: Low Level 需要修改或替換時,只需從父類擴展出一個新的子類,父類全然不知無需任何改動; 無論是父類還是子類,其中的 function 對外層的 XyzService 組件都是不可見的,即便是父類中的 public function 也不可見,因為只有持有類的實例對象才能訪問到其中的 function; 無論是父類還是子類,它們都是作為 XyzService 的內部類存在的,不會增加新的 java 類文件更不會增加大量無意義的 API(API 只有在被項目內複用或發佈出去供外部使用才有意義,只有唯一的調用者的 API 是沒有必要的); 組件依賴失控的問題當然也就不存在了。 SpringFramework 等框架型的開源項目中,其實早已大量使用 Template Method 設計模式,這本該給我們這些應用開發程序員帶來啓發和示範,但是很可惜業界沒有注意到和充分發揮它的價值。 NestedBusinessTemplat 模式就是對其充分和積極的應用,前面一節提到過的複用的兩種正確姿勢——打造自己的 lib 和 framework,其實 NestedBusinessTemplat 就是項目自身的 framework。 4.7 癥結 4:無處不在的 if else 牛皮癬 無論你的編程啓蒙語言是什麼,最早學會的邏輯控制語句一定是 if else,但是不幸的是它在你開始真正的編程工作以後,會變成一個損害項目質量的壞習慣。 幾乎所有的項目都存在 if else 氾濫的問題,但是卻沒有引起足夠重視警惕,甚至被很多程序員認為是正常現象。 首先我來解釋一下為什麼 if else 這個看上去人畜無害的東西是有害的、是需要嚴格管控的: if else if ...else 以及類似的 switch 控制語句,本質上是一種 hard coding 硬編碼行為,如果你同意“magic number 魔法數字”是一種錯誤的編程習慣,那麼同理,if else 也是錯誤的 hard coding 編程風格; hard coding 的問題在於當需求發生改變時,需要到處去修改,很容易遺漏和出錯; 以一段代碼為例來具體分析: if ("3".equals(object.getString("type"))){ String data = object.getString("data"); Integer zmf = JSON.parseObject(data).getJSONObject("taobao_user_info").getInteger("zm_score"); Map param = new HashMap(); param.put("phoneNumber", object.getString("mobile")); List list1 = personBaseDaoI.find("from xyz where phoneNumber=:phoneNumber", param); if (list1 !=null){ for (Dperson dperson:list1){ dperson.setZmScore(zmf); personBaseDaoI.saveOrUpdate(dperson); AppFlowUtil.updateAppUserInfo(dperson.getToken(),null,null,zmf); } }} if ("3".equals(object.getString("type"))) 顯然這裏的"3"是一個 magic number,沒人知道 3 是什麼含義,只能推測; 但是僅僅將“3”重構成常量 ABC_XYZ 並不會改善多少,因為 if (ABC_XYZ.equals(object.getString("type"))) 仍然是面向過程的編程風格,無法擴展; 到處被引用的常量 ABC_XYZ 並沒有比到處被 hard coding 的 magic number 好多少,只不過有了含義而已; 把常量升級成 Enum 枚舉類型呢,也沒有好多少,當需要判斷的類型增加了或判斷的規則改變了,還是需要到處修改——Shotgun Surgery(霰彈式修改) 並非所有的 if else 都有害,比如上面示例中的 if (list1 !=null) { 就是無害的,沒有必要去消除,也沒有消除它的可行性。判斷是否有害的依據: 如果 if 判斷的變量狀態只有兩種可能性(比如 boolean、比如 null 判斷)時,是無傷大雅的; 反之,如果 if 判斷的變量存在多種狀態,而且將來可能會增加新的狀態,那麼這就是個問題; switch 判斷語句無疑是有害的,因為使用 switch 的地方往往存在很多種狀態。 4.8 藥方 4:充血枚舉類型——Rich Enum Type 正如前面分析呈現的那樣,對於代碼中廣泛存在的狀態、類型 if 條件判斷,僅僅把被比較的值重構成常量或 enum 枚舉類型並沒有太大改善——使用者仍然直接依賴具體的枚舉值或常量,而不是依賴一個抽象。 於是解決方案就自然浮出水面了:在 enum 枚舉類型基礎上進一步抽象封裝,得到一個所謂的“充血”的枚舉類型,代碼説話: 實現多種系統通知機制,傳統做法: enum NOTIFY_TYPE { email,sms,wechat; } //先定義一個enum——一個只定義了值不包含任何行為的“貧血”的枚舉類型if(type==NOTIFY_TYPE.email){ //if判斷類型 調用不同通知機制的實現 。。。}else if (type=NOTIFY_TYPE.sms){ 。。。}else{ 。。。} 實現多種系統通知方式,充血枚舉類型——Rich Enum Type 模式: enum NOTIFY_TYPE { //1、定義一個包含通知實現機制的“充血”的枚舉類型 email("郵件",NotifyMechanismInterface.byEmail()), sms("短信",NotifyMechanismInterface.bySms()), wechat("微信",NotifyMechanismInterface.byWechat()); String memo; NotifyMechanismInterface notifyMechanism; private NOTIFY_TYPE(String memo,NotifyMechanismInterface notifyMechanism){//2、私有構造函數,用於初始化枚舉值 this.memo=memo; this.notifyMechanism=notifyMechanism; } //getters ...}public interface NotifyMechanismInterface{ //3、定義通知機制的接口或抽象父類 public boolean doNotify(String msg); public static NotifyMechanismInterface byEmail(){//3.1 返回一個定義了郵件通知機制的策的實現——一個匿名內部類實例 return new NotifyMechanismInterface(){ public boolean doNotify(String msg){ ....... } }; } public static NotifyMechanismInterface bySms(){//3.2 定義短信通知機制的實現策略 return new NotifyMechanismInterface(){ public boolean doNotify(String msg){ ....... } }; } public static NotifyMechanismInterface byWechat(){//3.3 定義微信通知機制的實現策略 return new NotifyMechanismInterface(){ public boolean doNotify(String msg){ ....... } }; }}//4、使用場景NOTIFY_TYPE.valueof(type).getNotifyMechanism().doNotify(msg); 充血枚舉類型——Rich Enum Type 模式的優勢: 不難發現,這其實就是 enum 枚舉類型和 Strategy Pattern 策略模式的巧妙結合運用; 當需要增加新的通知方式時,只需在枚舉類 NOTIFY_TYPE 增加一個值,同時在策略接口 NotifyMechanismInterface 中增加一個 by 方法返回對應的策略實現; 當需要修改某個通知機制的實現細節,只需修改 NotifyMechanismInterface 中對應的策略實現; 無論新增還是修改通知機制,調用方完全不受影響,仍然是 NOTIFY_TYPE.valueof(type).getNotifyMechanism().doNotify(msg); 與傳統 Strategy Pattern 策略模式的比較優勢:常見的策略模式也能消滅 if else 判斷,但是實現起來比較麻煩,需要開發更多的 class 和代碼量: 每個策略實現需單獨定義成一個 class; 還需要一個 Context 類來做初始化——用 Map 把類型與對應的策略實現做映射; 使用時從 Context 獲取具體的策略; Rich Enum Type 的進一步的充血: 上面的例子中的枚舉類型包含了行為,因此已經算作充血模型了,但是還可以為其進一步充血; 例如有些場景下,只是要對枚舉值做個簡單的計算獲得某種 flag 標記,那就沒必要把計算邏輯抽象成 NotifyMechanismInterface 那樣的接口,殺雞用了牛刀; 這時就可以在枚舉類型中增加 static function 封裝簡單的計算邏輯; 策略實現的進一步抽象: 當各個策略實現(byEmail bySms byWechat)存在共性部分、重複邏輯時,可以將其抽取成一個抽象父類; 然後就像前一章節——業務模板 Pattern of NestedBusinessTemplate 那樣,在各個子類之間實現優雅的邏輯分離和複用。 5. 重構前的火力偵察:為你的項目編制一套代碼庫目錄/索引——CODEX 以上就是我總結出的最常見也最影響代碼質量的 4 個問題及其解決方案: 職責單一、小顆粒度、高內聚、低耦合的業務邏輯層組件——倒金字塔結構; 打造項目自身的 lib 層和 framework——正確的複用姿勢; 業務模板 Pattern of NestedBusinessTemplate——控制邏輯分離; 充血的枚舉類型 Rich Enum Type——消滅硬編碼風格的 if else 條件判斷; 接下來就是如何動手去針對這 4 個方面進行重構了,但是事情還沒有那麼簡單。 上面所有的內容雖然來自實踐經驗,但是要應用到你的具體項目,還需要一個步驟——火力偵察——弄清楚你要重構的那個模塊的邏輯脈絡、算法以致實現細節,否則貿然動手,很容易遺漏關鍵細節造成風險,重構的效率更難以保證,陷入進退兩難的尷尬境地。 我 2019 年一整年經歷了 3 個代碼十分混亂的項目,最大的收穫就是摸索出了一個梳理爛代碼的最佳實踐——CODEX: 在閲讀代碼過程中,在關鍵位置添加結構化的註釋,形如://CODEX ProjectA 1 體檢預約流程 1 預約服務 API 入口 所謂結構化註釋,就是在註釋內容中通過規範命名的編號前綴、分隔符等來體現出其所對應的項目、模塊、流程步驟等信息,類似文本編輯中的標題 1、2、3; 然後設置 IDE 工具識別這種特殊的註釋,以便結構化的顯示。Eclipse 的 Tasks 顯示效果類似下圖; 這個結構化視圖,本質上相對於是代碼庫的索引、目錄,不同於 javadoc 文檔,CODEX 具有更清晰的邏輯層次和更強的代碼查找便利性,在 Eclipse Tasks 中點擊就能跳轉到對應的代碼行; 這些結構化註釋隨着代碼一起提交後就實現了團隊共享; 這樣的一份精確無誤、共享的、活的源代碼索引,無疑會對整個團隊的開發維護工作產生巨大助力; 進一步的,如果在 CODEX 中添加 Markdown 關鍵字,甚至可以將導出的 CODEX 簡單加工後,變成一張業務邏輯的 Sequence 序列圖,如下所示。 6. 總結陳詞——不要辜負這個程序員最好的時代 毫無疑問這是程序員最好的時代,互聯網浪潮已經席捲了世界每個角落,各行各業正在越來越多的依賴 IT。過去只有軟件公司、互聯網公司和銀行業會僱傭程序員,隨着雲計算的普及、產業互聯網和互聯網+興起,已經有越來越多的傳統企業開始僱傭程序員搭建 IT 系統來支撐業務運營。 資本的推動 IT 需求的旺盛,使得程序員成了稀缺人才,各大招聘平台上,程序員的崗位數量和薪資水平長期名列前茅。 但是我們這個羣體的整體表現怎麼樣呢,捫心自問,我覺得很難令人滿意,我所經歷過的以及近距離觀察到的項目,鮮有能夠稱得上成功的。這裏的成功不是商業上的成功,僅限於作為一個軟件項目和工程是否能夠以可接受的成本和質量長期穩定的交付。 商業的短期成功與否,很多時候與項目工程的成功與否沒有必然聯繫,一個商業上很成功的項目可能在工程上做的並不好,只是通過巨量的資金資源投入換來的暫時成功而已。 歸根結底,我們程序員羣體需要為自己的聲譽負責,長期來看也終究會為自己的聲譽獲益或受損。 我認為程序員最大的聲譽、最重要的職業素養,就是通過寫出高質量的代碼做好一個個項目、產品,來幫助團隊、幫助公司、幫助組織創造價值、增加成功的機會。 希望本文分享的經驗和方法能夠對此有所幫助! 本文是我的一位技術總監好友:權哥花了半個月時間寫出來的良心文章,強烈推薦給大家,文章很長很硬很有價值,大家可以收藏多看幾遍。希望大家看完之後轉發、點在看,好文章要讓更多的人看到。 特別推薦一個分享架構+算法的優質內容,還沒關注的小夥伴,可以長按關注一下: 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-10-25 關鍵詞: 互聯網 程序員

  • 《我想進大廠》之JVM奪命連環10問

    這是面試專題系列第五篇JVM篇。這一篇可能稍微比較長,沒有耐心的同學建議直接拖到最後。 説説JVM的內存佈局? Java虛擬機主要包含幾個區域: 堆:堆Java虛擬機中最大的一塊內存,是線程共享的內存區域,基本上所有的對象實例數組都是在堆上分配空間。堆區細分為Yound區年輕代和Old區老年代,其中年輕代又分為Eden、S0、S1 3個部分,他們默認的比例是8:1:1的大小。 棧:棧是線程私有的內存區域,每個方法執行的時候都會在棧創建一個棧幀,方法的調用過程就對應着棧的入棧和出棧的過程。每個棧幀的結構又包含局部變量表、操作數棧、動態連接、方法返回地址。 局部變量表用於存儲方法參數和局部變量。當第一個方法被調用的時候,他的參數會被傳遞至從0開始的連續的局部變量表中。 操作數棧用於一些字節碼指令從局部變量表中傳遞至操作數棧,也用來準備方法調用的參數以及接收方法返回結果。 動態連接用於將符號引用表示的方法轉換為實際方法的直接引用。 元數據:在Java1.7之前,包含方法區的概念,常量池就存在於方法區(永久代)中,而方法區本身是一個邏輯上的概念,在1.7之後則是把常量池移到了堆內,1.8之後移出了永久代的概念(方法區的概念仍然保留),實現方式則是現在的元數據。它包含類的元信息和運行時常量池。 Class文件就是類和接口的定義信息。 運行時常量池就是類和接口的常量池運行時的表現形式。 本地方法棧:主要用於執行本地native方法的區域 程序計數器:也是線程私有的區域,用於記錄當前線程下虛擬機正在執行的字節碼的指令地址 知道new一個對象的過程嗎? 當虛擬機遇見new關鍵字時候,實現判斷當前類是否已經加載,如果類沒有加載,首先執行類的加載機制,加載完成後再為對象分配空間、初始化等。 首先校驗當前類是否被加載,如果沒有加載,執行類加載機制 加載:就是從字節碼加載成二進制流的過程 驗證:當然加載完成之後,當然需要校驗Class文件是否符合虛擬機規範,跟我們接口請求一樣,第一件事情當然是先做個參數校驗了 準備:為靜態變量、常量賦默認值 解析:把常量池中符號引用(以符號描述引用的目標)替換為直接引用(指向目標的指針或者句柄等)的過程 初始化:執行static代碼塊(cinit)進行初始化,如果存在父類,先對父類進行初始化 Ps:靜態代碼塊是絕對線程安全的,只能隱式被java虛擬機在類加載過程中初始化調用!(此處該有問題static代碼塊線程安全嗎?) 當類加載完成之後,緊接着就是對象分配內存空間和初始化的過程 首先為對象分配合適大小的內存空間 接着為實例變量賦默認值 設置對象的頭信息,對象hash碼、GC分代年齡、元數據信息等 執行構造函數(init)初始化 知道雙親委派模型嗎? 類加載器自頂向下分為: Bootstrap ClassLoader啓動類加載器:默認會去加載JAVA_HOME/lib目錄下的jar Extention ClassLoader擴展類加載器:默認去加載JAVA_HOME/lib/ext目錄下的jar Application ClassLoader應用程序類加載器:比如我們的web應用,會加載web程序中ClassPath下的類 User ClassLoader用户自定義類加載器:由用户自己定義 當我們在加載類的時候,首先都會向上詢問自己的父加載器是否已經加載,如果沒有則依次向上詢問,如果沒有加載,則從上到下依次嘗試是否能加載當前類,直到加載成功。 説説有哪些垃圾回收算法? 標記-清除 統一標記出需要回收的對象,標記完成之後統一回收所有被標記的對象,而由於標記的過程需要遍歷所有的GC ROOT,清除的過程也要遍歷堆中所有的對象,所以標記-清除算法的效率低下,同時也帶來了內存碎片的問題。 複製算法 為了解決性能的問題,複製算法應運而生,它將內存分為大小相等的兩塊區域,每次使用其中的一塊,當一塊內存使用完之後,將還存活的對象拷貝到另外一塊內存區域中,然後把當前內存清空,這樣性能和內存碎片的問題得以解決。但是同時帶來了另外一個問題,可使用的內存空間縮小了一半! 因此,誕生了我們現在的常見的年輕代+老年代的內存結構:Eden+S0+S1組成,因為根據IBM的研究顯示,98%的對象都是朝生夕死,所以實際上存活的對象並不是很多,完全不需要用到一半內存浪費,所以默認的比例是8:1:1。 這樣,在使用的時候只使用Eden區和S0S1中的一個,每次都把存活的對象拷貝另外一個未使用的Survivor區,同時清空Eden和使用的Survivor,這樣下來內存的浪費就只有10%了。 如果最後未使用的Survivor放不下存活的對象,這些對象就進入Old老年代了。 PS:所以有一些初級點的問題會問你為什麼要分為Eden區和2個Survior區?有什麼作用?就是為了節省內存和解決內存碎片的問題,這些算法都是為了解決問題而產生的,如果理解原因你就不需要死記硬背了 標記-整理 針對老年代再用複製算法顯然不合適,因為進入老年代的對象都存活率比較高了,這時候再頻繁的複製對性能影響就比較大,而且也不會再有另外的空間進行兜底。所以針對老年代的特點,通過標記-整理算法,標記出所有的存活對象,讓所有存活的對象都向一端移動,然後清理掉邊界以外的內存空間。 那麼什麼是GC ROOT?有哪些GC ROOT? 上面提到的標記的算法,怎麼標記一個對象是否存活?簡單的通過引用計數法,給對象設置一個引用計數器,每當有一個地方引用他,就給計數器+1,反之則計數器-1,但是這個簡單的算法無法解決循環引用的問題。 Java通過可達性分析算法來達到標記存活對象的目的,定義一系列的GC ROOT為起點,從起點開始向下開始搜索,搜索走過的路徑稱為引用鏈,當一個對象到GC ROOT沒有任何引用鏈相連的話,則對象可以判定是可以被回收的。 而可以作為GC ROOT的對象包括: 棧中引用的對象 靜態變量、常量引用的對象 本地方法棧native方法引用的對象 垃圾回收器瞭解嗎?年輕代和老年代都有哪些垃圾回收器? 年輕代的垃圾收集器包含有Serial、ParNew、Parallell,老年代則包括Serial Old老年代版本、CMS、Parallel Old老年代版本和JDK11中的船新的G1收集器。 Serial:單線程版本收集器,進行垃圾回收的時候會STW(Stop The World),也就是進行垃圾回收的時候其他的工作線程都必須暫停 ParNew:Serial的多線程版本,用於和CMS配合使用 Parallel Scavenge:可以並行收集的多線程垃圾收集器 Serial Old:Serial的老年代版本,也是單線程 Parallel Old:Parallel Scavenge的老年代版本 CMS(Concurrent Mark Sweep):CMS收集器是以獲取最短停頓時間為目標的收集器,相對於其他的收集器STW的時間更短暫,可以並行收集是他的特點,同時他基於標記-清除算法,整個GC的過程分為4步。 初始標記:標記GC ROOT能關聯到的對象,需要STW 併發標記:從GCRoots的直接關聯對象開始遍歷整個對象圖的過程,不需要STW 重新標記:為了修正併發標記期間,因用户程序繼續運作而導致標記產生改變的標記,需要STW 併發清除:清理刪除掉標記階段判斷的已經死亡的對象,不需要STW 從整個過程來看,併發標記和併發清除的耗時最長,但是不需要停止用户線程,而初始標記和重新標記的耗時較短,但是需要停止用户線程,總體而言,整個過程造成的停頓時間較短,大部分時候是可以和用户線程一起工作的。 G1(Garbage First):G1收集器是JDK9的默認垃圾收集器,而且不再區分年輕代和老年代進行回收。 G1的原理了解嗎? G1作為JDK9之後的服務端默認收集器,且不再區分年輕代和老年代進行垃圾回收,他把內存劃分為多個Region,每個Region的大小可以通過-XX:G1HeapRegionSize設置,大小為1~32M,對於大對象的存儲則衍生出Humongous的概念,超過Region大小一半的對象會被認為是大對象,而超過整個Region大小的對象被認為是超級大對象,將會被存儲在連續的N個Humongous Region中,G1在進行回收的時候會在後台維護一個優先級列表,每次根據用户設定允許的收集停頓時間優先回收收益最大的Region。 G1的回收過程分為以下四個步驟: 初始標記:標記GC ROOT能關聯到的對象,需要STW 併發標記:從GCRoots的直接關聯對象開始遍歷整個對象圖的過程,掃描完成後還會重新處理併發標記過程中產生變動的對象 最終標記:短暫暫停用户線程,再處理一次,需要STW 篩選回收:更新Region的統計數據,對每個Region的回收價值和成本排序,根據用户設置的停頓時間制定回收計劃。再把需要回收的Region中存活對象複製到空的Region,同時清理舊的Region。需要STW 總的來説除了併發標記之外,其他幾個過程也還是需要短暫的STW,G1的目標是在停頓和延遲可控的情況下儘可能提高吞吐量。 什麼時候會觸發YGC和FGC?對象什麼時候會進入老年代? 當一個新的對象來申請內存空間的時候,如果Eden區無法滿足內存分配需求,則觸發YGC,使用中的Survivor區和Eden區存活對象送到未使用的Survivor區,如果YGC之後還是沒有足夠空間,則直接進入老年代分配,如果老年代也無法分配空間,觸發FGC,FGC之後還是放不下則報出OOM異常。 YGC之後,存活的對象將會被複制到未使用的Survivor區,如果S區放不下,則直接晉升至老年代。而對於那些一直在Survivor區來回複製的對象,通過-XX:MaxTenuringThreshold配置交換閾值,默認15次,如果超過次數同樣進入老年代。 此外,還有一種動態年齡的判斷機制,不需要等到MaxTenuringThreshold就能晉升老年代。如果在Survivor空間中相同年齡所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代。 頻繁FullGC怎麼排查? 這種問題最好的辦法就是結合有具體的例子舉例分析,如果沒有就説一般的分析步驟。發生FGC有可能是內存分配不合理,比如Eden區太小,導致對象頻繁進入老年代,這時候通過啓動參數配置就能看出來,另外有可能就是存在內存泄露,可以通過以下的步驟進行排查: jstat -gcutil或者查看gc.log日誌,查看內存回收情況 S0 S1 分別代表兩個Survivor區佔比 E代表Eden區佔比,圖中可以看到使用78% O代表老年代,M代表元空間,YGC發生54次,YGCT代表YGC累計耗時,GCT代表GC累計耗時。 [GC [FGC 開頭代表垃圾回收的類型 PSYoungGen: 6130K->6130K(9216K)] 12274K->14330K(19456K), 0.0034895 secs代表YGC前後內存使用情況 Times: user=0.02 sys=0.00, real=0.00 secs,user表示用户態消耗的CPU時間,sys表示內核態消耗的CPU時間,real表示各種牆時鐘的等待時間 這兩張圖只是舉例並沒有關聯關係,比如你從圖裏面看能到是否進行FGC,FGC的時間花費多長,GC後老年代,年輕代內存是否有減少,得到一些初步的情況來做出判斷。 dump出內存文件在具體分析,比如通過jmap命令jmap -dump:format=b,file=dumpfile pid,導出之後再通過 Eclipse Memory Analyzer等工具進行分析,定位到代碼,修復 這裏還會可能存在一個提問的點,比如CPU飆高,同時FGC怎麼辦?辦法比較類似 找到當前進程的pid,top -p pid -H 查看資源佔用,找到線程 printf “%x\n” pid,把線程pid轉為16進制,比如0x32d jstack pid|grep -A 10 0x32d查看線程的堆棧日誌,還找不到問題繼續 dump出內存文件用MAT等工具進行分析,定位到代碼,修復 JVM調優有什麼經驗嗎? 要明白一點,所有的調優的目的都是為了用更小的硬件成本達到更高的吞吐,JVM的調優也是一樣,通過對垃圾收集器和內存分配的調優達到性能的最佳。 簡單的參數含義 首先,需要知道幾個主要的參數含義。 -Xms設置初始堆的大小,-Xmx設置最大堆的大小 -XX:NewSize年輕代大小,-XX:MaxNewSize年輕代最大值,-Xmn則是相當於同時配置-XX:NewSize和-XX:MaxNewSize為一樣的值 -XX:NewRatio設置年輕代和年老代的比值,如果為3,表示年輕代與老年代比值為1:3,默認值為2 -XX:SurvivorRatio年輕代和兩個Survivor的比值,默認8,代表比值為8:1:1 -XX:PretenureSizeThreshold 當創建的對象超過指定大小時,直接把對象分配在老年代。 -XX:MaxTenuringThreshold設定對象在Survivor複製的最大年齡閾值,超過閾值轉移到老年代 -XX:MaxDirectMemorySize當Direct ByteBuffer分配的堆外內存到達指定大小後,即觸發Full GC 調優 為了打印日誌方便排查問題最好開啓GC日誌,開啓GC日誌對性能影響微乎其微,但是能幫助我們快速排查定位問題。-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:gc.log 一般設置-Xms=-Xmx,這樣可以獲得固定大小的堆內存,減少GC的次數和耗時,可以使得堆相對穩定 -XX:+HeapDumpOnOutOfMemoryError讓JVM在發生內存溢出的時候自動生成內存快照,方便排查問題 -Xmn設置新生代的大小,太小會增加YGC,太大會減小老年代大小,一般設置為整個堆的1/4到1/3 設置-XX:+DisableExplicitGC禁止系統System.gc(),防止手動誤觸發FGC造成問題 特別推薦一個分享架構+算法的優質內容,還沒關注的小夥伴,可以長按關注一下: 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-10-25 關鍵詞: C語言 嵌入式

  • 愛奇藝視頻精彩度分析算法及應用

    分享嘉賓:劉祁躍 愛奇藝 科學家 編輯整理:龔雲荷 出品平台:DataFunTalk 導讀: 視頻是愛奇藝的核心內容,視頻內容的精彩度分析,不僅關係着視頻的分發,也關係着視頻相關廣告的投放等,比如能否將廣告放在非常吸引人的點位上。 所以我們非常關注能否分析出有吸引力的內容,甚至根據分析的結果,二次創造出有吸引力的內容。 對於吸引力,我們在思考什麼是非常重要的。 這裏列出三點: 第一個是視頻質量,比如是否清晰、鏡頭是否晃動、是否有無意義的內容,這是基礎的質量問題。 第二個是視頻美學,比如色彩是否優美,構圖是否好,光線明暗對比度是否好。 當然,有了質量和美學還不足以説明視頻是否有吸引力,大部分的視頻是靠情節取勝,也就是靠視頻的內容去吸引人,不管是長視頻的電視劇、電影、動漫,還是橫版短視頻和豎版小視頻,都包含着當前視頻是何人何地發生何事,由這樣的內容反映精彩度。精彩度是視頻吸引力的第三點,也是最重要的一點。 01 方法及整體框架 1. 如何識別精彩 這就促使我們去思考,如何分析內容的精彩度,這裏有幾個維度:第一,內容標籤,比如打鬥等偏感官層面的信息或者是浪漫等偏高層語義方面的信息,這需要理解視頻內容。第二方面是程度等級,比如説打鬥,如果是武林高手之間的對決,相比於我們普通人之間打鬥會更精彩,所以需要一個分級打分機制。還有一些信息影響到用户對視頻的喜好,比如對明星、IP、劇集等的喜愛,都會影響用户對其精彩度的判斷。前面這3點是人們對於視頻精彩度的一個理性分析,但實際上精彩度還是較主觀的看法,同一個視頻,有些人覺得精彩,有些人則不覺得。一些上映之後成為收視率“黑馬”的作品,在上映之前,人們沒有預期到其足夠精彩,上線之後,卻成為爆款,這體現了對精彩度主觀判斷的侷限性,因此我們也要考慮視頻上線後的用户反饋。比如用户的播放、彈幕等行為,有些視頻片段用户會反覆播放,另一些則會被跳過。我們希望通過以上幾個方面,構建對於精彩度的認知。 2. 精彩度分析整體技術框架 由此,我們形成如圖的精彩度分析方案,該方案的適用對象較廣泛,不管是對完整的劇集,還是簡短的花絮,都可以適用,我們這裏聚焦於對電影電視劇的片段做分析。影視劇的整體精彩度比較宏觀,受參演明星,改編的小説等已知因素的影響,所以通過算法對整體做精彩度分析收益相對較小。當下我們更關注,對長視頻局部剪輯片段的打分。精彩的局部片段的識別,有助於啓發創作者對於局部精彩視頻的思考,有利於後續創作的提升。同時,精彩片段的識別,有助於二次傳播、碎片化時間的消費,以及廣告的投放等。如框圖所示,我們輸入的是視頻片段,然後進行多模態的視頻特徵提取,接下來分兩步,一個是基於GCN的弱監督模型,另一個是基於多任務學習的監督模型。 02 視頻精彩度分析算法 1. 精彩度監督模型 對於精彩度的監督模型,首先需要標註人員對視頻精彩度進行打分。考慮到數據的複雜性,會充分利用多模態和時序關係去提取信息。操作中會有一些具體技巧,比如由於其標註主觀性比較強,會進行噪聲建模,從迴歸分數變成一個擬合分佈。另外,評分和標籤是高度相關性的,因此可以通過多模型、多任務學習的方式來進行。 2. 不同模型提取特徵性能對比 這張圖顯示了採用不同的模型提取特徵,對最終精彩度輸出的影響。最初的方法是針對圖片信息採取2D的CNN,再去對幀級別feature進行融合;接着考慮由時序上的3D卷積模型來提特徵;然後嘗試根據預訓練模型來進行微調;再利用視覺+音頻的多模態的信息進一步提升。 3. 精彩度分數預測 監督模型的一個分支是精彩度分數預測。對於精彩度分數,會先做人工標註,但是因為主觀性偏向非常強,所以噪聲較大,可信度並不高。當標註為某一個分數,那它很大概率會是以這個分數為均值的正態或偏正態分佈。比如標註分數是六分,那該視頻可能很大的概率是六分,但也可能會小一些的概率是五分或七分。為減少噪聲影響,會對噪聲做一個建模,直觀的假設,將標註的分數看做一個正態分佈的均值。為了滿足概率積分的要求,實際上設計了一個偏正態分佈。分佈的方差通過理論分析+實驗,來確定一個比較合適的值。有了這個分佈,對於分數的迴歸,可以變成一個類似分類的任務,對於每一個離散值給出一個概率,這樣得到對分佈的預測,從而加權得到最終預測的分數。採取該策略後,我們發現對於噪聲比較大的主觀性標註任務還是有意義的,其它一些圖片迴歸任務我們也用了類似方法,取得了不錯的效果。 4. 看點多標籤模型 接下來看第二點,關於視頻內容的看點多標籤。比如像打鬥、爆炸,都是比較有意思的標籤,可能是會吸引人的。對於不同類型的視頻,看點的標籤是不一樣的。比如説對於偶像片來説,浪漫的標籤可能非常有吸引力;對於動作片來説,可能飆車、打鬥、槍戰等很有吸引力。多標籤模型,在近幾年各領域都廣泛關注,包括短視頻標籤、圖片多標籤、文本多標籤等。多標籤的難點,是如何對同樣的信息去生成不同的標籤,針對這個問題會有三個方案。第一種是利用信息不同區域對應不同的標籤,可以類比目標檢測。即劃分圖像的不同區域,用其本身及周邊的信息,去預測該區域的一個標籤。那第二個是層次的關係,比如從畫面視覺內容來説,一男一女在西餐廳吃燭光晚餐,則需要進行性別識別、場景識別、目標檢測等,同時它是一個浪漫的約會場景,所以還可以推理出上層的標籤。第三個要考慮的點,是標籤之間的依賴關係,有一些標籤很可能經常共同出現,比如説有海灘和陽光。有一些標籤之間不太容易共現,比如手機跟古裝片,可能是互斥關係。當然如果能識別這是一個穿越片,就可認為這兩個標籤共現是比較和諧的。在很多看點多標籤之間,有這種互相依賴的關係,如何去表達標籤的關係有很多方式,比如説CNN和RNN結合,通過RNN去顯示地表達標籤之間的依賴。那其它一些方式,比如通過標籤embedding,希望其去影響分類器,而對於這個embedding,可能會通過圖的拓撲結構,根據相似的鄰域標籤信息來修改embedding,從而讓這個embedding包含標籤之間的關係,再將這個embedding以某種方式去影響分類器。還有一種方式,就是訓練時找到一個嵌入的空間,把ground truth的多標籤投射到嵌入空間,利用多標籤去生成一個feature,同時對於待處理的數據也生成一個feature,要求這兩個feature要儘可能接近,之間的某種距離可以作為loss之一。這樣,嵌入空間的音視頻feature,即表達了多標籤的關係,可以認為是對多標籤的編碼,而後續的分類過程,就是對多標籤的解碼。 5. 多任務學習模型 評分和看點標籤這兩個模型高度相關,所以用了多任務學習。因為業務有非常多的需求,各需求之間往往有相關性,經常存在多任務學習的可能性。另外,海量數據下如何節省資源,也是非常現實的需求。如果我們通過多任務學習能夠降低資源消耗,更好的體現相關性,甚至還有可能提升指標,那會非常有動力去做多任務學習。我們現在的架構,底層共享網絡,上層建立評分和標籤網絡。訓練策略方面沒有標準化方式,採取一些經驗性的方式,動態調節權重,比如根據每一路分支loss下降的情況進行調整,或是動態分析每路分支的運行情況,修改訓練頻次,保持一致的收斂速度。 6. 弱監督模型 接下來我們再看一下,弱監督模型這一塊。我們有很多用户觀影行為數據,是否可用於擬合對分數的標註。比如觀看行為,觀看次數越高,一般也越精彩。但是不同視頻本身熱度不一樣,同一個視頻的不同部分,單純看播放量也不公平,因為很多用户不會看完整個視頻,一般前面的片段播放量會更高。所以,直接將用户行為作為精彩度的度量,雖然相對於人工標註的分數更能體現用户的實際偏好,但還是存在非常多的噪聲。為了減少噪聲影響,要做很多數據預處理,比如儘量避免用區分度不大的數據。除了關心絕對精彩度,也關心相對大小,即一個視頻中,哪些內容相對其餘部分更有吸引力。我們往往會從一個視頻當中,篩選相對精彩的內容,去做二次創作、投放廣告等。在這樣的訴求下,可以採用Ranking思想去設計Loss。因為噪聲較大,會給label計算置信度,比如可以用相似的樣本來做平滑。這裏我們還可以利用圖,設計圖卷積過濾高頻信息更新樣本feature,實現更好的聚類,並利用更新後的相近節點來修改樣本置信度,最終有效提升弱監督模型效果。 03 應用 1. 前情提要 前情提要是精彩度相關的一個應用,運用算法對每一集識別出精彩片段,通過一定策略剪輯。雖然前景提要本身是一個用户產品,但可以在上面投放廣告,並且處於片頭這個黃金位置,實現了很好的商業價值。 2. 拆條 第二個應用是長視頻拆條。做一個比較好的拆條,要從長視頻當中選出比較精彩的部分,同時滿足切分方式的合理性。可以方便投放在站內或者是站外的各種渠道上,這樣可利用用户的碎片化時間,一方面形成對短內容的消費,一方面也能夠起到短帶長的作用。所以要做拆條的話,不僅僅需要對內容本身的理解,也需要對精彩度做分析。 3. 自動生成封面 智能封面圖生成,目前線上的影視劇封面,採用自動生成動態圖的方式。對視頻中精彩片段進行打分,並需要保證片段的多樣性和代表性。對於圖片也會有精彩度、美學等分析。不管是靜態封面圖還是動態封面圖,都可以生成多個,然後去做個性化的分發,並通過線上的反饋來調整生成封面圖的策略。 4. 片段打分 還有一個應用,是直接對片段的精彩度打分,有利於冷啓動階段的分發;也能給創作者提供參考。 04 總結和展望 總結一下,當大家思考內容平台的時候,會非常關注內容是否精彩。針對精彩度分析,不只是一個單一的技術,更是一個綜合性的解決策略。可能會利用各種各樣的垂直算法、產品策略,工程策略等,最終形成可行方案。精彩度方案已被廣泛應用,並會從質量和效率兩個方面的提升來做評價。由於精彩度分析任務的特點,如需要用到海量數據、具有較強主觀性、有很多用户行為數據等,會牽涉到很多技術方向,像弱監督、多任務、多標籤、圖等等。此類偏主觀的分析,用户標準、用户行為以及先驗的外部信息,這三個維度都非常重要。 後續的展望,第一方面是在特徵提取上,儘量去融合更多的信息,包括文本的信息,比如台詞、彈幕等。第二個是在模型上,比如怎麼通過半監督的方式,把有標註和無標註的數據,放到一個統一框架中來。第三點是如何利用各種垂直識別,不管是底層的識別,還是偏上層的推理形成高層語義,需要能把這些信息利用起來,從而知道為什麼精彩,作出可解釋的精彩度評價。 嘉賓介紹: 劉祁躍 愛奇藝 | 科學家 劉祁躍,愛奇藝科學家,智能平台部視頻分析組負責人。負責對視頻內容的理解和生成,並應用到廣告、創作、分發等業務。 特別推薦一個分享架構+算法的優質內容,還沒關注的小夥伴,可以長按關注一下: 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-10-25 關鍵詞: 互聯網 視頻

首頁  上一頁  1 2 3 4 5 6 7 8 9 10 下一頁 尾頁
發佈文章