”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > LocalStack 和 Docker:如何免费提高本地开发的生产力

LocalStack 和 Docker:如何免费提高本地开发的生产力

发布于2024-11-03
浏览:334

LocalStack e Docker: Como aumentar a produtividade no desenvolvimento LOCAL sem custos

Desenvolver aplicações que interagem com serviços da AWS pode ser um desafio, especialmente quando se trata de configurar e testar recursos sem aumentar em custos ou enfrentar limitações de conectividade. O LocalStack surge como uma solução poderosa para emular serviços da AWS localmente, permitindo que você desenvolva e teste seu código de forma eficiente.

Neste post, vamos explorar como configurar o LocalStack e integrá-lo com aplicações em Golang, fornecendo exemplos práticos que beneficiarão desde desenvolvedores seniores até estagiários.

O que é o LocalStack?

O LocalStack é uma plataforma que simula serviços da AWS em sua máquina local. Ele permite que você desenvolva e teste funcionalidades que dependem de serviços como S3, DynamoDB, SQS, Lambda e muitos outros, sem precisar acessar a nuvem real da AWS.

Podemos dizer que suas maiores vantagens são as seguintes:

  • Custo ZERO: Evita custos associados ao uso dos serviços reais da AWS durante o desenvolvimento.
  • Desenvolvimento offline: Você pode trabalhar sem conexão com a internet.
  • Ciclo de feedback rápido: Teste suas funcionalidades localmente, acelerando o desenvolvimento.
  • Ambiente controlado: Simule diferentes cenários sem afetar ambientes de produção ou teste.

Configurando o ambiente

Vamos começar já "colocando a mão na massa". Para isso, iremos construir uma aplicação que cria usuários de forma assíncrona usando DynamoDB e SQS. Iremos utilizar o AWS SDK e o LocalStack, dessa forma o mesmo código funciona para o mundo real e para rodar localmente nossa aplicação.

Antes de começarmos, certifique-se de que o Docker e o Go estão instalados corretamente em sua máquina. Além disso, exporte as credenciais de acesso AWS (mesmo que sejam fictícias), já que o SDK da AWS requer essas informações.

export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test

Estrutura do projeto

Para manter nosso projeto organizado, seguiremos uma estrutura que separa claramente as responsabilidades:

├── cmd
│   ├── service
│   │   └── main.go
│   └── worker
│       └── main.go
├── internal
│   ├── config
│   │   └── config.go
│   └── server
│       └── server.go
├── pkg
│   ├── api
│   │   └── handlers
│   │       └── user.go
│   ├── aws
│   │   ├── client.go
│   │   ├── dynamo.go
│   │   └── sqs.go
│   └── service
│       ├── models
│       │   └── user.go
│       └── user.go
├── compose.yml
├── go.mod
└── go.sum

Os arquivos do projeto estão disponíveis no github

Explicação da Estrutura:

  • cmd/: Contém os executáveis da aplicação.
  • service/: O servidor HTTP.
  • worker/: O consumidor SQS.
  • internal/: Código interno não exposto para outros módulos.
  • config/: Gerencia a configuração AWS.
  • server/: Configuração do servidor e inicialização dos serviços AWS.
  • pkg/: Pacotes reutilizáveis.
  • api/handlers/: Handlers das rotas HTTP.
  • aws/: Interações com os serviços AWS.
  • service/: Lógica de negócios e modelos de dados.
  • compose.yml: Configuração do LocalStack.
  • go.mod e go.sum: Gerenciamento de dependências Go.

Configurando o LocalStack e a Aplicação

1- Criando o compose.yml

Como sempre, utilizaremos nosso amigo Docker para facilitar e não termos que instalar nada além de rodar o comando do compose para subir o LocalStack:

# compose.yml
services:
  localstack:
    image: localstack/localstack:latest
    container_name: localstack
    ports:
      - "4566:4566"
    environment:
      - SERVICES=dynamodb,sqs
      - DEBUG=1

2- Definindo o model

Crie o arquivo user.go dentro de pkg/service/models/:

package models

type User struct {
    ID       string `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
    Password string `json:"password,omitempty"` // Em um cenário real, nunca armazenar senhas em texto :D
    Address  string `json:"address"`
    Phone    string `json:"phone"`
}

3- Configurando a conexão com a AWS

Crie o arquivo config.go dentro de internal/config/. Ele será o singleton que carregará as configs para nosso LocalStack. Pense no singleton como um gerente de loja que mantém a mesma estratégia para todas as filiais. Não importa quantas lojas (clientes) existam, a estratégia (configuração) é consistente.:

package config

import (
    "context"
    "log"
    "sync"

    "github.com/aws/aws-sdk-go-v2/aws"
    awsConfig "github.com/aws/aws-sdk-go-v2/config"
)

const (
        UsersTable = "users"
        UsersQueue = "users_queue"
)

var (
    cfg        aws.Config
    once       sync.Once
    QueueURL   string
)

func GetAWSConfig() aws.Config {
    once.Do(func() {
        var err error
        cfg, err = awsConfig.LoadDefaultConfig(context.Background(),
            awsConfig.WithRegion("us-east-1"),
        )
        if err != nil {
            log.Fatalf("error during AWS config: %v", err)
        }
    })
    return cfg
}

4- Inicializando os clients

Crie o arquivo client.go em pkg/aws/. Ele será responsável por passar as configs que carregamos para os clients dos serviços da AWS que estamos emulando:

package aws

import (
    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/sqs"
)

var (
    DynamoDBClient *dynamodb.Client
    SQSClient      *sqs.Client
)

func InitClients(cfg aws.Config) {
    localstackEndpoint := "http://localhost:4566"

    DynamoDBClient = dynamodb.NewFromConfig(cfg, func(o *dynamodb.Options) {
        o.BaseEndpoint = aws.String(localstackEndpoint)
    })

    SQSClient = sqs.NewFromConfig(cfg, func(o *sqs.Options) {
        o.BaseEndpoint = aws.String(localstackEndpoint)
    })
}

5- Implementando funções que utilizaremos com os clients

Agora que já fizemos o loading das configurações para os clients dos serviços, chegou a hora de implementar os métodos que serão utilizados para criar fila, publicar mensagem e criar tabela.

Começaremos criando o sqs.go dentro do package pkg/aws/, onde teremos duas funções, a CreateQueue responsável por criar uma fila e a SendMessage responsável por mandar mensagens para a fila que criamos:

package aws

import (
    "context"
    "log"

    "github.com/aws/aws-sdk-go-v2/service/sqs"
)

func CreateQueue(queueName string) (string, error) {
    result, err := SQSClient.CreateQueue(context.Background(), &sqs.CreateQueueInput{
        QueueName: &queueName,
    })
    if err != nil {
        return "", err
    }
    return *result.QueueUrl, nil
}

func SendMessage(ctx context.Context, queueUrl, messageBody string) error {
    log.Printf("Sending message with body: %s to %s", messageBody, queueUrl)
    _, err := SQSClient.SendMessage(ctx, &sqs.SendMessageInput{
        QueueUrl:    &queueUrl,
        MessageBody: &messageBody,
    })
    return err
}

Se você reparar bem, eu preferi criar as funções bem genéricas.

  • CreateQueue: ela vai receber um nome de uma fila e criará esta fila com o nome que recebeu.
  • SendMessage: recebe a URL da fila onde deve publicar a mensagem e a mensagem que deve ser publicada. Dessa forma temos funções que podem ser reutilizadas sempre que necessário dentro de nosso código.

Agora vamos criar o dynamo.go dentro do mesmo package pkg/aws, assim fica tudo centralizado dentro de um mesmo pacote o que é referente aos serviços da AWS.

package aws

import (
    "context"
    "errors"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func CreateTable(tableName string) error {
    _, err := DynamoDBClient.CreateTable(context.Background(), &dynamodb.CreateTableInput{
        TableName: aws.String(tableName),
        AttributeDefinitions: []types.AttributeDefinition{
            {
                AttributeName: aws.String("ID"),
                AttributeType: types.ScalarAttributeTypeS,
            },
        },
        KeySchema: []types.KeySchemaElement{
            {
                AttributeName: aws.String("ID"),
                KeyType:       types.KeyTypeHash,
            },
        },
        BillingMode: types.BillingModePayPerRequest,
    })
    if err != nil {
        var resourceInUseException *types.ResourceInUseException
        if errors.As(err, &resourceInUseException) {
            return nil
        }
        return err
    }
    return nil
}

Aqui continuamos no mesmo conceito, criando uma função genérica para ser reutilizada caso precise em outro ponto do código. No dynamo temos apenas que criar uma função que criará a tabela:

  • CreateTable: recebe um nome de uma tabela e cria essa tabela.

6- Implementando o service

Primeiro vamos criar a entidade que User onde teremos as informações do usuário. Para isso crie um arquivo user.go dentro do package pkg/service/model que é onde ficarão todos os models de nossa aplicação.

package models

type User struct {
    ID       string `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
    Password string `json:"password"` // Em um cenário real, nunca armazenar senhas em texto :D
    Address  string `json:"address"`
    Phone    string `json:"phone"`
}

Agora vamos para o service que será responsável por cuidar das regras de negócio relacionadas ao User. Então vamos criar o user.go dentro do package pkg/service.

Teremos 3 funções dentro desse arquivo:

  • CreateUser: que será responsável por receber um novo User, validar se existe algum usuário com o mesmo email já salvo no DB e caso não exista, salvar um novo user no DB.
  • GetUserByEmail: busca pelo User baseado no email que ele recebeu como parâmetro
  • GetAllUsers: retorna todos os Users salvos no DB.

O código dela fica assim:

package service

import (
    "context"
    "errors"
    "fmt"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
    "github.com/google/uuid"
    "github.com/rflpazini/localstack/internal/config"
    awsClient "github.com/rflpazini/localstack/pkg/aws"
    "github.com/rflpazini/localstack/pkg/service/models"
)

func CreateUser(ctx context.Context, user *models.User) error {
    existingUser, err := GetUserByEmail(ctx, user.Email)
    if err == nil && existingUser != nil {
        return errors.New("email is already in use by another user")
    } else if err != nil && err.Error() != "user not found" {
        return fmt.Errorf("failed to verify if email is already in use: %w", err)
    }

    user.ID = uuid.NewString()

    item := map[string]types.AttributeValue{
        "ID":       &types.AttributeValueMemberS{Value: user.ID},
        "Name":     &types.AttributeValueMemberS{Value: user.Name},
        "Email":    &types.AttributeValueMemberS{Value: user.Email},
        "Password": &types.AttributeValueMemberS{Value: user.Password},
        "Address":  &types.AttributeValueMemberS{Value: user.Address},
        "Phone":    &types.AttributeValueMemberS{Value: user.Phone},
    }

    _, err = awsClient.DynamoDBClient.PutItem(context.Background(), &dynamodb.PutItemInput{
        TableName: aws.String(config.UsersTable),
        Item:      item,
    })
    if err != nil {
        return fmt.Errorf("failed to create user: %w", err)
    }

    return nil
}

func GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
    result, err := awsClient.DynamoDBClient.Scan(ctx, &dynamodb.ScanInput{
        TableName:        aws.String(config.UsersTable),
        FilterExpression: aws.String("Email = :email"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":email": &types.AttributeValueMemberS{Value: email},
        },
    })
    if err != nil {
        return nil, fmt.Errorf("failed to scan table: %w", err)
    }

    if len(result.Items) == 0 {
        return nil, errors.New("user not found")
    }

    item := result.Items[0]
    user := &models.User{
        ID:      item["ID"].(*types.AttributeValueMemberS).Value,
        Name:    item["Name"].(*types.AttributeValueMemberS).Value,
        Email:   item["Email"].(*types.AttributeValueMemberS).Value,
        Address: item["Address"].(*types.AttributeValueMemberS).Value,
        Phone:   item["Phone"].(*types.AttributeValueMemberS).Value,
    }

    return user, nil
}

func GetAllUsers() ([]*models.User, error) {
    result, err := awsClient.DynamoDBClient.Scan(context.Background(), &dynamodb.ScanInput{
        TableName: aws.String(config.UsersTable),
    })
    if err != nil {
        return nil, fmt.Errorf("failed to retrieve all users: %w", err)
    }

    if len(result.Items) == 0 {
        return nil, errors.New("no users found")
    }

    users := make([]*models.User, 0)

    for _, item := range result.Items {
        user := &models.User{
            ID:      item["ID"].(*types.AttributeValueMemberS).Value,
            Name:    item["Name"].(*types.AttributeValueMemberS).Value,
            Email:   item["Email"].(*types.AttributeValueMemberS).Value,
            Address: item["Address"].(*types.AttributeValueMemberS).Value,
            Phone:   item["Phone"].(*types.AttributeValueMemberS).Value,
        }
        users = append(users, user)
    }

    return users, nil
}

7- Implementando o handler

Crie os handlers que serão responsáveis por receber as requisições HTTP e interagir com os serviços.

Teremos 2 funções nesse handler do User:

  • GetUser: ele vai listar todos os usuários e caso receba o query param email buscará pelo usuário solicitado.
  • CreateUser: irá publicar na fila um usuário novo com base dados recebidos no request. Essa será uma operação async.
package handlers

import (
    "encoding/json"
    "net/http"

    "github.com/labstack/echo/v4"
    "github.com/rflpazini/localstack/internal/config"
    awsClient "github.com/rflpazini/localstack/pkg/aws"
    "github.com/rflpazini/localstack/pkg/service"
    "github.com/rflpazini/localstack/pkg/service/models"
)

func GetUser(c echo.Context) error {
    ctx := c.Request().Context()

    email := c.QueryParam("email")
    if email == "" {
        users, err := service.GetAllUsers()
        if err != nil {
            return err
        }
        return c.JSON(http.StatusOK, users)
    }

    user, err := service.GetUserByEmail(ctx, email)
    if err != nil {
        return c.JSON(http.StatusNotFound, err.Error())
    }

    return c.JSON(http.StatusOK, user)
}

func CreateUser(c echo.Context) error {
    ctx := c.Request().Context()

    user := new(models.User)
    if err := c.Bind(user); err != nil {
        return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
    }

    message, err := json.Marshal(user)
    if err != nil {
        return c.JSON(http.StatusInternalServerError, err.Error())
    }

    err = awsClient.SendMessage(ctx, config.QueueURL, string(message))
    if err != nil {
        return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
    }

    return c.NoContent(http.StatusCreated)
}

8- Configurando o server

Crie o servidor HTTP e configure as rotas em internal/server/server.go:

package server

import (
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/labstack/echo/v4"
    "github.com/rflpazini/localstack/internal/config"
    "github.com/rflpazini/localstack/pkg/api/handlers"
    awsClients "github.com/rflpazini/localstack/pkg/aws"
)

func Start(cfg aws.Config) {
    e := echo.New()
    awsClients.InitClients(cfg)

    initDependencies()

    e.POST("/user", handlers.CreateUser)
    e.GET("/user", handlers.GetUser)

    e.Logger.Fatal(e.Start(":8080"))
}

func initDependencies() {
    err := awsClients.CreateTable(config.UsersTable)
    if err != nil {
        log.Printf("create table error: %v", err)
    } else {
        log.Println("table created")
    }

    queueURL, err := awsClients.CreateQueue(config.UsersQueue)
    if err != nil {
        log.Printf("create queue error: %v", err)
    } else {
        config.QueueURL = queueURL
        log.Println("sqs queue created")
    }
}

Aqui temos duas funções, o Start e o initDependencies:

  • Start: inicia o servidor HTTP e registra as rotas. Além de chamar o initDependencies
  • initDependencies: inicia os serviços da AWS criando a tabela e a fila que precisamos para rodar nosso aplicativo.

9- Configurando o worker e o main

Dentro do package cmd criaremos duas pastas. Uma chamada service e outra worker.

A service terá o main.go será responsável por carregar as configurações e chamar nosso Start do server.

package main

import (
    "github.com/rflpazini/localstack/internal/config"
    "github.com/rflpazini/localstack/internal/server"
)

func main() {
    cfg := config.GetAWSConfig()
    server.Start(cfg)
}

O worker será a aplicação que consumirá as mensagens da fila. Lembra que criamos um service para salvar o usuário async? É com o worker que vamos consumir e salvar esse usuário no DB.

package main

import (
    "context"
    "encoding/json"
    "log"
    "time"

    "github.com/rflpazini/localstack/internal/config"
    "github.com/rflpazini/localstack/pkg/aws"
    "github.com/rflpazini/localstack/pkg/service"
    "github.com/rflpazini/localstack/pkg/service/models"

    "github.com/aws/aws-sdk-go-v2/service/sqs"
)

const (
    userQueueName = "users_queue"
)

func main() {
    ctx := context.Background()
    cfg := config.GetAWSConfig()
    aws.InitClients(cfg)

    queueURL := "http://localhost:4566/000000000000/"   userQueueName

    for {
        messages, err := aws.SQSClient.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{
            QueueUrl:            &queueURL,
            MaxNumberOfMessages: 10,
            WaitTimeSeconds:     5,
        })
        if err != nil {
            log.Printf("Erro ao receber mensagens: %v", err)
            time.Sleep(5 * time.Second)
            continue
        }

        for _, msg := range messages.Messages {
            var user models.User
            err := json.Unmarshal([]byte(*msg.Body), &user)
            if err != nil {
                log.Printf("Erro ao desserializar mensagem: %v", err)
                continue
            }

            err = service.CreateUser(ctx, &user)
            if err != nil {
                log.Printf("Create user error: %v", err)
            }

            _, err = aws.SQSClient.DeleteMessage(ctx, &sqs.DeleteMessageInput{
                QueueUrl:      &queueURL,
                ReceiptHandle: msg.ReceiptHandle,
            })
            if err != nil {
                log.Printf("Erro ao deletar mensagem: %v", err)
            }
        }

        time.Sleep(1 * time.Second)
    }
}

Ufa, terminamos a aplicação ?

Executando a Aplicação

Bora rodar tudo isso e ver como ficou nosso app. A primeira coisa que devemos fazer, é subir o compose para iniciar o LocalStack:

docker compose up -d 
[ ] Running 1/1
 ✔ Container localstack  Started    

Caso você tenha dúvida se o container esta ou não rodando, basta usar docker ps e ver se o container com a imagem do localstack aparece :)

Com o container do local stack rodando, vamos iniciar nossa aplicação e o worker.

Servidor & Worker

Primeiro rode o servidor, pois ele irá criar tanto a tabela quanto a fila que precisamos para que tudo funcione corretamente:

go run cmd/service/main.go

Com o servidor rodando, em uma nova janela de terminal, rode o worker que irá consumir nossa fila:

go run cmd/worker/main.go

Pronto, estamos com a aplicação e o worker rodando simultaneamente!

Testando as Funcionalidades

1- Registrando um Novo Usuário de Forma Assíncrona

Imagine que você está fazendo um pedido em um restaurante movimentado. Você faz o pedido (envia a requisição), o garçom anota e passa para a cozinha (fila SQS). Enquanto isso, você aguarda na mesa, e a comida é preparada e servida (processamento assíncrono).

Envie uma solicitação para registrar um novo usuário:

curl --location 'http://localhost:8080/user' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "Carlos Silva",
    "email": "[email protected]",
    "password": "senha123",
    "address": "Rua A, 123",
    "phone": "123456789"
}'

Você receberá um status response 201:

HTTP/1.1 201 Created

Observe o console onde o worker da fila SQS está sendo executado. Você deve ver uma saída indicando que o usuário foi criado:
2024/10/08 11:01:58 creating user: [email protected]

2- Verificando a criação do usuário

Recupere as informações do usuário para verificar se ele foi criado:

curl --location 'http://localhost:8080/[email protected]'

Você receberá a seguinte resposta, caso ele tenha sido salvo com sucesso:

{
     "id": "2a32193a-bcd6-4d8f-87dd-64e65f8a8f22",
     "name": "Carlos Souza",
     "email": "[email protected]",
     "address": "Rua Central, 456",
     "phone": "999888777"
}

Nesse mesmo endpoint se não colocarmos o email do usuário, vamos receber toda a base de volta. Você pode testar isso cadastrando vários usuários e fazendo o request:

curl --location 'http://localhost:8080/user'

Cadastrei um usuário com meu nome para testarmos:

[
     {
          "id": "bdccfced-000f-4daf-82cc-712a8f4af182",
          "name": "Rafael Pazini",
          "email": "[email protected]",
          "address": "Rua A, 123",
          "phone": "123456789"
     },
     {
          "id": "2a32193a-bcd6-4d8f-87dd-64e65f8a8f22",
          "name": "Carlos Souza",
          "email": "[email protected]",
          "address": "Rua Central, 456",
          "phone": "999888777"
     }
]

Considerações Finais

Neste guia, construímos uma aplicação Go que cria usuários de forma assíncrona usando DynamoDB e SQS, tudo isso localmente graças ao LocalStack em um contêiner Docker. Implementamos os handlers e serviços relacionados aos usuários, tornando a aplicação completa e funcional. Utilizamos analogias do dia a dia para facilitar a compreensão dos conceitos, como comparar a fila SQS a um garçom que recebe pedidos e os repassa para a cozinha.

Por que isso é importante?

Desenvolver e testar serviços AWS localmente com o LocalStack nos permite economizar tempo e recursos, além de facilitar o processo de desenvolvimento. É como ter um laboratório onde podemos experimentar e ajustar nossa aplicação antes de lançá-la no ambiente real.

O que aprendemos?

  • Como configurar o LocalStack em um contêiner Docker.
  • Como criar uma aplicação Go que interage com DynamoDB e SQS.
  • Como implementar o processamento assíncrono de mensagens.
  • Como desenvolver os handlers e serviços relacionados aos usuários.

Próximos passos:

Caso você queira se desafiar, fica aqui uma lição de casa para deixar a aplicação ainda mais robusta e próxima do mundo real:

  • Implementar autenticação e segurança.
  • Adicionar mais funcionalidades, como atualização e exclusão de usuários.
  • Integrar outros serviços da AWS conforme necessário.

É isso galera, espero que vocês gostem e deixem os comentários caso surja alguma dúvida!

Happy coding! ??‍?

版本声明 本文转载于:https://dev.to/rflpazini/localstack-e-docker-como-aumentar-a-produtividade-no-desenvolvimento-local-sem-custos-m2c?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • CSS可以根据任何属性值来定位HTML元素吗?
    CSS可以根据任何属性值来定位HTML元素吗?
    靶向html元素,在CSS 中使用任何属性值,在CSS中,可以基于特定属性(如下所示)基于特定属性的基于特定属性的emants目标元素: 字体家庭:康斯拉斯(Consolas); } 但是,出现一个常见的问题:元素可以根据任何属性值而定位吗?本文探讨了此主题。的目标元素有任何任何属性值,属...
    编程 发布于2025-05-13
  • Java的Map.Entry和SimpleEntry如何简化键值对管理?
    Java的Map.Entry和SimpleEntry如何简化键值对管理?
    A Comprehensive Collection for Value Pairs: Introducing Java's Map.Entry and SimpleEntryIn Java, when defining a collection where each element com...
    编程 发布于2025-05-13
  • 如何限制动态大小的父元素中元素的滚动范围?
    如何限制动态大小的父元素中元素的滚动范围?
    在交互式接口中实现垂直滚动元素的CSS高度限制,控制元素的滚动行为对于确保用户体验和可访问性是必不可少的。一种这样的方案涉及限制动态大小的父元素中元素的滚动范围。问题:考虑一个布局,其中我们具有与用户垂直滚动一起移动的可滚动地图div,同时与固定的固定sidebar保持一致。但是,地图的滚动无限期...
    编程 发布于2025-05-13
  • Java为何无法创建泛型数组?
    Java为何无法创建泛型数组?
    通用阵列创建错误 arrayList [2]; JAVA报告了“通用数组创建”错误。为什么不允许这样做?答案:Create an Auxiliary Class:public static ArrayList<myObject>[] a = new ArrayList<myO...
    编程 发布于2025-05-13
  • C++成员函数指针正确传递方法
    C++成员函数指针正确传递方法
    如何将成员函数置于c [&& && && && && && && && && && &&&&&&&&&&&&&&&&&&&&&&&华仪的函数时,在接受成员函数指针的函数时,要在函数上既要提供指针又可以提供指针和指针到函数的函数。需要具有一定签名的功能指针。要通过成员函数,您需要同时提供对象指针(此...
    编程 发布于2025-05-13
  • 为什么我的CSS背景图像出现?
    为什么我的CSS背景图像出现?
    故障排除:CSS背景图像未出现 ,您的背景图像尽管遵循教程说明,但您的背景图像仍未加载。图像和样式表位于相同的目录中,但背景仍然是空白的白色帆布。而不是不弃用的,您已经使用了CSS样式: bockent {背景:封闭图像文件名:背景图:url(nickcage.jpg); 如果您的html,css...
    编程 发布于2025-05-13
  • PHP阵列键值异常:了解07和08的好奇情况
    PHP阵列键值异常:了解07和08的好奇情况
    PHP数组键值问题,使用07&08 在给定数月的数组中,键值07和08呈现令人困惑的行为时,就会出现一个不寻常的问题。运行print_r($月份)返回意外结果:键“ 07”丢失,而键“ 08”分配给了9月的值。此问题源于PHP对领先零的解释。当一个数字带有0(例如07或08)的前缀时,PHP将...
    编程 发布于2025-05-13
  • 将图片浮动到底部右侧并环绕文字的技巧
    将图片浮动到底部右侧并环绕文字的技巧
    在Web设计中围绕在Web设计中,有时可以将图像浮动到页面右下角,从而使文本围绕它缠绕。这可以在有效地展示图像的同时创建一个吸引人的视觉效果。 css位置在右下角,使用css float and clear properties: img { 浮点:对; ...
    编程 发布于2025-05-13
  • 如何从2D数组中提取元素?使用另一数组的索引
    如何从2D数组中提取元素?使用另一数组的索引
    Using NumPy Array as Indices for the 2nd Dimension of Another ArrayTo extract specific elements from a 2D array based on indices provided by a second ...
    编程 发布于2025-05-13
  • 如何使用Depimal.parse()中的指数表示法中的数字?
    如何使用Depimal.parse()中的指数表示法中的数字?
    在尝试使用Decimal.parse(“ 1.2345e-02”中的指数符号表示法表示的字符串时,您可能会遇到错误。这是因为默认解析方法无法识别指数符号。 成功解析这样的字符串,您需要明确指定它代表浮点数。您可以使用numbersTyles.Float样式进行此操作,如下所示:[&& && && ...
    编程 发布于2025-05-13
  • Async Void vs. Async Task在ASP.NET中:为什么Async Void方法有时会抛出异常?
    Async Void vs. Async Task在ASP.NET中:为什么Async Void方法有时会抛出异常?
    在ASP.NET async void void async void void void void void的设计无需返回asynchroncon而无需返回任务对象。他们在执行过程中增加未偿还操作的计数,并在完成后减少。在某些情况下,这种行为可能是有益的,例如未期望或明确预期操作结果的火灾和...
    编程 发布于2025-05-13
  • 如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求模拟浏览器行为,以及伪造的用户代理提供了一个用户 - 代理标头一个有效方法是提供有效的用户式header,以提供有效的用户 - 设置,该标题可以通过browser和Acterner Systems the equestersystermery和操作系统。通过模仿像Chro...
    编程 发布于2025-05-13
  • 如何高效地在一个事务中插入数据到多个MySQL表?
    如何高效地在一个事务中插入数据到多个MySQL表?
    mySQL插入到多个表中,该数据可能会产生意外的结果。虽然似乎有多个查询可以解决问题,但将从用户表的自动信息ID与配置文件表的手动用户ID相关联提出了挑战。使用Transactions和last_insert_id() 插入用户(用户名,密码)值('test','test...
    编程 发布于2025-05-13
  • 切换到MySQLi后CodeIgniter连接MySQL数据库失败原因
    切换到MySQLi后CodeIgniter连接MySQL数据库失败原因
    Unable to Connect to MySQL Database: Troubleshooting Error MessageWhen attempting to switch from the MySQL driver to the MySQLi driver in CodeIgniter,...
    编程 发布于2025-05-13
  • Go web应用何时关闭数据库连接?
    Go web应用何时关闭数据库连接?
    在GO Web Applications中管理数据库连接很少,考虑以下简化的web应用程序代码:出现的问题:何时应在DB连接上调用Close()方法?,该特定方案将自动关闭程序时,该程序将在EXITS EXITS EXITS出现时自动关闭。但是,其他考虑因素可能保证手动处理。选项1:隐式关闭终止数...
    编程 发布于2025-05-13

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3