Dies ist der vierte Teil einer fünfteiligen Serie über Go und Oracle Cloud Infrastructure (OCI). In dieser Serie wird beschrieben, wie Go-Anwendungen auf Oracle Cloud Infrastructure in Compute-Instanzen (VMs), auf Kubernetes containerisiert oder als serverlose Funktionen erstellt und ausgeführt werden können. Die Artikel zeigen, wie Sie das Erstellen und Deployment dieser Go-Anwendungen mit OCI DevOps automatisieren. Ein wichtiges Thema ist die Verwendung von OCI-Services aus Go-Anwendungen - sowohl auf OCI ausgeführte als auch Go-Code, der an anderer Stelle ausgeführt wird. Zu den besprochenen OCI-Services gehören Object Storage, Streaming, Key Vault und Autonomous Database.
Um diesen Artikeln zu folgen, sollten die Leser mindestens Grundkenntnisse in der Erstellung von Go-Anwendungen haben. Es wird davon ausgegangen, dass die Leser Zugang zu ihrer eigenen Go-Entwicklungsumgebung haben. Einige der Beispiele und Screenshots werden speziell VS Code als Entwicklungstool erwähnen. Es können jedoch auch andere Editoren und IDEs verwendet werden. Der Go-Code in diesen Artikeln zeigt eine Reihe von Mechanismen in ihrer einfachsten Form für maximale Klarheit und mit den geringsten Abhängigkeiten. Leser sollten keine sinnvolle Funktionalität oder produktionsreifen Code erwarten.
In dieser Serie wird beschrieben, wie Sie OCI nutzen können. Um die Beispiele auszuprobieren, müssen Leser Zugriff auf einen OCI-Mandanten mit Berechtigungen zum Erstellen der OCI-Ressourcen haben, die in diesen Artikeln beschrieben werden. Die meisten verwendeten Ressourcen sind in Aways Free Tier (Compute-Instanz, VCN, Autonomous Database, Object Storage, Logging, Resource Manager) verfügbar oder verfügen über eine kostenlose Zuteilungsebene für eine begrenzte monatliche Nutzung (Functions, API Gateway, Streaming, Vault, DevOps).
Der erste Teil dieser Serie beschreibt das Provisioning einer Compute-Instanz basierend auf dem Oracle Linux Cloud Developer-Image, das Öffnen für eingehende und ausgehende Netzwerkaktivitäten sowie das Erstellen und Ausführen einer Go-Anwendung, die HTTP-Anforderungen verarbeitet und das von der Anwendung erzeugte Logging mit OCI Logging verbindet. Teil zwei befasst sich mit der Softwareentwicklung, der Automatisierung des Builds und der Bereitstellung der Anwendung mit dem OCI DevOps-Service. Dieser Service wird verwendet, um den Go-Quellcode zu speichern, die ausführbare Anwendung zu erstellen und als bereitstellbares Artefakt zu speichern und dieses Artefakt in einer Compute-Instanz bereitzustellen. Der Artikel zeigt auch, wie Sie einen HTTP-Endpunkt für die Anwendung über ein OCI-API-Gateway bereitstellen. Im dritten Teil wird gezeigt, wie Sie serverlose Funktionen in Go erstellen und auf OCI bereitstellen. Das Go-SDK für OCI wird eingeführt - zunächst für lokale, eigenständige Go-Anwendungen und anschließend für die Verwendung aus Funktionen, wobei die Resource-Principal-Authentifizierung genutzt wird. Dieses SDK wird für die Interaktion mit dem OCI Object Storage-Service zum Erstellen von Buckets sowie zum Schreiben und Lesen von Dateien verwendet.
Im vierten Teil, den Sie gerade lesen, wird die Interaktion zwischen Ihrer Go-Anwendung und einer Oracle Database erläutert. Dabei kann es sich um eine lokale oder On-Premise-Datenbank, eine Datenbank handeln, die auf den IaaS-Instanzen eines Cloud-Anbieters ausgeführt wird, oder um eine OCI Autonomous Database. Wenn Sie das standardmäßige Go-Datenbank-/SQL-Package mit einem Treiber für Oracle Database verwenden und die erforderlichen Konfigurationsdetails an den Treiber weitergeben, stellt sich heraus, dass die Nutzung von Oracle Database von Go ganz einfach ist. Die in Teil zwei erläuterte Go-Anwendung myserver wird erweitert, um sowohl mit einer Autonomous Database-Instanz auf OCI als auch mit dem OCI Object Storage-Service zu interagieren. Die Anwendung verwendet das Go-SDK für OCI, um Dateien aus einem Bucket in Object Storage zu lesen (und anschließend zu entfernen) und Datenbankdatensätze in Autonomous Database basierend auf ihrem Inhalt zu erstellen.
Die Go-Sprache unterstützt SQL-Interaktionen mit darin integrierten relationalen Datenbanken. Die Standard-Package-Datenbank/-SQL enthält Typen und Funktionen für die Anmeldung bei Datenbanken, die Ausführung von Transaktionen, das Abbrechen eines laufenden Vorgangs usw. Dieses Package kann für die gleiche Arbeit mit einigen NoSQL-Datenbanken wie MongoDB und Couchbase verwendet werden.
Eine Go-Anwendung, die über dieses Package mit einer Datenbank interagiert, benötigt keine technischen Implementierungsdetails für ein bestimmtes Datenbankprodukt. Diese Details werden normalerweise in einem Treiber für diese Datenbank implementiert. Die Anwendung importiert den erforderlichen Treiber für die Verbindung der Datenbank und teilt der Standard-Package-Datenbank/SQL mit, welcher Treiber verwendet werden soll und welche Verbindungsdetails für die Datenbank gelten. Die meisten Interaktionen mit der Datenbank sind unabhängig von der jeweiligen Datenbanktechnologie gleich. Datensätze werden über SQL-DML-Anweisungen erstellt, aktualisiert und gelöscht, und Datensätze werden über SQL-Abfragen abgerufen. Der gesamte Prozess ist in allen Datenbanken gleich, aber der genaue SQL-Dialekt kann variieren. Dies ist wahrscheinlich das einzige echte Hindernis für das einfache Verschieben von Go-Anwendungen zwischen verschiedenen Datenbankprodukten.
Der in diesem Abschnitt beschriebene Code befindet sich im Verzeichnis /applications/go-orcl-db im Code-Repository, das dieser Artikelserie beigefügt ist.
Go-Anwendung führt SQL aus - DDL, DML und Abfrage
Das einfachste, was mit einer Oracle Database aus einer Go-Anwendung zu tun ist, ist eine einzelne Zeile abzufragen. Der dafür erforderliche Code sieht ungefähr wie folgt aus:
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)
}
Die Importanweisung macht das Datenbank-/SQL-Package verfügbar. Mit dem Handle zum sql.DB kann eine SQL-Abfrage einfach ausgeführt werden (QueryRow) und das Ergebnis kann in lokale Variablen gescannt werden. Ganz einfach, unkompliziert und unabhängig von der Datenbankmarke, mit Ausnahme der spezifischen SQL-Anweisung, die in diesem Fall den Oracle-spezifischen Systimestamp verwendet.
Lassen Sie uns vorerst nicht darüber nachdenken, woher der db-Parameter stammt. In einer kleinen Weile werden wir über Datenbanktreiber sprechen, und hier wird alles aufgedeckt.
Eine etwas interessantere Funktion, die eine Tabelle erstellt, einen Datensatz einfügt, den Datensatz abfragt, weitere Datensätze erstellt, dann alle Zeilen abfragt und schließlich die Tabelle löscht, wird hier angezeigt. Sie finden diesen Code in der Datei oracle-database-client-app.go im Code-Repository.
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)
}
}
Dieser Code ist sehr funktional. Neben den SQL-Anweisungen gibt es keine datenbankspezifischen Implementierungsdetails. Die Hälfte des Codes scheint Fehlerbehandlung zu sein. Es sollte nicht allzu schwer zu verstehen sein, wie dieser Code die Datenbank manipuliert, mit Ausnahme der Tatsache, dass es noch keine Datenbank gibt, mit der gearbeitet werden kann, und (daher) auch kein Treiber, um die Verbindung herzustellen und die Kommunikation zu handhaben. Beheben wir dies, indem Sie zunächst eine lokale Datenbank ausführen und dann der Go-Anwendung einen Treiber für die Datenbank hinzufügen.
Lokale Oracle Database ausführen
Es gibt viele verschiedene Möglichkeiten, eine lokale Oracle Database einzurichten und auszuführen. Der einfachste Weg, den ich gefunden habe, verwendet ein Docker-Containerimage, mit dem ich eine lokale Datenbank mit einer sehr einfachen und einfachen Anweisung ausführen kann:
docker run -d -p 1521:1521 -e ORACLE_PASSWORD=TheSuperSecret1509! gvenzl/oracle-xe
Dadurch wird eine Oracle Database XE 21c-Instanz ausgeführt (zumindest beim Schreiben, wenn 21c das neueste verfügbare Containerimage ist), und die Kennwörter für SYS und SYSTEM werden auf den angegebenen Wert gesetzt. Die Datenbank ist auf Port 1521 auf localhost verfügbar.
Gerald Venzl von Oracle unterhält eine Reihe von (Docker-)Containerimages, auf denen eine schlanke Version von Oracle Database ausgeführt wird, die XE-Edition, die kostenlos verwendet werden kann (bis zu 20 GB Daten und maximal 2 CPU-Threads und 2 GB RAM). Er beschreibt diese Images und wie sie in einem Artikel mit dem Titel Einführung in gvenzl/oracle-XE: Oracle Database XE Docker-Images verwendet werden.
Führen Sie die folgenden Schritte aus, um zu prüfen, ob die lokale Oracle Database hochgefahren und gestartet ist:
```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
/
```
Jetzt ist es an der Zeit, sich über die Go-Anwendung bei dieser Datenbank anzumelden.
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.
Treiber für Oracle Database hinzufügen
Es gibt keinen offiziellen Go-Treiber für Oracle Database, mindestens keinen, der von Oracle veröffentlicht oder freigegeben wurde. Die inoffizielle Liste der Go-Datenbanktreiber enthält vier Einträge für Oracle Database. Drei erfordern die Installation der Oracle-Clientbibliotheken, eine nicht. Lassen Sie uns zunächst mit dem letzten arbeiten, Go-ora, einem reinen Treiber, der die gesamte Kommunikation mit der Datenbank selbst abwickelt. Details zu go-ora finden Sie auf der go-ora Homepage unter GitHub. Anschließend werden wir uns Godror ansehen, einen Treiber, für den die Librarys installiert werden müssen, und den, der unter den Oracle Database-Treibern für Go am prominentesten zu sein scheint.
Der Go-ora-Treiber kann der Go-Anwendung hinzugefügt werden mit:
go get github.com/sijms/go-ora/v2
Dadurch wird das Package heruntergeladen und der Datei go.mod ein erforderlicher Eintrag hinzugefügt. Für mich sieht das so aus:
module oracle-database-client
go 1.16
require (
github.com/sijms/go-ora/v2 v2.4.16 // indirect
)
In einer neuen Datei mit dem Namen pure-oracle-database-client.go (obwohl Go den Namen nicht wirklich interessiert) im selben Verzeichnis wie Datei oracle-database-client-app.go verarbeitet der folgende Code die Interaktion mit der lokalen Oracle Database über go-ora. Das Treiberpaket wird importiert, und der Aufruf von sql.Open, der implizit oracle referenziert, wählt go-ora als gewünschten Treiber aus.
Der Parameter dbParams besteht aus einer Zuordnung von Konfigurationseinstellungen (einschließlich Benutzername und Kennwort), Datenbankhost und -port sowie Service-Namen, die für die Herstellung einer Verbindung verwendet werden sollen. Die Verbindungszeichenfolge wird mit diesen Elementen zusammengesetzt und im Aufruf von sql.Open verwendet. Der anschließende Aufruf von db.Ping ist der erste Versuch, die Kommunikation mit der Datenbank wirklich herzustellen. Wenn dieser Aufruf erfolgreich ist, sind wir bereit für einige echte Datenbankaktionen.
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
}
Verbindung herstellen und ausführen
Die Datenbank wird ausgeführt, und wir haben eine Funktion, die mit einem reinen Oracle Database-Treiber funktioniert. Kehren wir nun zu oracle-database-client-app.go zurück und binden es zusammen.
Fügen Sie Funktion main in dieser Datei hinzu. Sie ruft GetSqlDBWithPureDriver auf, um eine sql.DB-Instanz mit den Datenbankverbindungsdetails zu erstellen, die in Map localDB definiert sind. Ändern Sie diese Werte, um sie an der Datenbankkonfiguration anzupassen. Der Funktionsaufruf legt die DB-Variable mit *sql.DB fest, die für weitere SQL-Vorgänge verwendet werden kann.
Wenn alle Datenbankinteraktionen abgeschlossen sind, muss die Verbindung geschlossen werden, um die Ressourcen freizugeben. Um dies zu gewährleisten, wird die Verzögerung in der Funktion main unmittelbar nach dem Aufruf von GetSqlDBWithPureDriver mit dem Aufruf von db.Close() hinzugefügt. Der Aufruf der Funktion sqlOperations, der db übergibt, bringt uns zu der Funktion, die wir vor zwei Abschnitten besprochen haben, in denen die Datenbank wirklich interagiert.
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)
}
Führen Sie die Anwendung über die Befehlszeile mit go run *.go aus. Die Ausgabe sieht wie folgt aus:
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
Mit dem Treiber GoDrOr arbeiten
Eine beliebte Alternative zu go-ora ist der Go-Packagegottror (früher goracle genannt, aber aufgrund von Markenproblemen umbenannt - Oracle Corporation möchte, dass niemand oracle in seinen Namen verwendet). Dieses Package stellt auch einen Oracle Database-Treiber bereit, den Datenbank/SQL verwenden kann, wenn eine sql.Open für oracle oder godror ausgeführt wird. Im Gegensatz zu go-ora muss für dieses Package eine Oracle Instant Client-Bibliothek auf dem System installiert werden, auf dem die Go-Anwendung ausgeführt wird.
Der GoDrOr-Treiber verwendet die Oracle Database-Programmierschnittstelle für C (ODPI-C). Es handelt sich um eine Open-Source-Bibliothek mit C-Code, die von der Oracle Corporation verwaltet wird und die Verwendung gängiger Oracle Call Interface-(OCI-)Features für Oracle Database-Treiber und Benutzeranwendungen vereinfacht. Bei der Verwendung von GoDrOr müssen wir ODPI-C nicht installieren oder sich dessen bewusst sein. Die Umgebung, in der unsere Go-Anwendung ausgeführt wird, einschließlich des Treibers, muss jedoch Oracle Client-Librarys enthalten.
Der einfachste Oracle Client ist der kostenlose Oracle Instant Client (siehe die Übersichtsseite zu Oracle Instant Client). Nur das Paket "Basic" oder "Basic Light" ist erforderlich. Oracle Client-Bibliotheken sind auch in jeder Oracle Database-Installation oder in jeder vollständigen Oracle Client-Installation verfügbar. Ausführliche Installationsanweisungen für Linux finden Sie in der Oracle Database-Dokumentation für Release 21c - Database Client Installation Guide for Linux - Installing Oracle Instant Client.
ODPI-C lädt verfügbare Oracle Client-Librarys dynamisch zur Laufzeit. Die Oracle-Client-Librarys werden im selben Verzeichnis wie die ODPI-C-Library (oder Anwendungsbinärbibliothek) durchsucht. Wenn sie nicht gefunden werden, werden sie im Standardsuchpfad des Betriebssystems gesucht, z.B. Pfad unter Windows oder LD_LIBRARY_PATH unter Linux. Schließlich wird auf anderen Plattformen als Windows auch $ORACLE_HOME/lib durchsucht. Einzelheiten dazu, wie ODPI-C die Oracle-Clientbibliotheken finden kann, finden Sie in ODIP-C GitHub Home - ODPI-C Installation.
Die Änderungen, die wir an der Anwendung vornehmen müssen, um von Go-ora zu Godror zu wechseln, sind minimal.
Zunächst wird der Godror-Treiber der Go-Anwendung hinzugefügt mit:
github.com/godror/godror
Dadurch wird das Package heruntergeladen und der Datei go.mod ein erforderlicher Eintrag hinzugefügt.
Erstellen Sie als Nächstes eine neue Datei mit dem Namen godror-based-oracle-database-client.go im selben Anwendungsverzeichnis. Diese Datei ähnelt der Datei pure-oracle-database-client.go, die Details zum Herstellen einer Verbindung über den go-ora-Treiber enthält.
Der Inhalt dieser neuen Datei:
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
}
Der Import für das Godror-Paket ist anders als der Import von go-ora. Der Rest des Codes ist genau derselbe wie zuvor.
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.
Um die Anwendung nicht mehr mit go-ora zu verwenden und mit der Verwendung von godror zu beginnen, müssen wir nur eine Zeile auskommentieren oder entfernen und eine weitere Zeile in Function Main hinzufügen, indem wir GetSqlDBWithGoDrOrDriver aufrufen:
func main() {
//db := GetSqlDBWithPureDriver(localDB)
db := GetSqlDBWithGoDrOrDriver(localDB)
Führen Sie die Anwendung erneut mit go run *.go aus, und Sie finden die gleiche Ausgabe wie zuvor. Die Tatsache, dass der Oracle Instant Client jetzt beteiligt ist, ist nicht spürbar. Das Verhalten ist offenbar unverändert, obwohl es nicht-funktionale Unterschiede wie die Leistung bestimmter Operationen geben könnte.
Datenbanktransaktionsmanagement
Was aus unserer vorherigen Diskussion nicht sofort ersichtlich ist, ist, dass wir niemals tatsächlich Daten in die Datenbank übertragen haben. Alle SQL-Aktionen fanden in einer einzigen Datenbanksession statt. Die beiden DDL-Vorgänge, mit denen die Tabelle erstellt und gelöscht wurde, haben die Transaktion implizit festgeschrieben, aber keine der INSERT-Anweisungen wurde festgeschrieben. Einige Datenbanken verfügen über eine Autocommit-Einstellung - einige sogar als Standard -, die jeden DML-Vorgang in eine Transaktion verwandelt, die automatisch festgeschrieben wird. Nicht so bei Oracle Database. Um die Änderungen an Datenbankdatensätzen festschreiben zu können, müssen diese Änderungen explizit festgeschrieben oder zurückgerollt werden, falls ihre dauerhaften Auswirkungen nicht erwünscht sind. In unserer Go-Anwendung können wir mit Datenbanktransaktionen explizit arbeiten. Dazu beginnen wir eine Transaktion (sql.Tx) in einer Datenbank, führen DML-Anweisungen für diese Transaktion aus und setzen die Transaktion schließlich fest oder zurück. Zum Beispiel:
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()
Eine Go-Anwendung mit einer lokalen (oder einer traditionell verbundenen) Oracle Database zu sprechen, war nicht so schwierig. Datenbanken, die für die verschlüsselte Kommunikation von Clients konfiguriert sind, die Oracle Wallet verwenden, wie Oracle Autonomous Database-Instanzen auf OCI, sind nicht schwieriger zu interagieren. Wir müssen unseren Code so erweitern, dass er mit der Oracle Wallet-Datei funktioniert. Natürlich müssen wir eine Autonomous Database-Instanz ausführen und das zugehörige Oracle Wallet anfordern.
Kostenlose Autonomous Database auf OCI ausführen
Das Ausführen einer Autonomous Database-Instanz ist fast einfacher als das Ausführen einer lokalen Datenbank. Eine ATP-Instanz kann auf verschiedene Arten erstellt werden (einschließlich über die OCI-CLI und Terraform), aber die einfachste Methode zum ersten Mal ist wahrscheinlich über die OCI-Browserkonsole.
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.
Erstellen Sie Ihre immer kostenlose ATP-Instanz:
Geben Sie im Suchfeld der Konsole "aut" ein, navigieren Sie zu "Autonomous Database Features*, klicken Sie auf die Schaltfläche "Autonomous Database erstellen".
Geben Sie im Erstellungsformular einen Anzeigenamen (vielleicht go-on-oci-db) und einen Datenbanknamen an, wählen Sie "Transaktionsverarbeitung" als Workload-Typ aus, schalten Sie den Schalter "Immer kostenlos" auf "Aktiv" um, geben Sie ein Kennwort für ADMIN an (und merken Sie es sich gut), akzeptieren Sie den Netzwerkzugriffstyp "Sicherer Zugriff von überall", und stellen Sie sicher, dass das Kontrollkästchen "Gegenseitige TLS-(mTLS-)Authentifizierung erforderlich" aktiviert ist.
Nachdem Sie auf die Schaltfläche "Create Autonomous Database" geklickt haben, um die Datenbank zu erstellen, wird der Provisioning-Status angezeigt:
Es dauert weniger als eine Minute, bis die Datenbank verfügbar ist.
Datenbank-Wallet mit Connect-Details herunterladen
Wir benötigen das Datenbank-Wallet, das die SSL-Zertifikate enthält, die für die mTLS-Interaktion erforderlich sind. Laden Sie das Wallet für die ATP-Instanz herunter. Klicken Sie zuerst auf der Seite "ATP" in der OCI-Konsole auf die Schaltfläche "DB-Verbindung", und klicken Sie dann auf "Wallet herunterladen".
Geben Sie ein Kennwort für das Wallet an. Möglicherweise ist dies für das spätere Lesen des Wallets erforderlich. Bitte halten Sie sich auch an dieses Passwort, obwohl ich dieses Passwort für die in diesem Artikel beschriebenen Schritte nicht benötigt habe.
Speichern Sie die ZIP-Datei. Sie wird in Kürze verwendet.
Demo-Benutzeraccount in Autonomous Database erstellen
Sie können ein Demo-Benutzerkonto in der autonomen Datenbank erstellen. Gehen Sie dazu wie folgt vor:
create user demo identified by thePassword1 default tablespace users temporary tablespace temp
/
grant connect, resource to demo
/
ALTER USER DEMO QUOTA UNLIMITED ON DATA
/
Wenn die Oracle Database, mit der ich interagieren möchte, mit der Verwendung eines Oracle Wallet verbunden werden muss, muss ich den Speicherort des Dateisystems von Oracle Wallet an den Treiber übergeben. Genauer gesagt muss ich den Pfad zu dem Verzeichnis angeben, das die Datei cwallet.sso enthält, die Teil des Wallets ist. Das Wallet wird normalerweise in einem ZIP-Archiv bereitgestellt. Dieses Archiv sollte extrahiert werden (oder zumindest diese Datei sollte), und der Pfad zu dem Verzeichnis, das die Datei enthält, wird als walletLocation bezeichnet. Extrahieren Sie an dieser Stelle cwallet.sso aus der Wallet-ZIP-Datei, und verschieben Sie diese Datei in einen Speicherort, auf den von der Go-Anwendung aus zugegriffen werden kann. Möglicherweise befindet sie sich sogar im selben Verzeichnis wie die Go-Anwendung selbst. Dies ist nicht die beste Praxis für produktionsgerechte Anwendungen, aber für die Zwecke dieses Artikels wird es ausreichen.
Die Verbindungsdetails für die autonome Datenbank, die angegeben werden müssen, bestehen aus derselben Gruppe von Elementen, die zuvor für die lokale Datenbank verwendet wurden. Der Datenbankservicename befindet sich in der Datei tnsnames.ora in der Wallet-ZIP-Datei oder auf der Seite "ATP-DB-Verbindung" in der OCI-Konsole als service_name. Der Wert für die Servereigenschaft ist als Host in diesen Speicherorten verfügbar.
Wenn die Eigenschaften erfasst werden, kann die folgende Zuordnungsdefinition in der Datei oracle-database-client-app.go direkt unter localDB hinzugefügt werden:
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
}
Interaktion mit Autonomous Database mit Driver go-ora starten
Die Go-ora-Treiberkonfiguration muss ein wenig erweitert werden, um den Wallet-Speicherort in die Verbindungszeichenfolge aufzunehmen und das sichere Kommunikationsprotokoll zu konfigurieren.
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)
...
Um die Anwendung für Autonomous Database auszuführen und die TEMP_TABLE-Akrobatik in der Cloud auszuführen, müssen Sie die Hauptfunktion leicht ändern:
func main() {
db := GetSqlDBWithPureDriver(autonomousDB)
//db := GetSqlDBWithGoDrOrDriver(localDB)
...
Das heißt: Ersetzen Sie die localDB-Referenz im Aufruf von GetSqlDBWithPureDriver durch autonomousDB.
Führen Sie die Anwendung erneut mit go run *.go aus. Die Ergebnisse werden genau die gleichen wie zuvor für die lokale Datenbank sein, aber sie werden wahrscheinlich etwas länger dauern, da jetzt Latenz in jeder Datenbankinteraktion eingeführt wird.
Interaktion mit Autonomous Database mit Driver Godror starten
Der Godror-Treiber verwendet ein etwas anderes Setup für die Arbeit mit einem Oracle Wallet im Vergleich zu go-ora. Die Funktion GetSqlDBWithGoDrOrDriver in File Godror-based-oracle-database-client.go wird erweitert, um diesen Fall zu bewältigen:
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()
...
Um die Anwendung mit dem Godror-Treiber für Autonomous Database auszuführen und die TEMP_TABLE-Akrobatik in der Cloud auszuführen, müssen Sie die Hauptfunktion leicht ändern:
func main() {
//db := GetSqlDBWithPureDriver(autonomousDB)
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
...
Führen Sie die Anwendung erneut mit go run *.go aus. Die Ergebnisse werden wieder genau die gleichen sein wie beim Go-ora-Treiber, aber es scheint (zumindest in meiner Umgebung), dass Aktionen durch Go-ora wesentlich schneller sind als die gleichen Aktionen durch Godror.
Das Code Repository enthält eine Anwendung namens Data Service im Verzeichnis /applications/data-service. Diese Anwendung ist eine Erweiterung der myserver-Anwendung, mit der wir in den Artikeln eins und zwei dieser Serie gearbeitet haben. Die Anwendung verarbeitet weiterhin HTTP-Anforderungen wie zuvor und implementiert diesmal auch eine einfache Daten-API. Mit PUT-, POST- und DELETE-Anforderungen kann die Anwendung Personendatensätze in einer Tabelle namens PEOPLE in einer Oracle Database erstellen, aktualisieren und entfernen. Mit GET-Anforderungen können die aktuellen Details für jede Person abgerufen werden.
Wir werden uns zunächst die interessanten Elemente in der Anwendung ansehen und sie dann lokal ausführen. Im nächsten Schritt wird diese Anwendung auf OCI in einer Compute-Instanz ausgeführt. Sie werden feststellen, dass es nichts Besonderes an einer Anwendung gibt, die bei der Bereitstellung auf OCI eine Autonomous Database-Interaktion hat. Oder zumindest nicht bis zur nächsten Rate in dieser Serie, bei der wir OCI Key Vault verwenden, um die Oracle Wallet-Details sicher zu speichern, die dank der auf dem Instanz-Principal basierenden Autorisierung die Anwendung zur Laufzeit abrufen kann. Im Moment ist das Wallet jedoch im Quellcode-Repository enthalten und in den Build- und Deployment-Pipelines verarbeitet. Das ist keine gute Praxis und wird im nächsten Artikel korrigiert.
Nachdem die Anwendung bereitgestellt wurde, prüfen wir, ob wir mit direktem Zugriff auf die Compute-Instanz darauf zugreifen können. Um eine gute Best Practice in Bezug auf (nicht) die direkte Bereitstellung von Services anzuwenden, erweitern wir das API-Gateway dann um eine weitere Route, die zum Datenservice und insbesondere zu seinen datenbankbasierten Funktionen führt.
Die endgültige Situation, die wir am Ende dieses Abschnitts auf OCI erreichen, sieht folgendermaßen aus:
Prüfen Sie den Datenservice, und konfigurieren Sie ihn für Ihre ATP-Instanz
Dateidaten - server.go ist neu in der Anwendung. Sie enthält alle Logik für die Interaktion mit der Datenbank und die Verarbeitung von HTTP-Anforderungen an die Anwendung, die in Pfaddaten enthalten sind: DATA_PATH. Die Registrierung in Funktion Hauptfunktion der Funktion DataHandler integriert die Datenverarbeitungsfunktionen.
http.HandleFunc(DATA_PATH, DataHandler)
Die Funktion "Main" wird ebenfalls um folgende Initialisierungsschritte erweitert:
func main() {
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
defer func() {
err := db.Close()
if err != nil {
fmt.Println("Can't close connection: ", err)
}
}()
InitializeDataServer(db)
...
Zu Beginn der my-server-Anwendung wird eine Datenbankverbindung erstellt und ein Datenserver eingerichtet. Die Anwendung verwendet den Godror-Treiber. Beachten Sie, dass die Compute-Instanz, bei der es sich um das Deployment-Ziel handelt, im Oracle Linux Cloud Developer-Image erstellt (wieder in einem Teil der Serie) wurde und Oracle Instant Client vorinstalliert ist.
Zur Ausführung muss nur die folgende Anwendung hinzugefügt werden:
Anschließend können Sie die Anwendung lokal ausführen, indem Sie
go run *.go
Die Anwendung wird gestartet und meldet den Dienst.
In einem separaten Terminalfenster verwenden Sie curl-Anweisungen, um mit der Personen-API zu interagieren. Diese HTTP-Anfragen führen dazu, dass zwei Datensätze erstellt werden - für Mickey Mouse und Bugs Bunny. Der Datensatz von Mickey wird einmal aktualisiert. Beide Datensätze werden einmal abgerufen. Dann werden beide gelöscht. Die endgültige GET-Anforderung gibt keine Daten zurück.
Fühlen Sie sich frei, Curl-Anforderungen hinzuzufügen oder nicht alle auszuführen. Beispiel: Sie können im SQL-Arbeitsblatt prüfen, ob die Personen-API die erwarteten Datenbankdatensätze erstellt hat.
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 und Build zur Bereitstellung und Ausführung
Diese Variante der myserver-Anwendung ist einsatzbereit und der Code befindet sich bereits im OCI-Code-Repository DevOps, da der gesamte Code aus dem Artikelquellen-Repository auf GitHub im zweiten Artikel dieser Serie im OCI-Code-Repository festgeschrieben wurde. Sie haben jedoch die Datei cwallet.sso (das Oracle Wallet für Ihre Autonomous Database-Instanz) hinzugefügt, und Sie haben die Dateidaten server.go aktualisiert, um Details zur Datenbankverbindung anzugeben. Bevor die Build-Pipeline auf OCI zum Erstellen und späteren Deployment der Anwendung verwendet werden kann, müssen Sie zunächst die neue Datei hinzufügen, die geänderte und diese hinzugefügte Datei festschreiben und die Änderungen an das OCI-Code-Repository DevOps übergeben.
Nach diesen git add-, commit- und push-Aktionen sollte das Code Repository go-on-oci-repo Ihre cwallet.sso und die von Ihnen geänderten data-service.go enthalten.
Sie können jetzt den Build-Pipeline-Build-Server wiederverwenden, der in Artikel zwei eingerichtet wurde, als wir das erste Mal OCI DevOps Build Pipelines besprochen haben. Die aktuelle Pipeline erwartet jedoch die Build-Spezifikationsdatei am Standardspeicherort. Dies gilt nicht für die geänderte myserver-Anwendung.
Die Pipeline erstellt ein neues Artefakt - eine ZIP-Datei mit der ausführbaren Datei, die aus den Go-Quellen im Verzeichnis /applications/data-service erstellt wurde und die Wallet-Datei und das Website-Unterverzeichnis enthält. Die Pipeline löst die Deployment-Pipeline aus, die das Artefakt in die Compute-Instanz bringt, die Anwendung in das Verzeichnis /tmp/yourserver kopiert und die Anwendung ausführt. Es beginnt, auf HTTP-Anforderungen auf dem Port zu horchen, der mit dem Deployment-Pipelineparameter HTTP_SERVER_PORT angegeben wird (oder bei 8080, wenn der Parameter nicht festgelegt ist).
Sie können auf die Personen-API über die öffentliche IP-Adresse für die VM zugreifen, wenn diese noch verfügbar ist:
curl -X "GET" :8095/data?name=Mickey+Mouse
Sie können eine Route im API-Gateway erstellen, um ordnungsgemäßen öffentlichen Zugriff auf die Personen-API zu ermöglichen. Stellen Sie sicher, dass Sie der Routendefinition alle Methoden hinzufügen, die von der API verarbeitet werden.
Wenn das Deployment aktualisiert wird, ist die Personen-API unter https://
Curl und andere HTTP-Tools wie Postman können für die Interaktion mit der API verwendet werden. Dabei werden alle Methoden zum Erstellen, Aktualisieren, Abrufen und Löschen von Personendatensätzen verwendet.
Der letzte Schritt in diesem Artikel kombiniert die Interaktion mit dem OCI Object Storage-Service (im vorherigen Artikel eingeführt) mit Vorgängen auf einer Autonomous Database-Instanz in einer einzelnen Go-Anwendung, die zuerst lokal ausgeführt und dann auf einer Compute-Instanz in OCI bereitgestellt und ausgeführt und über API Gateway verfügbar gemacht wird. Die bereitgestellte Funktionalität: Senden Sie eine HTTP-GET-Anforderung mit den Namen eines Objekts und einem Bucket in Object Storage. Das Objekt muss eine JSON-Datei sein, die Daten zu Personen im folgenden Format enthält:
[
{
"name": "Jasper",
"age": 19,
"comment": "Haute Couture"
},
{
"name": "James",
"age": 3,
"comment": "Golden retriever"
}
]
Die Datei wird gelesen, und Datensätze werden in Tabelle PEOPLE in der Autonomous Database für jeden der JSON-Einträge erstellt.
Alles, was Sie zur Anwendung hinzufügen müssen, um Folgendes auszuführen:
Anschließend können Sie die Anwendung lokal ausführen, indem Sie
go run *.go
Die Anwendung wird gestartet und meldet den Dienst.
In einem separaten Terminalfenster verwenden Sie curl-Anweisungen, um mit der neuen Dateiprozessor-API für Personen zu interagieren. Eine HTTP-Anforderung muss den Namen des Buckets und das Objekt übergeben, das die zu verarbeitenden JSON-Daten enthält. Der Service ruft die Datei ab, parst deren Inhalt und erstellt oder aktualisiert Datensätze der Tabelle PEOPLE in der autonomen Datenbank.
curl "localhost:8080/people?objectName=sample-persons.json&bucketName=the-bucket"
Mit einem Aufruf der Daten-API können Sie die Datensätze prüfen:
curl localhost:8080/data?name=Martha
Dies können Sie auch im SQL Developer-Arbeitsblatt tun:
Möglicherweise ist die Funktion PeopleJSONProcessor für die Datensatzerstellung (oder -aktualisierung) in der Tabelle PEOPLE interessant. Es wird ein Oracle-spezifischer Bulk-DML-Ansatz verwendet, bei dem Arrays von Werten für jeden der Bind-Parameter übergeben und alle Datensätze in einer einzigen DML-Anweisung erstellt werden. Ziemlich effizient.
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)
}
Lassen Sie uns nun diese Anwendung zu OCI in die Compute-Instanz bringen, die wir auch im vorherigen Abschnitt verwendet haben. Einige Schritte sind zur Vorbereitung erforderlich:
Nach diesen git add-, commit- und push-Aktionen sollte das Code Repository go-on-oci-repo Ihre cwallet.sso und die von Ihnen geänderten data-service.go enthalten.
Sie können jetzt den zuvor verwendeten Build-Pipeline-Build-Myserver wiederverwenden. Wie bereits erwähnt, müssen wir die Referenz auf die Build-Spezifikationsdatei aktualisieren.
Build-Ausführung starten. Legen Sie eine neue Version für den Parameter MYSERVER_VERSION fest, falls gewünscht.
Die Pipeline erstellt ein neues Artefakt - eine ZIP-Datei mit der ausführbaren Datei, die aus den Go-Quellen im Verzeichnis /applications/people-file-processor erstellt wurde und die Wallet-Datei und das Website-Unterverzeichnis enthält. Die Pipeline löst die Deployment-Pipeline aus, die das Artefakt in die Compute-Instanz bringt, die Anwendung in das Verzeichnis /tmp/yourserver kopiert und die Anwendung ausführt. Es beginnt, auf HTTP-Anforderungen auf dem Port zu horchen, der mit dem Deployment-Pipelineparameter HTTP_SERVER_PORT angegeben wird (oder bei 8080, wenn der Parameter nicht festgelegt ist).
Sie können auf die API über die öffentliche IP-Adresse für die VM zugreifen, sofern diese noch verfügbar ist. Es ist besser, eine Route im API-Gateway hinzuzufügen, um Zugriff auf den Personaldateiprozessor zu ermöglichen:
Definieren Sie den Pfad als /personnel-file-handler. Die GET-Methode sollte unterstützt werden. Der Typ der Route ist HTTP. Die URL lautet: http://<Öffentliche IP für Compute-Instanz>:8095/Personen. Prüfen Sie den Wert der HTTP_SERVER_PORT in der Deployment-Pipeline deploy-myserver-on-go-app-vm. Wenn der Wert nicht auf 8095 gesetzt ist, ändern Sie den URL-Wert so, dass die richtige Portnummer für die Anwendung verwendet wird.
Klicken Sie auf Next (Weiter) und dann auf Save changes (Änderungen speichern). Es dauert einen Moment, bis das Deployment des API-Gateways aktualisiert wird. Danach kann der Service, der eine Datei aus einem Object Storage-Bucket liest, den JSON-Inhalt verarbeitet und Datensätze in der Tabelle PEOPLE in der Autonomous Database-Instanz für die Einträge in dieser Datei erstellt, mit einer einfachen HTTP-GET-Anforderung an https://<public endpoind von API Gateway>/my-api/personnel-file-handler ausgelöst werden?objectName=sample-persons.json&bucketName=the-bucket.
Die Auswirkung des Aufrufs kann über die Personen-API geprüft werden, die Datensätze aus derselben Tabelle in derselben Datenbank liest: https://<public endpoind of API Gateway>/my-api/person?name=Jasper.
Das End-to-End-Bild dessen, was jetzt auf OCI bereitgestellt wird, wird in der nächsten Abbildung gezeigt.
Was nicht im Bild gezeigt wird und ist wichtig zu beachten:
Im nächsten Artikel sehen wir uns OCI Key Vault an, einen Service, der eine viel bessere Möglichkeit bietet, Oracle Wallet zu speichern und der Anwendung zur Deployment-Zeit (oder sogar zur Laufzeit) zur Verfügung zu stellen.
In diesem Artikel wurde gezeigt, wie Sie mit einer Oracle Database von Go-Anwendungen interagieren können, entweder eine herkömmliche oder die autonome Art. Wir haben gesehen, wie eine Verbindung von einer Go-Anwendung zu einer lokalen Oracle Database - sowie zu einer Autonomous Database - mit einem Treiber und möglicherweise unterstützenden Librarys hergestellt wird und wie DDL- und DML-SQL-Vorgänge mit diesen Datenbanken bequem von der Go-Anwendung aus durchgeführt werden können. Die Verwendung eines Oracle Wallet zur ordnungsgemäßen Verwaltung von (autonomen) Datenbankzugangsdaten wurde erörtert. Eine Anwendung, die auf einer OCI Compute-Instanz ausgeführt wird, über das Go-SDK für OCI mit dem Object Storage Service interagiert und die Autonomous Database - automatisch über OCI DevOps bereitgestellt - bearbeitet, stellte das Finale des Artikels bereit.
Der fünfte und letzte Artikel dieser Serie enthält zwei weitere OCI-Services, mit denen Go-Anwendungen interagieren können: OCI Streaming - ein High-Volume-Streaming-Nachrichtenbroker, der entkoppelte Interaktionen zwischen verschiedenen Microservices und anderen Komponenten ermöglicht - und OCI Key Vault zur Verwaltung von Secrets wie Oracle Wallet mit Datenbankzugangsdaten. In diesem Artikel wird auch eine dritte Art von Anwendungsplattform neben VM und serverloser Funktion in Form des verwalteten OCI Kubernetes Enginers (OKE) vorgestellt. Außerdem wird gezeigt, wie DevOps Deployment Pipelines unsere Go-Anwendungen automatisiert in OKE bereitstellen können.