顯示具有 超譯 標籤的文章。 顯示所有文章
顯示具有 超譯 標籤的文章。 顯示所有文章

2019年10月1日 星期二

[超譯] 解決 .Net Reference 的版本衝突

## 前言

最近遇到一個狀況,我包好的 dll 會用到 Newtonsoft.Json.dll (Json.Net),版本是 4.5。拿去給別人(A公司)用,他們也用了別人(B公司)的 dll,也用到 Newtonsoft.Json.dll (Json.Net),但是他們指定的是 9.0.0。當 Visual Studio 把我指定的 Newtonsoft.Json.dll 搬到執行檔旁邊,B公司的 dll 就無法啟動。當換成 B公司指定的,就換我的 dll 無法啟動。A公司的人大叫該怎麼辦?因為我們公司比較小,所以我要解決這個問題。

## 主旨

一般的解決方法都是叫大家重新抓過新版的 dll,像我們這次就是叫我這比較舊的版本要升級。但,支援的 .Net Framework 不能超過 4.5.2,超過會讓其他家的 dll 無法運作。這作法也沒什麼不好,只是沒有一勞永逸。要是將來我的 dll 給了幾家客戶,他們幾家要的 Newtonsoft.Json.dll 都不一樣,或是他們升級了,我這小公司就得配合找到對應的 Newtonsoft.Json.dll 來 rebuild 才行。

網路上用不同關鍵字找過,我認為這個比較好。https://michaelscodingspot.com/how-to-resolve-net-reference-and-nuget-package-version-conflicts/

## 超譯

重點,解法有兩個方向,四個方法。

兩個方向是 (1) 如果可以,全部改成同一個版本。 (2) 使用 dll side-by-side loading。

四個解法之中,作者建議解法(1)

### 解法(1) Use a single assembly version with Binding Redirect

在有 app.config 或 dll.config 的存在時,增加一個 runtime 標籤。舉例:

```
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="1.2.11" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
```
作者舉 log4net 為例,加上 runtime 標籤指示 CLR 使用 1.2.11 版的 log4net。

### 解法(2) Override AssemblyResolve for side-by-side loading (No need for strong names)

使用 AppDomain.CurrentDomain.AssemblyResolve 事件來處理不同版本的尋找問題。

首先將要處理的 dll 的 CopyLocal 設成 False。並將不同版本的 dll 放到不同磁碟位置。然後在執行檔的最開頭註冊 AssemblyResolve 事件。它在 app 找不到 dll 的時候會被呼叫,然後我們就寫好程式來處理它。

範例:

```
static void Main(string[] args)
{
    AppDomain.CurrentDomain.AssemblyResolve += (sender, resolveArgs) =>
    {
        string assemblyInfo = resolveArgs.Name;// e.g "Lib1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        var parts = assemblyInfo.Split(',');
        string name = parts[0];
        var version = Version.Parse(parts[1].Split('=')[1]);
        string fullName;
        if (name == "Lib1" && version.Major == 1 && version.Minor == 0)
        {
            fullName = new FileInfo(@"V10\Lib1.dll").FullName;
        }
        else if (name == "Lib1" && version.Major == 1 && version.Minor == 1)
        {
            fullName = new FileInfo(@"V11\Lib1.dll").FullName;
        }
        else
        {
            return null;
        }

        return Assembly.LoadFile(fullName);
    };
```

### 解法(3) Copy assemblies to different folders and use <codebase> to load them side-by-side (Requires strong names)

這個方法需要 dll 有強式名字。這點 Newtonsoft.Json.dll 或 log4net 這類公開的 dll 做得到。或是自己做的 dll 也來個強式名字也行。將要處理的 dll 的 CopyLocal 設成 False。

舉例:

```
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <dependentAssembly>
          <assemblyIdentity name="StrongNameLib" culture="neutral" publicKeyToken="6a770f8bdf3d476a" />
          <codeBase version="1.0.0.0" href="StrongNameLibV10/StrongNameLib.dll"/>
          <codeBase version="1.1.0.0" href="StrongNameLibV11/StrongNameLib.dll"/>
        </dependentAssembly>
      </assemblyBinding>
  </runtime>
</configuration>
```

這就是告訴 CLR 按照版本去找 dll。如果沒有強式名字,CLR 會忽略版本,所以總是找到第一個 codeBase 所列的 dll。href 也可以用絕對路徑,像是 @"FILE://c:/Lib/MyLib.dll"

### 解法(4) Install assemblies to the Global Assembly Cache (GAC)

這個方法是把要用到的 dll 註冊到 GAC (Global Assembly Cache) 裡。這會需要 dll 有強式名字,而且還要使用者手動執行指令(或是用安裝程式)將 dll 註冊到 GAC 裡。例如:

```
gacutil.exe -i "c:\Dev\Project\Debug\MyLib.dll"
```

將要處理的 dll 的 CopyLocal 設成 False。依照 runtime 的尋找規則,會先找尋 GAC 有沒有可用 dll,所以因為 GAC 都有註冊,所以各自會找到對應的版本來使用。

這個方法會需要使用者手動註冊或者使用 installer 將 dll 註冊到 GAC。也就這樣而已。但是有些人就是會嫌……。

2017年9月13日 星期三

[超譯] 微服務怎麼溝通?

# 微服務怎麼溝通?

我們在工作上做過一些分散式的開發與服務間的溝通。有些是透過開放 HTTP 服務。對於系統間的非同步訊息傳送,我公司使用 FubuMVC 與它的 .Net Core 取代物 "Jasper" 做為 service bus (translate "Jasper" to "MassTransit" or "NServiceBus" when you read this). 此格文是給我們團隊的架構的建議的草案,讓我們團隊為產品選擇何者做為微服務架構的一部份時有個參考。如果我同事看到這篇而有不同意的話,不用擔心。這份文件是活的,總是能討論的。

微服務通常需要傳送訊息給別人或處理別人的訊息,也許是微服務或客戶端。因此,為服務間通訊好好思考是值得的。

我們通常使用 HTTP 或 Jasper/FubuMVC service bus 做為服務間溝通。在思考如何選擇服務間溝通的工具之前,先要想想你的訊息的要求是什麼。服務間溝通約略是以下幾個分類:

1. Publish/Subscribe 非同步廣播一個訊息到所有的訂閱者,不預期一個立即的回應。它與「射後不理」的差異是隱含「保證送達」,也就是訊息會持續保留,直到它被發布出去。Jasper/FubuMVC service bus 有做到「保證送達」,它透過在 LightingQueue 的持續式 "store and forward" 機制,而且最後我們把 RabbitMQ 放進 Docker'ized hosting。

2. Request/Reply 呼叫另一個服務且期待一個回應。從 web service 查詢資料是一個例子。透過 service bus 送訊息且期待一個回應也是一個例子。query handler 就是 request/reply 的範例。

3. Fire and Forget 送出一個 request,然後不管任何回應,不在乎回應是否有來。這種模式多用在效能優先且訊息不是很要緊的情況。Jasper/FubuMVC 使用在 node 內通訊,用以協調訂閱與健康檢查,這是透過 LightningQueue 在 "fire and forget" 的模式來做。

## 以下情況使用 HTTP Service:
* 你的服務要開放 API 給外部使用者。
* 你的服務要給網頁瀏覽器使用。
* 你要開放查詢端點( query endpoint) 給其他服務,它們需要拿到資料馬上使用的方式。
* 你不需要「保證送達」。
* 你在前期不清楚未來你的服務的客戶端會是什麼其他的機制。目前 HTTP 普通存在於所有平台。
* 你需要開放你的服務給「非.Net」客戶端。用現存其他平台的 service bus 是完全可能的,但在這個情況,用 HTTP 端點會比較少阻力。

## 以下情況使用 Service Bus:
* 你需要耐用的 publish/subscribe 的模式。如果你的服務不需要等待下家系統的回應,你需要的可能是 publish/subscribe。
* 如果你會同一個訊息給不同的訂閱者。
* 如果你需要支援「動態訂閱」。也就是讓其他服務可以到你的服務註冊,然後從你的服務接收訊息。
* 如果你要「射後不理」的訊息方式,使用 service bus 在 LightningQueue 的 non-persistent mode。(參考 ZeroMQ)
* 你也許需要 Jasper/FubuMVC 裡的 "delayed message" 的好處。
* 你需要實作長命服務,例如 saga workflow
* 雖然可利用限縮 HTTP request 來控制,但透過 servcie bus 後面的 message queue 來處理大量負載會更簡單有效。
* 如果 message 處理順序很重要,你需要在 servcie bus 排隊。

## 灰色地帶
比較 HTTP 與 service bus 不是完全黑與白的選擇。service bus 也支援 request/reply 模式,你也可以用 HTTP 來做射而不理。兩個招數可以與我們現存的技術堆疊水平擴展。Jasper 最終會支援 HTTP 傳輸以及更有效率的 request/reply,這讓情況更混亂。如果你感覺不是很清楚哪個方向才對,那你的團隊所熟悉的方法會是較可接受的選擇。很可能,這意謂著,使用常見的 ASP.NET Core 堆疊給 HTTP Service 使用,而不是我們今天定做的 service bus 技術。

## 避免以下整合的方式
因為外部客戶端的關係,我們會必須使用下列的方式。但是非常強烈建議不要這麼做:
* publish 檔案到檔案系統及監控目錄
* publish 檔案到 FTP server
* 使用共享資料庫。關聯式資料庫的排隊機制不是很有效率,而且我們也不想要服務之間藉著共享資料庫而有太硬的耦合。
不同意嗎?還有什麼要加的?不用客氣,請幫我讓這個列表更圓滿,可以在下方留言。
原文連結:https://jeremydmiller.com/2017/05/24/how-should-microservices-communicate/

2016年8月8日 星期一

[超譯]Node.js 加 MQTT 入門

https://blog.risingstack.com/getting-started-with-nodejs-and-mqtt/

Node.js 加 MQTT 入門

這篇貼文由 Charlie Key 提供,他是 Structure 的 CEO 與 Co-Founder。Structure 是一個 IoT 的平台,讓你能輕鬆建立相連的經驗與解決方案。Charlie 已經用 Node.js 於工作幾年,現在用它來為 IoT 的世界充能。
Javascript 的世界持續地開發新彊界,像 Node.js 的技術可讓伺服端快速擴展,而現在達到 IoT 的世界。Node.js 現在可在許多嵌入式裝置內,像是 Intel Edison。與嵌入式裝置的溝通一向都可行的,但使用 Node.js 與 MQTT 這類協定讓溝通前所未有的簡單。
在這貼文,我們會看一下如何利用兩個技術(Node.js 與 MQTT)來傳送訊息,建立一個簡易的車庫開門應用程式。這只是此類通訊的其中的一個可能的應用。
MQTT 本身是個非常簡單的 publish / subscribe (出版/訂閱)協定。它讓你在一個主題上送訊息(你可以想像那些是頻道),經由一個中央管理的 message broker。整個協定故意非常輕量。這會讓它能輕易地在嵌入式裝置上執行。幾乎所有的微處理器都有函式庫可用讓它能收送 MQTT 的訊息。以下可以看到 MQTT 溝通的基本概念。
這裡一張架構圖
現在,想像一下我們要打造一個遠端控制的車庫開門系統,使用 MQTT。第一件事我們需要計畫車庫門與遠端遙控器要傳送什麼訊息。為了要讓這範例簡單,我們只打算能夠開門與關門就好。真實的架構圖會長成這樣:
又一張架構圖
門會有幾個狀態,已開、已關、開門中、關門中。真的門也許會有其他狀態,如 暫停。但我們今天暫不考慮。
我們的應用程式會分開兩個檔案,一個是給車庫用另一個給控制器用。我會在每個程式的上頭標名檔名。首先,我們會需要用 npm 安裝 mqtt 函式庫,然後設定我們要用的 broker。現在有很多開放的 broker 可用於測試,我會使用 broker.hivemq.com。再次強調,這只是測試用,不要在正式產品還用這個。以下是兩個檔案一開始都要的程式碼:
// contoller.js and garage.js
const mqtt = require('mqtt')  
const client = mqtt.connect('mqtt://broker.hivemq.com') 
接下來,我們要加一些程式碼來連上 broker。一定連上,我們會建立一個主題(頻道),這在車庫門連上時的溝通用的。在門這邊,是 publish(出版)訊息到這個主題,而控制器這邊則是subscribe(訂閱)。同樣,在這個時間點,我們會加一個區域變數,追蹤車庫門現在的狀態。你會發現我們的主題加了前綴 “garage/”,這是為了組織目的的簡化,你也可以隨自己喜歡來命名。
// garage.js
const mqtt = require('mqtt')  
const client = mqtt.connect('mqtt://broker.hivemq.com')

/**
* The state of the garage, defaults to closed
* Possible states : closed, opening, open, closing
*/

var state = 'closed'

client.on('connect', () => {  
  // Inform controllers that garage is connected
  client.publish('garage/connected', 'true')
})
在控制器端,我們不只是要訂閱這主題,我們也需要加上訊息接聽者,對訊息出版時採取動作。一但訊息收到,我們會使用一個變數,檢查變數的值且追蹤是否門還連在系統上。
// controller.js
const mqtt = require('mqtt')  
const client = mqtt.connect('mqtt://broker.hivemq.com')

var garageState = ''  
var connected = false

client.on('connect', () => {  
  client.subscribe('garage/connected')
})

client.on('message', (topic, message) => {  
  if(topic === 'garage/connected') {
    connected = (message.toString() === 'true');
  }
})
目前為止,門與控制器只知道,門是否連在系統上,我們還不能採取什麼動作。為了要讓控憲器知道門發生什麼事,我們再加上一個函式,送出現在門的狀態,函式長這樣:
// added to end of garage.js
function sendStateUpdate () {  
  console.log('sending state %s', state)
  client.publish('garage/state', state)
}
要使用這個函式,我們會加在車庫連上的呼叫裡:
// updated garage.js connect
client.on('connect', () => {  
  // Inform controllers that garage is connected
  client.publish('garage/connected', 'true')
  sendStateUpdate()
})
現在車庫門可以更新,告訴每個人它現在的狀態。現在控制器需要更新自己的門狀態的變數。然而在這個時間點,先更新訊息處理函式,對應不同的主題呼叫不同的函式。這會增加一點程式的結構性。整個更新完如下:
// updated controller.js
const mqtt = require('mqtt')  
const client = mqtt.connect('mqtt://broker.hivemq.com')

var garageState = ''  
var connected = false

client.on('connect', () => {  
  client.subscribe('garage/connected')
  client.subscribe('garage/state')
})

client.on('message', (topic, message) => {  
  switch (topic) {
    case 'garage/connected':
      return handleGarageConnected(message)
    case 'garage/state':
      return handleGarageState(message)
  }
  console.log('No handler for topic %s', topic)
})

function handleGarageConnected (message) {  
  console.log('garage connected status %s', message)
  connected = (message.toString() === 'true')
}

function handleGarageState (message) {  
  garageState = message
  console.log('garage state update to %s', message)
}
在這裡,我們的控制器可以跟上車庫門的狀態與連線狀態。現在可以加一些功能來控制我們的門。第一件事是讓車庫開始接聽一些訊息,告訴它開或關。
// updated garage.js connect call
client.on('connect', () => {  
  client.subscribe('garage/open')
  client.subscribe('garage/close')

  // Inform controllers that garage is connected
  client.publish('garage/connected', 'true')
  sendStateUpdate()
})
我們現在需要在車庫門這裡加個訊息接聽者:
// added to garage.js
client.on('message', (topic, message) => {  
  console.log('received message %s %s', topic, message)
})
在控制器這裡,我們也會加上傳送開門或關門訊息的能力。這有兩個簡單的函式。在一個真實的應用程式中,這會由外部輸入來呼叫(像是 web 應用程式,手機 app…等)。在這個範例中,我們會用個計時器來呼叫,只是測試這個系統而已。新增的程式碼如下:
// added to controller.js
function openGarageDoor () {  
  // can only open door if we're connected to mqtt and door isn't already open
  if (connected && garageState !== 'open') {
    // Ask the door to open
    client.publish('garage/open', 'true')
  }
}

function closeGarageDoor () {  
  // can only close door if we're connected to mqtt and door isn't already closed
  if (connected && garageState !== 'closed') {
    // Ask the door to close
    client.publish('garage/close', 'true')
  }
}

//--- For Demo Purposes Only ----//

// simulate opening garage door
setTimeout(() => {  
  console.log('open door')
  openGarageDoor()
}, 5000)

// simulate closing garage door
setTimeout(() => {  
  console.log('close door')
  closeGarageDoor()
}, 20000)
以上的程式碼包含開與關的功能。它們確認車庫已經連上系統而且不在已要求的狀態中。我們的控制器最後版本的程式碼如下:
// controller.js
const mqtt = require('mqtt')  
const client = mqtt.connect('mqtt://broker.hivemq.com')

var garageState = ''  
var connected = false

client.on('connect', () => {  
  client.subscribe('garage/connected')
  client.subscribe('garage/state')
})

client.on('message', (topic, message) => {  
  switch (topic) {
    case 'garage/connected':
      return handleGarageConnected(message)
    case 'garage/state':
      return handleGarageState(message)
  }
  console.log('No handler for topic %s', topic)
})

function handleGarageConnected (message) {  
  console.log('garage connected status %s', message)
  connected = (message.toString() === 'true')
}

function handleGarageState (message) {  
  garageState = message
  console.log('garage state update to %s', message)
}

function openGarageDoor () {  
  // can only open door if we're connected to mqtt and door isn't already open
  if (connected && garageState !== 'open') {
    // Ask the door to open
    client.publish('garage/open', 'true')
  }
}

function closeGarageDoor () {  
  // can only close door if we're connected to mqtt and door isn't already closed
  if (connected && garageState !== 'closed') {
    // Ask the door to close
    client.publish('garage/close', 'true')
  }
}

// --- For Demo Purposes Only ----//

// simulate opening garage door
setTimeout(() => {  
  console.log('open door')
  openGarageDoor()
}, 5000)

// simulate closing garage door
setTimeout(() => {  
  console.log('close door')
  closeGarageDoor()
}, 20000)
現在,車庫門必須對應這些訊息做反應。再一次,我們使用 switch 引導不同的主題。一但訊息被收到,門會試著處理它且確認它能到那狀態才動作。然後它會進入轉移狀態(開門中、關門中),送出更新訊息,最後到達持續狀態(已開、已關)。為了測試的目的,最後一個部份是用計時器來完成。在真實情況中,系統應該等待硬體訊號通知它已完成。
// updated garage.js message handler
client.on('message', (topic, message) => {  
  console.log('received message %s %s', topic, message)
  switch (topic) {
    case 'garage/open':
      return handleOpenRequest(message)
    case 'garage/close':
      return handleCloseRequest(message)
  }
})
開門與關門的處理函式可以加到檔案的最後。
// added to garage.js
function handleOpenRequest (message) {  
  if (state !== 'open' && state !== 'opening') {
    console.log('opening garage door')
    state = 'opening'
    sendStateUpdate()

    // simulate door open after 5 seconds (would be listening to hardware)
    setTimeout(() => {
      state = 'open'
      sendStateUpdate()
    }, 5000)
  }
}

function handleCloseRequest (message) {  
  if (state !== 'closed' && state !== 'closing') {
    state = 'closing'
    sendStateUpdate()

    // simulate door closed after 5 seconds (would be listening to hardware)
    setTimeout(() => {
      state = 'closed'
      sendStateUpdate()
    }, 5000)
  }
}
有了這些函式,我們現在有個完整功能的車庫系統。為了測試你可以開啟控制器程式然後再開車庫門程式。控制器會在開啟後 5 秒送開門指令,20 秒送關門指令。
最後我要建議的事是讓我們的車庫門更新自己的連線狀態,當我們的程式因為任何原因被關掉的時候。這個離開的乾淨程式碼是依照 stackoverflow answer 建議,然後改用 mqtt 訊息傳送。這可在放在車庫檔案的最後。所有的東西組合起來就得到最後的車庫檔案。
// garage.js
const mqtt = require('mqtt')  
const client = mqtt.connect('mqtt://broker.hivemq.com')

/**
 * The state of the garage, defaults to closed
 * Possible states : closed, opening, open, closing
 */
var state = 'closed'

client.on('connect', () => {  
  client.subscribe('garage/open')
  client.subscribe('garage/close')

  // Inform controllers that garage is connected
  client.publish('garage/connected', 'true')
  sendStateUpdate()
})

client.on('message', (topic, message) => {  
  console.log('received message %s %s', topic, message)
  switch (topic) {
    case 'garage/open':
      return handleOpenRequest(message)
    case 'garage/close':
      return handleCloseRequest(message)
  }
})

function sendStateUpdate () {  
  console.log('sending state %s', state)
  client.publish('garage/state', state)
}

function handleOpenRequest (message) {  
  if (state !== 'open' && state !== 'opening') {
    console.log('opening garage door')
    state = 'opening'
    sendStateUpdate()

    // simulate door open after 5 seconds (would be listening to hardware)
    setTimeout(() => {
      state = 'open'
      sendStateUpdate()
    }, 5000)
  }
}

function handleCloseRequest (message) {  
  if (state !== 'closed' && state !== 'closing') {
    state = 'closing'
    sendStateUpdate()

    // simulate door closed after 5 seconds (would be listening to hardware)
    setTimeout(() => {
      state = 'closed'
      sendStateUpdate()
    }, 5000)
  }
}

/**
 * Want to notify controller that garage is disconnected before shutting down
 */
function handleAppExit (options, err) {  
  if (err) {
    console.log(err.stack)
  }

  if (options.cleanup) {
    client.publish('garage/connected', 'false')
  }

  if (options.exit) {
    process.exit()
  }
}

/**
 * Handle the different ways an application can shutdown
 */
process.on('exit', handleAppExit.bind(null, {  
  cleanup: true
}))
process.on('SIGINT', handleAppExit.bind(null, {  
  exit: true
}))
process.on('uncaughtException', handleAppExit.bind(null, {  
  exit: true
}))
這了那些,我們完成了我們的車庫門控制器。我希望你能挑戰下一級。一些修改與一個 Intel Edison 會讓你建立一個完整的遠端車庫開門系統。此範例完整的原始碼也會放在 Github。
這只是一個開始。還有一些 MQTT 的新選項與能力,包含使用 SSL、使用者/密碼 認證來增加安全性。
如果你喜歡這篇貼文且想知道 Node.js 能到什麼程度,這有個超讚會議會來到:Node Community Convention。將會有許多偉大的演講,主題包含 IoT、系統放大……等等。

作者
Gabor Nagy
在 Marketing 是個全端。在 web 開發正在從零到英雄的路上。

2016年7月28日 星期四

[超譯] 核心版控演義

linux 核心的版本控制管理系統,曾經有經歷過一段動盪,linus 自己曾經在郵件列表裡回過一段文章,提出自己的看法。郵件標題是 「Kernel SCM saga..」
原文連結:
http://lkml.iu.edu/hypermail/linux/kernel/0504.0/1540.html

好,有很多人已經發現(有些人已經知道好幾個星期),我們正努力解決這一兩個月與 BK [譯註:BitKeeper] 之間的衝突。但,我們沒有成功,所以,核心組正在尋找替代方案。
(顯然這訊息會到 slashdot,所以應該 每個人都知道 )
我選了 BK 就讓能衝突消失的期待,似乎沒有成功 ( “你沒看見這幾G又幾G 的火熖嗎?”)。所以,在某種意義上是無法避免的。但我確信我曾希望能有合理的開源替代能選擇,為此,我們將必須忙和一陣子。
題外話,別怪 BitMover,就算這是一個常見的反應。Larry 真的是想把事情做好,但事情到了我不想要的地步,想要把兩塊湊在一起需要的膠水越多越好。
我們已經使用 BK 三年了,事實上,現在最大的問題是有許多人用過最好的工具之後,變得非常挑剔。包含我自己,我們都因為 BitKeeper 的幫助,在 merging 我的 tree,或把他們的 tree 傳給我的時候變得比較輕鬆。
當然,也許更大多數的人只是把 BK 當成更好(也更快)的「匿名 CVS」的客端。眼前的問題是我花了許多時間試圖找到最好的協作方案。
注意,BitKeeper 不是消失了。只是現在最真切的事情是,我決定不再使用 BK,主要因為我需要找到替代,而不是「讓東西持續正常」。我決定硬著頭皮,看看沒有 BK 的生活會是怎樣。到目前為止,是個黑白的世界。
不要過度解釋,我會用個星期閉關,(想做是 linus 只是去放假的正常事件),我也正在找人負責維護 BK tree,至少是能夠傳給我(單獨)補丁,我會自己 想辦法 merge。
所謂「單獨補丁 individual patches」是個關鍵字。題外話,BK 一個做得很好的事,許多人就算沒用 BK 也喜歡的,就是它對於改變的視圖很棒。這個不會消失。
事實上,BK 讓我們做事的方法有了根本的改變。像是 changeset 的視圖、讓我能相信子維護者能做更大的事、不用按順序上補丁…等。所以三年的 BK 使用不是浪費。我相信這會讓我們用更好的方式做事。我會尋找讓這些事情繼續的方法。
所以,我只是要說,我個人與 BK 相處愉快,與 Larry 也是。合作沒有成功讓核心開發有了巨大的困難。一個暫時性的問題,要產出在 BK 允許的範圍內使用的工具,這一點我們會繼續努力。
讓火開始燒吧。
Linus
PS. 不要再跟我說 subversion。如果你一定要,去研究 “monotone”,那看來是最可行的。但不要再煩開發者讓他們無法把事情做完。他們已經要煩惱我的問題了。

2016年2月27日 星期六

[超譯] 機器學習介紹

原文連結http://opensourceforu.efytimes.com/2016/02/an-introduction-to-machine-learning/


機器學習介紹

機器學習是計算機科學的一個領域,它探索「學習」與演算法的建構,從資料中學習,並依此做預測。機器學習涵蓋了模式辨認(pattern recognition)以及人工智慧中的計算式學習理論。它被用在許多應用中,像是自動駕駛車,有效率網頁搜尋,語音辨識…等。

編程(programming)基本上是告訴電腦要做什麼。傳統的編程習會假設寫程式的人已經理解問題,而且用這個理解,寫出一串清楚的指令來解決 某特定問題或完成特定任務。這類的例子非常多,事實上,絕大多數的程式都是這樣寫的,有著清楚可預期的輸入、輸出,與優美的演算法處理。例如,排序數字, 在文檔中移除某個字串,複製檔案…等等。
然而,有某些種類的問題,傳統編程或問題解決定方法不是這麼好使。例好,假設你有 50,000 個文件,必須分類成運動、商業、娛樂,又不能人工一個個處理。又例如,在上千個圖片裡找到特定的物件。在後面這個例子,物件可能是不同角度的相片,或是在 不同光線的條件下。你要如何分辨哪些相片有這個物件?另一個非常有用的例子是建立一個線上付款通道(online payment gateway)且希望防止詐欺交易。其中一個方法是辨認出潛在詐欺交易的跡象,在交易完成前啟動警報。這樣,你要如何準確預測而避免不必要的警報?
你可以簡單地說,這是不可能寫出非常準確的演算法來對付上述每個問題。我們能做的是,建立一個系統像人類專家一樣地工作。一個醫生可以檢驗報告看出病人生了什麼病。醫生的準確率不會是 100%,他多數時間會是正確的。沒人用程式寫出一個醫生,他這些知識來自學習與經驗。
我們需要的是一個系統能從經驗中學習,就算我們無法編程它叫它做特定的任務。我們做的是,給它看大量的範例,電腦是被編程為從大量的資訊中學習。在 它完成學習之後,就可以預測或做一些任務。這就是機器學習。醫生的例子有一點點與真正的機器學習不一樣,機器學習的核心思想是機器學習會有大量資料中學 習,而且會從經驗中改進。


機器學習與數學

許多機器學習演算法基本上企圖逼近一個未知的數學函數,而且絕大多數這些演算法本質上就是數學。因為資料有隨機性與不確定性,我們必須應用來自機率 與統計的概念。事實上,機器學習演算法非常依賴統計的概念,以致許多人認為機器學習該叫統計學習。離開統計,另一個被用到重要的數學分支是線性代數。矩陣 的概念,系統方程組的解,演算法的優化,都在機器學習中佔有重要地位。


機器學習與大數據

近來,機器學習與大數據走得很近。大數據指的是巨量的已存資料,而且是有意義地被使用。看一些例子來了解我說的。假設你已在交易平台上收集顧客交易 資料,這資料的量可以輕易地到達被認為是大數據的量,還有一些其他你存下的資料。現在,這些資料的用途是檢查顧客定單的狀態,計算商務會計上的盈虧,以及 其他技術或操作上的日常業務。除了這些,商業上也常想以獨特的方式利用這些資料。有些人對這分析使用特定的字。雖然直接地計算可以得到盈虧、總支出、總庫 存等答案,但稍進一步的資料操作會切片與切塊讓訊息顯現。我們依然會追求基本報表的背後訊息,辨認出不顯見的模式。我們也會想要建立預測模型。


不同的機器學習演算法

現在有兩種不同種類的演算法,受監督學習與無監督學習。受監督學習指的是,資料給序標籤,如下例所示:
中午12點時溫度 風速 風向 下午是否下雨
1 30度C 3 m/s
2 35度C 10 m/s 西
3 37度C 7 m/s 東北
4 34度C 8 m/s
假設我們必須預測下午是否下雨,使用溫度及風的資料。是否下雨記錄在「下午是否下雨」那一行,那就是標籤。這裡的溫度,風速、風向是預測子(predictor)或說是輸入變數,下午是否下雨則是輸出變數。
演算法從這樣的資料學習,叫做受監督學習演算法。有些資料是自動萃取/自動產生的,例如機器的日誌,多數需要人工加標籤,這樣增加資料獲得的成本。
受監督學習演算法的例子有「線性回歸演算法 linear regression algorithms」、「貝氏演算法 Naïve-Bayesan algorithms」「K近鄰演算法 k-nearest neighbour」…等等。
如果資料沒有標籤,它變成一個無監督學習演算法。例如:「K平均分群 k-means clustering」、「主成份分析 principal component analysis」…等等。
我們也可以用另一個邏輯來分類機器學習演算法,分成回歸演算法或是分類演算法。回歸演算法是指演算法真的預測一個數字,像是接下來幾天的溫度,股市收盤指數…等等。
分類演算法則是把輸入分類,例如是否下雨、股市漲還是跌、它是疾病 x、疾病 y 還是疾病 z……等等。


機器學習的工作

要理解與讚嘆的是,機器學習演算法基本上是數學演算法,我們可以用任何喜歡的電腦語言來實作。例如 C、PHP、Java、Python 甚至是 JavaScript。但我個人喜歡的是統計語言 R。在不同的語言有許多熱門的機器學習模組或函式庫,Weka 是強大的機器學習與資料採礦的軟體,它用 Java 寫的,而且很熱門。Scikit-learn 在 python 開發者中很熱門。Orange Machine Learning Toolbox 也是 pyhon 可用的工具。R 語言有許多非常基本的統計套件與模組,也有很多不同的套件像 e1071、Randomforest、Ann、…等依賴在不同的機器學習演算法上。Apache Mahout 是可拓展的機器學習演算法,通常用在大數據習統。Weka 非常強大,但它在商業使用上有授權問題,你可以用 Java-ML (Java machine learning algorithm)。雖然開發活動看起來停子了,它的函式庫仍值得一試,它擁有足夠的文件與教學讓人易於上手。我會建議有進一步需要的人可探索「深度學習演算法 deep learning algorithm」


原文作者

Mahesha Hiremath
The author is an independent data analytics consultant from Bengaluru. He is building a platform for electric vehicles-Project Vidyut. Project Vidyut is a not-for-profit initiative for promoting electric vehicles, and also to build open source electric vehicles. He can be reached at mahesha-at-projectvidyut-dot-org and tweets with handle@MaheshaHiremath.
Connect with him: Website

超譯系列是練習翻譯的技巧,順便分享給大家參考的資訊相關的資訊。
Exercise to translation. Give advise to me if something wrong.
譯者/translator: Ricky Teng
聯絡/contact: ricky-dot-teng-at-gmail-dot-com