2018年4月11日 星期三

Serverless All-Star 參加筆記

前言

Serverless目前還沒有一個公認的定義,這個技術對於一個開發人員,可以專注於自己的程式,而不用去管網站架設、系統組態設定,只要將自己的程式碼丟上雲端就能完成應用程式部署、立即使用寫好的Web服務。在實務上我已經將一些供其他模組呼叫的核心程式變成Serverless架構,這樣的組件化除了降低整個開發成本外,也讓我能專注的監控這些核心的服務的使用情況,目前導入起來感覺挺好的。在 2018.03.29 參加了Serverless All-Star 這個活動,來學習看看有沒有其他沒想到的應用,感謝現場熱心的學員有開共筆,一起記錄講師的重點:

https://hackmd.io/PPFD5nkzRXOcA59h5jf2jg?both

DevOps & Serverless - 陳正瑋 / 得寬科技DevOps Engineer

Serverless 提供更高的彈性及擴展性,但別想得太沒好,引用 Patrick Debois 演講內容 〝Less is More”,看似少了很多工,但對於各種角色應該注意以下事項:

  • Dev 要注意debug和monitoring的問題
  • Ops 要注意latency問題
  • Sec 要注意pipeline傳輸安全問題
  • Boss 要注意是否真的能省的錢


極速 IT 架構新趨勢:Serverless / 王宏仁 / iThome電腦報周刊副總編輯

IT Home調查 2018 IT趨勢-值得關注的技術:Web Assembly , Kubernets , 金融 Open API, Chatbot NLP

(抱歉位置太爛拍得很爛…XD)

20180329_095430

- NETFLIX 前技術長提到IT架構的變革:虛擬化 -> 雲端 -> 容器 –> Serverless無伺服器架構

用哪種?以房子做比喻:

- 自建機房:買套房/房子
- 虛擬化:自家房子+隔間
- Cloud:租一層公寓(廚房.客廳.玄關.大門)+標準隔間
- Serverless無伺服器架構:租沙發

Serveless 實務案例介紹

- Skylink公司透過無人機+影像辨識針對工廠安全做巡檢,資料都是丟Serverless
- 可口可樂針讓販賣機做可用性分析,數據資料全丟AWD lambda,但之後量大的話還是會考慮丟Web App,因不會比較省成本。
- Abilisense公司透過穿戴裝置分析環境聲音+預警
- 亞旭電腦用Serverless結合IoT,讓BOM表Cost計算更直覺


設計、開發及部署跨平台的Serverless應用/上官林傑 -台灣微軟技術傳教士

Serverless 重點:伺服器抽象化、事件驅動立即延展、錙銖必較

常見的Serveless架構情境

- 即時串流服務
- IoT senser data
- 週期性工作 每15分鐘清除無效資料
- 程式後端服務 使用頻率無法估計
- 對談機器人 使用頻率無法估計

Azure Stack介紹:http://technews.tw/2017/08/30/microsoft-azure-public-cloud-is-on-the-ground-with-the-help-from-local-telecomm/

Serverless 建議

- 專注做一件事
- 應該冪等 Idempotent
- 盡快結束

玩玩Azure Function臉部辨識:https://www.tryfunctions.com/ng-min/try?trial=true


使用 Serverless 進行網站監控/ 104 呂昭寬 資深架構師

學習成本

- 取代管理成本
- 黑箱和地雷
- 限制:最多3分鐘不然會timeout , cpu memory storage , 程式語言的問題 (java要先開jvm之類的,不太適合)

Faas的強項

目標選擇

  • 可靠性很高,透過internet運行
  • 適合中低負載
  • 適合排程運行

例如可以用Aws lambda發出請求給網站,網站回200 404 timeout,送給Cloudwatch整理成報表

也可以進一步檢查Domain name解析時間,等候請求時間,tcptls hand shake連線等等

20180329_113420


Spacer - 我為什麼自製了一個 Serverless 平台 / 柏傑(Poga)  - APlace DesignConsultant

本場次講者整合工具,自做了一個Serverless platform,可以去Github看看 https://github.com/poga/spacer

本次也看到 Lua 這個嵌入式程式語言,語法看起來蠻簡潔的:https://zh.wikipedia.org/wiki/Lua


OpenFaaS - 快速打造你的 Serverless 平台 / 王偉任(Weithenn) - 東森購物架構師

要評估容器技術,現在有很多可以直接線上玩了

實作容器部署
Play with Docker:https://labs.play-with-docker.com/
Play with Kubernetes:https://labs.play-with-k8s.com/


Ops as Code using Serverless / 黃冠元(Rick) / 91APP技術經理

整個演講的內容,講者已經整理到他blog

https://rickhw.github.io/2018/03/29/About/2018-Serverless-All-Star/

20180329_140133


Angular+Firebase:快速建立最小可行性產品 / 林承翰(JB) - 華立企業權任課長

講者是一位PM,目前會利用兩者技術來快速做一些prototype確認客戶需求

https://github.com/angular/angularfire2

推薦保哥之前也講過相關的題目:https://www.slideshare.net/WillHuangTW/jsdctw-2016-angular-2-firebase-serverless-67522007


秒發百萬推播 / 范建銘 - 工程師

講者是個很古意的工程師 XD

以往自建機房,因設備問題,推播App訊息常常因為效能問題導致無法在對的時間發送,並且常常會讓資料庫掛掉。評估過GCP和AWS的Serverless技術,只有AWS承諾能達到秒即百萬推播的服務,Serverless上手容易,經過1.2個月的技術評估就能將此需求上到雲方案,且非常的節省成本,解決問題,但實務上還是踩到一些雷,例如還是得分幾批發送、及有時會發生推播兩次的情形發生。

2018年4月4日 星期三

東西拿了就走 - Amazon Go 無人商店初體驗

Amazon Go 介紹

今年有幸至西雅圖參與 Miscrosoft MVP Global Summit , 還是撥空安排了一些行程 , 身為一個攻城獅怎麼不能來體驗 目前全球僅此一家的 Amazon Go 無人商店呢 ~

要進入 Amazon Go 之前必須要先下載App和註冊信用卡,進去以後透過App的QR Code 就能進去了,跟高鐵一樣~

20180308_121026

Just Walk Out Shpping

20180308_121041

入口處的機器,類似高鐵,只要將註冊完的App透過QR Code掃描即可進入

20180308_121139

身為一個攻城獅,大家只看個超市內的商品,而我們是抬頭看看這些攝影機的擺設 XD

20180308_121410

Amazon Go 裡面的商品就跟一般的美國商品沒兩樣

20180308_121935

買了一個Amazon的巧克力,非常適合當伴手禮

20180308_121739

店家外面有提供休息站,意外發現一名熱血工程師就在這邊打Code

20180308_122256

旁邊就是 Amazon 西雅圖總部 - 巨型植物園

20180308_122618

心得

目前仍在營運測試階段,故還是會有店員在現場,但主要負責是門口的教育訓練和補貨

使用體驗起來真的還不錯,想像一下進到 7-11 亂拿東西直接出門口的感覺

整個過程是很順的,出了門口閘道馬上收到信用卡刷卡通知

當下有跟朋友想了幾個測試情境,譬如交換外套、遮臉、把商品快速來回擺放…

但礙於實在太丟臉了,所以沒這樣做 XD

無人商店普及後,我想會大大改變我們的消費行為,期待那一天的到來 :)

技術

親身體驗後,回台馬上去研究了一下核心技術,以下有幾篇文章可以參考看看

https://buzzorange.com/techorange/2017/03/10/how-to-build-shop-like-amazon-go/

http://benevo.pixnet.net/blog/post/64772716-amazon-go-%E4%B8%89%E5%A4%A7%E6%A0%B8%E5%BF%83%E6%8A%80%E8%A1%93

2018年3月29日 星期四

使用 LinqPad 快速產生 Dapper 所需要的 SQL 語法 - Class、Insert、Update

前言

工作上目前已經很習慣用 Dapper 這種輕型的 ORM 來處理有關DAL層的應用,但相較於 Entity Freamwork,在處理新增/更新時就比較沒那麼方便,主要差異在Entity Freamwork 可以直接用物件操作的方式來做資料庫異動,而Dapper就必須寫SQL Commend,雖然有一些Open Source Extentions 已經封裝這些邏輯,譬如:SimpleCRUD﹉等,但實際使用常常得依規範設定一些屬性,有時發生錯誤時也不是那麼的直覺,故目前我還是會以SQL Commend為主。但產生Commend是一件麻煩事,如果搭配 Linqpad 寫一個Script 可以依需求快速產生所需要的commend,彈性是很大的,此篇文章介紹我在實務上我常用Scripts,來快速產生相關 SQL Commend

如何設定LinqPad

20180311_015834

Step 1 : 這邊可以直接瀏覽資料庫的Schema
Step 2 : 這邊使用C# Program 來撰寫我們要產生Script的程式
Step 3 : 選擇資料庫的連線字串

接著以下是我常用的Helper,只要貼到內容裡面即可產生


 

void Main()

{

 this.Connection

 .DumpClass(@"

SELECT * FROM Table

 ","ViewModelName")

 .Dump();

}

// Define other methods and classes here

public static class LINQPadExtensions

{

 private static readonly Dictionary TypeAliases = new Dictionary {

 { typeof(int), "int" },

 { typeof(short), "short" },

 { typeof(byte), "byte" },

 { typeof(byte[]), "byte[]" },

 { typeof(long), "long" },

 { typeof(double), "double" },

 { typeof(decimal), "decimal" },

 { typeof(float), "float" },

 { typeof(bool), "bool" },

 { typeof(string), "string" }

 };

 private static readonly HashSet NullableTypes = new HashSet {

 typeof(int),

 typeof(short),

 typeof(long),

 typeof(double),

 typeof(decimal),

 typeof(float),

 typeof(bool),

 typeof(DateTime)

 };

 public static string DumpClass(this IDbConnection connection, string sql,string Name)

 {

 if(connection.State != ConnectionState.Open)

 connection.Open();

 var cmd = connection.CreateCommand();

 cmd.CommandText = sql;

 var reader = cmd.ExecuteReader();

 var builder = new StringBuilder();

 do

 {

 if(reader.FieldCount <= 1) continue;

 builder.AppendLine("public class " + Name);

 builder.AppendLine("{");

 var schema = reader.GetSchemaTable();

 foreach (DataRow row in schema.Rows)

 {

 var type = (Type)row["DataType"];

 var name = TypeAliases.ContainsKey(type) ? TypeAliases[type] : type.Name;

 var isNullable = (bool)row["AllowDBNull"] && NullableTypes.Contains(type);

 var collumnName = (string)row["ColumnName"];

 builder.AppendLine(string.Format("\tpublic {0}{1} {2} {{ get; set; }}", name, isNullable ? "?" : string.Empty, collumnName));

 }

 builder.AppendLine("}");

 builder.AppendLine(); 

 } while(reader.NextResult());

 return builder.ToString();

 }

}


產生 Insert 語法



void Main()
{
 // SQL Command
 var sqlCommand = @"SELECT top 1 * FROM dbo.Table WITH (NOLOCK);";
 
 this.Connection.GenerateInsertCommand(sqlCommand.ToString(), "Table").Dump();
 
}
 
public static class LINQPadExtensions
{
 public static string GenerateInsertCommand(this IDbConnection connection, string sql, string tableName = "TableName")
 {
 if (connection.State != ConnectionState.Open)
 {
 connection.Open();
 }
 
 var cmd = connection.CreateCommand();
 cmd.CommandText = sql;
 var reader = cmd.ExecuteReader();
 
 var builder = new StringBuilder();
 do
 {
 if (reader.FieldCount <= 1)
 {
 continue;
 }
 
 builder.AppendFormat("INSERT INTO [dbo].[{0}]{1}", tableName, Environment.NewLine);
 builder.AppendLine("(");
 
 var schema = reader.GetSchemaTable();
 var columnNames = new List();
 
 foreach (DataRow row in schema.Rows)
 {
 var columnName = (string)row["ColumnName"];
 columnNames.Add(columnName);
 }
 
 foreach (var columnName in columnNames)
 {
 builder.AppendFormat(" [{0}]{1}{2}",
 columnName,
 columnNames.IndexOf(columnName).Equals(columnNames.Count - 1) ? "" : ",",
 Environment.NewLine);
 }
 
 builder.AppendLine(")");
 builder.AppendLine("VALUES");
 builder.AppendLine("(");
 
 foreach (var columnName in columnNames)
 {
 builder.AppendFormat(" @{0}{1}{2}",
 columnName,
 columnNames.IndexOf(columnName).Equals(columnNames.Count - 1) ? "" : ",",
 Environment.NewLine);
 }
 
 builder.AppendLine(");");
 builder.AppendLine();
 }
 while (reader.NextResult());
 
 return builder.ToString();
 }
}


產生 Update 語法


void Main()
{
 // SQL Command
 var tableName = "TableName";
 var sqlCommand = @"SELECT top 1 * FROM [dbo].["+tableName+"]";
 
 this.Connection.GenerateInsertCommand(sqlCommand.ToString(), tableName).Dump();
 
}
 
public static class LINQPadExtensions
{
 public static string GenerateInsertCommand(this IDbConnection connection, string sql, string tableName = "TableName")
 {
 if (connection.State != ConnectionState.Open)
 {
 connection.Open();
 }
 
 var cmd = connection.CreateCommand();
 cmd.CommandText = sql;
 var reader = cmd.ExecuteReader();
 
 var builder = new StringBuilder();
 do
 {
 if (reader.FieldCount <= 1)
 {
 continue;
 }
 
 builder.AppendFormat("UPDATE [dbo].[{0}] SET {1}", tableName, Environment.NewLine);
 // builder.AppendLine("(");
 
 var schema = reader.GetSchemaTable();
 var columnNames = new List();
 
 foreach (DataRow row in schema.Rows)
 {
 var columnName = (string)row["ColumnName"];
 columnNames.Add(columnName);
 }
 
 foreach (var columnName in columnNames)
 {
 builder.AppendFormat(" [{0}] = @{0}{1}{2}",
 columnName,
 columnNames.IndexOf(columnName).Equals(columnNames.Count - 1) ? "" : ",",
 Environment.NewLine);
 }
 
 builder.AppendLine("WHERE ID = @ID");
 }
 while (reader.NextResult());
 
 return builder.ToString();
 }
}