這是 Go 和 Oracle Cloud Infrastructure (OCI) 五個部分系列的第四個部分。本系列探討如何在運算執行處理 (VM) 中的 Oracle Cloud Infrastructure 上建立及執行 Go 應用系統、在 Kubernetes 上容器化,或以無伺服器函數的方式執行。文章顯示如何使用 OCI DevOps 自動建置和部署這些 Go 應用程式。重要的主題是如何從 Go 應用程式使用 OCI 服務,包括在 OCI 上執行的服務,以及在其他地方執行的 Go 程式碼。討論的 OCI 服務包括物件儲存、串流、金鑰保存庫和 Autonomous Database。
為了跟進這些文章,讀者應至少具備如何建立 Go 應用程式的基本知識。假設讀取者可以存取自己的 Go 開發環境。某些範例與畫面擷圖將會特別提到 VS Code 作為開發工具。不過,也可以使用其他編輯器和 IDE。這些文章中列出的 Go 程式碼會以最簡單的形式展示一些機制,以達到最大清晰度與最小相依性。讀者不應期待有意義的功能或符合生產需求的程式碼。
此系列說明如何開始使用 OCI。若要試用範例,讀者必須具備 OCI 租用戶的存取權,才能在這些文章中討論建立 OCI 資源。大部分使用的資源都可在永遠免費層 (運算執行處理、VCN、Autonomous Database、物件儲存、日誌記錄、資源管理程式) 中使用,或在每月有限的使用量 (函數、API 閘道、串流處理、保存庫、DevOps) 中使用免費分配層。
這些系列的第一個部分描述根據 Oracle Linux Cloud Developer 映像檔佈建運算執行處理、為內送和外送網路活動開啟運算執行處理,以及建立和執行 Go 應用程式,為應用程式產生的 HTTP 要求提供服務,以及將記錄日誌連線至 OCI 日誌記錄。第二部分是處理軟體工程、使用 OCI DevOps 服務建置和部署應用程式的自動化。此服務用於儲存 Go 原始程式碼、建置應用程式執行檔,並將其儲存為可部署的使用者自建物件、將該使用者自建物件部署至運算執行處理。這篇文章也說明如何透過 OCI API 閘道顯示應用程式的 HTTP 端點。第 3 部分顯示如何在 Go 中建立無伺服器函數,並在 OCI 上部署這些函數。導入適用於 OCI 的 Go SDK - 最先適用於本機、獨立 Go 應用程式,然後利用資源主體認證從函數中使用。此 SDK 可與 OCI Object Storage 服務互動,以建立儲存桶及寫入及讀取檔案。
您目前正在閱讀的第四部分,討論 Go 應用程式與 Oracle Database 之間的互動。這可以是本機或內部部署資料庫、在部分雲端廠商的 IaaS 執行處理上執行的資料庫,或是 OCI Autonomous Database。使用標準 Go 資料庫 /SQL 套件搭配 Oracle Database 的驅動程式,並將必要的組態詳細資料饋送至驅動程式,這表示從 Go 運用任何 Oracle Database 非常簡單。第二部分討論的 Go 應用程式 myserver 擴充為與 OCI 上的 Autonomous Database 執行處理和 OCI 物件儲存服務互動。此應用程式使用 Go SDK for OCI 從物件儲存的儲存桶讀取檔案 (隨後將其移除),並根據其內容在 Autonomous Database 中建立資料庫記錄。
Go 語言支援與內建關聯式資料庫的 SQL 互動。標準套件資料庫 /SQL 包含用於連線至資料庫、執行交易、取消進行中的作業等的類型與函數。此相同套件可用於與部分 NoSQL 資料庫 (例如 MongoDB 與 Couchbase) 相同的方式運作。
透過此套裝程式與資料庫互動的 Go 應用程式不需要擁有特定資料庫產品的技術實作詳細資訊。這些詳細資訊通常會在該資料庫的驅動程式中實行。應用程式會匯入資料庫連線所需的驅動程式,並告訴標準套件資料庫 /SQL 要使用的驅動程式,以及資料庫的連線詳細資料。無論特定資料庫技術為何,與資料庫的大部分互動都相同;記錄是透過 SQL DML 敘述句建立、更新與刪除,且記錄會透過 SQL 查詢擷取。資料庫的整體程序相同,但確切的 SQL 方言可能不同。這可能是唯一能在不同資料庫產品之間輕鬆移動 Go 應用程式的障礙。
本節中討論的程式碼位於本文系列隨附的程式碼儲存區域中的 /applications/go-orcl-db 目錄中。
Go Application do SQL - DDL、DML 和 Query
使用 Go 應用程式的 Oracle Database 最簡單的方法是查詢單一列。此專案所需的程式碼看起來像是這樣:主要套件
package main
import (
"database/sql"
"fmt"
)
func sqlOperations(db *sql.DB) {
var queryResultColumnOne string
row := db.QueryRow("SELECT to_char(systimestamp,'HH24:MI:SS') FROM dual")
err := row.Scan(&queryResultColumnOne)
if err != nil {
panic(fmt.Errorf("error scanning query result from database into target variable: %w", err))
}
fmt.Println("The time in the database ", queryResultColumnOne)
}
import 敘述句會使資料庫 /SQL 套裝程式可供使用。使用控點至 sql.DB,即可輕鬆執行 SQL 查詢 (QueryRow),並將結果掃描為區域變數。簡單、直接且資料庫品牌獨立,但特定 SQL 陳述式除外,在此情況下,會使用 Oracle 特定系統戳記。
現在,讓我們不要停留在資料庫參數的來源。我們會談一下資料庫驅動程式,這就是所有資料庫將被揭露的地方。
稍微有趣的函數,可建立表格、插入記錄、查詢記錄、建立至更多記錄,然後查詢所有資料列,最後刪除表格就會顯示在這裡。您可以在程式碼儲存區域的 oracle-database-client-app.go 檔案中找到此程式碼。
package main
import (
"database/sql"
"fmt"
)
const createTableStatement = "CREATE TABLE TEMP_TABLE ( NAME VARCHAR2(100), CREATION_TIME TIMESTAMP DEFAULT SYSTIMESTAMP, VALUE NUMBER(5))"
const dropTableStatement = "DROP TABLE TEMP_TABLE PURGE"
const insertStatement = "INSERT INTO TEMP_TABLE ( NAME , VALUE) VALUES (:name, :value)"
const queryStatement = "SELECT name, creation_time, value FROM TEMP_TABLE
func sqlOperations(db *sql.DB) {
_, err := db.Exec(createTableStatement)
handleError("create table", err)
defer db.Exec(dropTableStatement) // make sure the table is removed when all is said and done
stmt, err := db.Prepare(insertStatement)
handleError("prepare insert statement", err)
sqlresult, err := stmt.Exec("John", 42)
handleError("execute insert statement", err)
rowCount, _ := sqlresult.RowsAffected()
fmt.Println("Inserted number of rows = ", rowCount)
var queryResultName string
var queryResultTimestamp time.Time
var queryResultValue int32
row := db.QueryRow(queryStatement)
err = row.Scan(&queryResultName, &queryResultTimestamp, &queryResultValue)
handleError("query single row", err)
if err != nil {
panic(fmt.Errorf("error scanning db: %w", err))
}
fmt.Println(fmt.Sprintf("The name: %s, time: %s, value:%d ", queryResultName, queryResultTimestamp, queryResultValue))
_, err = stmt.Exec("Jane", 69)
handleError("execute insert statement", err)
_, err = stmt.Exec("Malcolm", 13)
handleError("execute insert statement", err)
// fetching multiple rows
theRows, err := db.Query(queryStatement)
handleError("Query for multiple rows", err)
defer theRows.Close()
var (
name string
value int32
ts time.Time
)
for theRows.Next() {
err := theRows.Scan(&name, &ts, &value)
handleError("next row in multiple rows", err)
fmt.Println(fmt.Sprintf("The name: %s and value:%d created at time: %s ", name, value, ts))
}
err = theRows.Err()
handleError("next row in multiple rows", err)
}
func handleError(msg string, err error) {
if err != nil {
fmt.Println(msg, err)
os.Exit(1)
}
}
此程式碼本質上相當重要。除了 SQL 敘述句之外,沒有資料庫特定的實行詳細資訊。一半的程式碼似乎是錯誤處理。除了尚未使用資料庫的事實之外,以及 (因此) 也沒有建立連線與處理通訊的驅動程式,這也不應該太難瞭解這個程式碼是如何操控資料庫。讓我們先執行本機資料庫,然後將資料庫驅動程式新增至 Go 應用程式來修正此問題。
執行本機 Oracle Database
有許多不同的方法可以啟動及執行本機 Oracle Database。我最簡單的方式是使用 Docker 容器映像檔,讓我使用一個非常簡單且直接的敘述句來執行本機資料庫:
docker run -d -p 1521:1521 -e ORACLE_PASSWORD=TheSuperSecret1509! gvenzl/oracle-xe
這會執行 Oracle Database XE 21c 執行處理 (至少是寫入時會執行的作業,當 21c 是最新的可用容器映像檔時),並將 SYS 和 SYSTEM 的密碼設為指示的值。此資料庫可在 localhost 的連接埠 1521 上使用。
Oracle 的 Gerald Venzl 維持一系列 (Docker) 容器映像檔,這些映像檔會執行小型版本的 Oracle Database (XE 版本),可免費使用 (最多 20GB 的資料,最多可使用 2 個 CPU 繫線和 2 GB RAM)。他在標題為 Introducing gvenzl/oracle-XE:Oracle Database XE Docker images 的文章中描述這些映像檔及如何使用這些映像檔。
請依照下列步驟驗證本機 Oracle Database 是否已啟動並在執行中:
```console
docker exec -it <container identifier=""> /bin/bash
``` </container>
```console
sqlplus system/TheSuperSecret1509! as sysdba
```
```sql
create user demo identified by demo default tablespace users temporary tablespace temp
/
grant connect, resource to demo
/
```
現在是從 Go 應用程式連線至此資料庫的時候了。
Note: You may be interested in installing the Oracle VS Code extension that allows making connections to Oracle Databases – local and remote – browse their contents and interact with them similar to SQL Developer and other desktop tools.
新增 Oracle Database 驅動程式
Oracle Database 沒有官方 Go 驅動程式,至少有一部由 Oracle 發佈或背書。非正式的 Go 資料庫驅動程式清單有四個 Oracle Database 項目。三種必須安裝 Oracle 用戶端磁帶櫃,另外一種磁帶櫃。讓我們首先與最後一個名為 go-ora 的合作,這是一個純驅動程式,它本身會處理與資料庫的所有通訊。go-ora 的詳細資訊可從 GitHub 的 go-ora 首頁取得。我們隨後也將介紹 godror (需要安裝磁帶櫃的驅動程式),以及 Oracle Database 驅動程式中最顯著的 Go。
go-ora 驅動程式可以透過下列方式新增至 Go 應用程式:
go get github.com/sijms/go-ora/v2
這會下載套裝軟體並將需求項目新增至 go.mod 檔案。對於看起來像這樣:
module oracle-database-client
go 1.16
require (
github.com/sijms/go-ora/v2 v2.4.16 // indirect
)
在與檔案 oracle-database-client-app.go 相同的目錄中,名為 pure-oracle-database-client.go 的新檔案 (雖然 Go 並不關心此名稱),但下列程式碼會處理透過 go-ora 與本機 Oracle Database 的互動。系統會匯入驅動程式套裝程式並呼叫 sql.Open (隱含地參照 oracle),選取 go-ora 作為選擇的驅動程式。
參數 dbParams 包含組態設定值的對應 (包括使用者名稱和密碼)、資料庫主機和連接埠,以及用於建立連線的服務名稱。連線字串是使用這些元素組成,並用於呼叫 sql.Open。db.Ping 的後續呼叫是第一次嘗試建立與資料庫的通訊。當此呼叫成功時,我們已準備好進行一些真正的資料庫動作。
package main
import (
"database/sql"
"fmt"
"net/url"
_ "github.com/sijms/go-ora/v2"
)
func GetSqlDBWithPureDriver(dbParams map[string]string) *sql.DB {
connectionString := "oracle://" + dbParams["username"] + ":" + dbParams["password"] + "@" + dbParams["server"] + ":" + dbParams["port"] + "/" + dbParams["service"]
db, err := sql.Open("oracle", connectionString)
if err != nil {
panic(fmt.Errorf("error in sql.Open: %w", err))
}
err = db.Ping()
if err != nil {
panic(fmt.Errorf("error pinging db: %w", err))
}
return db
}
連線與執行
資料庫正在執行中,我們有一個與純 Oracle Database 驅動程式搭配運作的功能。現在,讓我們返回 oracle-database-client-app.go 並將其連結在一起。
新增此檔案中的 main 函數。它會呼叫 GetSqlDBWithPureDriver,使用對應 localDB 中定義的資料庫連線詳細資訊來建立 sql.DB 執行處理。修改這些值以配合您的資料庫組態。函數呼叫會以 *sql.DB 設定資料庫變數,可用於進一步的 SQL 作業。
當所有資料庫互動都完成時,應關閉連線以釋出資源。若要確定這樣做,呼叫 GetSqlDBWithPureDriver 後立即將函數 main 中的遞延加上呼叫 db.Close()。呼叫函數 sqlOperations (透過資料庫) 會將我們帶入我們在兩個區段前所討論的函數,而資料庫確實與之互動。
var localDB = map[string]string{
"service": "XE",
"username": "demo",
"server": "localhost",
"port": "1521",
"password": "demo",
}
func main() {
db := GetSqlDBWithPureDriver(localDB)
defer func() {
err := db.Close()
if err != nil {
fmt.Println("Can't close connection: ", err)
}
}()
sqlOperations(db)
}
使用 go run *.go,從指令行執行應用程式。輸出結果看起來會像:
go run *.go
Inserted number of rows = 1
The name: John, time: 2022-04-25 05:31:02.489289 +0000 UTC, value:42
The name: John and value:42 created at time: 2022-04-25 05:31:02.489289 +0000 UTC
The name: Jane and value:69 created at time: 2022-04-25 05:31:02.506039 +0000 UTC
The name: Malcolm and value:13 created at time: 2022-04-25 05:31:02.509098 +0000 UTC
使用 GoDrOr 驅動程式
Go-ora 的熱門替代方案是 Go 套件的鏡像 (之前稱為 goracle,但由於商標問題而重新命名),Oracle Corporation 不希望任何人在名稱中使用 oracle。此套裝程式也提供一個 Oracle Database 驅動程式,當針對 oracle 或 godror 執行 sql.Open 時,資料庫 /SQL 便可使用這些驅動程式。此套裝程式與 go-ora 不同,需要在執行 Go 應用程式的系統上安裝 Oracle Instant Client 程式庫。
GoDrOr 驅動程式使用 Oracle Database Programming Interface for C (ODPI-C)。它是 C 程式碼的開放原始碼程式庫,由 Oracle Corporation 維護,可簡化 Oracle Database 驅動程式和使用者應用程式的通用 Oracle Call Interface (OCI) 功能。使用 GoDrOr 時,我們不需要安裝 ODPI-C 或甚至意識到 ODPI-C 是否存在。但是,執行 Go 應用程式 (包括驅動程式) 的環境需要包含 Oracle Client 程式庫。
最簡單的 Oracle Client 是免費的 Oracle Instant Client (請參閱 Oracle Instant Client 概觀頁面 )。只需要 "Basic" 或 "Basic Light" 套件。Oracle 用戶端程式庫也可用於任何 Oracle Database 安裝或完整 Oracle 用戶端安裝。如需 Linux 的詳細安裝說明,請參閱 Oracle Database Documentation for Release 21c - Database Client Installation Guide for Linux - Installing Oracle Instant Client (僅英文版)。
ODPI-C 會在程式實際執行時動態載入可用的 Oracle Client 程式庫。在與 ODPI-C 程式庫 (或應用程式二進位檔) 相同的目錄中搜尋 Oracle Client 程式庫。如果找不到它們,則會在標準作業系統搜尋路徑中搜尋它們,例如 Windows 上的 PATH 或 Linux 上的 LD_LIBRARY_PATH。最後,在 Windows 以外的平台上,也會搜尋 $ORACLE_HOME/lib。如需確保 ODPI-C 可以找到 Oracle 用戶端程式庫的詳細資訊,請查看 ODIP-C GitHub Home - ODPI-C Installation 。
我們必須對應用程式進行的變更,才能從使用 go-ora 切換至 godror。
首先,Godror 驅動程式會新增至 Go 應用程式,其中包含:
github.com/godror/godror
這會下載套裝軟體並將需求項目新增至 go.mod 檔案。
接著,在同一個應用程式目錄中建立一個名為 godror-based-oracle-database-client.go 的新檔案 - 這與 pure-oracle-database-client.go 非常類似,其中包含透過 go-ora 驅動程式連線的詳細資訊。
這個新檔案的內容 :
package main
import (
"database/sql"
"fmt"
_ "github.com/godror/godror"
)
func GetSqlDBWithGoDrOrDriver(dbParams map[string]string) *sql.DB {
var db *sql.DB
var err error
connectionString := "oracle://" + dbParams["username"] + ":" + dbParams["password"] + "@" + dbParams["server"] + ":" + dbParams["port"] + "/" + dbParams["service"]
db, err = sql.Open("oracle", connectionString)
err = db.Ping()
if err != nil {
panic(fmt.Errorf("error pinging db: %w", err))
}
return db
}
godror 套件的匯入與 go-ora 的匯入不同。其餘的程式碼與以前完全相同。
Note: when we use the Oracle Wallet and change to encrypted communications with the Autonomous Database, there will be more differences between the code used with the two drivers.
最後,若要讓應用程式停止使用 go-ora 並開始使用 godror,我們只需要註解或移除一行,並在函數 main 中新增另一行,呼叫 GetSqlDBWithGoDrOrDriver:
func main() {
//db := GetSqlDBWithPureDriver(localDB)
db := GetSqlDBWithGoDrOrDriver(localDB)
使用 go 執行 *.go 再次執行應用程式,您會發現與以前相同的輸出。Oracle Instant Client 現在所涉及的事實並不明顯。行為顯然沒有改變,即使可能有非功能性的差異 (例如某些作業的效能)。
資料庫交易管理
先前所做的討論並不明顯,因為我們從未實際向資料庫確認資料。所有 SQL 動作都發生在單一的資料庫階段作業中。建立和刪除表格的兩個 DDL 作業會隱含地確認交易,但是不會確認任何插入敘述句。某些資料庫具有自動確認設定 (某些甚至與其預設值),會將每個 DML 作業變成一個自動確認的交易。Oracle Database 並非如此。為了確認對資料庫記錄所做的變更,必須明確確認這些變更 - 或者倒回,以防它們的持續效果完全不適合。在我們的 Go 應用程式中,我們可以透過在資料庫上開始交易 (sql.Tx),對該交易執行 DML 敘述句,最後確認或倒回交易,明確地使用資料庫交易。例如:
ctx := context.Background()
tx, err := db.BeginTx(ctx, nil)
err = tx.ExecContext(ctx, DMLStatement, ... bind parameter values)
err = tx.ExecContext(ctx, AnotherDMLStatement, ... bind parameter values)
err = tx.Commit() // or tx.Rollback()
讓 Go 應用程式與本機 (或任何傳統連線) Oracle Database 交談並不困難。針對使用 Oracle Wallet 的從屬端 (例如 OCI 上的 Oracle Autonomous Database 執行處理) 進行加密通訊所設定的資料庫,不會再與這些資料庫互動。我們需要擴充程式碼才能使用 Oracle Wallet 檔案,當然需要執行 Autonomous Database 執行處理並取得關聯的 Oracle Wallet。
在 OCI 上執行免費 Autonomous Database
執行 Autonomous Database 執行個體幾乎比執行本機資料庫簡單。可承諾量執行個體可以數種方式建立 (包括透過 OCI CLI 和 Terraform),但您第一次最直接的方法可能是透過 OCI 瀏覽器主控台。
Note: Tim Hall provides a good description in his article Oracle Cloud : Autonomous Transaction Processing (ATP) – Create Service, and there are many more to be found.
讓我們建立永遠免費的 ATP 實例:
在主控台的搜尋方塊中輸入自動輸入,瀏覽至 *Autonomous Database Features*,按一下「建立 Autonomous Database」按鈕。
在建立表單中,提供顯示名稱 (可能為 go-on-oci-db) 和資料庫名稱、選取「交易處理」作為工作負載類型、切換「永遠免費」切換為作用中、提供 ADMIN 密碼 (並記住密碼)、接受來自任何位置的「網路存取類型安全」存取,以及確定勾選「需要雙向 TLS (mTLS)」認證核取方塊。
按下「建立 Autonomous Database」按鈕來建立資料庫之後,便會顯示啟動設定狀態:
資料庫使用不到 1 分鐘的時間。
下載含有連線詳細資訊的資料庫公事包
我們需要包含進行 mTLS 互動所需之 SSL 憑證的資料庫公事包。下載 ATP 執行處理的公事包。請先在 OCI 主控台的 ATP 頁面中按一下「資料庫連線」按鈕,然後按一下「下載公事包」。
提供公事包的密碼;之後讀取公事包時可能需要此密碼。也請您輸入這組密碼,但我還沒有必要輸入這組密碼才能完成本文所述的步驟。
儲存壓縮檔,我們即將使用。
在 Autonomous Database 中建立示範使用者帳戶
您可能想要在自治式資料庫中建立示範使用者帳戶。您可以使用下列步驟來執行此動作:
create user demo identified by thePassword1 default tablespace users temporary tablespace temp
/
grant connect, resource to demo
/
ALTER USER DEMO QUOTA UNLIMITED ON DATA
/
當我想要與之互動的 Oracle Database 需要與使用 Oracle Wallet 連線時,我需要將 Oracle Wallet 的檔案系統位置傳遞給驅動程式。更精確地說,我必須指定包含屬於公事包之 cwallet.sso 檔案的目錄路徑。公事包通常是以 zip 存檔方式提供。此歸檔應解壓縮 (或至少應解壓縮此檔案),而包含該檔案的目錄路徑將稱為 walletLocation。此時,從公事包壓縮檔擷取 cwallet.sso,然後將此檔案移至可從 Go 應用程式存取的位置 - 它甚至位於與 Go 應用程式本身相同的目錄中。這不是生產級應用程式的最佳作法,但基於本文的目的,這將是成功的。
需要提供的自治式資料庫連線詳細資訊,包括先前用於本機資料庫的一組相同元素。資料庫服務名稱可在公事包壓縮檔的 tnsnames.ora 檔案中,或在 OCI 主控台的「ATP 資料庫連線」頁面中以 service_name 身分找到。伺服器特性的值可作為這些位置中的主機使用。
收集特性時,可以在 localDB 下的檔案 oracle-database-client-app.go 中新增下列對應定義:
var autonomousDB = map[string]string{
"service": "k8j2fvxbaujdcfy_goonocidb_medium.adb.oraclecloud.com",
"username": "demo",
"server": "adb.us-ashburn-1.oraclecloud.com",
"port": "1522",
"password": "thePassword1",
"walletLocation": ".", // when the *.sso file has been moved into the application directory; otherwise provide the absolute directory path
}
使用驅動程式 go-ora 與 Autonomous Database 互動
必須擴充 go-ora 驅動程式組態,才能將公事包位置包括在連線字串中,並設定安全通訊協定。
func GetSqlDBWithPureDriver(dbParams map[string]string) *sql.DB {
connectionString := "oracle://" + dbParams["username"] + ":" + dbParams["password"] + "@" + dbParams["server"] + ":" + dbParams["port"] + "/" + dbParams["service"]
if val, ok := dbParams["walletLocation"]; ok && val != "" {
connectionString += "?TRACE FILE=trace.log&SSL=enable&SSL Verify=false&WALLET=" + url.QueryEscape(dbParams["walletLocation"])
}
db, err := sql.Open("oracle", connectionString)
...
若要針對 Autonomous Database 執行應用程式並在雲端執行其 TEMP_TABLE acrobatics,我們需要稍微變更主要函數:
func main() {
db := GetSqlDBWithPureDriver(autonomousDB)
//db := GetSqlDBWithGoDrOrDriver(localDB)
...
亦即:將呼叫 GetSqlDBWithPureDriver 中的 localDB 參照取代為 autonomousDB。
現在開始執行 *.go,再次執行應用程式。與本機資料庫相比的結果將完全相同,但由於每個資料庫互動都引進了現在的延遲,結果可能需要較長的時間才能產生。
使用 Driver godror 與 Autonomous Database 互動
godror 驅動程式與 go-ora 相比,使用 Oracle Wallet 的設定稍有不同。檔案 godror-based-oracle-database-client.go 中的函數 GetSqlDBWithGoDrOrDriver 已延伸以處理此情況:
func GetSqlDBWithGoDrOrDriver(dbParams map[string]string) *sql.DB {
var db *sql.DB
var err error
if val, ok := dbParams["walletLocation"]; ok && val != "" {
db, err = sql.Open("godror", fmt.Sprintf(`user="%s" password="%s"
connectString="tcps://%s:%s/%s?wallet_location=%s"
`, dbParams["username"], dbParams["password"], dbParams["server"], dbParams["port"], dbParams["service"], dbParams["walletLocation"]))
}
if val, ok := dbParams["walletLocation"]; !ok || val == "" {
connectionString := "oracle://" + dbParams["username"] + ":" + dbParams["password"] + "@" + dbParams["server"] + ":" + dbParams["port"] + "/" + dbParams["service"]
db, err = sql.Open("oracle", connectionString)
}
err = db.Ping()
...
若要針對 Autonomous Database 使用 godror 驅動程式執行應用程式,並在雲端執行其 TEMP_TABLE acrobatics,我們需要稍微變更主要功能:
func main() {
//db := GetSqlDBWithPureDriver(autonomousDB)
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
...
現在開始執行 *.go,再次執行應用程式。結果將再度與 go-ora 驅動程式完全相同,但似乎 (至少在我的環境中) 透過 go-ora 的動作比透過 godror 的相同動作更快。
「程式碼儲存區域」在 /applications/data-service 目錄中包含名為 data-service 的應用程式。此應用程式是 myserver 應用程式的延伸,我們在此序列的第 1 和 2 條中處理。應用程式仍會像之前一樣處理 HTTP 要求,而此時也會實行簡單的資料 API。使用 PUT、POST 和 DELETE 要求時,應用程式可用來建立、更新及移除 Oracle Database 中稱為 PEOPLE 之表格中的人員記錄。使用 GET 要求,可以擷取任何人員的目前詳細資料。
我們會先簡短檢視應用程式中有趣的元素,然後在本機執行。下一步是讓此應用程式在運算執行處理的 OCI 上執行。在 OCI 上部署時,您會發現與 Autonomous Database 互動的應用程式無關緊要。或者,至少要到此系列中的下一期分期付款為止,我們將使用 OCI Key Vault 安全地保留 Oracle Wallet 詳細資訊,這要歸功於執行處理主要授權 - 應用程式可以在程式實際執行時擷取。不過,目前公事包會包括在原始程式碼儲存區域中,並在組建和部署管線中處理。這並不是一個好的做法,將在下一篇文章中予以修正。
部署應用程式之後,我們會驗證是否可以直接存取運算執行處理來存取。若要針對 (而非直接公開公開公開公開公開公開公開) 套用最佳做法,我們接著再將 API 閘道擴充為導向資料服務的路由,特別是 API 閘道的功能。
我們於本節結束時在 OCI 上達成的最終情況如下:
檢查資料服務並設定您的可承諾量執行環境
檔案資料 - server.go 是應用程式中的新資料。它包含與資料庫互動的所有邏輯,以及處理任何 HTTP 要求至路徑資料中的應用程式;DATA_PATH。DataHandler 函數主要功能的註冊整合了資料處理功能。
http.HandleFunc(DATA_PATH, DataHandler)
函數 main 也會透過下列初始化步驟來擴充:
func main() {
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
defer func() {
err := db.Close()
if err != nil {
fmt.Println("Can't close connection: ", err)
}
}()
InitializeDataServer(db)
...
啟動我的伺服器應用程式時,會建立一個資料庫連線,然後設定資料伺服器。應用程式使用 godror 驅動程式。請注意,我們在 Oracle Linux Cloud Developer 映像檔上建立部署目標的運算執行處理 (在其中一個系列中回溯),並已預先安裝 Oracle Instant Client。
所有應用程式都需要新增才能執行:
然後您就可以在本機執行應用程式,使用
go run *.go
應用程式會啟動並報告職責。
在另一個終端機視窗中,使用 curl 敘述句與人員 API 互動。這些 HTTP 要求會導致建立兩筆記錄 - 適用於 Mickey Mouse 和 Bugs Bunny。米奇的記錄會更新一次。這兩個記錄都會擷取一次。然後兩者都會被刪除。最終 GET 要求未傳回任何資料。
請隨意新增 Curl 要求或不要全部執行。例如,您可以在「SQL 工作表」中檢查「人員 API」是否已建立預期的資料庫記錄。
curl -X "PUT" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse", "age":93, "comment": "Cartoon Character"}' localhost:8080/data
curl -X "PUT" -H "Content-Type: application/json" -d '{"name":"Bugs Bunny", "age":84, "comment": "an animated cartoon character created in the late 1930s by Leon Schlesinger Productions (later Warner Bros. Cartoons) and voiced originally by Mel Blanc."}' localhost:8080/data
curl -X "POST" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse", "age":93, "comment": "Cartoon Character and Movie Star, created in 1928 by Walt Disney and first appearing in Steamboat Willie; he is the mascot of The Walt Disney Company. His partner is Minnie and he has a pet dog called Pluto "}' localhost:8080/data
curl -X "GET" localhost:8080/data?name=Mickey+Mouse
curl -X "GET" localhost:8080/data?name=Bugs+Bunny
curl -X "DELETE" -H "Content-Type: application/json" -d '{"name":"Bugs Bunny"}' localhost:8080/data
curl -X "DELETE" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' localhost:8080/data
curl -X "GET" localhost:8080/data?name=Mickey+Mouse
導致部署與執行的確認、推送及建置
這個不同的 myserver 應用程式已可供使用,且程式碼已經在 OCI DevOps 程式碼儲存庫中,因為 GitHub 上文章來源儲存庫的所有程式碼都已確認至此系列第二篇文章中的 OCI 程式碼儲存庫。不過,您已新增 cwallet.sso 檔案 (Autonomous Database 執行處理的 Oracle Wallet),而且已更新檔案資料 - server.go 以提供資料庫連線詳細資訊。在 OCI 上使用組建管線組建並隨後部署應用程式之前,您必須先新增檔案、確認變更的檔案和新增的檔案,然後將變更推送至 OCI DevOps 程式碼儲存區域。
在這些 git 新增、確認和推送動作之後,程式碼儲存區域 go-on-oci-repo 應包含您修改的 cwallet.sso 和資料 service.go。
您現在可以在第一次討論 OCI DevOps 組建管線時,重複使用文章二中所設定的組建管線組建伺服器。不過,目前的管線預期會在預設位置建立設定檔案,而且不會對修改過的 myserver 應用程式執行。
管線將會產生新的使用者自建物件 - 一個壓縮檔,其中包含從目錄 /applications/data-service 中的 Go 來源建立的可執行檔,以及包含公事包檔案和網站子目錄。管線將會觸發將使用者自建物件帶到運算執行處理的部署管線,將應用程式複製到 /tmp/yourserver 目錄,然後執行應用程式。它會開始監聽部署管線參數 HTTP_SERVER_PORT 所指定連接埠上的 HTTP 要求 (如果未設定參數,則為 8080)。
您可以存取 VM 公用 IP 位址上的人員 API (如果仍然公開的話):
curl -X "GET" <public ip="" for="" compute="" instance="">:8095/data?name=Mickey+Mouse
</public>
您可以在 API 閘道上建立路由,以提供適當的人員 API 公用存取權。請確定將 API 處理的所有方法新增至路由定義。
更新部署時,可透過 https://
Curl 和其他 HTTP 工具 (例如 Postman) 可以使用所有方法來建立、更新、擷取及刪除人員記錄,與 API 互動。
本文的最後一個步驟將與 OCI Object Storage 服務 (上篇文章中介紹) 的互動與 Autonomous Database 執行處理的作業結合在單一 Go 應用程式中,此應用程式會先在本機執行,然後部署到 OCI 中的運算執行處理,然後透過 API 閘道公開。提供的功能:傳送含有物件名稱的 HTTP GET 要求以及物件儲存的儲存桶;物件必須是 JSON 檔案,其中包含下列格式之人員的資料:
[
{
"name": "Jasper",
"age": 19,
"comment": "Haute Couture"
},
{
"name": "James",
"age": 3,
"comment": "Golden retriever"
}
]
將會讀取檔案,並針對每個 JSON 項目在 Autonomous Database 的表格 PEOPLE 中建立記錄。
您需要新增到應用程式才能執行:
然後您就可以在本機執行應用程式,使用
go run *.go
應用程式會啟動並報告職責。
在個別的終端機視窗中,您可以使用 curl 敘述句與新的 Persons 檔案處理器 API 互動。HTTP 要求應傳入儲存桶的名稱和包含要處理之 JSON 資料的物件。此服務會擷取檔案、剖析檔案內容,以及建立或更新自治式資料庫中的 PEOPLE 表格。
curl "localhost:8080/people?objectName=sample-persons.json&bucketName=the-bucket"
使用對資料 API 的呼叫,您可以檢查資料記錄:
curl localhost:8080/data?name=Martha
而且您可以在 SQL Developer Worksheet 中執行相同的動作:
您可能對處理表格 PEOPLE 中記錄建立 (或更新) 的函數 PeopleJSONProcessor 有興趣。它使用 Oracle 特定的大量 DML 方法 - godror 驅動程式支援的語法 - 其中每個連結參數的值陣列都會傳入,而且所有記錄都會在單一 DML 敘述句中建立。高效率。
func PeopleJSONProcessor(peopleJson []byte) {
var persons []Person
json.Unmarshal(peopleJson, &persons)
nameVals := make([]string, len(persons))
ageVals := make([]int, len(persons))
descriptionVals := make([]string, len(persons))
for i, person := range persons {
ageVals[i] = person.Age
nameVals[i] = person.Name
descriptionVals[i] = person.JuicyDetails
}
database.Exec(`MERGE INTO PEOPLE t using (select :name name, :age age, :description description from dual) person
ON (t.name = person.name )
WHEN MATCHED THEN UPDATE SET age = person.age, description = person.description
WHEN NOT MATCHED THEN INSERT (t.name, t.age, t.description) values (person.name, person.age, person.description) `,
nameVals, ageVals, descriptionVals)
}
現在讓我們將此應用系統帶到 OCI,我們將在前一節使用的運算執行處理中使用。準備需要一些步驟:
在這些 git 新增、確認和推送動作之後,程式碼儲存區域 go-on-oci-repo 應包含您修改的 cwallet.sso 和資料 service.go。
您現在可以重複使用我們先前使用的組建管線組建伺服器。就像之前一樣,我們必須更新建置規格檔案的參照。
開始組建執行。如果需要,請設定參數 MYSERVER_VERSION 的新版本。
管線將會產生新的使用者自建物件 - 一個壓縮檔,其中包含從目錄 /applications/people-file-processor 中的 Go 來源建立的可執行檔,以及包含公事包檔案和網站子目錄。管線將會觸發將使用者自建物件帶到運算執行處理的部署管線,將應用程式複製到 /tmp/yourserver 目錄,然後執行應用程式。它會開始監聽部署管線參數 HTTP_SERVER_PORT 所指定連接埠上的 HTTP 要求 (如果未設定參數,則為 8080)。
您可以存取 VM 公用 IP 位址上的 API (如果仍然公開的話)。建議您在 API 閘道上新增路由,以公開對人事檔案處理器的存取:
將路徑定義為 /personnel-file-handler。應支援 GET 方法。路由的類型為 HTTP。URL 為:http://< 運算執行處理的公用 IP>:8095/people。檢查部署管線 deploy-myserver-on-go-app-vm 上的 HTTP_SERVER_PORT 值。如果未設為 8095,請修改 URL 值,以使用應用程式的適當連接埠號碼。
按下一步,然後按儲存變更。需要一些時間來重新整理 API 閘道的部署。一旦從物件儲存的儲存桶讀取檔案的服務,便會處理 JSON 內容,並在 Autonomous Database 執行處理中建立 PEOPLE 表格中的記錄,以便透過簡單的 HTTP GET 要求來觸發此檔案中的項目 https://<public endpoind of API Gateway>/my-api/personnel-file-handler?objectName=sample-persons.json&bucketName=the-bucket。
進行呼叫的效果可透過讀取相同資料庫中相同表格之記錄的人員 API 進行檢查:https://<public endpoind of API Gateway>/my-api/person?name=Jasper。
下圖顯示目前在 OCI 上部署內容的點對點圖。
圖片中未顯示的內容,請務必注意:
在下一篇文章中,我們將探討 OCI Key Vault (OCI Key Vault) 的服務,此服務能夠以更好的方式儲存 Oracle Wallet,並讓應用程式在建置期間 (甚至是程式實際執行) 可供使用。
本文示範如何從 Go 應用程式與 Oracle Database 互動,無論是傳統應用系統還是自治式應用系統。我們瞭解如何透過 Go 應用程式與本機 Oracle Database 以及 Autonomous Database 建立連線 - 使用驅動程式並可能支援程式庫,以及如何使用這些資料庫執行 DDL 與 DML SQL 作業,從 Go 應用程式的舒適性。討論使用 Oracle Wallet 管理 (自治式) 資料庫證明資料。在 OCI Compute 執行處理上執行的應用程式、透過 Go SDK for OCI 與物件儲存服務互動,以及操控透過 OCI DevOps 自動部署的 Autonomous Database,前提是這篇文章的詳細內容。
此系列中的第五和最後一篇文章新增了 Go 應用程式可以互動的另外兩項 OCI 服務:OCI Streaming - 一個大量串流訊息中介,允許不同微服務與其他元件之間的去耦互動 - 以及用於管理加密密碼的 OCI Key Vault,例如 Oracle Wallet 與資料庫證明資料。本文還在 VM 和無伺服器功能旁邊,以受管理的 OCI Kubernetes Enginer (OKE) 資源配置導入第三種應用程式平台,並說明 DevOps Deployment Pipelines 如何以自動化方式將 Go 應用程式部署至 OKE。
本頁面是機器翻譯的。