Questa è la quarta parte di una serie di cinque parti su Go e Oracle Cloud Infrastructure (OCI). Questa serie descrive come creare ed eseguire le applicazioni Go su Oracle Cloud Infrastructure in istanze di computazione (VM), containerizzate su Kubernetes o come funzioni serverless. Gli articoli mostrano come automatizzare la creazione e la distribuzione di queste applicazioni Go utilizzando OCI DevOps. Un argomento importante è come utilizzare i servizi OCI delle applicazioni Go, sia quelli in esecuzione su OCI che quelli Go in esecuzione altrove. I servizi OCI discussi includono Object Storage, Streaming, Key Vault e Autonomous Database.
Per seguire questi articoli, i lettori dovrebbero avere almeno una conoscenza di base su come creare applicazioni Go. Si presume che i lettori abbiano accesso al proprio ambiente di sviluppo Go. Alcuni esempi e screenshot menzioneranno specificamente VS Code come strumento di sviluppo. Tuttavia, possono essere utilizzati anche altri editor e IDE. Il codice Go presentato in questi articoli dimostra una serie di meccanismi nella loro forma più semplice per la massima chiarezza e con le meno dipendenze. I lettori non dovrebbero aspettarsi funzionalità significative o codice pronto per la produzione.
Questa serie descrive come passare a OCI. Per provare gli esempi, i lettori dovranno avere accesso a una tenancy OCI con le autorizzazioni per creare le risorse OCI discusse in questi articoli. La maggior parte delle risorse utilizzate è disponibile nel livello gratuito Aways (istanza di computazione, VCN, Autonomous Database, storage degli oggetti, log, Resource Manager) o dispone di un livello di assegnazione gratuito per un uso mensile limitato (Funzioni, gateway API, streaming, vault, DevOps).
La prima parte di questa serie descrive il provisioning di un'istanza di computazione basata sull'immagine sviluppatore cloud Oracle Linux, l'apertura per l'attività di rete in entrata e in uscita, la creazione e l'esecuzione di un'applicazione Go che soddisfa le richieste HTTP e la connessione dei log prodotti dall'applicazione a OCI Logging. La seconda parte riguarda l'ingegneria del software, l'automazione della creazione e della distribuzione dell'applicazione con il servizio DevOps OCI. Questo servizio viene utilizzato per memorizzare il codice sorgente Go, creare l'eseguibile dell'applicazione e memorizzarlo come artifact distribuibile, distribuendolo in un'istanza di computazione. L'articolo mostra anche come esporre un endpoint HTTP per l'applicazione tramite un gateway API OCI. La terza parte mostra come creare funzioni serverless in Go e distribuirle su OCI. Viene introdotto Go SDK per OCI, prima per le applicazioni Go locali e standalone e successivamente per l'uso dalle funzioni, sfruttando l'autenticazione del principal delle risorse. Questo SDK viene utilizzato per interagire con il servizio di storage degli oggetti OCI per creare bucket e scrivere e leggere file.
La quarta parte, che stai leggendo in questo momento, descrive l'interazione tra la tua applicazione Go e un Oracle Database. Può essere un database locale o on-premise, un database in esecuzione su alcune istanze IaaS del fornitore cloud o un Autonomous Database OCI. Utilizzando il pacchetto standard Go database/sql con un driver per Oracle Database e fornendo al driver i dettagli di configurazione richiesti, si è scoperto che sfruttare qualsiasi Oracle Database di Go è abbastanza semplice. Il myserver dell'applicazione Go discusso nella seconda parte viene esteso per interagire sia con un'istanza di Autonomous Database su OCI che con il servizio di storage degli oggetti OCI. L'applicazione utilizza l'SDK Go per OCI per leggere i file (e successivamente rimuoverli) da un bucket nello storage degli oggetti e creare record di database in Autonomous Database in base ai relativi contenuti.
Il linguaggio Go supporta le interazioni SQL con i database relazionali integrati. Il database/sql di package standard include tipi e funzioni per la connessione ai database, l'esecuzione di transazioni, l'annullamento di un'operazione in corso e altro ancora. Questo stesso pacchetto può essere utilizzato per lavorare allo stesso modo con alcuni database NoSQL come MongoDB e Couchbase.
Un'applicazione Go che interagisce con un database tramite questo package non deve avere dettagli tecnici di implementazione per un prodotto di database specifico. Questi dettagli vengono in genere implementati in un driver per il database. L'applicazione importa il driver richiesto per il database a cui connettersi e indica al database/sql del package standard quale driver utilizzare e quali sono i dettagli di connessione per il database. La maggior parte dell'interazione con il database è la stessa, indipendentemente dalla tecnologia di database specifica; i record vengono creati, aggiornati ed eliminati tramite istruzioni DML SQL e i record vengono recuperati tramite query SQL. Il processo complessivo è lo stesso tra i database, ma il dialetto SQL esatto può variare. Questo è probabilmente l'unico vero ostacolo per spostare facilmente le applicazioni Go tra diversi prodotti di database.
Il codice discusso in questa sezione si trova nella directory /applications/go-orcl-db del repository di codici che accompagna questa serie di articoli.
Go Application esegue SQL - DDL, DML e query
La cosa più semplice da fare con un Oracle Database da un'applicazione Go è eseguire una query su una singola riga. Il codice richiesto per questo aspetto è simile a questo: pacchetto principale
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)
}
L'istruzione di importazione rende disponibile il package database/sql. Utilizzando l'handle in sql.DB, è possibile eseguire facilmente una query SQL (QueryRow) e il risultato può essere analizzato nelle variabili locali. Brand molto semplice e diretto e indipendente dal database, ad eccezione dell'istruzione SQL specifica, che in questo caso utilizza l'indicatore di posizione specifico di Oracle.
Per ora, non soffermiamoci su da dove proviene il parametro db. Tra poco parleremo dei driver di database, ed è qui che tutto sarà rivelato.
Una funzione leggermente più interessante che crea una tabella, inserisce un record, esegue query sul record, crea a più record, quindi esegue query su tutte le righe e infine elimina la tabella viene visualizzata qui. Questo codice si trova nel file oracle-database-client-app.go nel repository di codici.
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)
}
}
Questo codice è abbastanza funzionale in natura. Oltre alle istruzioni SQL, non sono disponibili dettagli di implementazione specifici del database. La metà del codice sembra essere la gestione degli errori. Non dovrebbe essere troppo difficile capire come questo codice manipola il database, ad eccezione del fatto che non esiste ancora un database con cui lavorare, e (quindi) anche nessun driver per effettuare la connessione e gestire la comunicazione. Per ovviare a questo problema, eseguire prima un database locale e quindi aggiungere un driver al database nell'applicazione Go.
Esegui Oracle Database locale
Esistono molti modi diversi per rendere operativo un Oracle Database locale. Il modo più semplice che ho trovato utilizza un'immagine del container Docker che mi consente di eseguire un database locale con un'istruzione molto semplice e semplice:
docker run -d -p 1521:1521 -e ORACLE_PASSWORD=TheSuperSecret1509! gvenzl/oracle-xe
Questa operazione esegue un'istanza di Oracle Database XE 21c (almeno, questo è ciò che fa al momento della scrittura, quando 21c è l'immagine contenitore più recente disponibile) e imposta le password per SYS e SYSTEM sul valore indicato. Il database è disponibile sulla porta 1521 su localhost.
Gerald Venzl di Oracle gestisce una serie di immagini dei container (Docker) che eseguono una versione ridotta di Oracle Database, l'edizione XE, che è gratuita da usare (fino a 20 GB di dati e che utilizza un massimo di 2 thread CPU e 2 GB di RAM). Descrive queste immagini e come utilizzarle in un articolo intitolato Introduzione a gvenzl/oracle-XE: immagini Docker di Oracle Database XE.
Attenersi alla procedura riportata di seguito per verificare che l'Oracle Database locale sia attivo e in esecuzione.
```console
docker exec -it /bin/bash
```
```console
sqlplus system/TheSuperSecret1509! as sysdba
```
```sql
create user demo identified by demo default tablespace users temporary tablespace temp
/
grant connect, resource to demo
/
```
Ora è il momento di connettersi a questo database dall'applicazione 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.
Aggiungi un driver per Oracle Database
Non esiste alcun driver Go ufficiale per Oracle Database, almeno non uno pubblicato o approvato da Oracle. La lista non ufficiale dei driver di database Go contiene quattro voci per Oracle Database. Tre richiedono l'installazione delle librerie Oracle Client e una no. Iniziamo a lavorare con quell'ultimo, chiamato go-ora, un driver puro che da solo gestisce tutta la comunicazione con il database. I dettagli su go-ora sono disponibili sulla homepage di go-ora su GitHub. Successivamente verrà anche esaminato Godror, un driver che richiede l'installazione delle librerie e quello che sembra più prominente tra i driver di Oracle Database per Go.
Il driver go-ora può essere aggiunto all'applicazione Go con:
go get github.com/sijms/go-ora/v2
Questo scarica il pacchetto e aggiunge una voce obbligatoria al file go.mod. Per me è così:
module oracle-database-client
go 1.16
require (
github.com/sijms/go-ora/v2 v2.4.16 // indirect
)
In un nuovo file denominato pure-oracle-database-client.go (anche se Go non si preoccupa del nome) nella stessa directory del file oracle-database-client-app.go, il codice seguente gestisce l'interazione con Oracle Database locale tramite go-ora. Il pacchetto driver viene importato e la chiamata a sql.Open, che fa riferimento implicitamente a oracle, seleziona go-ora come driver preferito.
Il parametro dbParams è costituito da una mappa delle impostazioni di configurazione (incluso il nome utente e la password), l'host e la porta del database e il nome del servizio da utilizzare per effettuare una connessione. La stringa di connessione viene composta utilizzando questi elementi e utilizzata nella chiamata a sql.Open. La successiva chiamata a db.Ping è il primo tentativo di stabilire realmente le comunicazioni con il database. Quando questa chiamata ha successo, siamo pronti per alcune azioni reali del database.
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
}
Connessione ed esecuzione
Il database è in esecuzione e abbiamo una funzione che funziona con un driver Oracle Database puro. Ora torniamo a oracle-database-client-app.go e li colleghiamo.
Aggiungere la funzione principale in questo file. Chiama GetSqlDBWithPureDriver per creare un'istanza sql.DB utilizzando i dettagli di connessione al database definiti nella mappa localDB. Modificare questi valori per allinearli alla configurazione del database. La chiamata di funzione imposta la variabile db con *sql.DB, che può essere utilizzata per ulteriori operazioni SQL.
Una volta completate tutte le interazioni del database, è necessario chiudere la connessione per rilasciare le risorse. Per assicurarsi che ciò avvenga, il differimento della funzione principale immediatamente dopo la chiamata a GetSqlDBWithPureDriver viene aggiunto con la chiamata a db.Close(). La chiamata alla funzione sqlOperations che passa db ci porta alla funzione di cui abbiamo discusso due sezioni fa con cui il database è realmente interagito.
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)
}
Eseguire l'applicazione dalla riga di comando utilizzando go run *.go. L'output sarà simile al seguente:
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
Utilizzo del driver GoDrOr
Un'alternativa popolare a go-ora è Go package godror (precedentemente chiamato goracle, ma rinominato a causa di problemi relativi ai marchi - Oracle Corporation non vuole che nessuno utilizzi oracle nei propri nomi). Questo package fornisce anche un driver Oracle Database che il database/SQL può utilizzare quando viene eseguito sql.Open per oracle o Godror. Questo pacchetto, a differenza di go-ora, richiede l'installazione di una libreria Oracle Instant Client sul sistema su cui è in esecuzione l'applicazione Go.
Il driver GoDrOr utilizza l'interfaccia ODPI-C (Oracle Database Programming Interface for C). Si tratta di una libreria open source di codice C, gestita da Oracle Corporation, che semplifica l'uso delle funzioni comuni di Oracle Call Interface (OCI) per i driver e le applicazioni utente di Oracle Database. Quando si utilizza GoDrOr non è necessario installare ODPI-C o essere consapevoli della sua esistenza. Tuttavia, l'ambiente in cui è in esecuzione la nostra applicazione Go, incluso il driver, deve contenere librerie client Oracle.
Il più semplice Oracle Client è il servizio Oracle Instant Client gratuito (consultare la pagina generale di Oracle Instant Client). È richiesto solo il pacchetto "Basic" o "Basic Light". Le librerie Oracle Client sono disponibili anche in qualsiasi installazione di Oracle Database o in qualsiasi installazione completa di Oracle Client. Per istruzioni dettagliate sull'installazione per Linux, consultare il documento Oracle Database Documentation for Release 21c - Database Client Installation Guide for Linux - Installing Oracle Instant Client.
ODPI-C carica dinamicamente le librerie client Oracle disponibili in runtime. Le librerie del client Oracle vengono cercate nella stessa directory della libreria ODPI-C (o binario dell'applicazione). Se non vengono trovati, vengono cercati nel percorso di ricerca del sistema operativo standard, ad esempio PATH su Windows o LD_LIBRARY_PATH su Linux. Infine, su piattaforme diverse da Windows, viene anche cercato $ORACLE_HOME/lib. Per informazioni dettagliate su come garantire che ODPI-C sia in grado di trovare le librerie Oracle Client, vedere Home ODPI-C GitHub - Installazione ODPI-C.
Le modifiche che dobbiamo apportare all'applicazione per passare dall'uso di go-ora a Godror sono minime.
In primo luogo, il driver Godror viene aggiunto all'applicazione Go con:
github.com/godror/godror
Questo scarica il pacchetto e aggiunge una voce obbligatoria al file go.mod.
Successivamente, crea un nuovo file chiamato Godror-based-oracle-database-client.go nella stessa directory dell'applicazione, molto simile a Pure-oracle-database-client.go, che contiene i dettagli per la connessione tramite il driver go-ora.
Il contenuto di questo nuovo file:
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
}
L'importazione per il pacchetto Godror è diversa rispetto all'importazione di go-ora. Il resto del codice è esattamente lo stesso di prima.
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.
Infine, per far sì che l'applicazione smetta di usare go-ora e inizi a usare Godror, dobbiamo solo commentare o rimuovere una riga e aggiungerne un'altra nella funzione principale, chiamando GetSqlDBWithGoDrOrDriver:
func main() {
//db := GetSqlDBWithPureDriver(localDB)
db := GetSqlDBWithGoDrOrDriver(localDB)
Esegui nuovamente l'applicazione con go run *.go e troverai lo stesso output di prima. Il fatto che Oracle Instant Client sia ora coinvolto non è evidente. Il comportamento è apparentemente invariato, anche se potrebbero esserci differenze non funzionali come le prestazioni di alcune operazioni.
Gestione delle transazioni del database
Ciò che non è immediatamente evidente dalla nostra precedente discussione è che non abbiamo mai effettivamente impegnato dati nel database. Tutte le azioni SQL sono state eseguite in una singola sessione del database. Le due operazioni DDL che hanno creato ed eliminato la tabella hanno implicitamente eseguito il commit della transazione, ma non è stato eseguito il commit di alcuna istruzione INSERT. Alcuni database dispongono di un'impostazione di commit automatico, alcune anche come impostazione predefinita, che trasforma ogni operazione DML in una transazione di cui viene eseguito automaticamente il commit. Non è così con Oracle Database. Per eseguire il commit delle modifiche apportate ai record di database, è necessario eseguire esplicitamente il commit di tali modifiche o il rollback nel caso in cui i loro effetti duraturi non siano del tutto auspicabili. Nella nostra applicazione Go possiamo lavorare esplicitamente con le transazioni del database - avviando una transazione (sql.Tx) su un database, eseguendo istruzioni DML su quella transazione e infine eseguendo il commit o il rollback della transazione. Per esempio:
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()
Fare parlare un'applicazione Go con un Oracle Database locale (o qualsiasi altro connesso tradizionalmente) non è stato così difficile. I database configurati per le comunicazioni cifrate dai client che utilizzano Oracle Wallet, ad esempio le istanze di Oracle Autonomous Database su OCI, non sono più difficili da interagire. Per lavorare con il file Oracle Wallet, dobbiamo estendere il nostro codice e, naturalmente, dobbiamo eseguire un'istanza di Autonomous Database e acquisire l'Oracle Wallet associato.
Esegui Autonomous Database gratuito su OCI
Eseguire un'istanza di Autonomous Database è quasi più semplice rispetto all'esecuzione di un database locale. Un'istanza ATP può essere creata in diversi modi (anche tramite l'interfaccia CLI OCI e Terraform), ma il metodo più semplice per la prima volta è probabilmente tramite la console del browser 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.
Creare l'istanza ATP sempre gratuita:
Digita automaticamente nella casella di ricerca della console, vai a *Funzioni di Autonomous Database*, fai clic sul pulsante Crea Autonomous Database.
Nel modulo di creazione, fornire un nome visualizzato (forse go-on-oci-db) e un nome di database, selezionare Elaborazione delle transazioni come tipo di carico di lavoro, attivare/disattivare l'opzione Sempre gratis, fornire una password per ADMIN (e ricordarla bene), accettare l'accesso sicuro del tipo di accesso alla rete ovunque e assicurarsi che la casella di controllo Richiedi autenticazione TLS reciproca (mTLS) sia selezionata.
Dopo aver premuto il pulsante Crea Autonomous Database per creare il database, viene visualizzato lo stato di provisioning:
La disponibilità del database richiede meno di un minuto.
Scarica wallet database con dettagli di connessione
È necessario il wallet del database contenente i certificati SSL necessari per l'interazione mTLS. Scaricare il wallet per l'istanza ATP. Fare prima clic sul pulsante Connessione DB nella pagina ATP nella console OCI, quindi fare clic su Scarica wallet.
Fornire una password per il wallet; questa operazione potrebbe essere necessaria per leggere il wallet in un secondo momento. Aspetta anche questa password, anche se non ho bisogno di questa password per i passaggi descritti in questo articolo.
Salva il file zip, verrà utilizzato al più presto.
Crea account utente demo in Autonomous Database
È possibile creare un account utente demo nel database autonomo. È possibile eseguire questa operazione con i passi riportati di seguito.
create user demo identified by thePassword1 default tablespace users temporary tablespace temp
/
grant connect, resource to demo
/
ALTER USER DEMO QUOTA UNLIMITED ON DATA
/
Quando l'Oracle Database con cui si desidera interagire deve essere connesso all'uso di un Oracle Wallet, è necessario passare la posizione del file system di Oracle Wallet al driver. Più precisamente, devo specificare il percorso della directory che contiene il file cwallet.sso che fa parte del wallet. Il wallet viene in genere fornito in un archivio zip. Questo archivio dovrebbe essere estratto (o almeno questo file dovrebbe) e il percorso della directory che contiene il file è quello che sarà chiamato walletLocation. A questo punto, estrarre cwallet.sso dal file zip del wallet e spostare questo file in una posizione accessibile dall'applicazione Go. Potrebbe anche trovarsi nella stessa directory dell'applicazione Go stessa. Questa non è la best practice per le applicazioni di livello produttivo, ma ai fini di questo articolo sarà sufficiente.
I dettagli di connessione per il database autonomo da fornire sono costituiti dallo stesso set di elementi utilizzati in precedenza per il database locale. Il nome del servizio di database è disponibile nel file tnsnames.ora nel file zip del wallet o nella pagina Connessione al database ATP nella console OCI come service_name. Il valore della proprietà server è disponibile come host in queste posizioni.
Quando le proprietà vengono raccolte, è possibile aggiungere la seguente definizione di mappa nel file oracle-database-client-app.go, proprio sotto localDB:
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
}
Vai all'interazione con Autonomous Database utilizzando Driver go-ora
La configurazione del driver go-ora deve essere estesa un po 'per includere la posizione del wallet nella stringa di connessione e configurare il protocollo di comunicazione sicuro.
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)
...
Per eseguire l'applicazione su Autonomous Database ed eseguire le acrobazie TEMP_TABLE nel cloud, è necessario modificare leggermente la funzione principale:
func main() {
db := GetSqlDBWithPureDriver(autonomousDB)
//db := GetSqlDBWithGoDrOrDriver(localDB)
...
Cioè: sostituire il riferimento localDB nella chiamata a GetSqlDBWithPureDriver con autonomousDB.
Ora esegui nuovamente l'applicazione con go run *.go. I risultati saranno esattamente gli stessi di prima rispetto al database locale, ma probabilmente impiegheranno un po' più di tempo per essere prodotti poiché ora viene introdotta la latenza in ciascuna delle interazioni del database.
Vai all'interazione con Autonomous Database utilizzando Driver Godror
Il driver Godror utilizza una configurazione leggermente diversa per l'utilizzo di un Oracle Wallet rispetto al go-ora. La funzione GetSqlDBWithGoDrOrDriver nel file godror-based-oracle-database-client.go è estesa per gestire questo caso:
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()
...
Per eseguire l'applicazione con il driver Godror su Autonomous Database ed eseguire le acrobazie TEMP_TABLE nel cloud, è necessario modificare leggermente la funzione principale:
func main() {
//db := GetSqlDBWithPureDriver(autonomousDB)
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
...
Ora esegui nuovamente l'applicazione con go run *.go. I risultati saranno di nuovo esattamente gli stessi del driver go-ora, ma sembra (almeno nel mio ambiente) che le azioni attraverso go-ora siano sostanzialmente più veloci delle stesse azioni attraverso il dio.
Il repository di codici contiene un'applicazione denominata data-service, nella directory /applications/data-service. Questa applicazione è un'estensione dell'applicazione myserver con cui abbiamo lavorato negli articoli uno e due di questa serie. L'applicazione gestisce ancora le richieste HTTP come prima, e questa volta implementa anche una semplice API di dati. Utilizzando le richieste PUT, POST e DELETE, l'applicazione può essere utilizzata per creare, aggiornare e rimuovere i record persona da una tabella denominata PEOPLE in Oracle Database. Utilizzando le richieste GET, è possibile recuperare i dettagli correnti per qualsiasi persona.
Daremo prima un breve sguardo agli elementi interessanti dell'applicazione, quindi lo eseguiamo localmente. Il passo successivo consiste nel far eseguire questa applicazione su OCI in un'istanza di computazione. Scoprirai che non c'è nulla di molto speciale in un'applicazione con interazione con Autonomous Database quando si tratta di implementazione su OCI. In alternativa, almeno fino alla prossima puntata di questa serie in cui utilizzeremo OCI Key Vault per memorizzare in modo sicuro i dettagli di Oracle Wallet che, grazie all'autorizzazione basata sul principal dell'istanza, l'applicazione può recuperare in runtime. Per il momento, tuttavia, il wallet viene incluso nel repository di codici di origine ed elaborato nelle pipeline di build e distribuzione. Questa non è una buona pratica e sarà rettificata nel prossimo articolo.
Una volta distribuita l'applicazione, verifichiamo se possiamo accedervi con accesso diretto all'istanza di computazione. Per applicare una buona prassi relativa (non) all'esposizione pubblica diretta dei servizi, estendiamo quindi il gateway API con altri instradamenti che portano al servizio di dati e, in particolare, alle sue funzionalità basate sul database.
La situazione finale che otteniamo su OCI alla fine di questa sezione è simile alla seguente:
Ispeziona il servizio dati e configura per l'istanza ATP
Dati file: server.go è nuovo nell'applicazione. Contiene tutta la logica per l'interazione con il database e la gestione di qualsiasi richiesta HTTP all'applicazione che arriva sui dati di percorso; il DATA_PATH. La registrazione in funzione principale della funzione DataHandler integra le funzionalità di gestione dei dati.
http.HandleFunc(DATA_PATH, DataHandler)
La funzione principale è inoltre estesa con questi passi di inizializzazione:
func main() {
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
defer func() {
err := db.Close()
if err != nil {
fmt.Println("Can't close connection: ", err)
}
}()
InitializeDataServer(db)
...
All'avvio dell'applicazione my-server viene creata una connessione al database e viene impostato il server di dati. L'applicazione utilizza il driver Godror. Si noti che si fa uso del fatto che l'istanza di computazione che è la destinazione di distribuzione è stata creata (indietro nella parte uno della serie) nell'immagine per sviluppatori di Oracle Linux Cloud e che Oracle Instant Client è preinstallato.
Tutte le applicazioni da aggiungere per l'esecuzione sono:
È quindi possibile eseguire l'applicazione localmente, utilizzando
go run *.go
L'applicazione inizia e riporta per dovere.
In una finestra terminale separata, utilizzare le istruzioni curl per interagire con l'API persona. Queste richieste HTTP causeranno la creazione di due record - per Topolino e Bugs Bunny. Il record di Mickey viene aggiornato una volta. Entrambi i record vengono recuperati una volta. Quindi entrambi vengono eliminati. La richiesta GET finale non restituisce dati.
Sentiti libero di aggiungere richieste di curl o di non eseguire tutte. È possibile controllare, ad esempio nel foglio di lavoro SQL, se l'API Persona ha creato i record di database previsti.
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
Commit, push e build per la distribuzione e l'esecuzione
Questa variante dell'applicazione myserver è pronta per l'uso e il codice si trova già nel repository di codici DevOps OCI, poiché tutto il codice del repository di origine dell'articolo su GitHub è stato impegnato nel repository di codici OCI nel secondo articolo di questa serie. Tuttavia, è stato aggiunto il file cwallet.sso (Oracle Wallet per l'istanza di Autonomous Database) e i dati del file sono stati aggiornati: server.go per fornire i dettagli di connessione al database. Prima di poter utilizzare la pipeline di build su OCI per creare e distribuire successivamente l'applicazione, è necessario prima aggiungere il nuovo file, eseguire il commit sia del file modificato che di questo file aggiunto ed eseguire il push delle modifiche nel repository di codici DevOps OCI.
Dopo queste azioni di aggiunta, commit e push di git, il repository di codici go-on-oci-repo deve contenere il file cwallet.sso e i dati service.go modificati.
Ora puoi riutilizzare il build-myserver della pipeline di build impostato nell'articolo due quando abbiamo discusso per la prima volta delle pipeline di build DevOps OCI. Tuttavia, la pipeline corrente prevede che il file di specifica della build si trovi nella posizione predefinita e che non verrà eseguito per l'applicazione myserver modificata.
La pipeline produrrà un nuovo artifact, un file zip con l'eseguibile creato dalle origini Go nella directory /applications/data-service e contenente il file wallet e la sottodirectory del sito Web. La pipeline attiverà la pipeline di distribuzione che porterà l'artifact all'istanza di computazione, copierà l'applicazione nella directory /tmp/yourserver ed eseguirà l'applicazione. Inizia ad ascoltare le richieste HTTP sulla porta specificata dal parametro della pipeline di distribuzione HTTP_SERVER_PORT (o su 8080 se il parametro non è impostato).
Se la VM è ancora esposta, puoi accedere all'API persona nell'indirizzo IP pubblico:
curl -X "GET" :8095/data?name=Mickey+Mouse
È possibile creare un instradamento nel gateway API per fornire l'accesso pubblico appropriato all'API persona. Assicurarsi di aggiungere tutti i metodi gestiti dall'API alla definizione di instradamento.
Quando la distribuzione viene aggiornata, l'API persona è disponibile in https://
Curl e altri strumenti HTTP come Postman possono essere utilizzati per interagire con l'API, utilizzando tutti i metodi per creare, aggiornare, recuperare ed eliminare i record persona.
Il passo finale di questo articolo combina l'interazione con il servizio di storage degli oggetti OCI (introdotto nell'articolo precedente) con le operazioni su un'istanza di Autonomous Database in una singola applicazione Go che viene prima eseguita localmente e poi distribuita ed eseguita su un'istanza di computazione in OCI ed esposta tramite il gateway API. La funzionalità fornita: inviare una richiesta GET HTTP con i nomi di un oggetto e di un bucket nello storage degli oggetti; l'oggetto deve essere un file JSON contenente i dati sulle persone nel formato seguente:
[
{
"name": "Jasper",
"age": 19,
"comment": "Haute Couture"
},
{
"name": "James",
"age": 3,
"comment": "Golden retriever"
}
]
Il file verrà letto e i record verranno creati nella tabella PEOPLE nell'Autonomous Database per ciascuna voce JSON.
Tutto ciò che è necessario aggiungere all'applicazione per eseguire:
È quindi possibile eseguire l'applicazione localmente, utilizzando
go run *.go
L'applicazione inizia e riporta per dovere.
In una finestra terminale separata, si utilizzano le istruzioni curl per interagire con la nuova API del processore di file Persone. Una richiesta HTTP deve passare nel nome del bucket e nell'oggetto che contiene i dati JSON da elaborare. Il servizio recupererà il file, ne analizzerà il contenuto e creerà o aggiornerà i record nella tabella PEOPLE nel database autonomo.
curl "localhost:8080/people?objectName=sample-persons.json&bucketName=the-bucket"
Utilizzando una chiamata all'API dei dati è possibile ispezionare i record di dati:
curl localhost:8080/data?name=Martha
E puoi fare lo stesso nel foglio di lavoro di SQL Developer:
Potrebbe interessarti la funzione PeopleJSONProcessor, che gestisce la creazione (o l'aggiornamento) del record nella tabella PEOPLE. Utilizza un approccio Bulk DML specifico di Oracle, la sintassi supportata dal driver Godror, in cui vengono passati array di valori per ciascuno dei parametri di bind e tutti i record vengono creati in una singola istruzione DML. Molto efficiente.
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)
}
Ora trasferiamo questa applicazione su OCI, nell'istanza di computazione che abbiamo utilizzato anche nella sezione precedente. Alcuni passaggi sono necessari come preparazione:
Dopo queste azioni di aggiunta, commit e push di git, il repository di codici go-on-oci-repo deve contenere il file cwallet.sso e i dati service.go modificati.
Ora puoi riutilizzare il build-myserver della pipeline di build utilizzato in precedenza. Proprio come abbiamo fatto in precedenza, dobbiamo aggiornare il riferimento al file di specifica della build.
Avviare un'esecuzione della build. Se si desidera, impostare una nuova versione per il parametro MYSERVER_VERSION.
La pipeline produrrà un nuovo artifact, un file zip con l'eseguibile creato dalle origini Go nella directory /applications/people-file-processor e contenente il file wallet e la sottodirectory del sito Web. La pipeline attiverà la pipeline di distribuzione che porterà l'artifact all'istanza di computazione, copierà l'applicazione nella directory /tmp/yourserver ed eseguirà l'applicazione. Inizia ad ascoltare le richieste HTTP sulla porta specificata dal parametro della pipeline di distribuzione HTTP_SERVER_PORT (o su 8080 se il parametro non è impostato).
Puoi accedere all'API nell'indirizzo IP pubblico della VM, se ancora esposto. È meglio aggiungere un instradamento sul gateway API per esporre l'accesso al processore di file del personale:
Definire il percorso come /personnel-file-handler. Il metodo GET deve essere supportato. Il tipo di instradamento è HTTP. L'URL è: http://<IP pubblico per l'istanza di computazione>:8095/people. Controllare il valore del file HTTP_SERVER_PORT nella pipeline di distribuzione deploy-myserver-on-go-app-vm. Se non è impostato su 8095, modificare il valore dell'URL per utilizzare il numero di porta appropriato per l'applicazione.
Fare clic su Next, quindi su Save changes. L'aggiornamento della distribuzione del gateway API richiederà un momento. Una volta effettuato, il servizio che legge un file da un bucket di storage degli oggetti elabora il contenuto JSON e crea record nella tabella PEOPLE nell'istanza di Autonomous Database per le voci in questo file può essere attivato con una semplice richiesta GET HTTP a https://<public endpoind of API Gateway>/my-api/personnel-file-handler?objectName=sample-persons.json&bucketName=the-bucket.
L'effetto della chiamata può essere analizzato tramite l'API persona che legge i record dalla stessa tabella nello stesso database: https://<public endpoind of API Gateway>/my-api/person?name=Jasper.
La figura successiva mostra l'immagine end-to-end di ciò che ora viene distribuito su OCI.
Cosa non è mostrato nella foto ed è importante notare:
Nel prossimo articolo esaminiamo OCI Key Vault, un servizio che offre un modo molto migliore per memorizzare Oracle Wallet e renderlo disponibile all'applicazione in fase di distribuzione (o addirittura in fase di runtime).
Questo articolo ha dimostrato come interagire con un Oracle Database da applicazioni Go, tradizionali o autonome. Abbiamo visto come viene stabilita una connessione da un'applicazione Go a un Oracle Database locale, nonché a un Autonomous Database, utilizzando un driver ed eventualmente supportando le librerie e come è possibile eseguire le operazioni DDL e DML SQL con questi database comodamente dall'applicazione Go. È stato discusso l'uso di un Oracle Wallet per la corretta gestione delle credenziali del database (autonome). Un'applicazione in esecuzione su un'istanza di OCI Compute, che interagisce tramite l'SDK Go per OCI con il servizio di storage degli oggetti e che manipola Autonomous Database, distribuito automaticamente tramite OCI DevOps, ha fornito la fine dell'articolo.
Il quinto e ultimo articolo di questa serie aggiunge altri due servizi OCI con cui le applicazioni Go possono interagire: OCI Streaming - un broker di messaggi in streaming ad alto volume che consente interazioni disaccoppiate tra diversi microservizi e altri componenti - e OCI Key Vault per gestire segreti come Oracle Wallet con le credenziali del database. Questo articolo introduce anche un terzo tipo di piattaforma applicativa accanto a VM e funzione serverless, sotto forma di OCI Kubernetes Enginer (OKE) gestito, e mostra come le pipeline di distribuzione DevOps possono distribuire le nostre applicazioni Go in OKE in modo automatico.