2021-01-12 腾讯CSIG一面

1.channel如何做到线程安全的

Channel是Go中的一个核心类型,可以理解是一个先进先出的队列,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯。而且Go的设计思想就是:不要通过共享内存来通信,而是通过通信来共享内存,前者就是传统的加锁,后者就是Channel。也就是说,设计Channel的主要目的就是在多任务间传递数据的,这当然是安全的。

“不要通过共享内存来通信,要通过通信来共享内存”,这句话是GO社区中非常经典的一句话,但是我们如何理解这句话呢?
GO语言的协程(goroutine)是从进程、线程基础上发展而来的,它比进程线程更轻量级,但是都是为并发而生。既然是并发,那么不可避免的会有多个不同并发实体之间传播或者交换信息,也即通信。要理解“不要通过共享内存来通信,要通过通信来共享内存”这句话,我们得从跟进程线程间通信的比较来了解。
因为GO的协程间的通信都是在同一个进程内进行,所以没有必要跟进程间通信做比较,只需要跟线程间通信做比较即可。

线程间通信
两个或者多个线程间要传播或者交换信息,通常的做法有:

分配一个共享的变量,多个线程获取或者修改这个变量来交换信息。变量本质上就是一片共享的内存。
通过队列来实现消费者和生产者的模式来进行通信。那么队列本质上也是线程间共享同一块内存。当然也可以把队列理解为一个共享的变量。
线程同步机制来控制线程之间的运行,比如同步锁、信号量、条件变量。共享变量以及队列也都需要用到线程同步机制来保证数据和通信的正确性。
一般线程同步在线程间交换的信息仅仅是控制信息,比如某个A线程释放了锁,B线程能获取到锁并开始运行,这个不涉及数据的交换。数据的交换主要还是通过共享内存(共享变量或者队列)来实现,为了保证数据的安全和正确性,共享内存就必需要加锁等线程同步机制。而线程同步使用起来特别麻烦,容易造成死锁,且过多的锁会造成线程的阻塞以及这个过程中上下文切换带来的额外开销。

GO中通过channel来共享内存
GO语言中,要传递某个数据给另一个goroutine(协程),可以把这个数据封装成一个对象,然后把这个对象的指针传入某个channel中,另外一个goroutine从这个channel中读出这个指针,并处理其指向的内存对象。GO从语言层面保证同一个时间只有一个goroutine能够访问channel里面的数据,为开发者提供了一种优雅简单的工具,所以GO的做法就是使用channel来通信,通过通信来传递内存数据,使得内存数据在不同的goroutine中传递,而不是使用共享内存来通信。

https://blog.csdn.net/c359719435/article/details/78785189


2.golang代码中定义的空结构体有什么作用

golang 空结构体 struct{} 可以用来节省内存。

建议用于传递信号的通道,因为不占用内存。

参考代码:

package main
 
import (
    "fmt"
    "unsafe"
)
 
 
func main() {
    
    
    empStruct()
} 
 
//空结构体的实例和作用
func empStruct(){
    
    
    //空结构体的特点:1、不占用内存;2、地址不变
    var s struct{
    
    }
    var s1 struct{
    
    }
    fmt.Println("空结构体占用内存的情况:",unsafe.Sizeof(s))
    fmt.Printf("空结构体指针指向情况:s = %p, s1 = %p,两个指针的比较结果:%v",&s,&s1,&s==&s1)
    strChan := make(chan string,3)
    signChan := make(chan struct{
    
    },1)  //接收数据信号
    signChan1 := make(chan struct{
    
    },2) //操作完成信号
 
    go func(){
    
    
        // 用来接收信息
        <- signChan  //阻塞协程,直到signChan接收到值
        for value := range strChan{
    
    
            fmt.Println("接收到值为:",value)
        }
        signChan1 <- struct{
    
    }{
    
    }
    }()
 
    go func(){
    
    
        // 模拟发送数据
        for index,value := range []string{
    
    "1","2","3"}{
    
    
            fmt.Println("发送数据:",value)
            strChan <- value
            if index==2{
    
    
                signChan <- struct{
    
    }{
    
    } 
            }
        }
        close(strChan)
        signChan1 <- struct{
    
    }{
    
    }
    }()
 
    fmt.Println("等待上面连个协程运行结束")
    <- signChan1  
    <- signChan1  //阻塞,直到上面两个协程完成
}

输出结果:

===================空结构体测试=============
空结构体占用内存的情况: 0
空结构体指针指向情况:s = 0x58ccd8, s1 = 0x58ccd8,两个指针的比较结果:true等待上面连个协程运行结束
发送数据: 1
发送数据: 2
发送数据: 3
接收到值为: 1
接收到值为: 2
接收到值为: 3

空结构体的特点

  1. 不占用内存
  2. 地址不变

空结构体作用
建议用于传递信号的通道,因为不占用内存

https://blog.csdn.net/weixin_30432579/article/details/101605643


3.fmt使用%v和%+v的区别?

  1. %v 只输出所有的值

  2. %+v 先输出字段类型,再输出该字段的值

  3. %#v 先输出结构体名字值,再输出结构体(字段类型+字段的值)

测试代码:

package main
import "fmt"
 
type student struct {
    
    
	id   int32
	name string
}
 
func main() {
    
    
	a := &student{
    
    id: 1, name: "xiaoming"}
 
	fmt.Printf("a=%v	\n", a)
	fmt.Printf("a=%+v	\n", a)
	fmt.Printf("a=%#v	\n", a)
}

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


3.post过来的数据是如何解析的

  1. 解析json数据:err := c.ShouldBindJSON(&param)
  2. 解析form数据:err := c.ShouldBind(&param)

参考代码:

type AlterReason struct {
    
    
	AlterReason string `json:"alter_reason"`
}

func GetReportInfoHandler(c *gin.Context) {
    
    
	var param AlterReason

	if err := c.ShouldBindJSON(&param); err != nil {
    
    
		log.Errorf("In GetReportInfoHandler, fail to bind params: %v", err)
		c.Error(ErrInvalidJSON)
		return
	}
	...
}

type CameraReportParam struct {
    
    
	KitchenID          string `form:"kitchen_id"`
	DeviceID           string `form:"device_id"`
	CameraID           string `form:"camera_id"`
	VideoStreamAddress string `form:"video_stream_address"`
	DetectTime         string `form:"detect_time"`
	AlterReasons       string `form:"alter_reasons"`
}

func ReportCameraStatusHandler(c *gin.Context) {
    
    
	var param CameraReportParam

	if err := c.ShouldBind(&param); err != nil {
    
    
		log.Errorf("In ReprotCameraStatusHandler, fail to bind params: %v", err)
		c.Error(ErrInvalidForm)
		return
	}
	...
}

4.get与post的区别

  1. GET在浏览器回退时是无害的,而POST会再次提交请求。

  2. GET产生的URL地址可以被Bookmark,而POST不可以。

  3. GET请求会被浏览器主动cache,而POST不会,除非手动设置。

  4. GET请求只能进行url编码,而POST支持多种编码方式。

  5. GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

  6. GET请求在URL中传送的参数是有长度限制的,而POST么有。

  7. 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。

  8. GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

  9. GET参数通过URL传递,POST放在Request body中。

GET和POST还有一个重大区别,简单的说:

GET产生一个TCP数据包;POST产生两个TCP数据包。

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
注意:并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

https://www.cnblogs.com/logsharing/p/8448446.html


5.HTML渲染过程

浏览器加载和渲染html的顺序

  1. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的。

  2. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完)。

  3. 如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载。

  4. 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染。

  5. JS、CSS中如有重定义,后定义函数将覆盖前定义函数。

JS的加载

  1. 不能并行下载和解析(阻塞下载)。

  2. 当引用了JS的时候,浏览器发送1个js request就会一直等待该request的返回。因为浏览器需要1个稳定的DOM树结构,而JS中很有可能有代码直接改变了DOM树结构,比如使用 document.write 或 appendChild,甚至是直接使用的location.href进行跳转,浏览器为了防止出现JS修改DOM树,需要重新构建DOM树的情况,所以 就会阻塞其他的下载和呈现。

DOM
JavaScript操作网页的接口,全称为“文档对象模型”(Document Object Model)。
有这几个概念:文档、元素、节点
整个文档是一个文档节点
每个标签是一个元素节点
包含在元素中的文本是文本节点
每一个属性是一个属性节点
注释属于注释节点

DOM树
DOM树是层级结构
所谓层级结构是指元素和元素之间的关系:父子,兄弟
解析器输出的树是由DOM元素和属性节点组成的
当我们说树中包含DOM节点时,意思就是这个树是由实现了DOM接口的元素组成。这些实现包含了其它一些浏览器内部所需的属性。
脱离文档流后层级结构关系还是没有变的。

DOM 是 Document Object Model(文档对象模型)的缩写。提供给Javascript用来动态修改文档状态。

https://www.jianshu.com/p/0ec77136ec48


6.动态网页如何work

静态网页:

(1)静态网页不能简单地理解成静止不动的网页,他主要指的是网页中没有程序代码,只有HTML(即:超文本标记语言),一般后缀为.html,.htm,或者.xml等。虽然静态网页的页面一旦做成,内容就不会再改变了。但是,静态网页也包括一些能动的部分,这些主要是一些GIF动画等

(2)静态网页的打开,用户可以直接双击,并且不管任何人任何时间打开的页面的内容都是不变的。

动态网页:

(1)动态网页是指跟静态网页相对的一种网页编程技术。动态网页的网页文件中除了HTML标记以外,还包括一些特定功能的程序代码,这些代码可以使得浏览器和服务器可以交互,所以服务器端根据客户的不同请求动态的生成网页内容。

即:动态网页相对于静态网页来说,页面代码虽然没有变,但是显示的内容却是可以随着时间、环境或者数据库操作的结果而发生改变的。

(2)动态网页,与网页上的各种动画、滚动字幕等视觉上的动态效果没有直接关系,动态网页也可以是纯文字内容的,也可以是包含各种动画的内容,这些只是网页具体内容的表现形式,无论网页是否具有动态效果,只要是采用了动态网站技术(如PHP、ASP、JSP等)生成的网页都可以称为动态网页。

Ajax
Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式、快速动态网页应用的网页开发技术,无需重新加载整个网页的情况下,能够更新部分网页的技术

通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

HTML5最大特色之一就是支持音频视频,在通过增加了、两个标签来实现对多媒体中的音频、视频使用的支持,只要在Web网页中嵌入这两个标签,而无需第三方插件(如Flash)就可以实现音视频的播放功能。HTML5对音频、视频文件的支持使得浏览器摆脱了对插件的依赖,加快了页面的加载速度,扩展了互联网多媒体技术的发展空间。


7.Linux如何查看进程pid,如何查看具体进程信息

查看进程pid

ps -ef | grep "name"

显示所有运行中的进程

# ps aux | less

其中,
-A:显示所有进程
a:显示终端中包括其它用户的所有进程
x:显示无控制终端的进程

top命令提供了运行中系统的动态实时视图


8.爬虫执行过程?如何生成下一页的URL?

整体流程

  • 引擎找到要执行的爬虫,并执行爬虫的 start_requests 方法,并的到一个 迭代器。

  • 迭代器循环时会获取Request对象,而request对象中封装了要访问的URL和回调函数。

  • 将所有的request对象(任务)放到调度器中,用于以后被下载器下载。

  • 下载器去调度器中获取要下载任务(就是Request对象),下载完成后执行回调函数。

  • 回到spider的回调函数中,
    yield Request()
    yield Item()

如何生成下一页的URL
摸索各页请求URL规律,自行构造URL


9.ua和refer分别是什么含义?

1、User Agent
User Agent是标识请求的浏览器身份的,网站常用这个参数来分辨爬虫。如豆瓣网,当请求headers中没带User Agent时,返回404.

2、referer
Referer是HTTP协议中的一个请求报头,用于告知服务器用户的来源页面。当请求不带referer时,返回403错误。

https://www.cnblogs.com/bulh/articles/13265045.html


10.protobuf中“1”,“2”,“3”是什么意思?

package tutorial;
message Person {
    
    
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;
}

它们是字段编号 - 它们在线表示中用于标识哪个字段与值相关联。这意味着重命名字段不是一个重大变化(就线格式而言),名称本身不必序列化。

举例:
在这里插入图片描述

https://www.jianshu.com/p/3493c8d314dc


11.监控系统的系统架构是怎样的?(数据流是怎么流动的)


12.控制端负责哪些工作?


13.中台负责哪些工作?


14.检测端如何拿到摄像头的视频流?

通过摄像头的rtsp流地址,拿到对应的视频流。

https://ask.csdn.net/questions/696295


不好的点
1.项目系统架构介绍不清楚
2.前端知识欠缺
3.golang和Linux需要加强

猜你喜欢

转载自blog.csdn.net/weixin_43202635/article/details/112532631