Trabalhando em aplicativos Go com o Oracle Database e o Oracle Cloud Autonomous Database

Esta é a quarta parte de uma série de cinco partes sobre Go e Oracle Cloud Infrastructure (OCI). Esta série discute como os aplicativos Go podem ser criados e executados no Oracle Cloud Infrastructure em Instâncias de Computação (VMs), em contêineres no Kubernetes ou como Funções sem servidor. Os artigos mostram como automatizar a criação e a implantação desses aplicativos Go usando o OCI DevOps. Um tópico importante é como usar serviços do OCI de aplicativos Go - tanto aqueles em execução no OCI quanto no código Go em execução em outro lugar. Os serviços do OCI discutidos incluem Armazenamento de Objetos, Streaming, Key Vault e Autonomous Database.

caminho a seguir-em-oci-artigo

Para acompanhar esses artigos, os leitores devem ter pelo menos conhecimento básico de como criar aplicativos Go. Supõe-se que os leitores tenham acesso ao seu próprio ambiente de desenvolvimento Go. Alguns dos exemplos e capturas de tela mencionarão especificamente o VS Code como ferramenta de desenvolvimento. No entanto, outros editores e IDEs também podem ser usados. O código Go apresentado nestes artigos demonstra uma série de mecanismos em sua forma mais simples para máxima clareza e com o mínimo de dependências. Os leitores não devem esperar uma funcionalidade significativa ou um código pronto para produção.

Esta série descreve como começar a usar o OCI. Para experimentar os exemplos, os leitores precisarão ter acesso a uma tenancy do OCI com permissões para criar os recursos do OCI discutidos nesses artigos. A maioria dos recursos usados está disponível no Aways Free Tier (Instância de Computação, VCN, Autonomous Database, Object Storage, Logging, Resource Manager) ou tem uma camada de alocação gratuita para uso mensal limitado (Funções, Gateway de API, Streaming, Vault, DevOps).

A primeira parte dessa série descreve o provisionamento de uma Compute Instance com base na imagem do Oracle Linux Cloud Developer, abrindo-a para atividade de rede de entrada e saída e criando e executando um aplicativo Go que atende a solicitações HTTP e conectando o log produzido pelo aplicativo ao OCI Logging. A segunda parte trata de engenharia de software, automação da criação e implantação do aplicativo com o serviço OCI DevOps. Esse serviço é usado para armazenar o código-fonte Go, criar o executável do aplicativo e armazená-lo como artefato implantável, implantando esse artefato em uma Instância de Computação. O artigo também mostra como expor um ponto final HTTP para o aplicativo por meio de um Gateway de API do OCI. A terceira parte mostra como criar funções sem servidor no Go e implantá-las no OCI. O Go SDK para OCI foi introduzido - primeiro para aplicativos Go locais e independentes e, posteriormente, para uso em funções, aproveitando a autenticação do controlador de recursos. Esse SDK é usado para interagir com o serviço OCI Object Storage para criar buckets e gravar e ler arquivos.

A quarta parte, que você está lendo agora, discute a interação entre seu aplicativo Go e um Oracle Database. Pode ser um banco de dados local ou local, um banco de dados em execução em algumas instâncias IaaS do fornecedor de nuvem ou um OCI Autonomous Database. Usando o pacote padrão de banco de dados/sql Go com um driver para o Oracle Database e alimentando os detalhes de configuração necessários para o driver, verifica-se que aproveitar qualquer Oracle Database do Go é bastante simples. O myserver do aplicativo Go discutido na segunda parte é estendido para interagir com uma instância do Autonomous Database no OCI e no serviço OCI Object Storage. O aplicativo usa o Go SDK para OCI para ler arquivos (e subsequentemente removê-los) de um bucket no Object Storage e criar registros de banco de dados no Autonomous Database com base em seu conteúdo.

Aplicativo Go Local falando com o Banco de Dados Local

A linguagem Go tem suporte para interações SQL com bancos de dados relacionais incorporados a ela. O banco de dados/sql do pacote padrão inclui tipos e funções para conexão com bancos de dados, execução de transações, cancelamento de uma operação em andamento e muito mais. Esse mesmo pacote pode ser usado para trabalhar da mesma forma com alguns bancos de dados NoSQL, como MongoDB e Couchbase.

Um aplicativo Go que interage com um banco de dados por meio deste pacote não precisa ter detalhes de implementação técnica para um produto de banco de dados específico. Esses detalhes geralmente são implementados em um driver para esse banco de dados. O aplicativo importa o driver necessário para que o banco de dados se conecte e informa ao banco de dados/sql do pacote padrão qual driver usar e quais são os detalhes da conexão do banco de dados. A maior parte da interação com o banco de dados é a mesma, independentemente da tecnologia de banco de dados específica; os registros são criados, atualizados e excluídos por meio de instruções SQL DML, e os registros são recuperados por meio de consultas SQL. O processo geral é o mesmo entre bancos de dados, mas o dialeto SQL exato pode variar. Este é provavelmente o único obstáculo real para mover facilmente aplicativos Go entre diferentes produtos de banco de dados.

O código discutido nesta seção está localizado no diretório /applications/go-orcl-db no repositório de código que acompanha esta série de artigos.

O Go Application executa SQL: DDL, DML e Consulta

A coisa mais simples a fazer com um Oracle Database de um aplicativo Go é consultar uma única linha. O código necessário para isso é semelhante a este: package main

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

A instrução import disponibiliza o pacote database/sql. Usando o identificador para sql.DB, uma consulta SQL pode ser facilmente executada (QueryRow) e o resultado pode ser verificado em variáveis locais. Muito simples, direto e independente da marca do banco de dados, exceto pela instrução SQL específica, que, nesse caso, usa o systimestamp específico da Oracle.

Por enquanto, não vamos nos deter sobre de onde vem o parâmetro db. Em pouco tempo vamos falar sobre drivers de banco de dados, e é aí que tudo será revelado.

Uma função um pouco mais interessante que cria uma tabela, insere um registro, consulta o registro, cria para mais registros e, em seguida, consulta todas as linhas e, finalmente, elimina a tabela é mostrada aqui. Você encontrará esse código no arquivo oracle-database-client-app.go no repositório 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 é de natureza bastante funcional. Além das instruções SQL, não há detalhes de implementação específicos do banco de dados. Metade do código parece ser um tratamento de erros. Não deve ser muito difícil entender como esse código manipula o banco de dados, exceto pelo fato de que não há banco de dados com o qual trabalhar ainda e (portanto) também não há driver para fazer a conexão e lidar com a comunicação. Vamos corrigir isso executando primeiro um banco de dados local e, em seguida, adicionando um driver ao banco de dados no aplicativo Go.

Executar o Oracle Database local

Há muitas maneiras diferentes de ativar e executar um Oracle Database local. A maneira mais fácil que encontrei usa uma imagem de contêiner do Docker que me permite executar um banco de dados local com uma instrução muito simples e direta:

docker run -d -p 1521:1521 -e ORACLE_PASSWORD=TheSuperSecret1509! gvenzl/oracle-xe

Isso executa uma instância do Oracle Database XE 21c (pelo menos, é isso que ela faz no momento da gravação, quando 21c é a imagem de contêiner disponível mais recente) e define as senhas de SYS e SYSTEM como o valor indicado. O banco de dados está disponível na porta 1521 no localhost.

Gerald Venzl, da Oracle, mantém uma série de Imagens de Contêiner (Docker) que executam uma versão slim do Oracle Database, a edição XE, que é gratuita para uso (até 20 GB de dados e usando no máximo 2 threads de CPU e 2 GB de RAM). Ele descreve essas imagens e como usá-las em um artigo intitulado Apresentando imagens do gvenzl/oracle-XE: Oracle Database XE Docker.

Siga estas etapas para verificar se o Oracle Database local está ativo e em execução:

  1. Encontre o identificador do contêiner com docker ps | grep gvenzl. Em seguida, abra um shell Bash no contêiner:
    ```console
    
    docker exec -it <container identifier=""> /bin/bash
    ``` </container>
  2. Agora, conecte-se ao banco de dados e execute instruções SQL:
    ```console
    
    sqlplus system/TheSuperSecret1509! as sysdba
    ```
  3. Crie um usuário (esquema) com o qual trabalhar nas seções a seguir, por exemplo, um usuário demo inocente:
    ```sql
    
    create user demo identified by demo default tablespace users temporary tablespace temp 
    /
    grant connect, resource to demo 
    /
    ```

Agora, é hora de se conectar a esse banco de dados a partir do aplicativo 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.

Adicionar um Driver para o Oracle Database

Não há driver oficial Go para o Oracle Database, pelo menos nenhum publicado ou endossado pela Oracle. A lista não oficial de drivers de banco de dados Go tem quatro entradas para o Oracle Database. Três requerem a instalação das bibliotecas do Oracle Client e uma não. Vamos primeiro trabalhar com esse último, chamado go-ora, um driver puro que, por si só, lida com toda a comunicação com o banco de dados. Detalhes sobre o go-ora estão disponíveis na página inicial do go-ora em GitHub. Posteriormente, também analisaremos o godror, um driver que exige que as bibliotecas sejam instaladas e o que parece mais proeminente entre os drivers do Oracle Database para Go.

O driver go-ora pode ser adicionado ao aplicativo Go com:

go get github.com/sijms/go-ora/v2

Isso faz download do pacote e adiciona uma entrada obrigatória ao arquivo go.mod. Para mim, que se parece com isso:

module oracle-database-client


go 1.16

require (
github.com/sijms/go-ora/v2 v2.4.16 // indirect
)

Em um novo arquivo chamado pure-oracle-database-client.go (embora o Go realmente não se importe com o nome) no mesmo diretório do arquivo oracle-database-client-app.go, o código a seguir trata a interação com o Oracle Database local por meio do go-ora. O pacote de drivers é importado e a chamada para sql.Open, que faz referência implícita à oracle, seleciona go-ora como o driver de sua escolha.

O parâmetro dbParams consiste em um mapa de definições de configuração (incluindo o nome de usuário e a senha), o host e a porta do banco de dados e o nome do serviço a ser usado para fazer uma conexão. A string de conexão é composta usando esses elementos e usada na chamada para sql.Open. A chamada subsequente para db.Ping é a primeira tentativa de realmente estabelecer comunicações com o banco de dados. Quando essa chamada for bem-sucedida, estaremos prontos para algumas ações reais do banco de dados.

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
}

Conectando e executando

O banco de dados está em execução e temos uma função que funciona com um driver puro do Oracle Database. Agora, vamos retornar ao oracle-database-client-app.go e uni-lo.

Adicione a função principal neste arquivo. Ele chama GetSqlDBWithPureDriver para criar uma instância sql.DB usando os detalhes da conexão do banco de dados definidos no mapa localDB. Modifique esses valores para se alinhar à configuração do banco de dados. A chamada de função define a variável db com *sql.DB, que pode ser usada para outras operações SQL.

Quando todas as interações do banco de dados forem concluídas, a conexão deverá ser fechada para liberar os recursos. Para garantir que isso seja feito, o diferimento na função principal imediatamente após a chamada para GetSqlDBWithPureDriver é adicionado com a chamada para db.Close(). A chamada para a função sqlOperations que passa db nos leva à função que discutimos há duas seções, na qual o banco de dados realmente interage.

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

Execute o aplicativo a partir da linha de comando usando go run *.go. A saída será semelhante a:

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

Trabalhando com o driver GoDrOr

Uma alternativa popular para go-ora é o Godror do pacote Go (anteriormente chamado goracle, mas renomeado por causa de problemas de marca registrada - a Oracle Corporation não quer que ninguém use oracle em seus nomes). Este pacote também fornece um driver do Oracle Database que o banco de dados/SQL pode usar quando um sql.Open é executado para oracle ou godror. Este pacote, ao contrário do go-ora, requer que uma biblioteca do Oracle Instant Client seja instalada no sistema que executa o aplicativo Go.

  • Instalação da Biblioteca do Oracle Instant Client

    O driver GoDrOr usa a Interface de Programação do Oracle Database para C (ODPI-C). É uma biblioteca de código-fonte aberto do código C - mantida pela Oracle Corporation - que simplifica o uso de recursos comuns do OCI (Oracle Call Interface) para drivers do Oracle Database e aplicativos do usuário. Ao usar GoDrOr, não precisamos instalar o ODPI-C nem estar cientes de sua existência. No entanto, o ambiente no qual nosso aplicativo Go, incluindo o driver, está sendo executado precisa conter bibliotecas do Oracle Client.

    O Oracle Client mais simples é o Oracle Instant Client gratuito (consulte a página de visão geral do Oracle Instant Client). Somente o pacote "Basic" ou "Basic Light" é necessário. As bibliotecas do Oracle Client também estão disponíveis em qualquer instalação do Oracle Database ou instalação completa do Oracle Client. As instruções detalhadas de instalação do Linux podem ser encontradas na Documentação do Oracle Database para Versão 21c - Guia de Instalação do Database Client para Linux - Instalação do Oracle Instant Client.

    A ODPI-C carrega dinamicamente as bibliotecas do Oracle Client disponíveis no runtime. As bibliotecas do Oracle Client são pesquisadas no mesmo diretório que a biblioteca ODPI-C (ou binário do aplicativo). Se eles não forem encontrados, eles serão pesquisados no caminho de pesquisa do sistema operacional padrão, por exemplo, PATH no Windows ou LD_LIBRARY_PATH no Linux. Finalmente, em plataformas diferentes do Windows, $ORACLE_HOME/lib também é pesquisado. Para obter detalhes sobre como garantir que a ODPI-C possa localizar as bibliotecas do Oracle Client, verifique o ODIP-C GitHub Home - Instalação da ODPI-C.

  • Modificando o aplicativo Go para trabalhar com GoDrOr

    As mudanças que temos que fazer no aplicativo, a fim de mudar de usar go-ora para godror são mínimas.

    Primeiro, o driver godror é adicionado ao aplicativo Go com:

    github.com/godror/godror

    Isso faz download do pacote e adiciona uma entrada obrigatória ao arquivo go.mod.

    Em seguida, crie um novo arquivo chamado godror-based-oracle-database-client.go no mesmo diretório de aplicativos - que é muito semelhante ao pure-oracle-database-client.go, que contém detalhes para conexão por meio do driver go-ora.

    O conteúdo deste novo arquivo:

    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
    }

    A importação para o pacote godror é diferente em comparação com a importação de go-ora. O restante do código é exatamente o mesmo 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 fazer o aplicativo parar de usar go-ora e começar a usar godror, basta comentar ou remover uma linha e adicionar outra na função principal, chamando GetSqlDBWithGoDrOrDriver:

    func main() {
    
    //db := GetSqlDBWithPureDriver(localDB)
    db := GetSqlDBWithGoDrOrDriver(localDB)

    Execute o aplicativo novamente com go run *.go e você encontrará a mesma saída de antes. O fato de o Oracle Instant Client estar envolvido agora não é perceptível. O comportamento é aparentemente inalterado, embora possa haver diferenças não funcionais, como o desempenho de certas operações.

Gerenciamento de Transação do Banco de Dados

O que não é imediatamente óbvio de nossa discussão anterior é que nunca comprometemos realmente os dados com o banco de dados. Todas as ações SQL ocorreram em uma única sessão do banco de dados. As duas operações DDL que criaram e eliminaram a tabela confirmaram implicitamente a transação, mas nenhuma das instruções de inserção foi submetida a commit. Alguns bancos de dados têm uma definição de commit automático - alguns até mesmo como padrão - que transforma todas as operações DML em uma transação que é submetida a commit automaticamente. Não é assim com o Oracle Database. Para fazer commit das alterações feitas nos registros do banco de dados, essas alterações devem ser submetidas a commit explicitamente - ou submetidas a rollback caso seus efeitos duradouros não sejam desejáveis. Em nosso aplicativo Go, podemos trabalhar com transações de banco de dados explicitamente - iniciando uma transação (sql.Tx) em um banco de dados, executando instruções DML nessa transação e, finalmente, confirmando ou revertendo a transação. Por exemplo:

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 conversando com o OCI Autonomous Database

Fazer um aplicativo Go falar com um Oracle Database local (ou tradicionalmente conectado) não foi tão difícil. Os bancos de dados configurados para comunicações criptografadas de clientes que usam a Oracle Wallet - como instâncias do Oracle Autonomous Database no OCI - não são mais difíceis de interagir. Precisamos estender nosso código para trabalhar com o arquivo da Oracle Wallet e, claro, precisamos executar uma instância do Autonomous Database e adquirir a Oracle Wallet associada.

Execute o Autonomous Database gratuito na OCI

Executar uma Instância do Autonomous Database é quase mais simples do que executar um banco de dados local. Uma instância ATP pode ser criada de várias maneiras (incluindo por meio da CLI do OCI e do Terraform), mas o método mais direto da sua primeira vez provavelmente é por meio da console do navegador do 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 criar sua instância ATP sempre gratuita:

Digite aut na caixa de pesquisa da console, navegue até *Autonomous Database Features*, clique no botão Create Autonomous Database.


caminho de entrada-oci-artigo-4-nav-autônomo-db

No formulário de criação, forneça um nome de exibição (talvez go-on-oci-db) e o nome do banco de dados, selecione Processamento de Transação como o tipo de carga de trabalho, alterne a opção Always Free para ativa, forneça uma senha para ADMIN (e lembre-se bem disso), aceite o acesso Seguro do Tipo de Acesso à Rede de qualquer lugar e certifique-se de que a caixa de seleção Exigir autenticação mútua de TLS (mTLS) esteja marcada.


criar-bd autônomo

Depois de pressionar o botão Criar Autonomous Database para criar o banco de dados, o status de provisionamento será apresentado:


provisionamentoatp

Leva menos de um minuto para que o banco de dados esteja disponível.

Fazer Download da Wallet do Banco de Dados com Detalhes do Connect

Precisamos da wallet do banco de dados que contém os certificados SSL necessários para a interação mTLS. Faça download da wallet da instância ATP. Primeiro clique no botão Conexão do BD na página ATP na Console do OCI e, em seguida, clique em Fazer Download da wallet.


downloaddbwallet

Forneça uma senha para a wallet; isso pode ser necessário para ler a wallet posteriormente. Aguarde essa senha também, embora eu não tenha precisado dessa senha para as etapas descritas neste artigo.

Salve o arquivo zip. Nós o usaremos em breve.


downloadwallet2

Criar conta de usuário de Demonstração no Autonomous Database

Talvez você queira criar uma conta de usuário de demonstração no banco de dados autônomo. Isso pode ser feito com estas etapas:

  1. Na página de detalhes ATP, clique no botão Database Actions. Conecte-se como administrador do usuário e use a senha que você usou ao configurar o ATP.

  2. No Launchpad do Database Actions, clique no bloco SQL. A Planilha SQL é aberta.

  3. sqldevelope
  4. Cole as instruções abaixo na planilha e no ícone para executar o script (ou use o botão F5 no teclado). Essas instruções criam um usuário (esquema) para trabalhar nas seguintes seções, da mesma forma que fizemos no banco de dados local:
    create user demo identified by thePassword1 default tablespace users temporary tablespace temp
    
    /
    grant connect, resource to demo 
    /
    ALTER USER DEMO QUOTA UNLIMITED ON DATA
    /

Modificar aplicativo Go com detalhes de conexão ATP e Oracle Wallet

Quando o Oracle Database com o qual eu quero interagir precisa estar conectado com o uso de um Oracle Wallet, preciso informar o local do sistema de arquivos do Oracle Wallet para o driver. Mais precisamente, preciso especificar o caminho para o diretório que contém o arquivo cwallet.sso que faz parte da wallet. A wallet geralmente é fornecida em um arquivo zip. Este arquivo deve ser extraído (ou pelo menos este arquivo deve) e o caminho para o diretório que contém o arquivo é o que será chamado de walletLocation. Nesse ponto, extraia cwallet.sso do arquivo zip da wallet e mova esse arquivo para um local acessível do aplicativo Go - ele pode até estar no mesmo diretório do próprio aplicativo Go. Esta não é a melhor prática para aplicações de grau de produção, mas para os fins deste artigo será suficiente.

Os detalhes da conexão do banco de dados autônomo que precisam ser fornecidos consistem no mesmo conjunto de elementos usados anteriormente para o banco de dados local. O nome do serviço de banco de dados pode ser encontrado no arquivo tnsnames.ora no arquivo zip da wallet ou na página Conexão do BD ATP na Console do OCI como service_name. O valor da propriedade do servidor está disponível como host nesses locais.

Quando as propriedades são reunidas, a seguinte definição de mapa pode ser adicionada ao arquivo oracle-database-client-app.go, diretamente em 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
}

Interação Go com o Autonomous Database usando o go-ora do Driver

A configuração do driver go-ora precisa ser estendida um pouco para incluir o local da wallet na string de conexão e configurar o protocolo de comunicação seguro.

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 executar o aplicativo no Autonomous Database e fazer suas acrobacias TEMP_TABLE na nuvem, precisamos alterar um pouco a função principal:

func main() {

db := GetSqlDBWithPureDriver(autonomousDB)
//db := GetSqlDBWithGoDrOrDriver(localDB)
...

Ou seja: substitua a referência localDB na chamada para GetSqlDBWithPureDriver por autonomousDB.

Agora execute o aplicativo novamente com go run *.go. Os resultados serão exatamente os mesmos de antes em relação ao banco de dados local, mas provavelmente levarão mais tempo para serem produzidos, pois agora a latência é introduzida em cada uma das interações do banco de dados.

Interação com o Autonomous Database usando o Driver godror

O driver godror usa uma configuração um pouco diferente para trabalhar com uma Oracle Wallet em comparação com o go-ora. A função GetSqlDBWithGoDrOrDriver no arquivo godror-based-oracle-database-client.go é estendida para tratar deste 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 executar o aplicativo com o driver godror no Autonomous Database e fazer suas acrobacias TEMP_TABLE na nuvem, precisamos alterar um pouco a função principal:

func main() {

//db := GetSqlDBWithPureDriver(autonomousDB)
db := GetSqlDBWithGoDrOrDriver(autonomousDB)
...

Agora execute o aplicativo novamente com go run *.go. Os resultados serão novamente exatamente os mesmos do driver go-ora, mas parece (pelo menos no meu ambiente) que as ações através do go-ora são substancialmente mais rápidas do que as mesmas ações através do godror.

Implantar o Aplicativo Go conversando com o Autonomous Database no OCI

O Repositório de Código contém um aplicativo chamado data-service, no diretório /applications/data-service. Esta aplicação é uma extensão da aplicação myserver com a qual trabalhamos nos artigos um e dois desta série. O aplicativo ainda lida com solicitações HTTP como antes, e desta vez também implementa uma API de dados simples. Usando solicitações PUT, POST e DELETE, o aplicativo pode ser usado para criar, atualizar e remover registros de pessoa de uma tabela chamada PEOPLE em um Oracle Database. Usando solicitações GET, os detalhes atuais de qualquer pessoa podem ser recuperados.

Vamos primeiro dar uma breve olhada nos elementos interessantes do aplicativo e, em seguida, executá-lo localmente. A próxima etapa é fazer com que esse aplicativo seja executado no OCI em uma instância do serviço Compute. Você descobrirá que não há nada de muito especial em um aplicativo que tenha interação com o Autonomous Database quando se trata de implantação no OCI. Ou, pelo menos não até a próxima parcela desta série em que usaremos o OCI Key Vault para manter com segurança os detalhes da Oracle Wallet que - graças à autorização baseada no controlador de instâncias - o aplicativo pode ser recuperado no runtime. Por enquanto, no entanto, a wallet é incluída no repositório de código-fonte e processada nos pipelines de build e implantação. Isso não é uma boa prática e será corrigido no próximo artigo.

Depois que o aplicativo é implantado, verificamos se podemos acessá-lo com acesso direto à instância de computação. Para aplicar uma boa prática em relação a (não) expor serviços publicamente diretamente, estendemos o Gateway de API com mais uma rota que leva ao serviço de dados e, especificamente, aos recursos baseados em banco de dados.

A situação final que alcançamos na OCI no final desta seção é a seguinte:


devops-oci-vm

Inspecione o serviço de dados e configure sua instância ATP

Dados do arquivo - server.go é novo no aplicativo. Ele contém toda a lógica para interagir com o banco de dados e tratar qualquer solicitação HTTP para o aplicativo que entra nos dados de caminho; o DATA_PATH. O registro na função principal da função DataHandler integra os recursos de tratamento de dados.

http.HandleFunc(DATA_PATH, DataHandler)

A função principal também é estendida com estas etapas de inicialização:

func main() {

db := GetSqlDBWithGoDrOrDriver(autonomousDB)
defer func() {
  err := db.Close()
  if err != nil {
      fmt.Println("Can't close connection: ", err)
  }
}()
InitializeDataServer(db)
...

No início do aplicativo do meu servidor, uma conexão de banco de dados é criada e o servidor de dados é configurado. O aplicativo usa o driver godror. Observe que usamos o fato de que a instância do serviço Compute que é o destino de implantação foi criada (na parte de uma das séries) na imagem do Oracle Linux Cloud Developer e tem o Oracle Instant Client pré-instalado.

Todas as necessidades do aplicativo adicionadas para execução são:

  1. Copie o arquivo cwallet.sso para o diretório raiz do aplicativo
  2. Defina os detalhes da conexão do Autonomous Database nos dados - server.go

Você pode, então, executar localmente o aplicativo, usando

go run *.go

O aplicativo é iniciado e relata o dever.

Em uma janela de terminal separada, você usa instruções curl para interagir com a API da Pessoa. Essas solicitações HTTP farão com que dois registros sejam criados - para Mickey Mouse e Bugs Bunny. O registro do Mickey é atualizado uma vez. Ambos os registros são recuperados uma vez. Em seguida, ambos são excluídos. A solicitação GET final não retorna dados.

Sinta-se à vontade para adicionar solicitações de curl ou não executar todas. Você pode verificar, na Planilha SQL, por exemplo, se a API Pessoa criou os registros de banco de dados esperados.


planilha 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 que levam à Implantação e Execução

Essa variante do aplicativo myserver está pronta para uso e o código já está no Repositório de Código DevOps do OCI, pois todo o código do repositório de origem do artigo em GitHub foi submetido a commit no Repositório de Código do OCI no segundo artigo desta série. No entanto, você adicionou o arquivo cwallet.sso (a Oracle Wallet para sua instância do Autonomous Database) e atualizou os dados do arquivo - server.go para fornecer detalhes da conexão do banco de dados. Para que o pipeline de build possa ser usado no OCI para criar e implantar subsequentemente o aplicativo, primeiro você precisa adicionar o novo arquivo, confirmar o arquivo alterado e esse arquivo adicionado e enviar as alterações ao Repositório de Código DevOps do OCI.

Após essas ações de adição, commit e push do git, o go-on-oci-repo do Repositório de Código deve conter seu cwallet.sso e os dados-service.go que você modificou.

Agora você pode reutilizar o build-myserver do pipeline de build que foi configurado no artigo dois quando discutimos os Pipelines de Build do OCI DevOps pela primeira vez. No entanto, o pipeline atual espera o arquivo de especificação de build no local padrão, e isso não acontecerá para o aplicativo myserver corrigido.

  1. Abra a página de detalhes do build-myserver do Pipeline de Build na Console do OCI. Abra os detalhes do estágio de build gerenciado. Clique em Editar.

  2. segurança do build

  3. Altere o valor no campo Caminho do arquivo de especificação de Build para /applications/data-service/build_spec.yaml, a especificação de build que é modificada para criar a versão estendida do myserver. Clique em Salvar.

  4. pipeline

  5. Inicie uma execução de build. Defina uma nova versão para o parâmetro MYSERVER_VERSION, se quiser.

O pipeline produzirá um novo artefato - um arquivo zip com o executável criado a partir das origens Go no diretório /applications/data-service e contendo o arquivo de wallet e o subdiretório do site. O pipeline acionará o pipeline de implantação que trará o artefato para a instância do serviço Compute, copiará o aplicativo para o diretório /tmp/yourserver e executará o aplicativo. Ele começa a fazer listening de solicitações HTTP na porta especificada pelo parâmetro de pipeline de implantação HTTP_SERVER_PORT (ou em 8080 se o parâmetro não estiver definido).

Você poderá acessar a API Pessoa no endereço IP público da VM, se isso ainda estiver exposto:

curl -X "GET"  <public ip="" for="" compute="" instance="">:8095/data?name=Mickey+Mouse

 </public>

Você pode criar uma rota no Gateway de API para fornecer acesso público adequado à API da Pessoa. Certifique-se de adicionar todos os métodos que a API trata à definição de rota.


pipeline

Quando a implantação é atualizada, a API da Pessoa fica disponível em https:// /my-api/person?name=Mickey+Mouse.


instância de computação

O Curl e outras ferramentas HTTP, como o Postman, podem ser usados para interagir com a API, usando todos os métodos para criar, atualizar, recuperar e excluir registros de pessoa.


navegador

Aplicativo Go no OCI interagindo com o Autonomous Database e o serviço Object Storage

A etapa final deste artigo combina a interação com o serviço OCI Object Storage (introduzido no artigo anterior) com operações em uma instância do Autonomous Database em um único aplicativo Go que é primeiro executado localmente e depois implantado e executado em uma instância de computação no OCI e exposto por meio do Gateway de API. A funcionalidade fornecida: envie uma solicitação HTTP GET com os nomes de um objeto e um bucket no Object Storage; o objeto deve ser um arquivo JSON que contenha dados sobre pessoas no seguinte formato:

[

{
  "name": "Jasper",
  "age": 19,
  "comment": "Haute Couture"
},
{
  "name": "James",
  "age": 3,
  "comment": "Golden retriever"
}
]

O arquivo será lido e os registros serão criados na tabela PEOPLE no Autonomous Database para cada uma das entradas JSON.

Tudo o que você precisa adicionar ao aplicativo para executar:

  1. Copie o arquivo cwallet.sso para o diretório raiz do aplicativo
  2. Defina os detalhes da conexão do Autonomous Database nos dados - server.go
  3. Edit my-server.go - defina o valor correto para compartmentOCID
  4. Faça upload do site/amostra do arquivo-persons.json para um bucket no serviço Object Storage (sinta-se livre para editar o arquivo ou fazer upload de outro arquivo com conteúdo semelhante)

Você pode, então, executar localmente o aplicativo, usando

go run *.go

O aplicativo é iniciado e relata o dever.

Em uma janela de terminal separada, você usa instruções curl para interagir com a nova API do processador de arquivos Persons. Uma solicitação HTTP deve informar o nome do bucket e o objeto que contém os dados JSON a serem processados. O serviço extrairá o arquivo, analisará seu conteúdo e criará ou atualizará os registros da tabela PEOPLE no banco de dados autônomo.

curl "localhost:8080/people?objectName=sample-persons.json&bucketName=the-bucket"

Usando uma chamada para a API de dados, você pode inspecionar os registros de dados:

curl localhost:8080/data?name=Martha

E você pode fazer o mesmo na Planilha do SQL Developer:


personnelfileprocessado

Você pode estar interessado na função PeopleJSONProcessor, que trata da criação (ou atualização) de registro na tabela PEOPLE. Ele usa uma abordagem Bulk DML específica da Oracle - sintaxe suportada pelo driver godror - em que matrizes de valores para cada um dos parâmetros de bind são transmitidos e todos os registros são criados em uma única instrução DML. Muito 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)
}

Agora vamos trazer esse aplicativo para o OCI, para a instância de Computação que também usamos na seção anterior. Algumas etapas são necessárias como preparação:

  1. Editar objeto de arquivo-processor.go: alterar o valor da constante RUN_WITH_INSTANCE_PRINCIPAL_AUTHENTICATION de falso para verdadeiro (este flag é falso ao ser executado localmente e verdadeiro ao ser executado em uma Instância de Computação no OCI em que a autenticação e a autorização do Controlador de Instâncias são usadas)
  2. Ações Git: adicione cwallet.sso e confirme esse novo arquivo junto com os arquivos alterados my-server.go, object-processor.go e data-service.go; envie o commit para o OCI Code Repository
  3. Certifique-se de que exista uma política do serviço IAM que permita que o grupo dinâmico go-on-oci-instances leia objetos no compartimento; isso possibilita que o aplicativo em execução na VM chame o Object Storage Service para ler o arquivo JSON com registros de pessoa. A instrução de política pode ser lida: Permitir que o grupo dinâmico go-on-oci-instances leia objetos no compartimento go-on-oci

Após essas ações de adição, commit e push do git, o go-on-oci-repo do Repositório de Código deve conter seu cwallet.sso e os dados-service.go que você modificou.

Agora você pode reutilizar o build-myserver do pipeline de build que usamos antes. Assim como fizemos anteriormente, temos que atualizar a referência ao arquivo de especificação de build.

  1. Abra a página de detalhes do build-myserver do Pipeline de Build na Console do OCI
  2. Abrir os detalhes do estágio de build gerenciado
  3. Clique em Editar
  4. Altere o valor no campo Caminho do arquivo de especificação de Build para /applications/people-file-processor/build_spec.yaml, a especificação de build que é modificada para criar a versão estendida do myserver
  5. Clique em Salvar

especificação de build

Inicie uma execução de build. Defina uma nova versão para o parâmetro MYSERVER_VERSION, se quiser.

O pipeline produzirá um novo artefato - um arquivo zip com o executável criado a partir das origens Go no diretório /applications/people-file-processor e contendo o arquivo de wallet e o subdiretório do site. O pipeline acionará o pipeline de implantação que trará o artefato para a instância do serviço Compute, copiará o aplicativo para o diretório /tmp/yourserver e executará o aplicativo. Ele começa a fazer listening de solicitações HTTP na porta especificada pelo parâmetro de pipeline de implantação HTTP_SERVER_PORT (ou em 8080 se o parâmetro não estiver definido).

Você pode acessar a API no endereço IP público da VM - se isso ainda estiver exposto. É melhor adicionar uma rota no Gateway de API para expor o acesso ao processador de arquivos de equipe:

  1. Navegue até os detalhes do -api-gateway
  2. Abrir a guia Implantações
  3. Clique em Editar
  4. Abra a segunda etapa de Rotas e adicione uma nova rota.

Defina o Caminho como /personnel-file-handler. O método GET deve ser suportado. O tipo de rota é HTTP. O URL é: http://<IP Público para Instância de Computação>:8095/people. Verifique o valor de HTTP_SERVER_PORT no pipeline de implantação deploy-myserver-on-go-app-vm. Se não estiver definido como 8095, modifique o valor do URL para usar o número de porta adequado para o aplicativo.


processador

Pressione Próximo e Salvar alterações. Levará um momento para que a Implantação do Gateway de API seja atualizada. Assim que tiver, o serviço que ler um arquivo de um bucket do Object Storage, processar o conteúdo JSON e criar registros na tabela PEOPLE na instância do Autonomous Database para as entradas nesse arquivo poderá ser acionado com uma solicitação HTTP GET simples para https://<public endpoind do API Gateway>/my-api/personnel-file-handler?objectName=sample-persons.json&bucketName=the-bucket.

O efeito de fazer a chamada pode ser inspecionado por meio da API da pessoa que lê registros dessa mesma tabela no mesmo banco de dados: https://<public endpoind do Gateway de API>/my-api/person?name=Jasper.

A imagem de ponta a ponta do que agora é implantado na OCI é mostrada na próxima figura.


version2

O que não é mostrado na imagem e é importante notar:

  • o arquivo do Oracle Wallet implantado com o aplicativo (no artefato criado com base na origem no Repositório de Código)
  • referência codificada para o compartimento no aplicativo
  • a política que concede permissão à instância de computação para ler objetos

No próximo artigo, analisamos o OCI Key Vault, um serviço que oferece uma maneira muito melhor de armazenar o Oracle Wallet e disponibilizá-lo para o aplicativo no momento da implantação (ou até mesmo no runtime).

Conclusão

Este artigo demonstrou como é possível interagir com um Oracle Database a partir de aplicações Go, sejam elas tradicionais ou autônomas. Vimos como é feita uma conexão de um aplicativo Go com um Oracle Database local - bem como com um Autonomous Database - usando um driver e possivelmente suportando bibliotecas, e como as operações DDL e DML SQL podem ser executadas com esses bancos de dados no conforto do aplicativo Go. Foi discutido o uso de uma Oracle Wallet para o gerenciamento adequado das credenciais (autônomas) do banco de dados. Um aplicativo em execução em uma Instância do OCI Compute, interagindo por meio do Go SDK para OCI com o Object Storage Service e manipulando o Autonomous Database - implantado automaticamente por meio do OCI DevOps - forneceu o final do artigo.

O quinto e último artigo desta série adiciona mais dois serviços do OCI com os quais as aplicações Go podem interagir: OCI Streaming - um broker de mensagens de streaming de alto volume que permite interações desacopladas entre diferentes microsserviços e outros componentes - e o OCI Key Vault para gerenciar segredos como o Oracle Wallet com credenciais de banco de dados. Este artigo também apresenta um terceiro tipo de plataforma de aplicativo ao lado de VM e função sem servidor, na forma do OCI Kubernetes Enginer (OKE) gerenciado, e mostra como os Pipelines de Implantação do DevOps podem implantar nossos aplicativos Go no OKE de maneira automatizada.

Esta página foi traduzida automaticamente.

Oracle Chatbot
Disconnected