数千道面试题尽在GolangRoadmap,一个年轻的GO开发者社区https://www.golangroadmap.com/,目前是邀请制注册,邀请码:Gopher-1035-0722
感觉百度Go岗位相对C++还是少很多,面试是群面,换了两次面试官才匹配到一个Golang的,然后就被反复摩擦,还是太菜了!
上来简单自我介绍一下,没有问项目,直接开始问Golang
-
写代码实现两个协程交替打印"1",“2”,这是个比较经典的问题了,务必掌握。
package main import ( "fmt" "sync" ) func main() { var ch1, ch2 = make(chan struct{}), make(chan struct{}) var wg sync.WaitGroup wg.Add(2) go func(s string) { defer wg.Done() for i := 1; i <= 10; i++ { <-ch1 fmt.Println(s) ch2 <- struct{}{} } <-ch1 }("1") go func(s string) { defer wg.Done() for i := 1; i <= 10; i++ { <-ch2 fmt.Println(s) ch1 <- struct{}{} } }("2") ch1 <- struct{}{} wg.Wait() }
-
defer与返回值的问题,这个问题主要考察对return语句的理解,"return xxx"包含三个步骤
(1)返回值=xxx
(2)调用defer函数
(3)将返回值返回
当返回值没有声明时,你可以把返回值当作一个Go语言自己声明的变量,如下代码package main import "fmt" func main() { fmt.Println(f1())//打印0 } func f1() int { var a int defer func() { a++ }() //返回值=a,此时a=0 //执行defer,a++,此时a=1 //返回 返回值,返回值=0 return a }
当返回值提前声明时
package main import "fmt" func main() { fmt.Println(f1())//打印1 } func f1() (a int) { defer func() { a++ }() //返回值a=a,此时a=0 //执行defer,a++,此时a=1 //返回a return a//等价与return }
再看一种情况
package main import "fmt" func main() { fmt.Println(f1())//打印6 } func f1() (a int) { defer func() { a++ }() //返回值a=5 //执行a++,此时a=6 //返回a return 5 }
再看一种情况
package main import "fmt" func main() { fmt.Println(f1())//打印5 } func f1() (y int) { x:=5 defer func() { x++ }() //y=x,此时x=5,y=5 //执行x++,此时x=6,y=5 //返回y return x }
最后一种情况
package main import "fmt" func main() { fmt.Println(f1())//打印6 } func f1() (x int) { defer func(x int) { x++ }(x) //x=5 //x++ //返回x return 5 }
-
如果优雅关闭http服务器
http-server运行过程中,若进程被关闭,那么正在处理的请求可能只被处理了一半就停止了,可能会产生数据的不一致。
优雅关机是指:首先,停止接收新请求;然后,等待队列中的请求被处理完毕;最后,应用程序退出;
Golang http.Server 结构体有一个终止服务的方法 Shutdown,使用 Shutdown 可以优雅的终止服务,其不会中断活跃连接。其工作过程为:首先关闭所有开启的监听器,然后关闭所有闲置连接,最后等待活跃的连接均闲置了才终止服务。若传入的 context 在服务完成终止前已超时,则 Shutdown 方法返回 context 的错误,否则返回任何由关闭服务监听器所引起的错误。当 Shutdown 方法被调用时,Serve、ListenAndServe 及 ListenAndServeTLS 方法会立刻返回 ErrServerClosed 错误。请确保 Shutdown 未返回时,勿退出程序。
signal 包的 Notify 函数提供系统信号通知的能力
func Notify(c chan<- os.Signal, sig …os.Signal)
参数 c 是调用者的信号接收通道,Notify 可将进入的信号转到 c。sig 参数为需要转发的信号类型,若不指定,所有进入的信号都将会转到 c。信号不会阻塞式的发给 c:调用者需确保 c 有足够的缓冲空间,以应对指定信号的高频发送。对于用于通知仅一个信号值的通道,缓冲大小为 1 即可。有了signal.Notify,传入一个 chan 并指定中断参数,这样当系统中断时,即可接收到信号。参看如下代码,当使用 Ctrl+C 时,c 会接收到中断信号,程序会在打印“program interrupted”语句后退出。func main() { c := make(chan os.Signal) signal.Notify(c, os.Interrupt) <-c log.Fatal("program interrupted") }
接下来我们使用如上 signal.Notify 结合 http.Server 的 Shutdown 方法实现服务优雅的终止。如下代码,Handler其会在2s后返回 hello。创建一个 http.Server 实例,指定端口与 Handler。声明一个 processed chan,其用来保证服务优雅的终止后再退出主 goroutine。新启一个 goroutine,其会监听 os.Interrupt 信号,一旦服务被中断即调用服务的 Shutdown 方法,确保活跃连接的正常返回(本代码使用的 Context 超时时间为 3s,大于服务 Handler 的处理时间,所以不会超时)。处理完成后,关闭 processed 通道,最后主 goroutine 退出。
var addr = flag.String("server addr", ":8080", "server address") func main() { // handler handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(2 * time.Second) fmt.Fprintln(w, "hello") }) // server srv := http.Server{ Addr: *addr, Handler: handler, } // make sure idle connections returned processed := make(chan struct{}) go func() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) <-c ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() if err := srv.Shutdown(ctx); nil != err { log.Fatalf("server shutdown failed, err: %v\n", err) } log.Println("server gracefully shutdown") close(processed) }() // serve err := srv.ListenAndServe() if http.ErrServerClosed != err { log.Fatalf("server not gracefully shutdown, err :%v\n", err) } // waiting for goroutine above processed <-processed }