Esta es la cuarta parte de una serie de cinco partes sobre Go y Oracle Cloud Infrastructure (OCI). En esta serie se analiza cómo las aplicaciones Go se pueden crear y ejecutar en Oracle Cloud Infrastructure en instancias informáticas (VM), en contenedores en Kubernetes o como funciones sin servidor. Los artículos muestran cómo automatizar la creación y el despliegue de estas aplicaciones Go mediante OCI DevOps. Un tema importante es cómo utilizar los servicios de OCI de las aplicaciones Go: tanto las que se ejecutan en OCI como el código Go que se ejecuta en otro lugar. Los servicios de OCI tratados incluyen Object Storage, Streaming, Key Vault y Autonomous Database.
Para seguir estos artículos, los lectores deben tener al menos conocimientos básicos sobre cómo crear aplicaciones Go. Se supone que los lectores tienen acceso a su propio entorno de desarrollo Go. Algunos de los ejemplos y capturas de pantalla mencionarán específicamente VS Code como herramienta de desarrollo. Sin embargo, también se pueden utilizar otros editores e IDE. El código Go presentado en estos artículos demuestra una serie de mecanismos en su forma más simple para la máxima claridad y con las menos dependencias. Los lectores no deben esperar una funcionalidad significativa ni un código listo para la producción.
En esta serie se describe cómo pasar a OCI. Para probar los ejemplos, los lectores deberán tener acceso a un arrendamiento de OCI con permisos para crear los recursos de OCI que se tratan en estos artículos. La mayoría de los recursos utilizados están disponibles en la cuenta gratuita de Aways (instancia informática, VCN, Autonomous Database, Object Storage, Logging, Resource Manager) o tienen un nivel de asignación gratuita para un uso mensual limitado (funciones, gateway de API, flujo, almacén, DevOps).
La primera parte de esta serie describe el aprovisionamiento de una instancia informática basada en la imagen de Oracle Linux Cloud Developer, su apertura para la actividad de red entrante y saliente, y la creación y ejecución de una aplicación Go que atienda solicitudes HTTP y conecte el registro producido por la aplicación al registro de OCI. La segunda parte se ocupa de la ingeniería de software, la automatización de la creación y el despliegue de la aplicación con el servicio OCI DevOps. Este servicio se utiliza para almacenar el código fuente de Go, crear el ejecutable de la aplicación y almacenarlo como artefacto desplegable, desplegando ese artefacto en una instancia informática. En el artículo también se muestra cómo exponer un punto final HTTP para la aplicación a través de un gateway de API de OCI. La tercera parte muestra cómo crear funciones sin servidor en Go y desplegarlas en OCI. Se presenta el SDK de Go para OCI, primero para aplicaciones Go locales e independientes y, posteriormente, para su uso desde funciones, aprovechando la autenticación de entidad de recurso. Este SDK se utiliza para interactuar con el servicio OCI Object Storage para crear cubos y escribir y leer archivos.
La cuarta parte, que está leyendo en este momento, analiza la interacción entre la aplicación Go y una instancia de Oracle Database. Puede ser una base de datos local o local, una base de datos que se ejecute en algunas instancias IaaS del proveedor de servicios en la nube o una instancia de OCI Autonomous Database. Al utilizar el paquete SQL/base de datos de Go estándar con un controlador para Oracle Database y proporcionar al controlador los detalles de configuración necesarios, resulta que aprovechar cualquier instancia de Oracle Database de Go es bastante sencillo. El servidor myserver de la aplicación Go que se trata en la segunda parte se amplía para interactuar con una instancia de Autonomous Database en OCI y el servicio OCI Object Storage. La aplicación utiliza el SDK de Go para OCI para leer archivos (y eliminarlos posteriormente) de un cubo en Object Storage y crear registros de base de datos en Autonomous Database en función de su contenido.
El lenguaje Go tiene soporte para interacciones SQL con bases de datos relacionales integradas en él. La base de datos/sql de paquetes estándar incluye tipos y funciones para conectarse a bases de datos, ejecutar transacciones, cancelar una operación en curso y mucho más. Este mismo paquete se puede utilizar para trabajar de la misma forma con algunas bases de datos NoSQL como MongoDB y Couchbase.
Una aplicación Go que interactúa con una base de datos a través de este paquete no necesita tener detalles de implantación técnica para un producto de base de datos específico. Estos detalles se suelen implantar en un controlador para esa base de datos. La aplicación importa el controlador necesario para que la base de datos se conecte y le indica a la base de datos/sql del paquete estándar qué controlador utilizar y cuáles son los detalles de conexión para la base de datos. La mayor parte de la interacción con la base de datos es la misma, independientemente de la tecnología de base de datos específica; los registros se crean, actualizan y suprimen mediante sentencias SQL DML, y los registros se recuperan mediante consultas SQL. El proceso general es el mismo en todas las bases de datos, pero el dialecto SQL exacto puede variar. Este es probablemente el único obstáculo real para mover fácilmente aplicaciones Go entre diferentes productos de base de datos.
El código que se trata en esta sección se encuentra en el directorio /applications/go-orcl-db en el repositorio de código que acompaña a esta serie de artículos.
La aplicación Go realiza SQL: DDL, DML y consulta
Lo más sencillo que se puede hacer con una instancia de Oracle Database desde una aplicación Go es consultar una sola fila. El código requerido para esto se ve algo así: paquete principal
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)
}
La sentencia import hace que el paquete database/sql esté disponible. Al utilizar el manejador para sql.DB, se puede ejecutar fácilmente una consulta SQL (QueryRow) y el resultado se puede explorar en variables locales. Sencilla y sencilla, independiente de la marca de la base de datos, excepto por la sentencia SQL específica, que en este caso utiliza el registro de hora de sistema específico de Oracle.
Por ahora, no nos detengamos en dónde viene el parámetro db. En poco tiempo hablaremos de controladores de base de datos, y ahí es donde todo se revelará.
Aquí se muestra una función un poco más interesante que crea una tabla, inserta un registro, consulta el registro, crea más registros y, a continuación, consulta todas las filas y, por último, borra la tabla. Este código se encuentra en el archivo oracle-database-client-app.go del repositorio de código.
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)
}
}
Este código es de naturaleza bastante funcional. Aparte de las sentencias SQL, no hay detalles de implantación específicos de la base de datos. La mitad del código parece ser el manejo de errores. No debería ser demasiado difícil entender cómo este código manipula la base de datos, excepto por el hecho de que todavía no hay ninguna base de datos con la que trabajar, y (por lo tanto) tampoco hay ningún controlador para realizar la conexión y manejar la comunicación. Supongamos esto ejecutando primero una base de datos local y, a continuación, agregando un controlador a la base de datos en la aplicación Go.
Ejecutar Oracle Database local
Hay muchas formas diferentes de poner en marcha una instancia local de Oracle Database. La forma más sencilla que he encontrado utiliza una imagen de contenedor de Docker que me permite ejecutar una base de datos local con una sentencia muy simple y directa:
docker run -d -p 1521:1521 -e ORACLE_PASSWORD=TheSuperSecret1509! gvenzl/oracle-xe
Esto ejecuta una instancia de Oracle Database XE 21c (al menos, eso es lo que hace en el momento de escribir, cuando 21c es la última imagen de contenedor disponible) y define las contraseñas para SYS y SYSTEM en el valor indicado. La base de datos está disponible en el puerto 1521 en localhost.
Gerald Venzl, de Oracle, mantiene una serie de imágenes de contenedor (Docker) que ejecutan una versión delgada de Oracle Database, la edición XE, que es gratuita (hasta 20 GB de datos y que utiliza un máximo de 2 threads de CPU y 2 GB de RAM). Describe estas imágenes y cómo utilizarlas en un artículo titulado Introducción a las imágenes de gvenzl/oracle-XE: Oracle Database XE Docker.
Siga estos pasos para verificar que la instancia de Oracle Database local está activa y en ejecución:
```console
docker exec -it <container identifier=""> /bin/bash
``` </container>
```console
sqlplus system/TheSuperSecret1509! as sysdba
```
```sql
create user demo identified by demo default tablespace users temporary tablespace temp
/
grant connect, resource to demo
/
```
Ahora, es el momento de conectarse a esta base de datos desde la aplicación 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.
Agregar un controlador para Oracle Database
No hay ningún controlador Go oficial para Oracle Database, al menos no uno publicado o respaldado por Oracle. La lista no oficial de controladores de base de datos Go tiene cuatro entradas para Oracle Database. Tres requieren la instalación de las bibliotecas de cliente de Oracle y una no. Primero vamos a trabajar con ese último, llamado go-ora, un controlador puro que por sí mismo maneja toda la comunicación con la base de datos. Los detalles sobre go-ora están disponibles en la página de inicio de go-ora en GitHub. Posteriormente también veremos godror, un controlador que requiere la instalación de las bibliotecas y el que parece más destacado entre los controladores de Oracle Database para Go.
El controlador go-ora se puede agregar a la aplicación Go con:
go get github.com/sijms/go-ora/v2
Esto descarga el paquete y agrega una entrada necesaria al archivo go.mod. Para mí eso se ve así:
module oracle-database-client
go 1.16
require (
github.com/sijms/go-ora/v2 v2.4.16 // indirect
)
En un nuevo archivo denominado pure-oracle-database-client.go (aunque Go no se preocupa realmente por el nombre) en el mismo directorio que el archivo oracle-database-client-app.go, el siguiente código maneja la interacción con la instancia local de Oracle Database mediante go-ora. El paquete de controlador se importa y la llamada a sql.Open, que hace referencia implícitamente a oracle, selecciona go-ora como el controlador que desee.
El parámetro dbParams consta de una asignación de valores de configuración (incluidos el nombre de usuario y la contraseña), el host y el puerto de la base de datos y el nombre de servicio que se va a utilizar para realizar una conexión. La cadena de conexión se compone mediante estos elementos y se utiliza en la llamada a sql.Open. La llamada posterior a db.Ping es el primer intento de establecer realmente las comunicaciones con la base de datos. Cuando esta llamada se realiza correctamente, estamos preparados para algunas acciones de base de datos reales.
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
}
Conexión y ejecución
La base de datos se está ejecutando y tenemos una función que funciona con un controlador de Oracle Database puro. Ahora, volvamos a oracle-database-client-app.go y atémoslo juntos.
Agregue la función principal en este archivo. Llama a GetSqlDBWithPureDriver para crear una instancia sql.DB mediante los detalles de conexión de base de datos definidos en la asignación localDB. Modifique estos valores para que se alineen con la configuración de la base de datos. La llamada de función define la variable de base de datos con *sql.DB, que se puede utilizar para operaciones SQL adicionales.
Cuando se hayan completado todas las interacciones con la base de datos, la conexión se debe cerrar para liberar los recursos. Para asegurarse de que esto se hace, el aplazamiento en la función principal inmediatamente después de la llamada a GetSqlDBWithPureDriver se agrega con la llamada a db.Close(). La llamada a la función sqlOperations que pasa la base de datos nos lleva a la función que discutimos hace dos secciones donde la base de datos está realmente interactuada.
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)
}
Ejecute la aplicación desde la línea de comandos con go run *.go. La salida tendrá el siguiente aspecto:
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
Trabajar con el controlador GoDrOr
Una alternativa popular a go-ora es el Godror del paquete Go (anteriormente llamado goracle, pero renombrado debido a problemas de marcas comerciales: Oracle Corporation no desea que nadie use oracle en sus nombres). Este paquete también proporciona un controlador de Oracle Database que Database/SQL puede utilizar cuando se realiza sql.Open para oracle o godror. Este paquete, a diferencia de go-ora, requiere que se instale una biblioteca de Oracle Instant Client en el sistema que ejecuta la aplicación Go.
El controlador GoDrOr utiliza la interfaz de programación de Oracle Database para C (ODPI-C). Es una biblioteca de código abierto de código C - mantenido por Oracle Corporation - que simplifica el uso de las características comunes de Oracle Call Interface (OCI) para los controladores de Oracle Database y las aplicaciones de usuario. Al utilizar GoDrOr no es necesario instalar ODPI-C ni ser consciente de su existencia. Sin embargo, el entorno en el que se ejecuta la aplicación Go, incluido el controlador, debe contener bibliotecas de cliente de Oracle.
El cliente de Oracle más sencillo es el Oracle Instant Client gratuito (consulte la página de visión general de Oracle Instant Client). Solo se requiere el paquete "Basic" o "Basic Light". Las bibliotecas de Oracle Client también están disponibles en cualquier instalación de Oracle Database o en cualquier instalación completa de Oracle Client. Puede encontrar instrucciones de instalación detalladas para Linux en la Documentación de Oracle Database para la versión 21c - Guía de instalación de Database Client para Linux - Instalación de Oracle Instant Client.
ODPI-C carga dinámicamente las bibliotecas de cliente de Oracle disponibles en tiempo de ejecución. Las bibliotecas de cliente de Oracle se buscan en el mismo directorio que la biblioteca ODPI-C (o binario de aplicación). Si no se encuentran, se buscan en la ruta de búsqueda del sistema operativo estándar, por ejemplo, PATH en Windows o LD_LIBRARY_PATH en Linux. Por último, en plataformas distintas de Windows, también se busca $ORACLE_HOME/lib. Para obtener más información sobre cómo garantizar que ODPI-C pueda encontrar las bibliotecas de cliente de Oracle, consulte Inicio de ODIP-C GitHub - Instalación de ODPI-C.
Los cambios que tenemos que hacer en la aplicación para cambiar de usar go-ora a godror son mínimos.
En primer lugar, el controlador de Godror se agrega a la aplicación Go con:
github.com/godror/godror
Esto descarga el paquete y agrega una entrada necesaria al archivo go.mod.
A continuación, cree un nuevo archivo denominado godror-based-oracle-database-client.go en el mismo directorio de aplicaciones, que es muy similar a pure-oracle-database-client.go, que contiene detalles para la conexión mediante el controlador go-ora.
El contenido de este nuevo archivo:
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
}
La importación para el paquete Godror es diferente en comparación con la importación de Go-ora. El resto del código es exactamente el mismo que antes.
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.
Finalmente, para que la aplicación deje de usar go-ora y comience a usar godror, solo necesitamos comentar o eliminar una línea y agregar otra en la función principal, llamando a GetSqlDBWithGoDrOrDriver:
func main() {
//db := GetSqlDBWithPureDriver(localDB)
db := GetSqlDBWithGoDrOrDriver(localDB)
Vuelva a ejecutar la aplicación con go run *.go y encontrará la misma salida que antes. El hecho de que Oracle Instant Client esté implicado ahora no es perceptible. El comportamiento aparentemente no cambia, a pesar de que podría haber diferencias no funcionales como el rendimiento de ciertas operaciones.
Gestión de transacciones de base de datos
Lo que no es inmediatamente obvio de nuestra discusión anterior es que nunca hemos comprometido datos en la base de datos. Todas las acciones SQL se han realizado en una sola sesión de base de datos. Las dos operaciones DDL que han creado y borrado la tabla han confirmado implícitamente la transacción, pero ninguna de las sentencias insert se ha confirmado. Algunas bases de datos tienen una configuración de confirmación automática, algunas incluso como su valor por defecto, que convierte cada operación DML en una transacción que se confirma automáticamente. No es así con Oracle Database. Para confirmar los cambios realizados en los registros de la base de datos, estos cambios se deben confirmar explícitamente o se debe realizar un rollback en caso de que sus efectos duraderos no sean deseables después de todo. En nuestra aplicación Go podemos trabajar con transacciones de base de datos de forma explícita: iniciando una transacción (sql.Tx) en una base de datos, ejecutando sentencias DML en esa transacción y, finalmente, confirmando o realizando un rollback de la transacción. Por ejemplo:
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()
Hacer que una aplicación Go hable con una Oracle Database local (o con cualquier otra conexión tradicional) no fue tan difícil. Las bases de datos configuradas para comunicaciones cifradas de clientes que utilizan Oracle Wallet, como las instancias de Oracle Autonomous Database en OCI, ya no son más difíciles de interactuar. Necesitamos ampliar nuestro código para trabajar con el archivo de Oracle Wallet y, por supuesto, tenemos que ejecutar una instancia de Autonomous Database y adquirir Oracle Wallet asociado.
Ejecute Autonomous Database gratis en OCI
Ejecutar una instancia de Autonomous Database es casi más sencillo que ejecutar una base de datos local. Se puede crear una instancia de ATP de varias formas (incluso a través de la CLI de OCI y Terraform), pero el método más sencillo por primera vez es probablemente a través de la consola del explorador de 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.
Vamos a crear su instancia de ATP siempre gratis:
Escriba aut en el cuadro de búsqueda de la consola, vaya a *Características de Autonomous Database*, haga clic en el botón Crear instancia de Autonomous Database.
En el formulario de creación, proporcione un nombre mostrado (tal vez go-on-oci-db) y un nombre de base de datos, seleccione Transaction Processing como tipo de carga de trabajo, cambie el conmutador Always Free a Active, proporcione una contraseña para ADMIN (y recuerde bien), acepte el acceso seguro de tipo de acceso de red desde cualquier lugar y asegúrese de que la casilla de control Require mutual TLS (mTLS) authentication está marcada.
Después de pulsar el botón Crear Autonomous Database para crear la base de datos, se presenta el estado de aprovisionamiento:
La base de datos tarda menos de un minuto en estar disponible.
Descargar cartera de base de datos con detalles de conexión
Necesitamos la cartera de base de datos que contiene los certificados SSL necesarios para la interacción mTLS. Descargue la cartera para la instancia de ATP. En primer lugar, haga clic en el botón Conexión a base de datos de la página ATP de la consola de OCI y, a continuación, haga clic en Descargar cartera.
Proporcione una contraseña para la cartera; puede que sea necesaria para leerla más adelante. Espere a esta contraseña también, aunque no he necesitado esta contraseña para los pasos descritos en este artículo.
Guarde el archivo zip, lo utilizaremos pronto.
Creación de una cuenta de usuario de demostración en Autonomous Database
Puede que desee crear una cuenta de usuario de demostración en la base de datos autónoma. Para ello, siga estos pasos:
create user demo identified by thePassword1 default tablespace users temporary tablespace temp
/
grant connect, resource to demo
/
ALTER USER DEMO QUOTA UNLIMITED ON DATA
/
Cuando es necesario conectar la instancia de Oracle Database con la que quiero interactuar con el uso de Oracle Wallet, necesito transferir la ubicación del sistema de archivos de Oracle Wallet al controlador. Más concretamente, necesito especificar la ruta de acceso al directorio que contiene el archivo cwallet.sso que forma parte de la cartera. La cartera se suele proporcionar en un archivo zip. Este archivo se debe extraer (o al menos este archivo debe) y la ruta al directorio que contiene el archivo se denominará walletLocation. En este punto, extraiga cwallet.sso del archivo zip de cartera y mueva este archivo a una ubicación a la que se pueda acceder desde la aplicación Go. Puede que incluso esté en el mismo directorio que la propia aplicación Go. Esta no es la mejor práctica para aplicaciones de grado de producción, pero para los fines de este artículo será suficiente.
Los detalles de conexión de la base de datos autónoma que se deben proporcionar constan del mismo juego de elementos utilizados anteriormente para la base de datos local. El nombre del servicio de base de datos se puede encontrar en el archivo tnsnames.ora del archivo zip de cartera o en la página Conexión de base de datos ATP de la consola de OCI como service_name. El valor de la propiedad del servidor está disponible como host en estas ubicaciones.
Cuando se recopilan las propiedades, se puede agregar la siguiente definición de mapa en el archivo oracle-database-client-app.go, justo debajo de 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
}
Interactúe con Autonomous Database mediante Driver go-ora
La configuración del controlador go-ora debe ampliarse un poco para incluir la ubicación de la cartera en la cadena de conexión y configurar el protocolo de comunicación segura.
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)
...
Para ejecutar la aplicación en Autonomous Database y realizar sus acrobacias TEMP_TABLE en la nube, necesitamos cambiar ligeramente la función principal:
func main() {
db := GetSqlDBWithPureDriver(autonomousDB)
//db := GetSqlDBWithGoDrOrDriver(localDB)
...
Es decir: sustituya la referencia localDB de la llamada a GetSqlDBWithPureDriver por autonomousDB.
Ahora vuelva a ejecutar la aplicación con go run *.go. Los resultados serán exactamente los mismos que antes en la base de datos local, pero probablemente tardarán un poco más en producirse, ya que ahora se introduce la latencia en cada una de las interacciones de la base de datos.
Interactúe con Autonomous Database mediante Driver godror
El controlador godror utiliza una configuración ligeramente diferente para trabajar con Oracle Wallet en comparación con go-ora. La función GetSqlDBWithGoDrOrDriver del archivo godror-based-oracle-database-client.go se amplía para manejar este 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()
...
Para ejecutar la aplicación con el controlador godror en Autonomous Database y realizar sus acrobacias TEMP_TABLE en la nube, necesitamos cambiar ligeramente la función principal:
func main() {
//db := GetSqlDBWithPureDriver(autonomousDB)
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
...
Ahora vuelva a ejecutar la aplicación con go run *.go. Los resultados serán exactamente los mismos que con el controlador go-ora, pero parece (al menos en mi entorno) que las acciones a través de go-ora son sustancialmente más rápidas que las mismas acciones a través de godror.
El repositorio de código contiene una aplicación denominada data-service, en el directorio /applications/data-service. Esta aplicación es una extensión de la aplicación myserver con la que trabajamos en los artículos uno y dos de esta serie. La aplicación aún maneja las solicitudes HTTP como antes, y esta vez también implementa una API de datos simple. Mediante las solicitudes PUT, POST y DELETE, la aplicación se puede utilizar para crear, actualizar y eliminar registros de persona de una tabla denominada PEOPLE en Oracle Database. Mediante las solicitudes GET, se pueden recuperar los detalles actuales de cualquier persona.
Primero echaremos un vistazo breve a los elementos interesantes de la aplicación y, a continuación, los ejecutaremos localmente. El siguiente paso es hacer que esta aplicación se ejecute en OCI en una instancia informática. Descubrirá que no hay nada muy especial en una aplicación que tenga interacción con Autonomous Database cuando se trata de un despliegue en OCI. O, al menos, no hasta la siguiente entrega de esta serie en la que utilizaremos OCI Key Vault para mantener de forma segura los detalles de Oracle Wallet que, gracias a la autorización basada en principal de instancia, la aplicación puede recuperar en tiempo de ejecución. Sin embargo, por ahora, la cartera se incluye en el repositorio de código fuente y se procesa en los pipelines de creación y despliegue. Esa no es una buena práctica y será rectificada en el próximo artículo.
Una vez desplegada la aplicación, verificamos si podemos acceder a ella con acceso directo a la instancia informática. Para aplicar una buena práctica sobre (no) exponer los servicios directamente al público, ampliamos el gateway de API con una ruta más que conduce al servicio de datos y, específicamente, sus capacidades basadas en bases de datos.
La situación final que logramos en OCI al final de esta sección es la siguiente:
Inspeccionar el servicio de datos y configurar su instancia de ATP
Datos de archivo: server.go es nuevo en la aplicación. Contiene toda la lógica para interactuar con la base de datos y manejar cualquier solicitud HTTP a la aplicación que entra en los datos de ruta; DATA_PATH. El registro en la función principal de la función DataHandler integra las capacidades de manejo de datos.
http.HandleFunc(DATA_PATH, DataHandler)
La función principal también se amplía con estos pasos de inicialización:
func main() {
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
defer func() {
err := db.Close()
if err != nil {
fmt.Println("Can't close connection: ", err)
}
}()
InitializeDataServer(db)
...
Al inicio de la aplicación de mi servidor, se crea una conexión de base de datos y se configura el servidor de datos. La aplicación utiliza el controlador Godror. Tenga en cuenta que hacemos uso del hecho de que la instancia informática que es el destino de despliegue se ha creado (de nuevo en la primera parte de la serie) en la imagen de Oracle Linux Cloud Developer y tiene Oracle Instant Client preinstalado.
Todas las aplicaciones que se deben agregar para poder ejecutarse son:
A continuación, puede ejecutar la aplicación localmente mediante
go run *.go
La aplicación se inicia y se informa para el servicio.
En una ventana de terminal independiente, puede utilizar sentencias curl para interactuar con la API de persona. Estas solicitudes HTTP harán que se creen dos registros: para Mickey Mouse y Bugs Bunny. El registro de Mickey se actualiza una vez. Ambos registros se recuperan una vez. A continuación, se suprimen ambos. La solicitud GET final no devuelve ningún dato.
Siéntase libre de agregar solicitudes curl o no ejecutar todas. Puede comprobar, en la hoja de trabajo de SQL, por ejemplo, si la API de persona ha creado los registros de base de datos esperados.
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
Confirmar, transferir y crear para desplegar y ejecutar
Esta variante de la aplicación myserver está lista para su uso y el código ya está en el repositorio de código DevOps de OCI, ya que todo el código del repositorio de origen de artículo en GitHub se ha confirmado en el repositorio de código de OCI en el segundo artículo de esta serie. Sin embargo, ha agregado el archivo cwallet.sso (Oracle Wallet para la instancia de Autonomous Database) y ha actualizado los datos del archivo server.go para proporcionar los detalles de conexión a la base de datos. Para que el pipeline de compilación se pueda utilizar en OCI para crear y desplegar posteriormente la aplicación, primero debe agregar el nuevo archivo, confirmar tanto el archivo cambiado como el agregado y transferir los cambios al repositorio de código DevOps de OCI.
Después de estas acciones de adición, confirmación y transferencia de git, el repositorio de código go-on-oci-repo debe contener cwallet.sso y los datos-service.go que ha modificado.
Ahora puede reutilizar el servidor de compilación de pipeline de compilación que se configuró en el artículo dos cuando hablamos por primera vez de los pipelines de compilación DevOps de OCI. Sin embargo, el pipeline actual espera que el archivo de especificación de compilación esté en la ubicación por defecto y eso no lo hará para la aplicación myserver modificada.
El pipeline producirá un nuevo artefacto: un archivo zip con el ejecutable creado a partir de los orígenes Go en el directorio /applications/data-service y que contiene el archivo de cartera y el subdirectorio del sitio web. El pipeline disparará el pipeline de despliegue que llevará el artefacto a la instancia informática, copiará la aplicación en el directorio /tmp/yourserver y ejecutará la aplicación. Empieza a recibir solicitudes HTTP en el puerto especificado por el parámetro de pipeline de despliegue HTTP_SERVER_PORT (o en 8080 si el parámetro no está definido).
Puede acceder a la API de persona en la dirección IP pública de la máquina virtual, si aún está expuesta:
curl -X "GET" <public ip="" for="" compute="" instance="">:8095/data?name=Mickey+Mouse
</public>
Puede crear una ruta en API Gateway para proporcionar un acceso público adecuado a la API de persona. Asegúrese de agregar todos los métodos que maneja la API a la definición de ruta.
Cuando se actualiza el despliegue, la API de persona está disponible en https://
Curl y otras herramientas HTTP como Postman se pueden utilizar para interactuar con la API, utilizando todos los métodos para crear, actualizar, recuperar y eliminar registros de personas.
El paso final de este artículo combina la interacción con el servicio OCI Object Storage (introducido en el artículo anterior) con operaciones en una instancia de Autonomous Database en una única aplicación Go que se ejecuta primero localmente y, a continuación, se despliega y ejecuta en una instancia informática de OCI y se expone a través de API Gateway. La funcionalidad proporcionada: envíe una solicitud HTTP GET con los nombres de un objeto y un cubo en Object Storage; el objeto debe ser un archivo JSON que contenga datos de personas con el siguiente formato:
[
{
"name": "Jasper",
"age": 19,
"comment": "Haute Couture"
},
{
"name": "James",
"age": 3,
"comment": "Golden retriever"
}
]
El archivo se leerá y los registros se crearán en la tabla PEOPLE de Autonomous Database para cada una de las entradas de JSON.
Todo lo que necesita agregar a la aplicación para ejecutar:
A continuación, puede ejecutar la aplicación localmente mediante
go run *.go
La aplicación se inicia y se informa para el servicio.
En una ventana de terminal independiente, puede utilizar sentencias curl para interactuar con la nueva API del procesador de archivos de Persons. Una solicitud HTTP debe transferir el nombre del cubo y el objeto que contiene los datos JSON que se van a procesar. El servicio recuperará el archivo, analizará su contenido y creará o actualizará los registros de la tabla PEOPLE en la base de datos autónoma.
curl "localhost:8080/people?objectName=sample-persons.json&bucketName=the-bucket"
Mediante una llamada a la API de datos, puede inspeccionar los registros de datos:
curl localhost:8080/data?name=Martha
Y puede hacer lo mismo en la hoja de trabajo de SQL Developer:
Puede que le interese la función PeopleJSONProcessor, que gestiona la creación (o actualización) de registros en la tabla PEOPLE. Utiliza un enfoque DML en bloque específico de Oracle (sintaxis soportada por el controlador godror) donde se transfieren matrices de valores para cada uno de los parámetros de enlace y todos los registros se crean en una sola sentencia DML. Muy eficiente.
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)
}
Ahora vamos a llevar esta aplicación a OCI, a la instancia informática que también hemos utilizado en la sección anterior. Se necesitan algunos pasos como preparación:
Después de estas acciones de adición, confirmación y transferencia de git, el repositorio de código go-on-oci-repo debe contener cwallet.sso y los datos-service.go que ha modificado.
Ahora puede reutilizar el servidor de compilación de pipeline de compilación que hemos utilizado anteriormente. Como hicimos anteriormente, tenemos que actualizar la referencia al archivo de especificación de compilación.
Inicie una ejecución de compilación. Defina una nueva versión para el parámetro MYSERVER_VERSION si lo desea.
El pipeline producirá un nuevo artefacto: un archivo zip con el ejecutable creado a partir de los orígenes Go en el directorio /applications/people-file-processor y que contiene el archivo de cartera y el subdirectorio del sitio web. El pipeline disparará el pipeline de despliegue que llevará el artefacto a la instancia informática, copiará la aplicación en el directorio /tmp/yourserver y ejecutará la aplicación. Empieza a recibir solicitudes HTTP en el puerto especificado por el parámetro de pipeline de despliegue HTTP_SERVER_PORT (o en 8080 si el parámetro no está definido).
Puede acceder a la API en la dirección IP pública de la máquina virtual, si aún está expuesta. Es mejor agregar una ruta en el gateway de API para exponer el acceso al procesador de archivos de personal:
Defina la ruta como /personnel-file-handler. Se debe admitir el método GET. El tipo de ruta es HTTP. La URL es: http://<IP pública para instancia informática>:8095/people. Compruebe el valor de HTTP_SERVER_PORT en el pipeline de despliegue deploy-myserver-on-go-app-vm. Si no está definido en 8095, modifique el valor de URL para utilizar el número de puerto adecuado para la aplicación.
Pulse Next (Siguiente) y, a continuación, Save changes (Guardar cambios). El despliegue de API Gateway tardará unos minutos en refrescarse. Una vez que lo ha hecho, el servicio que lee un archivo de un cubo de Object Storage, procesa el contenido JSON y crea registros en la tabla PEOPLE de la instancia de Autonomous Database para las entradas de este archivo se puede disparar con una solicitud HTTP GET simple a https://<public endpoind of API Gateway>/my-api/personnel-file-handler?objectName=sample-persons.json&bucketName=the-bucket.
El efecto de realizar la llamada se puede inspeccionar a través de la API de persona que lee registros de esa misma tabla en esa misma base de datos: https://<public endpoind of API Gateway>/my-api/person?name=Jasper.
La imagen completa de lo que ahora se despliega en OCI se muestra en la siguiente figura.
Lo que no se muestra en la imagen y es importante tener en cuenta:
En el siguiente artículo analizamos OCI Key Vault, un servicio que ofrece una forma mucho mejor de almacenar Oracle Wallet y ponerlo a disposición de la aplicación en el momento del despliegue (o incluso en tiempo de ejecución).
En este artículo se muestra cómo se puede interactuar con una instancia de Oracle Database desde aplicaciones Go, ya sea tradicional o autónoma. Hemos visto cómo se realiza una conexión desde una aplicación Go a una instancia local de Oracle Database, así como a una instancia de Autonomous Database, utilizando un controlador y posiblemente soportando bibliotecas, y cómo se pueden realizar operaciones SQL DDL y DML con estas bases de datos desde la comodidad de la aplicación Go. Se analizó el uso de Oracle Wallet para una gestión adecuada de credenciales de base de datos (autónomas). Una aplicación que se ejecuta en una instancia informática de OCI, que interactúa a través del SDK de Go para OCI con el servicio Object Storage y manipula la instancia de Autonomous Database, desplegada automáticamente a través de OCI DevOps, proporcionó el final del artículo.
El quinto y último artículo de esta serie agrega dos servicios de OCI más con los que las aplicaciones Go pueden interactuar: OCI Streaming, un agente de mensajes de flujo de alto volumen que permite interacciones desacopladas entre diferentes microservicios y otros componentes, y OCI Key Vault para gestionar secretos como Oracle Wallet con credenciales de base de datos. Este artículo también presenta un tercer tipo de plataforma de aplicaciones junto a VM y función sin servidor, en la forma del iniciador de Kubernetes de OCI (OKE) gestionado, y muestra cómo los pipelines de despliegue de DevOps pueden desplegar nuestras aplicaciones Go en OKE de forma automatizada.
Esta página ha sido traducida por una máquina.