Utilizzo delle applicazioni Go con Oracle Database e Oracle Cloud Autonomous Database

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.

articolo-modalità-accesso-oci

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.

Applicazione Local Go che parla con il database locale

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.

  1. Trovare l'identificativo del contenitore con docker ps | grep gvenzl. Quindi aprire una shell Bash nel contenitore:
     ```console
    
    docker exec -it  /bin/bash
    ``` 
  2. Ora, connettersi al database ed eseguire le istruzioni SQL:
     ```console
    
    sqlplus system/TheSuperSecret1509! as sysdba
    ``` 
  3. Creare un utente (schema) da utilizzare nelle seguenti sezioni, ad esempio un utente demo innocente:
     ```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.

  • Installazione della libreria Oracle Instant Client

    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.

  • Modifica dell'applicazione Go in modo che funzioni con GoDrOr

    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() 

Go Application parla con OCI Autonomous Database

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.


articolo-4-nav-autonomo-db

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.


create-autonomous-db

Dopo aver premuto il pulsante Crea Autonomous Database per creare il database, viene visualizzato lo stato di provisioning:


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.


downloaddbwallet

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.


downloadwallet2

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.

  1. Nella pagina Dettagli ATP, fare clic sul pulsante Azioni database. Connettersi come amministratore utente e utilizzare la password utilizzata durante la configurazione di ATP.

  2. Nel Launchpad Database Actions, fare clic sulla casella SQL. Il foglio di lavoro SQL è aperto.

  3. sqldeveloper
  4. Incollare le istruzioni riportate di seguito nel foglio di lavoro e l'icona per eseguire lo script (oppure utilizzare il pulsante F5 sulla tastiera). Queste istruzioni creano un utente (schema) con cui lavorare nelle seguenti sezioni, proprio come abbiamo fatto nel database locale:
     create user demo identified by thePassword1 default tablespace users temporary tablespace temp
    
    /
    grant connect, resource to demo 
    /
    ALTER USER DEMO QUOTA UNLIMITED ON DATA
    /

Modifica applicazione Go con dettagli di connessione ATP e Oracle Wallet

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.

Distribuisci l'applicazione Go parlando con Autonomous Database in OCI

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:


devops-oci-vm

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:

  1. Copiare il file cwallet.sso nella directory radice dell'applicazione
  2. Definire i dettagli di connessione ad Autonomous Database nei dati: server.go

È 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.


foglio di lavoro SQL

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.

  1. Aprire la pagina dei dettagli del server di build della pipeline di build nella console OCI. Aprire i dettagli per la fase di build gestita. Fare clic su Modifica.

  2. buildpsec

  3. Modificare il valore nel campo Percorso file specifiche build in /applications/data-service/build_spec.yaml, la specifica di build modificata per creare la versione estesa di myserver. Fare clic su Salva.

  4. pipeline

  5. 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/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.


pipeline

Quando la distribuzione viene aggiornata, l'API persona è disponibile in https:// /my-api/person?name=Mickey+Mouse.


istanza di computazione

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.


browser

Vai all'applicazione su OCI interagendo con Autonomous Database e il servizio di storage degli oggetti

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:

  1. Copiare il file cwallet.sso nella directory radice dell'applicazione
  2. Definire i dettagli di connessione ad Autonomous Database nei dati: server.go
  3. Modifica my-server.go: imposta il valore corretto per compartmentOCID
  4. Caricare il sito Web o l'esempio persons.json del file in un bucket del servizio di storage degli oggetti (è possibile modificare il file o caricare un file diverso con contenuti simili)

È 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:


selfileprocessed persona

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:

  1. Modifica oggetto file-processor.go: modificare il valore della costante RUN_WITH_INSTANCE_PRINCIPAL_AUTHENTICATION da false a true (questo flag è false quando viene eseguito localmente e true quando viene eseguito su un'istanza di computazione in OCI in cui vengono utilizzate l'autenticazione e l'autorizzazione del principal dell'istanza)
  2. Azioni Git: aggiungere cwallet.sso ed eseguire il commit del nuovo file insieme ai file modificati my-server.go, object-processor.go e data-service.go; eseguire il push del commit nel repository di codici OCI
  3. Assicurarsi che esista un criterio IAM che consenta alle istanze go-on-oci-instances del gruppo dinamico di leggere gli oggetti nel compartimento. Ciò consente all'applicazione in esecuzione sulla VM di richiamare il servizio di storage degli oggetti per leggere il file JSON con i record persona. L'istruzione del criterio potrebbe leggere: Consenti alle istanze go-on-oci del gruppo dinamico di leggere gli oggetti nel compartimento go-on-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 utilizzato in precedenza. Proprio come abbiamo fatto in precedenza, dobbiamo aggiornare il riferimento al file di specifica della build.

  1. Aprire la pagina dei dettagli del build-myserver della pipeline di build nella console OCI
  2. Aprire i dettagli per la fase di build gestita
  3. Fare clic su Modifica
  4. Modificare il valore nel campo Percorso del file delle specifiche di build in /applications/people-file-processor/build_spec.yaml, la specifica di build modificata per creare la versione estesa di myserver
  5. Fare clic su Salva

specifica 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:

  1. Andare ai dettagli per the-api-gateway
  2. Apri la scheda Distribuzioni
  3. Fare clic su Modifica
  4. Aprire il secondo passo per Cicli e aggiungere un nuovo instradamento.

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.


processore

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.


version2

Cosa non è mostrato nella foto ed è importante notare:

  • il file Oracle Wallet distribuito con l'applicazione (nell'artifact creato dall'origine nel repository di codici)
  • riferimento non modificabile al compartimento nell'applicazione
  • il criterio che concede l'autorizzazione all'istanza di computazione per leggere gli oggetti

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).

Conclusione

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.