Golang | 腾讯一面

go的调度

Golang的调度器采用M:N调度模型,其中M代表用户级别的线程(也就是goroutine),而N代表的事内核级别的线程。Go调度器的主要任务就是N个OS线程上调度M个goroutine。这种模型允许在少量的OS线程上运行大量的goroutine

Go调度器使用了三种队列来管理goroutine

  1. 全局队列(Global Queue):此队列中包含了所有刚创建的goroutine
  2. 本地队列(Local Queue):每个P(Processor,处理器)都有一个本地队列,P会有限从本地队列中取出goroutine来执行。
  3. 网络轮循器(Netpoller):此队列中包含了所有在等待网络时间(如IO操作)的goroutine。当网络事件就绪时,对应的goroutine会被放入到全局队列中,等待被P取出。

Go的调度器采用了工作窃取(Work Stealing)和手动抢占(Preemption)的策略

  • 工作窃取:当一个P的本地队列中没有goroutine时,它会尝试从全局队列或其他P的本地队列中窃取goroutine来执行。
  • 手动抢占:为了防止一个goroutine长时间占用P而导致其他goroutine饿死,Go的调度器会定期地进行抢占操作。在Go 1.14之前,Go的调度器只在函数调用时才会进行抢占,从Go 1.14开始引入了异步抢占,即允许在任何安全点进行抢占。

go struct能不能比较

Golangstruct是否能比较取决于struct内变量的类型:

所有字段可比较:只有当结构体的所有字段都可以进行比较时,结构体本身才可以进行比较。如果结构体包含不可比较的字段,如切片(slice)、映射(map)或函数(func),则该结构体不能进行比较。

逐字段比较:结构体的比较是逐字段进行的,即比较结构体的每个字段的值。如果所有字段的值都相等,则两个结构体相等;否则,它们不相等。

go defer

基本用法

在Go语言中,defer关键字用于在函数返回前执行一段代码或调用一个清理函数。这对于处理文件关闭、解锁或者返回一些资源到资源池等操作非常有用。

其基本用法如下所示:

package mainimport "fmt"func main() {example()
}func example() {defer fmt.Println("world")fmt.Println("hello")
}

defer fmt.Println("world")语句会在函数example返回之前执行,所以输出的结果是:

在这里插入图片描述

执行顺序

当我们在一个函数内部调用defer关键字,Go实际上会把它后面的函数(通常是一个匿名函数或者清理函数)压入一个栈中。当外部函数准备返回的时候,Go会按照先进先出的(LIFO)的顺序调用并执行这个栈中的所有函数。

比如,如下示例:

package mainimport "fmt"func main() {example()
}func example() {defer fmt.Println("first")defer fmt.Println("second")defer fmt.Println("third")fmt.Println("function body")
}

其输出结果为:在这里插入图片描述

参数计算时机

需要注意的是,defer语句的参数会在defer语句处就计算好,而不是在外部函数返回时才计算。比如如下例子

package mainimport "fmt"func main() {example()
}func example() {i := 0defer fmt.Println(i) i++fmt.Println(i)
}
/*
1
0
*/

实际应用举例

关闭文件
package mainimport ("fmt""io/ioutil""os"
)func readFile(fileName string) (string, error) {file, err := os.Open(fileName)if err != nil {return "", err}defer file.Close() // 确保文件在函数返回前被关闭content, err := ioutil.ReadAll(file)if err != nil {return "", err}return string(content), nil
}func main() {content, err := readFile("example.txt")if err != nil {fmt.Println("Error:", err)return}fmt.Println("File content:", content)
}
解锁互斥锁
package mainimport ("fmt""sync"
)var mtx sync.Mutex
var cnt intconst N int = 10func increment() {mtx.Lock()defer mtx.Unlock()cnt++
}func main() {var wg sync.WaitGroupfor i := 0; i < N; i++ {wg.Add(1)go func() {defer wg.Done()increment()}()}wg.Wait()fmt.Println("Final count: ", cnt)
}
释放网络连接
package mainimport ("fmt""io/ioutil""net/http"
)func fetchURL(url string) (string, error) {resp, err := http.Get(url)if err != nil {return "", err}defer resp.Body.Close() // 确保连接在函数返回前被关闭body, err := ioutil.ReadAll(resp.Body)if err != nil {return "", err}return string(body), nil
}func main() {content, err := fetchURL("http://baidu.com")if err != nil {fmt.Println("Error:", err)return}fmt.Println("URL content:", content)
}

select可以用于什么

select关键字用于处理同时来自多个通道的数据。它的基本工作原理是“随机选择”满足条件的分支去执行。如果没有分支满足条件(即所有通道都无法读/写),select会阻塞,直到有分支满足条件。如果select包含default分支,当其他分支都不满足条件时,default分支会被执行。

Goselect底层使用了一种名为scase的结构体,表示一个select分支,包含了通道和对应的操作类型(发送或者接收)。同时,它还会使用一个名为hchan 的结构体来表示通道的内部结构。

以下是select的一些重要的特性:

  • 公平性:在Go语言中,select语言会随机选择一个可以运行的case执行,保证了每一个case都有公平的机会被执行,避免了饥饿问题。
  • 非阻塞:如果select中所有的case都无法运行,而且存在default分支,那么select就不会被阻塞,而是执行default分支。
  • 可用于时间操作select经常与time.After, time.Tick等函数一起使用,用于实现超时操作或者定时操作。
  • 可用于退出操作select经常和context一起使用,当收到context的取消信号时,可以安全地退出协程。
package mainimport ("fmt""time"
)func selectExample(c1, c2 chan int, quit chan bool) {for {select {case v := <-c1:fmt.Println("Received from c1:", v)case v := <-c2:fmt.Println("Received from c2:", v)case <-quit:fmt.Println("Quit signal received. Exiting.")returndefault:fmt.Println("No data received.")time.Sleep(1 * time.Second) // 添加延迟以避免过度占用CPU}}
}func main() {// 创建通道c1 := make(chan int)c2 := make(chan int)quit := make(chan bool)// 启动selectExample函数作为goroutinego selectExample(c1, c2, quit)// 启动发送数据到c1的goroutinego func() {for i := 0; i < 5; i++ {c1 <- itime.Sleep(1 * time.Second)}}()// 启动发送数据到c2的goroutinego func() {for i := 0; i < 5; i++ {c2 <- i + 10time.Sleep(2 * time.Second)}}()// 从主goroutine发送退出信号time.Sleep(10 * time.Second) // 等待足够时间以查看输出quit <- truefmt.Println("Sent quit signal and waiting to exit.")
}

context包的用途

Golang中,context为我们提供了在跨API边界和进程之间传递请求作用域的deadline,取消信号,和其他请求响应的值的能力。

context包定义了Context类型,它在API边界和进程之间提供了一种传递传递请求作用域的deadline,取消信号,和其他请求响应的值的能力。一个Context的生命周期通常与请求处理的生命周期相同,并且可以包含在多个API调用和goroutine之间共享数据和取消信号。

context的主要方法有:
  • Deadline:返回当前Context合适会被取消。如果Context不会被取消,则返回ok为false
  • Done:返回一个通道,当Context被取消或超时的时候,该通道会被关闭。
  • Err:返回Context为何被取消。
  • Value:返回与Context相关的值,这些值必须是线程安全的。

Go语言的context包提供了两个函数用于创建Context对象:context.Background()context.TODO(),前者通常用在主函数、初始化以及测试代码中,表示一个空的Context,后者通常用在不确定应该使用什么Context,或者函数以后会更新以便接受一个Context参数。

此外,context包还提供了WithCancelWithDeadlineWithTimeoutWithValue函数,从现有的Context派生出新的Context

  • context.Background(): 返回一个空的Context,这个Context通常被用在main函数、初始化以及测试时。
  • context.TODO(): 当不确定应该使用什么Context,或者还未决定如何传递Context时,可以使用这个。
  • context.WithCancel(parent Context) (ctx Context, cancel CancelFunc): 根据已有的Context创建一个新的可取消的Context。
  • context.WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc): 根据已有的Context和设置的截止时间创建一个新的Context。
  • context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc): 基于已有的Context和指定的超时时间创建一个新的Context。
使用场景:
  1. 超时控制:我们可以通过context.WithTimeout创建一个超时的Context,当超时时间到达,该Context就会自动取消。比如,在数据库操作或外部服务调用时,可以通过设置超时的context来防止系统因为某个部分的响应缓慢而整体性能下降。
  2. 请求传递:在微服务或者并发编程的环境中,我们可以通过context.WithValue将请求相关的数据绑定到Context中,在函数调用链路上下游之间传递。
  3. 请求取消:我们可以通过Context.WithCancelcontext.WithTimeout创建一个可被取消的Context,并在需要取消的时候调用Contextcancel函数。比如在Web服务器中,如果客户端中断了请求,服务器可以通过context来取消所有相关的goroutine,从而避免资源浪费。
  4. 优雅地处理goroutine的生命周期: 通过contextDone通道,可以监听到取消信号,从而在goroutine中适时地释放资源,停止执行,保证程序的健売性和响应性。
package mainimport ("context""fmt""time"
)func calculate(ctx context.Context, ch chan []int) {for {select {case data := <-ch:// 模拟一些处理过程process(data)case <-ctx.Done():// 如果context收到取消信号(超时或显式取消),则停止操作return}}
}func process(data []int) {// 实际处理数据的函数fmt.Println(data)
}func ContextCase() {ctx := context.Background()                         // 创建一个背景contextctx = context.WithValue(ctx, "desc", "ContextCase") // 向context中添加键值对,这里添加描述信息"ContextCase"// 创建一个带有超时的context,超时时间为2秒ctx, cancel := context.WithTimeout(ctx, time.Second*2)defer cancel() // 确保在函数结束时取消context,以释放相关资源data := [][]int{ // 初始化数据{1, 2},{3, 2},}ch := make(chan []int) // 创建一个传递数据的channelgo calculate(ctx, ch)  // 启动一个goroutine来处理从channel接收的数据for i := 0; i < len(data); i++ {ch <- data[i] // 向channel发送数据}time.Sleep(time.Second * 10) // 主goroutine睡眠10秒
}func main() {ContextCase()
}

client如何实现长连接

  • 使用 net 包进行低级 TCP 连接,适用于自定义协议或非 HTTP 通信。
  • 使用 http 包配置 http.Client 保持 HTTP 连接。
  • 使用 WebSocket 实现实时双向通信,适用于需要实时数据传输的应用。

主协程如何等其余协程完再操作

使用sync.WaitGroup()

package mainimport ("fmt""sync""time"
)func main() {var wg sync.WaitGroup// 启动多个协程numGoroutines := 3for i := 1; i <= numGoroutines; i++ {wg.Add(1) // 每启动一个协程前调用 Add(1)go func(id int) {defer wg.Done() // 协程完成后调用 Done()fmt.Printf("Goroutine %d is working...\n", id)time.Sleep(time.Second * time.Duration(id))fmt.Printf("Goroutine %d is done.\n", id)}(i)}// 等待所有协程完成wg.Wait()fmt.Println("All goroutines have completed. Main function can proceed.")
}

slice,len,cap,共享,扩容

len求当前元素的数量,即长度。cap求切片的容量。

Golang切片的扩容机制如下:

  1. 初始容量
    • 新创建的切片默认容量为0或根据创建时提供的元素数量确定。
  2. 扩容规则
    • 当需要扩容时,Go语言的切片扩容机制通常是将其容量翻倍(cap *= 2),直到达到一定的阈值后(例如1024),扩容策略改为每次增长大约1/4(cap += cap / 4),以降低大型切片的扩容开销。
    • 自Go 1.16版本开始,扩容策略稍有改变,但仍基于翻倍原则,但在容量大于1024时,并非简单地增加1/4,而是使用更复杂的算法,旨在更好地平衡内存使用和性能。
  3. 特殊情况
    • 当切片的当前容量小于1000时,Go可能并不会严格遵循上述翻倍策略,而是采用更保守的扩容策略,以适应小容量切片的需求。
    • 如果所需的新增容量远大于当前容量,Go可能会直接将容量设置为所需容量,而不是简单地翻倍。
  4. 性能考量
    • 扩容操作伴随着内存分配和数据拷贝,因此在编写代码时应尽量避免过于频繁的扩容操作,可以预先估计数据量并使用make函数指定合适的初始容量。

map如何顺序读取

map本身是无顺序的,所以要想顺序读取,就必须将map中的内容存到一个切片中,然后对切片进行排序。

实现set

通过map来实现,map 的键可以用来存储集合中的元素,而值可以被设为 struct{}bool。使用 struct{} 的好处是它不占用额外的内存空间。

package mainimport ("fmt"
)// Set 结构体表示一个集合
type Set struct {elements map[string]struct{}
}// NewSet 创建一个新的 Set
func NewSet() *Set {return &Set{elements: make(map[string]struct{}),}
}// Add 向集合中添加元素
func (s *Set) Add(element string) {s.elements[element] = struct{}{}
}// Remove 从集合中删除元素
func (s *Set) Remove(element string) {delete(s.elements, element)
}// Contains 检查集合中是否包含某个元素
func (s *Set) Contains(element string) bool {_, exists := s.elements[element]return exists
}// Size 返回集合中元素的数量
func (s *Set) Size() int {return len(s.elements)
}// Clear 清空集合
func (s *Set) Clear() {s.elements = make(map[string]struct{})
}// Elements 返回集合中的所有元素
func (s *Set) Elements() []string {keys := make([]string, 0, len(s.elements))for k := range s.elements {keys = append(keys, k)}return keys
}func main() {mySet := NewSet()mySet.Add("apple")mySet.Add("banana")mySet.Add("orange")fmt.Println("Set contains 'apple':", mySet.Contains("apple")) fmt.Println("Set size:", mySet.Size())                        mySet.Remove("banana")fmt.Println("Set contains 'banana':", mySet.Contains("banana")) fmt.Println("Set elements:", mySet.Elements())                   
}

实现消息队列(多生产者,多消费者)

定义生产者和消费者函数

  • producer 函数生成消息并将其发送到通道。
  • consumer 函数从通道接收消息并处理它们。

创建一个带缓冲的通道

  • queue 是一个带缓冲的通道,用来在生产者和消费者之间传递消息。

启动多个生产者和消费者

  • 使用 sync.WaitGroup 来跟踪生产者和消费者的完成情况。
  • 每个生产者和消费者在各自的 goroutine 中运行。

等待所有生产者完成并关闭通道

  • 使用 wg.Wait() 等待所有生产者完成。
  • 关闭通道以通知消费者没有更多消息。

等待所有消费者完成

  • 使用另一个 sync.WaitGroup 来跟踪消费者的完成情况。
  • 确保所有消费者在通道关闭后都能处理完剩余的消息。
package mainimport ("fmt""sync""time"
)// 生产者函数
func producer(id int, queue chan<- int, wg *sync.WaitGroup) {defer wg.Done()for i := 0; i < 5; i++ {msg := id*10 + ifmt.Printf("Producer %d: produced %d\n", id, msg)queue <- msgtime.Sleep(time.Millisecond * 100) // 模拟生产时间}
}// 消费者函数
func consumer(id int, queue <-chan int, wg *sync.WaitGroup) {defer wg.Done()for msg := range queue {fmt.Printf("Consumer %d: consumed %d\n", id, msg)time.Sleep(time.Millisecond * 150) // 模拟消费时间}
}func main() {var wg sync.WaitGroupqueue := make(chan int, 10) // 创建一个带缓冲的通道// 启动多个生产者numProducers := 3for i := 1; i <= numProducers; i++ {wg.Add(1)go producer(i, queue, &wg)}// 启动多个消费者numConsumers := 2for i := 1; i <= numConsumers; i++ {wg.Add(1)go consumer(i, queue, &wg)}// 等待所有生产者完成wg.Wait()close(queue) // 关闭通道以通知消费者没有更多消息// 再次等待所有消费者完成var consumerWg sync.WaitGroupfor i := 1; i <= numConsumers; i++ {consumerWg.Add(1)go func() {defer consumerWg.Done()for msg := range queue {fmt.Printf("Final Consumer %d: consumed %d\n", i, msg)time.Sleep(time.Millisecond * 150)}}()}consumerWg.Wait()
}

大文件排序

分块排序

  • 使用将大文件分块读取到内存,每次读取 chunk_size 行。
  • 对每个小块进行排序,并将已排序的小块写入临时文件。
  • 将每个小块文件的路径存储在 chunks 列表中。

归并排序

  • 打开所有小块文件,并初始化一个最小堆,将每个文件的第一行加入堆中。
  • 逐步从堆中取出最小元素,并将其写入输出文件。
  • 从相应的小块文件中读取下一行数据,如果该文件未读完,将下一行数据加入堆中;否则,关闭该文件。
  • 重复以上过程直到堆为空,表示所有数据已排序并写入输出文件。

清理临时文件

  • 合并完成后,删除所有临时小块文件。

基本排序,哪些是稳定的

稳定的排序算法:冒泡排序、插入排序、归并排序、基数排序、计数排序。

不稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。

Http get跟head

HTTP GET

功能
  • 请求资源GET 请求用于从服务器请求指定的资源。服务器返回资源的内容和相关的头部信息。
  • 常见用途:获取网页内容、下载文件、获取 API 数据等。
特点
  • 包含响应体GET 请求的响应通常包含资源的完整内容,即响应体。
  • 安全性和幂等性GET 请求是安全的(不会改变服务器的状态),也是幂等的(多次相同请求结果相同)。
示例
GET /index.html HTTP/1.1
Host: www.example.com

HTTP HEAD

功能
  • 请求头部信息HEAD 请求与 GET 请求类似,但服务器只返回头部信息,不返回响应体。
  • 常见用途:检查资源是否存在、获取资源的元数据(如内容类型、长度、最后修改时间等)。
特点
  • 无响应体HEAD 请求的响应不包含资源的内容,只有头部信息。
  • 安全性和幂等性HEAD 请求是安全的,也是幂等的,通常用于在不下载整个资源的情况下获取资源的信息。
示例
HEAD /index.html HTTP/1.1
Host: www.example.com

Http 401,403

状态码含义主要原因重试机会
401未授权客户端未通过身份验证或未提供有效的凭据可以,通过提供正确的身份验证信息
403禁止访问客户端已通过身份验证,但没有权限访问资源不可以,权限问题无法通过重试解决

Http keep-alive

在 HTTP/1.0 中,每个请求/响应对都是通过单独的 TCP 连接来处理的,这意味着每次请求都需要开销较大的连接建立和关闭过程。HTTP/1.1 引入了持久连接(persistent connections),即 Keep-Alive,允许在同一个连接上处理多个请求和响应。

GET /index.html HTTP/1.1
Host: www.example.com
Connection: keep-alive
Keep-Alive: timeout=10, max=100

timeout=10 表示如果在10秒内没有新的请求,连接将关闭;max=100 表示最多可以在该连接上发送100个请求。

Http能不能一次连接多次请求,不等后端返回

  1. 并行请求: 虽然单个 TCP 连接只能处理一个请求的响应周期,但客户端可以通过创建多个 TCP 连接来实现并行请求。通常,浏览器和现代 HTTP 客户端会使用多个并发的 TCP 连接来同时发起多个请求,以减少请求之间的等待时间。
  2. HTTP/2 多路复用: HTTP/2 协议支持在同一个 TCP 连接上进行多个并发的请求和响应。这种技术称为多路复用(Multiplexing),它允许多个 HTTP 请求和响应同时在同一个连接上进行传输,提高了性能和效率。
  3. 长连接(Keep-Alive): HTTP/1.1 引入了持久连接(Keep-Alive),允许在同一个 TCP 连接上发送多个 HTTP 请求和响应。在此模式下,客户端在发送完一个请求后不关闭连接,可以继续发送其他请求,服务器也可以在响应后保持连接打开以发送后续响应。这种方式虽然不能真正并行发送多个请求,但可以减少连接的建立和关闭开销,提升效率。

time-wait的作用

确保连接的正确关闭: TIME_WAIT状态允许所有可能的延迟数据包被接收和处理,避免新的连接收到旧连接的数据包。

防止重复的数据包: 由于TCP连接关闭时,可能还有一些数据包在网络中传输。进入TIME_WAIT状态可以确保这些数据包有足够的时间被处理,避免影响后续连接。

提供时间处理丢失的FIN包: 在关闭连接的过程中,双方需要确认FIN包。如果ACK包丢失了,发送FIN的一方需要在TIME_WAIT期间重新发送FIN包,确保连接能正常关闭。

避免端口重用冲突: TIME_WAIT状态确保在特定时间内,不能重用同一端口,以防止与旧连接的冲突。

数据库如何建索引

1. 基本索引类型

  1. B-树索引

    • 最常见的索引类型,适用于大多数情况。
    • 在MySQL中使用CREATE INDEX语句创建B-树索引。
    CREATE INDEX index_name ON table_name(column_name);
    
  2. 唯一索引

    • 确保索引列中的所有值都是唯一的。
    CREATE UNIQUE INDEX index_name ON table_name(column_name);
    
  3. 全文索引

    • 用于全文搜索,适用于大文本字段的快速搜索。
    • MySQL中可以对TEXT类型的字段使用全文索引。
    CREATE FULLTEXT INDEX index_name ON table_name(column_name);
    
  4. 哈希索引

    • 基于哈希表的索引,适用于等值查询。
    • 一些数据库如MySQL的Memory引擎支持哈希索引。
    CREATE INDEX index_name USING HASH ON table_name(column_name);
    

2. 创建复合索引

复合索引是基于多个列创建的索引,适用于涉及多个列的查询。

CREATE INDEX index_name ON table_name(column1, column2);

孤儿进程,僵尸进程

孤儿进程 (Orphan Process)

孤儿进程是指父进程已经终止,但子进程仍在运行的进程。操作系统会自动将孤儿进程的父进程重新分配给系统的init进程(在Unix和Linux系统中,PID为1的进程),从而确保这些孤儿进程不会没有父进程。

僵尸进程 (Zombie Process)

僵尸进程是指一个已经终止的子进程,但它的退出状态信息仍然保存在系统中,尚未被父进程读取。这个进程占用了一个进程ID(PID),但没有实际的进程资源。僵尸进程通常由父进程在调用wait()waitpid()函数之前子进程终止时产生。

git文件版本,使用顺序,merge跟rebase

Merge(合并)

git merge用于将一个分支的更改合并到当前分支。合并操作会创建一个新的提交,记录分支合并的历史。

git checkout main
git merge new_feature
Rebase(变基)

git rebase用于将一个分支上的提交应用到另一个分支的基础之上。它会重写提交历史,使之看起来像是在目标分支的基础上直接进行的更改。

git checkout new_feature
git rebase main

最后给大家推荐一个LinuxC/C++高级架构系统教程的学习资源与课程,可以帮助你有方向、更细致地学习C/C++后端开发,具体内容请见 https://xxetb.xetslk.com/s/1o04uB

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

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

相关文章

vue3 常用的知识点

setup:容许在script当中书写组合式API 并且vue3的template不再要求唯一的根元素 <script setup>const name app; </script>组合式API的用法&#xff1a; 可以直接在script标签中定义变量或者函数&#xff0c;然后直接在template当中使用 <template>{{mes…

编程类精品GPTs

文章目录 编程类精品GPTs前言种类ChatGPT - GrimoireProfessional-coder-auto-programming 总结 编程类精品GPTs 前言 代码类的AI, 主要看以下要点: 面对含糊不清的需求是否能引导出完整的需求面对完整的需求是否能分步编写代码完成需求编写的代码是否具有可读性和可扩展性 …

javaEE-03-cookie与session

文章目录 Cookie创建Cookie获取Cookie更新CookieCookie 生命控制Cookie 有效路径 Session 会话创建和获取sessionSession 域数据的存取Session 生命周期控制浏览器和 Session 之间关联 Cookie Cookie 是服务器通知客户端保存键值对的一种技术,客户端有了 Cookie 后&#xff0c…

【ARM】MDK-ARM软件开发工具的最终用户许可协议获取

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 了解MDK-ARM系列产品内软件开发工具的最终用户许可协议的获取。 2、 问题场景 对于部分外企客户需要软件开发工具的最终用户许可协议作为产品资料&#xff0c;以便附录并说明。 3、软硬件环境 1&#xff09;、软件…

Shell编程之正则表达式与文本处理器2--sed

目录 一、sed 工具 1. 概述 2. sed 原理 3、常用操作选项 3.1 常用选项 3.2 操作命令 4. sed 的使用 5. 具体操作 5.1 打印输出 p 5.1.1 显示范围、单行、指定行输出、指定往后加几行输出 5.1.2 显示奇偶行 5.1.3 将指定内容的行打印出来 5.1.4 只输出行号…

ESP32和mDNS学习

目录 mDNS的作用mDNS涉及到的标准文件组播地址IPv4 多播地址IPv6 多播地址预先定义好的组播地址 mDNS调试工具例程mDNS如何开发和使用注册服务查询服务 mDNS的作用 mDNS 是一种组播 UDP 服务&#xff0c;用来提供本地网络服务和主机发现。 你要和设备通信&#xff0c;需要记住…

python通过omniORBpy调用CORBA

omniORB参考地址&#xff1a; omniORB omniORB - Browse Files at SourceForge.net omniORB - Browse /omniORBpy/omniORBpy-4.3.0 at SourceForge.net Windows 普通使用好像不需要安装omniorb。就对接北向接口业务需要使用python3.10的windows包&#xff0c;但目前好像没有…

【CI/CD】docker + Nginx自动化构建部署

CI/CD是什么 CI/CD 是持续集成&#xff08;Continuous Integration&#xff09;和持续部署&#xff08;Continuous Deployment&#xff09;或持续交付&#xff08;Continuous Delivery&#xff09;的缩写&#xff0c;它们是现代软件开发中用于自动化软件交付过程的实践。 1、…

Centos安装、迁移gitlab

Centos安装迁移gitlab 一、下载安装二、配置rb修改&#xff0c;起服务。三、访问web&#xff0c;个人偏好设置。四、数据迁移1、查看当前GitLab版本2、备份旧服务器的文件3、将上述备份文件拷贝到新服务器同一目录下&#xff0c;恢复GitLab4、停止新gitlab数据连接服务5、恢复备…

GO-学习-03-基本数据类型

数据类型&#xff1a;基本数据类型和复合数据类型 基本数据类型&#xff1a;整型、浮点型、布尔型、字符串 复合数据类型&#xff1a;数组、切片、结构体、函数、map、通道&#xff08;channel&#xff09;、接口 整型&#xff1a; package main import "fmt" im…

Apache Doris + Paimon 快速搭建指南|Lakehouse 使用手册(二)

湖仓一体&#xff08;Data Lakehouse&#xff09;融合了数据仓库的高性能、实时性以及数据湖的低成本、灵活性等优势&#xff0c;帮助用户更加便捷地满足各种数据处理分析的需求。在过去多个版本中&#xff0c;Apache Doris 持续加深与数据湖的融合&#xff0c;已演进出一套成熟…

32单片机开发bootloader程序

一&#xff0c;单片机为什么要使用bootloader 1、使用bootloader的好处 1) 程序隔离&#xff1a;可以同时存在多个程序&#xff0c;只要flash空间够大&#xff0c;或者通过外挂flash&#xff0c;可以实现多个程序共存&#xff0c;在多个程序之间切换使用。 2&#xff09;方便程…

OpenHarmony 入门——初识JS/ArkTS 侧的“JNI” NAPI 常见的函数详解(二)

引言 前面一篇文章OpenHarmony 入门——初识JS/ArkTS 侧的“JNI” NAPI&#xff08;一&#xff09;介绍了NAPI的基础理论知识&#xff0c;今天重点介绍下NAPI中重要的函数。 一、Native 侧的NAPI的相关的C函数 以下面一段代码为例介绍下主要函数的功能和用法。 napi_value …

系统模块时序图的重要性:解锁系统模块交互的全景视图

在复杂的系统开发中,理解和管理不同模块之间的交互是成功的关键。时序图是一种有效的工具,可以帮助我们清晰地展示这些交互,提升设计和开发的效率。本文将深入探讨系统模块之间的时序图,并通过实例展示其实际应用。 1. 什么是系统模块之间的时序图? 系统模块之间的时序图…

Layui表格向下滑动时表头固定悬浮

记录&#xff1a;Layui表格向下滑动时表头固定悬浮 使用table的height参数&#xff1a; 示例 //“方法级渲染”配置方式 table.render({ //其它参数在此省略height: 315 //固定值 }); table.render({ //其它参数在此省略height: full-20 //高度最大化减去差值 }); 等价于&am…

项目的小结

1.实现实时聊天 1.服务端建立一个ConcurrentHashMap<> 用来存储在线用户&#xff0c;用户账号和socket然后&#xff0c;如果有个人发了信息&#xff0c;就去数据库中查询&#xff0c;然后根据这个在线用户进行传递信息 服务端框架&#xff1a; public class ServerMain {…

git sendemail使用

教程参考&#xff1a; git-send-email - 以电子邮件形式发送补丁集 1、安装git-email 2、配置 SMTP 服务器 git config --global sendemail.smtpserver smtp.163.com git config --global sendemail.smtpserverport 465 git config --global sendemail.smtpuser xxxxxx163.c…

Hyperledger Fabric 网络体验 - 网络启动过程概览

进入fabric-samples/test-network目录&#xff0c;执行指令&#xff1a; ./network.sh up -i 2.5执行完指令能看到fabric已经启动。 作为第一次Fabric网络体验&#xff0c;网络启动主要包含三个操作&#xff0c;分别是生成配置文件、启动网络和操作网络。 配置文件 使用cr…

传知代码-智慧医疗:纹理特征VS卷积特征(论文复现)

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 论文链接&#xff1a;https://www.sciencedirect.com/science/article/abs/pii/S1076633223003537?__cf_chl_rt_tkJ9Aipfxyk5d.leu48P20ePFNd4B2aunaSmzVpXCg.7g-1721292386-0.0.1.1-6249 论文概述 今天我们把视线…

【系统架构设计师】十八、信息系统架构设计理论与实践②

目录 四、企业信息系统的总体框架 4.1 战略系统 4.2 业务系统 4.3 应用系统 4.4 企业信息基础设施 4.5 业务流程重组BPR 4.6 业务流程管理BPM 五、信息系统架构设计方法 5.1 行业标准的体系架构框架 5.2 架构开发方法 5.3 信息化总体架构方法 5.4 信息化建设生命周…