」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > LocalStack 和 Docker:如何免費提升本地開發的生產力

LocalStack 和 Docker:如何免費提升本地開發的生產力

發佈於2024-11-03
瀏覽:105

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如有侵犯,請聯絡study_golang@163 .com刪除
最新教學 更多>
  • 大批
    大批
    方法是可以在物件上呼叫的 fns 數組是對象,因此它們在 JS 中也有方法。 slice(begin):將陣列的一部分提取到新數組中,而不改變原始數組。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index ...
    程式設計 發佈於2024-11-13
  • Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta:列偏移的刪除和恢復Bootstrap 4 在其Beta 1 版本中引入了重大更改柱子偏移了。然而,隨著 Beta 2 的後續發布,這些變化已經逆轉。 從 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    程式設計 發佈於2024-11-13
  • 如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    在 PHP 中組合關聯數組在 PHP 中,將兩個關聯數組組合成一個數組是常見任務。考慮以下請求:問題描述:提供的代碼定義了兩個關聯數組,$array1 和 $array2。目標是建立一個新陣列 $array3,它合併兩個陣列中的所有鍵值對。 此外,提供的陣列具有唯一的 ID,而名稱可能重疊。要求是建...
    程式設計 發佈於2024-11-13
  • 在 Go 中使用 WebSocket 進行即時通信
    在 Go 中使用 WebSocket 進行即時通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    程式設計 發佈於2024-11-13
  • 使用 html css 和 javascript 的圖片滑桿 carosual https://www.instagram.com/webstreet_code/
    使用 html css 和 javascript 的圖片滑桿 carosual https://www.instagram.com/webstreet_code/
    ?帶有縮圖和懸停效果的圖像輪播? 嘿,開發社群! ? 在我的最新影片中,我建立了一個優雅的圖像輪播,其縮圖突出顯示具有平滑懸停效果的活動圖像。這種互動式設計增強了使用者參與度,並為您的 Web 專案增添了現代感。 主要特點: 響應式佈局:輪播在所有螢幕尺寸上都能完美調整。 互動式縮圖:可點擊...
    程式設計 發佈於2024-11-12
  • React 的核心:理解元件重新渲染
    React 的核心:理解元件重新渲染
    在學習程式語言時,我們經常深入研究語法並專注於快速建立某些東西,有時會忽略一個關鍵問題:這種語言實際上解決了什麼問題,以及它在幕後如何運作?將我們的注意力轉移到理解語言的核心目的和機制上,可以讓學習速度更快、適應性更強,使我們能夠輕鬆駕馭最複雜的項目。語法總是可以找到的——即使是最經驗豐富的開發人員...
    程式設計 發佈於2024-11-12
  • JavaScript 中的 Deferreds、Promise 和 Future 有什麼區別?
    JavaScript 中的 Deferreds、Promise 和 Future 有什麼區別?
    JavaScript 中 Deferreds、Promise 和 Future 的區別在 JavaScript 中,deferreds、promise 和 futures 通常用於處理非同步操作。這些概念中的每一個都有其獨特的一組特徵:Deferreds在正式文件中從未明確定義,deferreds ...
    程式設計 發佈於2024-11-12
  • 為什麼我的 Web 應用程式中的請求之間沒有維護 Gorilla 會話變數?
    為什麼我的 Web 應用程式中的請求之間沒有維護 Gorilla 會話變數?
    使用 Gorilla 會話時未維護會話變數問題使用 Gorilla Sessions Web 工具包時,會話變數不會跨請求保留。當伺服器啟動並且使用者存取 localhost:8100/ 時,他們將被導向到 login.html,因為會話值不存在。登入後,會話變數將被存儲,並且使用者將被重定向到 h...
    程式設計 發佈於2024-11-12
  • 如何在Python中像“column -t”指令一樣顯示列化資料?
    如何在Python中像“column -t”指令一樣顯示列化資料?
    在 Python 中顯示列式資料在命令列管理工具領域,通常需要以良好對齊的方式呈現資料列。雖然製表符提供了一種簡單的解決方案,但在處理不同長度的資料時它們會失敗。本文旨在透過提出受 Linux「column -t」命令行為啟發的 Python 解決方案來應對這項挑戰。 Python 提供了一個強大的...
    程式設計 發佈於2024-11-12
  • 在 NumPy 數組中尋找特定行的有效方法:問題和解決方案
    在 NumPy 數組中尋找特定行的有效方法:問題和解決方案
    高效查找NumPy 數組中特定行的實例使用NumPy 數組時,可能會遇到需要確定是否array 包含特定行,但ndarray 的標準contains 方法引發了問題。本文針對此問題提出了高效且 Python 的解決方案。 一種方法涉及使用 .tolist() 將 NumPy 數組轉換為 Python...
    程式設計 發佈於2024-11-12
  • 如何解決在伺服器上使用 Matplotlib 的 Python 腳本的「_tkinter.TclError:無顯示名稱且無 $DISPLAY 環境變數」問題?
    如何解決在伺服器上使用 Matplotlib 的 Python 腳本的「_tkinter.TclError:無顯示名稱且無 $DISPLAY 環境變數」問題?
    _tkinter.TclError:沒有顯示名稱,也沒有$DISPLAY 環境變數_tkinter.TclError:沒有顯示名稱,也沒有$DISPLAY 環境變數問題使用Matplotlib 的Python 腳本在伺服器上失敗,並出現錯誤「產生繪圖時沒有顯示名稱和$DISPLAY 環境變數」。出現...
    程式設計 發佈於2024-11-12
  • 如何使用 Apache Commons IO 在 Java 中遞歸刪除目錄?
    如何使用 Apache Commons IO 在 Java 中遞歸刪除目錄?
    在 Java 中遞歸刪除目錄在 Java 中刪除空目錄非常簡單。然而,當處理包含子目錄和檔案的目錄時,該過程變得更加複雜。本文深入探討了使用 Apache Commons IO 函式庫遞歸刪除整個目錄的有效方法。 Apache Commons IO 簡介Apache Commons IO 提供了一套...
    程式設計 發佈於2024-11-12
  • 為什麼即使在同一包中使用 FXML,我的 JavaFX 應用程式也會拋出“Location is required.”錯誤?
    為什麼即使在同一包中使用 FXML,我的 JavaFX 應用程式也會拋出“Location is required.”錯誤?
    JavaFX「需要位置。」儘管FXML 位於同一個套件中仍出現錯誤在JavaFX 應用程式中,遇到「java .lang.NullPointerException: Location is required」錯誤通常表示無法載入FXML 檔案。即使 FXML 檔案與 Application 類別位於...
    程式設計 發佈於2024-11-12
  • `std::enable_if` 是如何運作的:揭開其實現和使用的神秘面紗?
    `std::enable_if` 是如何運作的:揭開其實現和使用的神秘面紗?
    理解std::enable_if:破解其目的和實現理解std::enable_if:破解其目的和實現雖然std::enable_if 的本質是在某些上下文中掌握的,但它的錯綜複雜的問題,特別是模板語句中的第二個參數和對std::enable_if 的賦值,仍然是個謎。深入研究其工作原理將解開這些謎團...
    程式設計 發佈於2024-11-12
  • 如何在 Go 中實作 Python 風格的生成器,同時避免記憶體洩漏?
    如何在 Go 中實作 Python 風格的生成器,同時避免記憶體洩漏?
    Go 中的Python 風格產生器了解通道緩衝區在您的程式碼中,您觀察到增加通道緩衝區大小從1 到10 透過減少上下文切換來增強效能。這個觀念是正確的。較大的緩衝區允許 fibonacci goroutine 提前填充多個點,從而減少 goroutine 之間持續通訊的需要。 通道生命週期和記憶體管...
    程式設計 發佈於2024-11-12

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3