T*T

一般的判断标准是看副本创建的成本和需求。

T*T*TT*T

简单地比较Redis与Memcached的区别:
1、 Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。memcache数据结构单一,redis丰富一些,数据操作方面,redis更好一些,较少的网络IO次数
2、 Redis支持数据的备份,即master-slave模式的数据备份。
3、 Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

4、存储数据安全--memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化);

5、灾难恢复--memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复;

memcache能接受的key的最大长度是250个字符。对iterm 的过期时间最大可达30天;最大能存储的单个iterm是1MB,大于1MB可以考虑在客户端压缩或拆分到多个key中。

缓存击穿和缓存穿透区别,如何解决?

缓存穿透

 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

      解决方案:

  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
  2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击

缓存击穿

      缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

      解决方案:

  1. 设置热点数据永远不过期。
  2. 加互斥锁,

缓存雪崩

      缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,        缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

     解决方案

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
  3. 设置热点数据永远不过期。

一、raft算法

一个 etcd 集群,通常会由 3 个或者 5 个节点组成,多个节点之间通过 Raft 一致性算法的完成分布式一致性协同,算法会选举出一个主节点作为 leader,由 leader 负责数据的同步与数据的分发。当 leader 出现故障后系统会自动地选取另一个节点成为 leader,并重新完成数据的同步。客户端在多个节点中,仅需要选择其中的任意一个就可以完成数据的读写,内部的状态及数据协同由 etcd 自身完成

  • 简单:基于HTTP+JSON的API让你用url命令就可以轻松使用。
  • 安全:可选SSL客户认证机制。
  • 快速:每个实例每秒支持一千次写操作。
  • 可信:使用Raft算法充分实现了分布式。

leader选举的过程是:1、增加term号;2、给自己投票;3、重置选举超时计时器;4、发送请求投票的RPC给其它节点

Raft是一个用于管理日志一致性的协议。它将分布式一致性分解为多个子问题:Leader选举(Leader election)、日志复制(Log replication)、安全性(Safety)、日志压缩(Log compaction)等。同时,Raft算法使用了更强的假设来减少了需要考虑的状态,使之变的易于理解和实现。Raft将系统中的角色分为领导者(Leader)、跟从者(Follower)和候选者(Candidate):

  • Leader:接受客户端请求,并向Follower同步请求日志,当日志同步到大多数节点上后告诉Follower提交日志

  • Follower:接受并持久化Leader同步的日志,在Leader告之日志可以提交之后,提交日志

  • Candidate:Leader选举过程中的临时角色

1、Raft分为哪几个部分?

  主要是分为leader选举、日志复制、日志压缩、成员变更

2、Raft中任何节点都可以发起选举吗?

  Raft发起选举的情况有如下几种:

  • 刚启动时,所有节点都是follower,这个时候发起选举,选出一个leader;

  • 当leader挂掉后,时钟最先跑完的follower发起重新选举操作,选出一个新的leader。

  • 成员变更的时候会发起选举操作。

3、Raft中选举中给候选人投票的前提?

  Raft确保新当选的Leader包含所有已提交(集群中大多数成员中已提交)的日志条目。这个保证是在RequestVoteRPC阶段做的,candidate在发送RequestVoteRPC时,会带上自己的last log entry的term_id和index,follower在接收到RequestVoteRPC消息时,如果发现自己的日志比RPC中的更新,就拒绝投票。日志比较的原则是,如果本地的最后一条log entry的term id更大,则更新,如果term id一样大,则日志更多的更大(index更大)。

5、Raft数据一致性如何实现?

  主要是通过日志复制实现数据一致性,leader将请求指令作为一条新的日志条目添加到日志中,然后发起RPC 给所有的follower,进行日志复制,进而同步数据

6、Raft的日志有什么特点?

  日志由有序编号(log index)的日志条目组成,每个日志条目包含它被创建时的任期号(term)和用于状态机执行的命令

10、Raft日志压缩是怎么实现的?增加或删除节点呢??

  在实际的系统中,不能让日志无限增长,否则系统重启时需要花很长的时间进行回放,从而影响可用性。Raft采用对整个系统进行snapshot来解决,snapshot之前的日志都可以丢弃(以前的数据已经落盘了)

  snapshot里面主要记录的是日志元数据,即最后一条已提交的 log entry的 log index和term

二、Go进程线程区别

进程是什么呢?

直白地讲,进程就是应用程序的启动实例。比如我们运行一个游戏,打开一个软件,就是开启了一个进程。

进程拥有代码和打开的文件资源、数据资源、独立的内存空间。

线程又是什么呢?

线程从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。

线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是这样的)。

1 进程是资源分配的单位 
2 线程是操作系统调度的单位 
3 进程切换需要的资源很最大,效率很低 
4 线程切换需要的资源一般,效率一般 
5 协程切换任务资源很小,效率高 

6 多进程、多线程根据cpu核数不一样可能是并行的 也可能是并发的。协程的本质就是使用当前进程在不同的函数代码中切换执行,可以理解为并行。 协程是一个用户层面的概念,不同协程的模型实现可能是单线程,也可能是多线程。

协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是平行运行的,在线程里面可以开启协程,让程序在特定的时间内运行。

协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能.

线程和 goroutine 切换调度开销方面
线程/goroutine 切换开销方面,goroutine 远比线程小
线程:涉及模式切换(从用户态切换到内核态)、16个寄存器、PC、SP...等寄存器的刷新等。
因为协程在用户态由协程调度器完成,不需要陷入内核,这代价就小了。
所以goroutine:只有三个寄存器的值修改 - PC / SP / DX.

三、sql怎么改变表的结构?怎么做查询?

修改表名:   rename

alter table 表名 rename 新名

增加字段: add

alter table 表名 add 字段名 数据类型 约束

删除字段:drop

alter table 表名 drop 字段名

修改字段:change

alter table 表名 change 旧字段名 新字段名 数据类型 约束条件

修改字段顺序

alter table 表名 add 字段名 数据类型 约束条件 first  
#将该字段放在第一行
alter table 表名 add 字段名 数据类型 约束条件 after 字段名2 
#将新添的字段放在字段名2后面

五、普通锁和读写锁的区别

1、互斥锁

互斥锁能够保证在同一时间段内仅有一个goroutine持有锁,即保证某一时间段内有且仅有一个goroutine访问共享资源,其他申请锁的goroutine将会被阻塞直到锁释放,然后重新争抢锁的持有权。

有两种工作模式:一种正常模式:排队等待资源。二是饥饿模式:抢夺模式。

其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁也叫做全局锁。

func (m *Mutex) Unlock()用于解锁m,如果在使用Unlock()前未加锁,就会引起一个运行错误.已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁。

互斥锁只能锁定一次,当在解锁之前再次进行加锁,便会死锁状态,如果在加锁前解锁,便会报错“panic: sync: unlock of unlocked mutex”

2、读写锁

同一时间段只能有一个goroutine获取到写锁。

同一时间段可以有人员多个goroutine获取到读锁。

同一时间段内只能存在写锁或读锁(写锁和读锁互斥)

RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.

写锁:信号量加一个非常大的数值,释放写锁:减去一个极大的数值。

读锁:信号量加1,读锁解锁:信号量减1.

func (rw *RWMutex) Lock()  写锁,如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定
func (rw *RWMutex) Unlock() 写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误

func (rw *RWMutex) RLock() 读锁,当有写锁时,无法加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于"读多写少"的场景

func (rw *RWMutex)RUnlock() 读锁解锁,RUnlock 撤销单次RLock 调用,它对于其它同时存在的读取器则没有效果。若 rw 并没有为读取而锁定,调用 RUnlock 就会引发一个运行时错误(注:这种说法在go1.3版本中是不对的,例如下面这个例子)。

读写锁的写锁只能锁定一次,解锁前不能多次锁定读锁可以多次,但读解锁次数最多只能比读锁次数多一次,一般情况下我们不建议读解锁次数多余读锁次数

六、有缓存channel和无缓存channel区别

     无缓冲通道:cap()和len()都是0。用于通信和goroutine同步。

     有缓冲通道:len代表没有读取的元素数,cap代表整个通道的容量。主要用于通信。

goroutine退出后,写到缓冲通道中的数据不会消失,它可以缓冲和适配两个goroutine处理速度不一致的情况,缓冲通道和消息队列类似,有削峰和增大吞吐量的功能。

七、context上下文控制,waitGroup和context的区别。如何处理异常defer?

实际编程中goroutine会拉起新的goroutine,新的goroutine又会拉起另一个新的goroutine,最终形成一个树状的结构。

Go1.7提供了一个标准库context来解决这个问题,它提供两种功能退出通知和元数据传递context库设计的目的就是跟踪goroutine调用,在其内部维护一个调用树,并在这些调用树中传递通知和元数据。

context包提供的核心功能是多个goroutine之间的退出通知机制传递数据知识一个辅助功能,应谨慎使用。

sync.WaitGroup

作用: 控制多个 goroutine 同时完成

适用场景: 好多个 goroutine 协同做一件事情的时候,因为每个 goroutine 做的都是这件事情的一部分,只有全部的 goroutine 都完成,这件事情才算是完成,这是等待的方式

九、用户线程和内核态线程的区别

用户线程

不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。不需要用户态/核心态切换,速度快,操作系统内核不知道多线程的存在,因此一个线程阻塞将使得整个进程(包括它的所有线程)阻塞。由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少。

优点

  • 线程的调度不需要内核直接参与,控制简单。

  • 可以在不支持线程的操作系统中实现。

  • 创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多。

  • 允许每个进程定制自己的调度算法,线程管理比较灵活。

  • 线程能够利用的表空间和堆栈空间比内核级线程多。

  • 同一进程中只能同时有一个线程在运行,如果有一个线程使用了系统调用而阻塞,那么整个进程都会被挂起。另外,页面失效也会产生同样的问题

缺点

  • 资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用

系统线程

由操作系统内核创建和撤销。内核维护进程及线程的上下文信息以及线程切换。一个内核线程由于I/O操作而阻塞,不会影响其它线程的运行。

优点

  • 当有多个处理机时,一个进程的多个线程可以同时执行

缺点

  • 由内核进行调度

十、虚拟内存?操作系统如何实现虚拟内存?

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。

对虚拟内存的定义是基于对地址空间的重定义的,即把地址空间定义为「连续的虚拟内存地址」,以借此「欺骗」程序,使它们以为自己正在使用一大块的「连续」地址。

也就是说虚拟内存能提供一大块连续的地址空间,对程序来说它是连续的,完整的实际上虚拟内存是映射在多个物理内存碎片上,还有部分映射到了外部磁盘存储器上。虚拟内存有以下两个优点:

1.虚拟内存地址空间是连续的,没有碎片

2.虚拟内存的最大空间就是cup的最大寻址空间,不受内存大小的限制,能提供比内存更大的地址空间

虚拟内存技术的实现是建立在离散分配的内存管理的基础上的

目前最常用的三种实现虚拟内存技术的方法:

  • 请求分页存储管理
  • 请求分段存储管理
  • 请求段页式存储管理

以上三种方式,无论哪种,都需要以下三个条件:

  • 一定容量的内存和外存

程序执行时,只需要将程序的一部分装入内存,就可以运行了

  • 缺页中断

若需要执行的程序未在内存中(即“缺页/段”),则处理器会通知操作系统将相应的页/段调入到内存中,与此同时也会将不常用的页/段调出到外外存中

  • 虚拟地址空间

逻辑地址转化为物理地址

十二、goroutine什么时候被挂起?defer执行效率?

十三、经典快排思路,稳不稳定?归并排序思路 时间复杂度?

快速排序速度取决于选择的基准值。不稳定。复杂度:最糟糕 

归并排序的算法跟有序度无关,所以时间复杂度很稳定都是O(nLogN);空间复杂度为: O(n)。

在排序算法中快速排序的效率是非常高的,但是还有种排序算法的效率可以与之媲美,那就是归并排序

快速排序:

1、选择一个基准值

2、把数组粗略的排序成两个子数组:小于基准值的元素组成的子数组大于基准值的元素组成的子数组

3、对这两个子数组进行快速排序,直到子数组里面只有一个元素,那么就自然排好序了,可以总结为先排序再递归

归并排序:

       假设集合一共有n个元素,算法将会对集合进行逐层的折半分组。直到数组里只有一个元素,这时候才开始排序,让两个数组间排好序,依次按照递归的返回来把两个数组进行排好序,到最后就可以把整个数组排好序;

归并排序是一种效率较高的排序方法,它分为拆分和合并两大部分,先拆后合。
拆分:就是将一堆无序的数拆成单个,方便合并。
合并:就是将拆分好的数按照排序规则再拼凑起来。

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。但是很消耗空间,一般来说在内部排序不会用这种方法,而是用快速排序;外部排序才会考虑到使用这种方法

c是算法所需的固定时间量,被称为常量,快速查找的常量比归并排序小,因此如果他们的运行时间都为O(nLogN),快速查找的速度将更快

十四、怎么保证不丢包?怎么办?一直重传的话,能传多少次?

TCP协议保证数据传输可靠性的方式主要有:

校验和:发送的数据包的二进制相加然后取反,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。 

确认应答+序列号:TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。 

超时重传:当TCP发出一个段后,它启动一个定时器等待目的端确认收到这个报文段如果不能及时收到一个确认,将重发这个报文段。 

流量控制:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据

拥塞控制:当网络拥塞时,减少数据的发送。

         发送方有拥塞窗口,发送数据前比对接收方发过来的即使窗口,取小

拥塞控制是TCP在传输时尽可能快的将数据传输,并且避免拥塞造成的一系列问题。是可靠性的保证,同时也是维护了传输的高效性。

TCP通过一个定时器采样了RTT(报文段往返时间)并计算RTT的偏差,但是如果网络上突然延时增加,那么,TCP对这个事情做出的应对只有重传数据,,然而重传数据会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这就导致了恶性循环,最终形成“网络风暴”------TCP的拥塞机制就是用于应对这种情况。首先,需要了解一个概念,为了在发送端调节所要发送的数据量,定义了一个“拥塞窗口”,在发送数据时,将拥塞窗口的大小与接收端ack的窗口大小作比较,取较小者作为发送数据量的上限。

拥塞控制主要是四个算法:

1)慢启动:刚加入网络的的连接,一点一点的提速,不要一上来就把路占满。

连接建好的开始先初始化cwnd=1,表明可以传一个MSS(536字节)大小的数据。

每当收到一个ACK(确认应答),cwnd++,呈线性上升。

每当过了一个RTT,cwnd=cwnd*2

阈值(slow start threhold) 是一个上限,当cwnd>=阈值时,就会进入“拥塞避免算法”。

2)拥塞避免:当拥塞窗口cwnd达到一个阈值时,,窗口大小不再呈指数上升,而是以线性上升,避免增长过快导致网络拥塞。

每当收到一个ACK,cwnd =cwnd+1/cwnd

每当收到一个RTT,cwnd=cwnd+1

拥塞发生:当发生丢包或者数据重传时,表示网络已经拥塞,分两种情况进行处理:

等到RTO超时,重传数据包。

阈值sshthresh=cwnd/2

cwnd重置为1

3)快恢复:在收到3个重复ACK时就开启重传,而不用等到RTO超时,

阈值sshthresh=cwnd=cwnd/2

进入快速恢复算法。

4)快速恢复。至少收到了3个duplicate ACKS,说明网络也不那么糟糕,可以快速恢复。

cwnd=sshthresh+3*MSS

重传duplicate ACK 指定的数据包。

如果再收到duplicate ACK,那么cwnd=cwnd+1

超时重传机制。简单理解就是发送方在发送完数据后等待一个时间,时间到达没有接收到ACK报文,那么对刚才发送的数据进行重新发送。如果是刚才第一个原因,接收方收到二次重发的数据后,便进行ACK应答。如果是第二个原因,接收方发现接收的数据已存在(判断存在的根据就是序列号,所以上面说序列号还有去除重复数据的作用),那么直接丢弃,仍旧发送ACK应答。

由于TCP传输时保证能够在任何环境下都有一个高性能的通信,因此这个最大超时时间(也就是等待的时间)是动态计算的。稍大于RTT+RTT偏差

超时以500ms(0.5秒)为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。重发一次后,仍未响应,那么等待2*500ms的时间后,再次重传。等待4*500ms的时间继续重传。以一个指数的形式增长。累计到一定的重传次数,TCP就认为网络或者对端出现异常,强制关闭连接

TCP与UDP的区别?

1、TCP是面向连接的可靠的数据传输服务;UDP是面向无连接的,尽最大努力保证数据传输服务,不保证可靠性

2、TCP是面向字节流,UDP面向报文

3、TCP传输数据慢,UDP传输数据快

4、TCP有拥塞机制,UDP没有拥塞机制

5、TCP只能是一对一的通信,UDP支持一对一、一对多、多对一、多对多的通信

6、TCP首部开销大有20个字节,UDP开销较小有8个字节

7、TCP提供可靠的全双工通信服务,UDP是半双工,只能单向传播。

TCP和UDP的应用场景:

TCP适用于效率要求低,准确度要求高的场景。TCP适用于电子邮件(SMTP)、远程终端接入、万维网、文件传送(FTP)等。

UDP适用于效率要求高,准确性要求低的场景,如视频直播、QQ、语音等及时通信,广播通信。

二十八、TCP粘包问题如何处理?

TCP 粘包/拆包的原因及解决方法

TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送也可能把小的封装成一个大的数据包发送。

TCP粘包/分包的原因:

应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象;

解决方法

消息定长:FixedLengthFrameDecoder类

包尾增加特殊字符分割:行分隔符类:LineBasedFrameDecoder或自定义分隔符类 :DelimiterBasedFrameDecoder

将消息分为消息头和消息体:LengthFieldBasedFrameDecoder类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。

十五、time_Wait的作用?

十六、Go如何调度,假设4核到额CPU应该有几个线程或者说几个M?那能有几个goroutine?goroutine的上限是多少个?

单纯的goroutine,一开始每个占用4K内存,所以这里会受到内存使用量的限制,还有goroutine是通过系统线程来执行的,golang默认最大的线程数是10000个。可以通过https://gowalker.org/runtime/debug#SetMaxThreads

来修改。但要注意线程和goroutine不是一一对应关系,理论上内存足够大,而且goroutine不是计算密集型的话,可以开启无限个goroutine。

十七、反射:

十九、HTTP优缺点、HTTP状态码、HTTP与HTTPS区别

HTTP报文格式:header+body //头部信息也是key-value简单文本格式。

HTTP协议优点:简单,灵活易于扩展。

HTTP缺点:无状态、明文传输、不安全。

HTTPS在HTTP与TCP层之间增加了SSL/TLS安全传输层。

HTTP状态码:1XX类状态码属于提示信息,是协议处理中的一种中间状态。

200表示服务器成功处理了客户端的请求。

3xx表示重定向。

4xx表示客户端发送的报文有误,服务器无法处理(400)。401表示未授权、403表示(禁止)服务器拒绝请求、404服务器找不到请求的网页。

5XX服务器错误。

二十、select可以用于干什么?

用于多路监听多个通道

二十一、go struct 能不能比较?

结构体可以比较  reflect.DeepEqual(u1, u2)  //不但可以比较结构,数组和映射map都可以比较,但是性能会有所影响。

二十二WaitGroup

WaitGroup用来等待多个goroutine完成,调用Add设置需要等待的goroutine的数目,每一个goroutine结束时调用done,Wait()被main用来等待所有的goroutine完成。

二十三、runtime

runtime.NumGoroutine()返回当前程序的goroutine数目。

二十四、Linux常用命令

Linux端口占用命令:netstat -tunlp 

-t:显示TCP端口

-u:显示UDP端口

-n:显示数字地址

-l:仅显示侦听端口

-p:显示进程的PID和名称。

netstat -tnlp | grep:80   :查找TCP 端口80上侦听的进程

kill pid  杀死进程

删除文件: rm -rf 、

查找日志: cat xx.log | grep 'xxx' | more

解压缩: tar.gz   tar -xzvf file.tar.gz

创建、修改文件: vi  

cURL(client  URL)是一个利用URL语法在命令行下工作的文件传输工具:

-H
-v
-owget
-X
-d

$ curl -d'login=emma&password=123'-X POST https://google.com/login

ssh远程登陆服务器: ssh 客户端用户名@服务器ip地址

scp本地发送至服务器: 本地文件路径 远程用户名@ip地址:远程路径

scp -r ./test.txt root@0.0.0.0:/home/me

远程拷贝到本地:

scp -r 远程用户名@ip地址:远程路径 本地文件路径

二十五、k8s是什么?有那些常用组件?

Kubernetes是一个大规模集群管理工具,还是一个管理跨主机容器化应用的系统。Kubernetes它的设计哲学就是维护应用容器集群一直处于用户所期待的状态。为了践行这一价值观,Kubernetes建立了一套健壮的集群自恢复机制,包括容器的自动重启、自动重调度以及自动备份等。

核心组件:APIServer、scheduler、kubelet。

Kubernetes有两种节点组成:master节点工作节点,

master节点:是管理节点。主要有三个重要组件:APIServer、scheduler和controller manager。

     APIServer:负责响应用户的管理请求、进行指挥协调工作。

位于master节点上的APIServer将负责与master节点、工作节点上的各个组件之间的交互,以及集群外用户与集群的交互,在集群中处于消息手法的中心地位。对任何资源进行增删改查的操作都要交给APIServer处理后才能交给etcd。

    scheduler:将待调度的pod绑定到合适的工作节点上。scheduler的输入是待调度pod和可用的工作节点列表,输出是应用调度算法从列表中选择一个最优的用于绑定待调度的pod节点。

    controller manager:是一组控制器的合集,负责控制管理对应的资源,如副本(replication)和工作节点(node)。控制pod、工作节点等资源正常运行的本质,就是靠各种controller定时对pod、工作节点等资源进行检查,然后判断这些资源的实际运行状态是否与用户对他们的期望一致,若不一致,则通知APIServer进行具体的“增删改”操作。

相比之下,APIServer负责接收用户的请求,并完成集群内资源的“增删改”,而controller manager在系统中扮演的角色是默默地管理这些资源,确保他们永远在用户所期待的状态。

工作节点:是容器运行的节点。主要运行了两个重要组件:kubelet和kube-proxy

kubelet:可以看做一个管理维护pod运行的agent。

kube-proxy:负责将service的流量转发到对应的endpoint.(实际生产环境基本上已经废弃此组件,而选择了其他流量住哪发组件)。endpoint服务端点控制器

Kubernetes的架构体现了很多分布式系统设计的最佳实践,比如组件之间松耦合,各个组件之间不直接存在依赖关系,而是都通过APIServer进行交互。

服务理念:pod、service、replication controller

pod:专门对容器进行分组管理

replication controller(副本管理控制器):决定了一个pod有多少同时运行的副本。

service:首先由于重调度的原因,pod在Kubernetes中的IP地址不是固定的,因此需要一个代理来确保需要使用pod的应用不需要知晓pod的真实IP地址。另一个原因是当使用replication controller创建了多个pod的副本时,需要一个代理来为这些pod做负载均衡。service定名为proxy或者router更贴切。

一个请求到达pod的过程?

1、APIServer在接收到用户的请求之后,会根据用户提交的参数值来创建一个运行时的pod对象。

2、根据API请求的上下文和该pod对象的元数据来验证两者的namespace是否匹配,如不匹配则创建失败。

3、namespace匹配之后,APIServer会向pod对象注入一些系统元数据,包括创建时间和uid等。如果定义pod时未提供pod的名字,则APIServer会将pod的uid作为pod的名字。

4、APIServer接下来会检查pod对象中的必需字段是否为空,只要有一个字段为空,就会抛出异常并终止创建过程。

5、在etcd中持久化该pod对象,将异步调用返回结果封装成restful.Response,完成操作结果反馈。

至此,APIServer在pod创建的流程中的任务已经完成,剩余步骤将由Kubernetes其他组件(kubelet和kube-proxy)通过watch APIServer继续执行下去。

二十六、etcd是什么?如何保持高可用性?选举机制,脑裂如何解决?

  1. 什么是HTTP Restful Api
    就是用url定位资源,用HTTP描述操作。
  2. url构成
    url=协议+主机域名+资源路径(请求页面路径)+请求参数
  3. HTTP的请求报文格式
    请求报文=请求行+请求头+请求体
  4. HTTP的响应报文格式
    响应报文=初始状态行+6个响应头+响应体

二十七、数组和切片的区别?

golang中的引用类型有:数组、切片、map、channel、interface。

从语法上来看,数组遵循传统的三要素 – 名称、类型、长度。
而切片只有名称、类型,这意味着切片是不定长的。

从内存的角度来看,数据是一整块连续的、固定长度、固定位置的内存。
而切片则是一个指针,指向一块内存,当容量不够时就开辟更大的内存。

三十、如何有序遍历map?

其实我们可以借助一个slice来保存map的key,通过遍历排序后的slice来达到根据keys遍历map的效果

package main

import (
    "fmt"
    "sort"
)

func main() {
    m := map[string]int{"Apple": 20, "Tomato": 12, "Banana": 18}
    //给key排序一下
    keys := make([]string, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    sort.Strings(keys)

    //再次遍历
    for _, k := range keys {
        fmt.Println(k, m[k])
    }
}

我们知道上面的永远会根据a,b,c,d这样keys的顺序来输出,不满足我们的要求。
好在已经有人实现了有序Map,下载地址:这是一个链接 elliotchance/orderedmap

package main

import (
    "fmt"
    "github.com/elliotchance/orderedmap"
)

func main() {
    //新建一个order map
    m := orderedmap.NewOrderedMap()
    m.Set("a", 1)
    m.Set("b", 2)
    m.Set("d", 3)
    m.Set("c", 4)
    //遍历一下
    for _, key := range m.Keys() {
        value, _ := m.Get(key)
        fmt.Printf("%v=%v\n", key, value)
    }
}

三十一、消息队列

1、为什么使用消息队列?   解耦、异步、削峰

传统模式的缺点:

  • 系统间耦合性太强,如上图所示,系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦
  • 一些非必要的业务逻辑以同步的方式运行,太耗费时间。
  • 并发量大的时间,所有的请求直接怼到数据库,造成数据库连接异常

中间件模式的优点:

  • 将消息写入消息队列,需要消息的系统自己从消息队列中订阅,从而系统A不需要做任何修改。
  • 将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快相应速度
  • 系统A慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的。

2、使用了消息队列会有什么缺点?

  • 系统可用性降低:你想呀,本来其他系统只要运行好好的,那你的系统就是正常的。现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低
  • 系统复杂性增加:加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,刺痛复杂性增大。

三十三、死锁如何避免?

死锁是所有并发程序彼此等待的结果。

coffman条件是帮助检测、防止和纠正死锁的技术依据。

出现死锁的四个必要条件:

1、相互排斥:并发程序同时拥有资源的独占权。

2、等待条件:并发进程必须同时拥有一个资源,并等待额外的资源。

3、没有抢占:并发进程拥有的资源只能被该进程释放,即可满足

4、循环等待。