第 20 章 - Golang 网络编程

news2024/11/19 21:49:12

下面将详细介绍Go语言中的网络编程,包括TCP编程、UDP编程和HTTP服务器的创建。为了更好地理解这些概念,我将提供一些简单的示例代码来说明每个部分。

1. TCP 编程

TCP(传输控制协议)是一种面向连接的协议,它确保数据包以正确的顺序到达,并且在发送方和接收方之间建立了一个可靠的通信通道。

创建一个TCP服务器
package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }
    defer listener.Close()
    fmt.Println("Listening on 0.0.0.0:8080")

    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            continue
        }

        go handleRequest(conn)
    }
}

func handleRequest(conn net.Conn) {
    defer conn.Close()

    reader := bufio.NewReader(conn)
    for {
        message, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("Error reading:", err.Error())
            return
        }

        fmt.Print("Received message:", string(message))
        newMessage := "Pong\n"
        conn.Write([]byte(newMessage)) // 发送响应
    }
}
创建一个TCP客户端
package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func main() {
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        fmt.Println("Error connecting:", err.Error())
        os.Exit(1)
    }
    defer conn.Close()

    _, err = conn.Write([]byte("Ping\n"))
    if err != nil {
        fmt.Println("Error sending:", err.Error())
        os.Exit(1)
    }

    message, err := bufio.NewReader(conn).ReadString('\n')
    if err != nil {
        fmt.Println("Error reading:", err.Error())
        os.Exit(1)
    }

    fmt.Print("Message from server:", message)
}

2. UDP 编程

UDP(用户数据报协议)是一种无连接的协议,它不保证数据包的顺序或可靠性,但是它的开销比TCP小得多。

创建一个UDP服务器
package main

import (
    "fmt"
    "net"
)

func main() {
    addr, _ := net.ResolveUDPAddr("udp", ":8081")
    conn, _ := net.ListenUDP("udp", addr)
    defer conn.Close()

    buf := make([]byte, 1024)

    for {
        n, clientAddr, _ := conn.ReadFromUDP(buf)
        fmt.Printf("Received from %s: %s\n", clientAddr.String(), string(buf[:n]))

        // 回复消息
        conn.WriteToUDP([]byte("Pong"), clientAddr)
    }
}
创建一个UDP客户端
package main

import (
    "fmt"
    "net"
)

func main() {
    addr, _ := net.ResolveUDPAddr("udp", "localhost:8081")
    conn, _ := net.DialUDP("udp", nil, addr)
    defer conn.Close()

    message := "Ping"
    conn.Write([]byte(message))

    buf := make([]byte, 1024)
    n, _, _ := conn.ReadFromUDP(buf)
    fmt.Println("Server response:", string(buf[:n]))
}

3. HTTP 服务器

HTTP是用于传输超文本的应用层协议。在Go中,可以使用net/http包轻松地创建HTTP服务器。

创建一个简单的HTTP服务器
package main

import (
    "fmt"
    "net/http"
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!")
}

func main() {
    http.HandleFunc("/", helloWorld)
    http.ListenAndServe(":8080", nil)
}

以上就是Go语言中TCP、UDP和HTTP编程的基本介绍及示例。通过这些例子,你可以看到Go语言在网络编程方面的简洁性和强大性。

接下来我会进一步扩展前面的内容,增加一些更高级的功能和实践案例,帮助你更深入地了解Go语言在网络编程中的应用。

4. HTTP 服务器的进阶功能

处理静态文件

除了处理动态请求外,HTTP服务器还可以配置为提供静态文件服务,比如HTML、CSS、JavaScript等。

package main

import (
    "log"
    "net/http"
)

func main() {
    // 设置静态文件目录
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

    // 定义一个简单的路由
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "index.html")
    })

    log.Println("Starting server on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

在这个例子中,/static/路径下的所有请求都会被重定向到名为static的目录下。同时,根路径/会返回index.html文件。

使用中间件

中间件是在处理请求之前或之后执行的一段代码。它可以用来做日志记录、身份验证等。

package main

import (
    "log"
    "net/http"
)

// Middleware function
func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World!"))
}

func main() {
    router := http.NewServeMux()
    router.HandleFunc("/", helloWorld)

    // 使用中间件
    loggedRouter := loggingMiddleware(router)

    log.Println("Starting server on :8080")
    if err := http.ListenAndServe(":8080", loggedRouter); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

5. 并发处理

Go语言的并发模型基于goroutines,这是一种轻量级的线程,由Go运行时管理和调度。在处理网络请求时,使用goroutines可以非常高效地实现并发处理。

并发TCP服务器
package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func handleConnection(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    for {
        message, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("Error reading:", err.Error())
            return
        }
        fmt.Print("Received message:", string(message))
        newMessage := "Pong\n"
        conn.Write([]byte(newMessage)) // 发送响应
    }
}

func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }
    defer listener.Close()
    fmt.Println("Listening on 0.0.0.0:8080")

    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            continue
        }

        // 使用goroutine并发处理每个连接
        go handleConnection(conn)
    }
}

6. 错误处理和优雅关闭

在生产环境中,错误处理和优雅关闭是非常重要的。Go语言提供了丰富的错误处理机制和优雅关闭的方法。

优雅关闭TCP服务器
package main

import (
    "bufio"
    "context"
    "fmt"
    "net"
    "os"
    "sync"
    "time"
)

var wg sync.WaitGroup

func handleConnection(ctx context.Context, conn net.Conn) {
    defer wg.Done()
    defer conn.Close()

    reader := bufio.NewReader(conn)
    for {
        select {
        case <-ctx.Done():
            return
        default:
            message, err := reader.ReadString('\n')
            if err != nil {
                fmt.Println("Error reading:", err.Error())
                return
            }
            fmt.Print("Received message:", string(message))
            newMessage := "Pong\n"
            conn.Write([]byte(newMessage)) // 发送响应
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }
    defer listener.Close()
    fmt.Println("Listening on 0.0.0.0:8080")

    go func() {
        <-time.After(10 * time.Second)
        cancel()
    }()

    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            continue
        }

        wg.Add(1)
        go handleConnection(ctx, conn)
    }

    wg.Wait()
    fmt.Println("Server gracefully shut down.")
}

在这个例子中,我们使用了context来管理服务器的生命周期。当cancel函数被调用时,所有的goroutines都会收到信号并停止处理新的请求,从而实现优雅关闭。

7. 安全性

在实际应用中,安全性是一个非常重要的方面。对于HTTP服务器,可以使用TLS(传输层安全协议)来加密通信。

创建一个支持TLS的HTTP服务器

首先,你需要生成一个自签名证书(或者使用正式的CA签发的证书)。

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes

然后,修改你的HTTP服务器代码以支持TLS:

package main

import (
    "log"
    "net/http"
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World!"))
}

func main() {
    http.HandleFunc("/", helloWorld)

    log.Println("Starting server on :8443")
    if err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil); err != nil {
        log.Fatal("ListenAndServeTLS: ", err)
    }
}

在这个例子中,http.ListenAndServeTLS函数用于启动一个支持TLS的HTTP服务器。cert.pemkey.pem分别是证书和私钥文件。

总结

通过上述示例,你可以看到Go语言在网络编程中的强大功能和灵活性。从简单的TCP/UDP服务器到复杂的HTTP服务器,再到并发处理、优雅关闭和安全性,Go语言都提供了简单而强大的工具。

接下来我们将继续扩展网络编程的主题,涵盖更多高级功能和实用技巧,包括负载均衡、分布式系统、服务发现、API网关等。

11. 负载均衡

负载均衡是将客户端请求分发到多个后端服务器的技术,以提高系统的可用性和性能。Go语言可以通过多种方式实现负载均衡,包括使用Nginx、HAProxy等外部负载均衡器,或者在应用层实现简单的轮询负载均衡。

使用Nginx作为负载均衡器

首先,安装Nginx并配置负载均衡:

http {
    upstream backend {
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://backend;
        }
    }
}

然后,启动两个简单的HTTP服务器:

// server1.go
package main

import (
    "fmt"
    "log"
    "net/http"
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello from Server 1!"))
}

func main() {
    http.HandleFunc("/", helloWorld)
    log.Println("Starting server on :8081")
    if err := http.ListenAndServe(":8081", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}
// server2.go
package main

import (
    "fmt"
    "log"
    "net/http"
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello from Server 2!"))
}

func main() {
    http.HandleFunc("/", helloWorld)
    log.Println("Starting server on :8082")
    if err := http.ListenAndServe(":8082", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

12. 分布式系统和服务发现

在分布式系统中,服务发现是一个关键组件,它帮助服务实例找到彼此。常见的服务发现工具包括Consul、Etcd和Zookeeper。

使用Consul进行服务发现

首先,安装Consul并启动Consul服务:

consul agent -dev

然后,注册服务到Consul:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
)

func registerService(serviceName, serviceID, serviceAddress string, servicePort int) error {
    registration := map[string]interface{}{
        "ID":      serviceID,
        "Name":    serviceName,
        "Address": serviceAddress,
        "Port":    servicePort,
        "Check": map[string]interface{}{
            "HTTP":      fmt.Sprintf("http://%s:%d/health", serviceAddress, servicePort),
            "Interval":  "10s",
            "Timeout":   "1s",
            "DeregisterCriticalServiceAfter": "5m",
        },
    }

    data, err := json.Marshal(registration)
    if err != nil {
        return err
    }

    resp, err := http.Post("http://127.0.0.1:8500/v1/agent/service/register", "application/json", strings.NewReader(string(data)))
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return err
    }

    if resp.StatusCode != 200 {
        return fmt.Errorf("failed to register service: %s", body)
    }

    return nil
}

func healthCheck(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello from Service!"))
}

func main() {
    serviceName := "my-service"
    serviceID := "my-service-1"
    serviceAddress := "127.0.0.1"
    servicePort := 8080

    if err := registerService(serviceName, serviceID, serviceAddress, servicePort); err != nil {
        log.Fatalf("Failed to register service: %v", err)
    }

    http.HandleFunc("/health", healthCheck)
    http.HandleFunc("/", helloWorld)

    log.Println("Starting server on :8080")
    if err := http.ListenAndServe(fmt.Sprintf(":%d", servicePort), nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

13. API 网关

API网关是微服务架构中的一个重要组件,它充当所有客户端请求的单一入口点。常见的API网关实现包括Kong、Traefik和Envoy。

使用Traefik作为API网关

首先,安装Traefik并配置Traefik:

# traefik.toml
[entryPoints]
  [entryPoints.web]
    address = ":80"

[providers.file]
  filename = "dynamic.toml"
  watch = true

[api]
  dashboard = true
# dynamic.toml
[http.routers]
  [http.routers.my-router]
    rule = "Host(`example.com`)"
    service = "my-service"
    entryPoints = ["web"]

[http.services]
  [http.services.my-service.loadBalancer]
    [[http.services.my-service.loadBalancer.servers]]
      url = "http://127.0.0.1:8080"
    [[http.services.my-service.loadBalancer.servers]]
      url = "http://127.0.0.1:8081"

然后,启动Traefik:

traefik --configfile=traefik.toml

14. 高级中间件

在实际应用中,中间件可以用于实现各种功能,如日志记录、认证、限流等。

认证中间件
package main

import (
    "log"
    "net/http"
    "strings"
)

func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        parts := strings.Split(authHeader, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        token := parts[1]
        if token != "secret-token" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        next.ServeHTTP(w, r)
    })
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World!"))
}

func main() {
    router := http.NewServeMux()
    router.HandleFunc("/", helloWorld)

    // 使用认证中间件
    loggedRouter := authMiddleware(router)

    log.Println("Starting server on :8080")
    if err := http.ListenAndServe(":8080", loggedRouter); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

15. 日志记录和监控

日志记录和监控是确保系统稳定运行的重要手段。Go语言提供了多种日志库和监控工具,如Logrus、Prometheus和Grafana。

使用Logrus进行日志记录
package main

import (
    "log"
    "net/http"

    "github.com/sirupsen/logrus"
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
    logrus.Info("Handling request")
    w.Write([]byte("Hello World!"))
}

func main() {
    logrus.SetFormatter(&logrus.JSONFormatter{})
    logrus.SetLevel(logrus.DebugLevel)

    http.HandleFunc("/", helloWorld)

    log.Println("Starting server on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}
使用Prometheus进行监控

首先,安装Prometheus和Prometheus客户端库:

go get github.com/prometheus/client_golang/prometheus/promhttp

然后,添加监控指标:

package main

import (
    "log"
    "net/http"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    requestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total number of HTTP requests.",
        },
        []string{"method", "path"},
    )
)

func init() {
    prometheus.MustRegister(requestsTotal)
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
    requestsTotal.WithLabelValues(r.Method, r.URL.Path).Inc()
    w.Write([]byte("Hello World!"))
}

func main() {
    http.HandleFunc("/", helloWorld)

    // 添加Prometheus监控端点
    http.Handle("/metrics", promhttp.Handler())

    log.Println("Starting server on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

总结

通过上述示例,你可以看到Go语言在网络编程中的多样性和灵活性。从负载均衡到分布式系统,再到API网关和高级中间件,Go语言都提供了强大的支持。希望这些示例能帮助你更好地理解和应用Go语言在网络编程中的各种场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2243637.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——14.哈希(2)(模拟实现)

1.概念介绍 1.1开散列 开散列&#xff08;Open Hashing&#xff09;&#xff0c;也叫链地址法&#xff0c;是一种解决哈希冲突的方法。每个哈希表槽位保存一个链表&#xff0c;所有散列到同一位置的元素都存储在该链表中。当插入元素发生冲突时&#xff0c;将新元素添加到相应…

使用Web Speech API实现语音识别与合成技术

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Web Speech API实现语音识别与合成技术 使用Web Speech API实现语音识别与合成技术 使用Web Speech API实现语音识别与合成技…

自动驾驶系列—面向自动驾驶的模型迭代:工具、平台与最佳实践

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

【Golang】——Gin 框架中的模板渲染详解

Gin 框架支持动态网页开发&#xff0c;能够通过模板渲染结合数据生成动态页面。在这篇文章中&#xff0c;我们将一步步学习如何在 Gin 框架中配置模板、渲染动态数据&#xff0c;并结合静态资源文件创建一个功能完整的动态网站。 文章目录 1. 什么是模板渲染&#xff1f;1.1 概…

网络基础 - NAT 篇

一、全局 IP 地址(公网 IP 地址)和私有 IP 地址 RFC 1918 规定了用于组建局域网的私有 IP 地址&#xff1a; 10.0.0.0 ~ 10.255.255.255172.16.0.0 ~ 172.31.255.255192.168.0.0 ~ 192.168.255.255 包含在以上范围内的 IP 地址都属于私有 IP 地址&#xff0c;而在此之外的 I…

ClickHouse的介绍、安装、数据类型

1、介绍和安装 1.1、简介 ClickHouse是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;主要用于在线分析处理查询&#xff08;OLAP&#xff09;&#xff0c;能够使用SQL查询实时生成分析数据报告。 OLAP&…

基于AOA算术优化的KNN数据聚类算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于AOA算术优化的KNN数据聚类算法matlab仿真。通过AOA优化算法&#xff0c;搜索最优的几个特征数据&#xff0c;进行KNN聚类&#xff0c;同时对比不同个数特征下…

tcp 超时计时器

在 TCP&#xff08;传输控制协议&#xff09;中有以下四种重要的计时器&#xff1a; 重传计时器&#xff08;Retransmission Timer&#xff09; 作用&#xff1a;用于处理数据包丢失的情况。当发送方发送一个数据段后&#xff0c;就会启动重传计时器。如果在计时器超时之前没有…

《Probing the 3D Awareness of Visual Foundation Models》论文解析——多视图一致性

一、论文简介 论文讨论了大规模预训练产生的视觉基础模型在处理任意图像时的强大能力&#xff0c;这些模型不仅能够完成训练任务&#xff0c;其中间表示还对其他视觉任务&#xff08;如检测和分割&#xff09;有用。研究者们提出了一个问题&#xff1a;这些模型是否能够表示物体…

【论文阅读】WaDec: Decompiling WebAssembly Using Large Language Model

论文阅读笔记:WaDec: Decompiling WebAssembly Using Large Language Model 1. 来源出处 论文标题: WaDec: Decompiling WebAssembly Using Large Language Model作者: Xinyu She, Yanjie Zhao, Haoyu Wang会议: 39th IEEE/ACM International Conference on Automated Softwar…

【数字孪生】从Abaqus到Unity有限元应力云图

从abaqus到unity&#xff1a; 目录 1. 数据准备 1.1 abaqus中提取element rpt文件 element rpt文件格式&#xff1a; 1.2 abaqus中提取node rpt文件&#xff1a; node rpt文件格式&#xff1a; 2. python预处理以上数据&#xff1a; 2.1 提取node rpt中的节点坐标及应力…

一次需升级系统的wxpython安装(macOS M1)

WARNING: The scripts libdoc, rebot and robot are installed in /Users/用户名/Library/Python/3.8/bin which is not on PATH. 背景&#xff1a;想在macos安装Robot Framework &#xff0c;显示pip3不是最新&#xff0c;更新pip3后显示不在PATH上 参看博主文章末尾 MAC系统…

MySQL45讲 第二十五讲 高可用性深度剖析:从主备原理到策略选择

文章目录 MySQL45讲 第二十五讲 高可用性深度剖析&#xff1a;从主备原理到策略选择一、MySQL 主备基础原理&#xff08;一&#xff09;主备关系与数据同步&#xff08;二&#xff09;主备切换流程 二、主备延迟分析&#xff08;一&#xff09;主备延迟的定义与计算&#xff08…

跨越网络边界:IPv6与零信任架构的深度融合

2024年&#xff0c;工信部发布了《关于开展“网络去NAT”专项工作 进一步深化IPv6部署应用的通知》&#xff0c;加速了国内网络由IPv4向IPv6的转型步伐。未来&#xff0c;各行各业将逐步去NAT&#xff0c;逐步向IPv6迁移。在此过程中&#xff0c;网络安全解决方案和产品能力将面…

Linux—ln(link files)命令使用方法(How to create links on Linux)

Linux—ln&#xff08;link files&#xff09;命令使用方法 在 Linux 系统中工作时&#xff0c;需要在不同的目录中使用相同的文件时&#xff0c;不必在每个目录下都复制一份文件&#xff0c;这样不仅浪费磁盘空间&#xff0c;还会导致文件管理上的混乱。 ln(link files) 便是…

我要成为算法高手-位运算篇

目录 1. 判断字符是否唯一2. 消失的数字3. 两整数之和4. 只出现一次的数字II5. 消失的两个数字 前情提要&#xff1a;如果对一些常见的二进制位运算不熟悉&#xff0c;请看这篇文章&#xff1a; 常见的位运算 1. 判断字符是否唯一 面试题 01.01. 判定字符是否唯一 - 力扣&…

1Panel 推送 SSL 证书到阿里云、腾讯云

本文首发于 Anyeの小站&#xff0c;点击链接 访问原文体验更佳 前言 都用 CDN 了还在乎那点 1 年证书钱么&#xff1f; 开句玩笑话&#xff0c;按照 Apple 的说法&#xff0c;证书有效期不该超过 45 天。那么证书有效期的缩短意味着要更频繁地更新证书。对于我这样的“裸奔”…

23种设计模式-访问者(Visitor)设计模式

文章目录 一.什么是访问者模式&#xff1f;二.访问者模式的结构三.访问者模式的应用场景四.访问者模式的优缺点五.访问者模式的C实现六.访问者模式的JAVA实现七.代码解释八.总结 类图&#xff1a; 访问者设计模式类图 一.什么是访问者模式&#xff1f; 访问者模式&#xff08;…

JavaScript——DOM编程、JS的对象和JSON

一、DOM编程 DOM(Document Object Model)编程&#xff1a;就是使用document对象的API&#xff0c;完成对网页HTML文档进行动态修改&#xff0c;以实现网页数据&#xff0c;和样式动态变化效果的编程。 (一)DOM获取元素的多种方法 1.查找元素的函数 getElementById("id值…

Pr:音频过渡

Adobe Premiere Pro 自带一组共三个音频过渡 Audio Transitions效果。 对音频剪辑之间应用交叉淡化 Crossfade过渡&#xff0c;操作方式类似于应用视频过渡效果。 对于交叉淡化&#xff0c;要保证前剪辑的出点之后及后剪辑的入点之前有足够的预留内容&#xff08;也称“手柄”&…