企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 一、golang中关于grpc和rest都使用过吗?grpc相对于rest的优势是什么?为什么选择groc? **两种API架构概述** * grpc:gRPC是RPC框架中的一种,RPC(remote procedure call 远程过程调用)框架目标就是让远程服务调用更加简单、透明。RPC 框架负责屏蔽底层的传输方式(TCP 或者 UDP)、序列化方式(XML/Json/ 二进制)和通信细节。服务调用者可以像调用本地接口一样调用远程的服务提供者,而不需要关心底层通信细节和调用过程。RPC是一种设计理念,而gRPC是基于此种设计理念设计的真实框架。 * rest:描述的是在网络中client和server的一种交互形式;一个架构样式的网络系统,指的是一组架构约束条件和原则。 **grpc相对于rest的优势** gRPC 对接口有严格的约束条件,安全性更高,对于高并发的场景更适用 **为什么选择grpc** * grpc有明确的接口规范和对于流的支持; * RPC 效率更高。RPC使用自定义的 TCP 协议,可以让请求报文体积更小,或者使用 HTTP2 协议,也可以很好的减少报文的体积,提高传输效率。 ## 二、golang里面常用到的技术栈有哪些? 协程、通道、web框架、密码学等 ## 三、gin框架的好处是什么? * 快速:基于Radix树的路由,性能非常强大。 * 支持中间件:内置许多中间件,如Logger,Gzip,Authorization等。 * 崩溃恢复:可以捕捉panic引发的程序崩溃,使Web服务可以一直运行。 * JSON验证:可以验证请求中JSON数据格式。 * 多种数据渲染方式:支持HTML、JSON、YAML、XML等数据格式的响应。 * 扩展性:非常简单扩展中间件。 ## 四、无缓冲通道和缓冲通道的区别是什么? * 无缓冲通道,在通道满了之后就会阻塞所在的goroutine。(需要在其他goroutine中取出该通道中的元素,才能解除它所在通道的阻塞,不然就会一直阻塞下去。) * 缓冲通道,存完了东西可以不取出来,不会阻塞;除非通道被塞满才会被阻塞 * 缓冲通道相较于无缓冲区的通道在用法上是要灵活一些的,不会出现一次写入,一次读完就会堵塞。 ## 五、select的用处是什么? 过select可以监听channel上的数据流动。 select的用法与switch语言非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述。 示例代码如下: ~~~text select { case <-chan1: // 如果chan1成功读到数据,则进行该case处理语句 case chan2 <- 1: // 如果成功向chan2写入数据,则进行该case处理语句 default: // 如果上面都没有成功,则进入default处理流程 } ~~~ ## 六、defer的用途和使用场景是什么? defer也称延迟执行 defer作用:可用于捕获程序异常,在某个方法中,出现异常时,defer可捕获此异常并进行打印,使用关键字defer向函数声明退出调用,即主函数退出时,defer后的函数才被调用。defer语句的作用是不管程序是否出现异常,均在函数退出时自动执行相关代码。 ## 七、defer的执行顺序是什么? defer语句并不会马上执行,而是会进入一个栈,函数return前,会按先进后出的顺序执行。也说是说最先被定义的defer语句最后执行。 > 注:先进后出的原因是后面定义的函数可能会依赖前面的资源,自然要先执行;否则,如果前面先执行,那后面函数的依赖就没有 ## 八、defer函数遇到return以后是怎么执行的? 先defer再return,函数执行之后,return返回之前,按照先进后出的顺序执行 ## 九、对于进程,线程,协程的理解是什么 * 线程可以理解为轻量级的进程 协程可以理解为轻量级的线程 * 协程最大的优势就是可以轻松的创建上百万个,而不会导致系统资源衰减 ## 十、map怎么顺序读取? map不能顺序读取,是因为他是无序的,想要有序读取,首先的解决的问题就是,把key变为有序,所以可以把key放入切片,对切片进行排序,遍历切片,通过key取值。 代码示例: ~~~text package main import ( "fmt" "sort" ) func main() { map1 := make(map[int]string) map1[1] = "红孩儿" map1[2] = "牛魔王" map1[3] = "白骨精" map1[4] = "小钻风" map1[5] = "黄袍怪" map1[6] = "孔雀大明王" map1[7] = "白毛鼠" //获取所有的key,取值后存储到切片 keys := make([]int,0,len(map1)) for k,_ := range map1{ keys = append(keys,k) } fmt.Println(keys) //对key值进行排序 //内置函数sort包下的排序方法 sort.Ints(keys) fmt.Println(keys) for _,key := range keys{ fmt.Println(key,"-->",map1[key]) } //冒泡排序方法 for i := 1;i<len(keys);i++ { for j := 0;j<len(keys)-1;j++ { if keys[j] > keys[j+1] { keys[j],keys[j+1] = keys[j+1],keys[j] } } } for i := 1;i<=len(keys);i++ { fmt.Println(i,"-->",map1[i]) } } ~~~ ## 十一、你在项目里面会用到什么数据结构,例如map、slice 都会用到,包括基本数据类型:int、float、string、bool ,复合数据类型有:指针、数组、切片、字典(map)、通道、结构和接口 > 注:map和slice也会用到,当有明确的key值时,使用map,如果没有明显的key,就使用切片 ## 十二、如果用range修改切片元素的值,会发生什么? 我们经常会使用到range来帮助我们遍历一些数据,通常情况下都是查看操作多一些,但是当我们需要对其原地址上的内容进行变更时,通常都是使用 for i:=0; i<len(); i++ 来修改值。在使用range的时候,通常会将该数据结构进行拷贝,来遍历这一份拷贝后的副本,使用的是一个值传递,如果我们进行修改,修改的就只是副本,对原地址上的值不会产生任何影响。 ## 十三、了解空指针吗? * 当一个指针被定义后没有分配到任何变量时,它的值为 nil。 * nil 指针也称为空指针。 * nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。 ## 十四、一般怎么比较两个结构体,怎么判断他们是否相等? 一般没有效率太高的方法: * if判断比较:使用if一个个比较两个结构体中元素的值:if(p1->age==p2->age),如果有一个元素不等,即是两个实例不相等。 * 指针直接比较:如果保存的是同一个实例地址,则(p1==p2)为真。 ## 十五、make和new的区别是什么? * make 只用于 chan,map,slice 的初始化; * new 用于给类型分配内存空间,并且置零; * make 返回类型本身,new 返回指向类型的指针。 ## 十六、说一下你对并发编程的理解? * 所谓并发编程是指在一台处理器上“同时”处理多个任务。 * 宏观的并发是指在一段时间内,有多个程序在同时运行。 * 并发在微观上,是指在同一时刻只能有一条指令执行,但多个程序指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个程序快速交替的执行。 ## 十七、碰到过分布式锁的问题吗?分布式锁的原理你清楚吗? golang中的分布式锁可使用etcd进行实现,实现原理如下: * 在ectd系统里创建一个key * 如果创建失败,key存在,则监听该key的变化事件,直到该key被删除,回到1 * 如果创建成功,则认为我获得了锁