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

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

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

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]删除
最新教程 更多>
  • 如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    在 PHP 中组合关联数组在 PHP 中,将两个关联数组组合成一个数组是一项常见任务。考虑以下请求:问题描述:提供的代码定义了两个关联数组,$array1和$array2。目标是创建一个新数组 $array3,它合并两个数组中的所有键值对。 此外,提供的数组具有唯一的 ID,而名称可能重合。要求是构...
    编程 发布于2024-11-14
  • 在 Go 中使用 WebSocket 进行实时通信
    在 Go 中使用 WebSocket 进行实时通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    编程 发布于2024-11-14
  • Java 字符串虽然是对象,但如何用双引号初始化?
    Java 字符串虽然是对象,但如何用双引号初始化?
    Java中用“ ”初始化String:一种特殊情况Java的String类,尽管是面向对象的实体,但可以被初始化使用双引号。这种特性引发了一个问题:在面向对象编程的范围内,这怎么可能?Java 的独特方法与 Java 中的一切都必须的概念相反作为一个对象,出于性能原因,该语言的设计者选择保留原始数据...
    编程 发布于2024-11-14
  • 为什么我的“不在”检查元组在 Python 中不起作用?
    为什么我的“不在”检查元组在 Python 中不起作用?
    验证 Python 列表中元素的存在在 Python 中使用列表时,通常需要检查特定元素是否存在。对于依赖于列表中不存在元素的条件分支,通常使用以下语法:if element not in list: # Code to execute if element is not in the li...
    编程 发布于2024-11-14
  • AtomicInteger 如何提高多线程环境中的并发性?
    AtomicInteger 如何提高多线程环境中的并发性?
    并发编程中的 AtomicIntegerAtomicInteger 是一个 Java 类,支持并发访问底层整数值。了解 AtomicInteger 的实际应用对于优化多线程环境中的并发性至关重要。典型用例AtomicInteger 有两个主要用途: 原子计数器:它可以用作共享计数器,可以递增或由多个...
    编程 发布于2024-11-14
  • 如何跟踪 Java 的 For-Each 循环中的迭代计数?
    如何跟踪 Java 的 For-Each 循环中的迭代计数?
    在 Java 的 For-Each 循环中访问迭代索引在 Java 的 for-each 循环中,访问当前迭代计数可以证明是有点挑战性。与传统的 for 循环(例如 for (int i = 0; i < array.length; i ))不同,for-each 循环(例如 for (String...
    编程 发布于2024-11-14
  • 如何在 Golang Web 服务器中流式传输 MP4 视频?
    如何在 Golang Web 服务器中流式传输 MP4 视频?
    GoLang Web 服务器流视频问:Golang Web 服务器配置为服务 HTML、CSS、JavaScript 和图像失败尝试流式传输 MP4 视频。该问题是由于处理大型视频文件而引起的。 Chrome 要求服务器支持超过一定大小的视频的 Range 请求,但提供的代码没有解决这个问题。通过实...
    编程 发布于2024-11-14
  • 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-14
  • 如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    MySQL配置不正确:相对路径的问题在Django中运行python manage.py runserver时,可能会遇到以下错误:ImproperlyConfigured: Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-...
    编程 发布于2024-11-14
  • 如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 识别今天生日的用户使用 MySQL 确定今天是否是用户的生日涉及查找生日匹配的所有行今天的日期。这可以通过一个简单的 MySQL 查询来实现,该查询将存储为 UNIX 时间戳的生日与今天的日期进行比较。以下 SQL 查询将获取今天有生日的所有用户: FROM USERS ...
    编程 发布于2024-11-14
  • CSS 中的“display: table-column”实际上做了什么?
    CSS 中的“display: table-column”实际上做了什么?
    CSS“display: table-column”应该如何工作?在 HTML 中,表格由行组成,每行含有细胞。 CSS 扩展了这个概念,允许设计者定义特定的行和列布局。虽然“display: table-row”和“display: table-cell”很简单,但“display: table-...
    编程 发布于2024-11-14
  • Babel 6 如何以不同的方式处理默认导出?
    Babel 6 如何以不同的方式处理默认导出?
    重大变更:Babel 6 导出默认行为随着 Babel 6 的发布,默认导出的处理方式发生了重大变化。虽然 Babel 之前添加了 module.exports = Exports["default"] 行,但此功能已被删除。此修改需要更改模块导入语法。以前,使用旧语法的代码:v...
    编程 发布于2024-11-14
  • 掌握 Next.js 中的 SSR:如何提升 SEO 和用户体验
    掌握 Next.js 中的 SSR:如何提升 SEO 和用户体验
    SSR(服务器端渲染)是 Next.js 中生成页面的另一种方法。在本文中,我想解释什么是 SSR、它是如何工作的,以及如何在 Next.js 项目的 Page Router 和 App Router 中实现它。 什么是SSR? SSR是一种在用户发出请求后生成静态页面(或预渲染页面...
    编程 发布于2024-11-14
  • 为什么 PHP 5.2 不允许抽象静态类方法?
    为什么 PHP 5.2 不允许抽象静态类方法?
    PHP 5.2 严格模式:为什么不允许抽象静态类方法?在 PHP 5.2 中,启用严格警告可能会触发熟悉的警告:“静态函数不应该是抽象的”。此警告源于 PHP 5.2 中引入的一项更改,该更改不允许抽象静态类方法。原因:历史监督PHP 5.2 最初缺乏后期静态绑定,使抽象静态函数变得无用。由于抽象静...
    编程 发布于2024-11-14
  • 如何为 10 个连续点的每段绘制不同颜色的线?
    如何为 10 个连续点的每段绘制不同颜色的线?
    用不同的颜色绘制一条线问题陈述给定两个列表,latt和lont,目标是绘制一条线,其中每个列表10 个连续点的线段以不同的方式表示color.解决方案线段数量有限如果线段数量较少,例如10个或更少,一个简单的方法就是使用循环以唯一的颜色绘制每个段。import numpy as np import ...
    编程 发布于2024-11-14

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

Copyright© 2022 湘ICP备2022001581号-3