语言基础总结

Go
与Go的差别

  1. 基础语法

  2. 在Golang中区别与Java最显著的一点是,Go不存在“类”这个概念,Go语言只提供了两个关键类型:struct,interface,Go的函数可以依赖于结构体来调用或者依赖于包名调用。Golang通过组合(Composition)来实现代码复用。而Java中强调使用面向对象,通过类、继承、多态等特性来实现代码复用。Go的组合的关系是( A has a B 也就是A由B组成),Java的继承关系是(A is B)。在编程的时候,Java的父类修改容易影响到子类,耦合较高,Go的组合的方式是组件独立的低耦合关系。Go可以自由组合多个组件,比如struct A由struct B C组成,对比之下,Java有单继承限制,需要通过接口实现多继承。所以Go的灵活性更高,但是当对象关系明确符合继承关系时,Java的继承体系更清晰,Java的面向对象在企业级应用里面使用很广。

  3. A -> B, C

  4. Struct A {

  5. B

  6. }

  7. Java: B, C

  8. A.func()

  9. 异常处理:

  10. java通过 try catch,来捕获错误。

  11. Go通过多返回值的方式,来处理异常,Go的函数可以返回多个返回值,一般函数会带有error返回值,通过判断error是否为空来处理异常
    a() (string s, Error error) {
    return s, nil
    }

  12. 高级特性和生态

  • Java支持AOP(切面)、annotation注解的能力,这些都是Go所不具备的。Go 有struct tag可以补充对应的一部分内容,但是表达能力不如Java。另外Java支持泛型,支持得比较丰富,Go的泛型只支持简单功能,只在高版本支持。
  • 生态上,Java有Spring体系,非常适合企业级的开发,而Go相对来说Web框架没有Java全。Go更多的用在云原生领域。
  1. 并发编程:
  • Go的并发编程能力上,因为channel、goroutine的引入,使得多线程编程等场景,Go比Java要方便。Java 中 CPU 资源分配对象是 Thread,Go 中 CPU 资源分配对象是 goroutine。Java Thread 与系统线程为一一对应关系, Goroutine是Go 实现的用户级线程,与系统线程是 m:n 关系;另外,Go中使用goroutine 时可以通过Channel进行通信,这里使用了CSP(Communicating Sequential Processes)模型,使用Channel进行通信来实现内存共享,而Java中是使用共享内存来通信,多个线程共享内存空间, 容易导致数据访问混乱, 如果某个线程误操作导致内存挂掉,可能搞坏整个线程组, 健壮性没有Go高。Goroutine是非常轻量的,goroutine的栈空间默认是2kb,数千个 Goroutine 也不会影响性能。 创建和销毁比Java的线程开销小
    [图片]
    Java与go的区别

基础语法

Java
Java是典型的面向对象语言,在Java中有面向对象语言的封装、继承、多态的特性以及“类(class),继承(extends)、实现(implements)”等关键字,通过面向对象来实现代码复用。

Go
Go不存在“类”这个概念,Go使用了结构体,Go的函数可以依赖于结构体来调用或者依赖于包名调用。Golang通过组合(Composition)来实现代码复用。Go的组合的关系是( A has a B 也就是A由B组成),Java的继承关系是(A is B)

goroutine原理 / GMP模型
goroutine的实现使用GMP模型 G是goroutine M是线程,P是逻辑处理器. Go里面每个线程可以对应多个协程,当一个协程执行体阻塞了,调度器会调度另一个协程执行,最大效率地利用操乍系统分给系统线程的时间片。Go的线程和goroutine3是M:N模型。
[a, b, c]

go语言里面用go关键字创建一个 goroutine准备运行的时候,这个 goroutine 就会被放到调度器的全局运行队列中。之后,调度器就 将这些队列中的 goroutine 分配给一个逻辑处理器,并放到这个逻辑处理器对应的本地运行队列中。本地运行队列中的 goroutine 会一直等待直到自己被分配的逻辑处理器执行。
有时,正在运行的 goroutine 需要执行一个阻塞的系统调用,如打开一个文件。当这类调用 发生时,线程和 goroutine 会从逻辑处理器上分离,该线程会继续阻塞,等待系统调用的返回。 与此同时,这个逻辑处理器就失去了用来运行的线程。所以,调度器会创建一个新线程,并将其 绑定到该逻辑处理器上。之后,调度器会从本地运行队列里选择另一个 goroutine 来运行。一旦 被阻塞的系统调用执行完成并返回,对应的 goroutine 会放回到本地运行队列,而之前的线程会 保存好,以便之后可以继续使用。
[图片]
在goroutine里面是可以实现并行编程的,有多个逻辑处理器时,调度器会将 goroutine 平等分配到每个逻辑处理器上。这会让 goroutine 在不同的线程上运行。
所以和Java很明显的区别是,在Go里面goroutine和操作系统的线程不是一一对应的。

5个处理器
goroutine抢占调度是怎样的
在Go有抢占调度的策略, 避免某个Goroutine因为系统调用或者其他原因,长期阻塞其他goroutine的运行:
比如说正在运行的 goroutine A需要执行一个阻塞的系统调用,比如打开一个文件。当这类调用发生时,这个goroutine A和这个goroutine所对应的线程A, 会从逻辑处理器上分离,线程A会继续阻塞,等待系统调用的返回。 与此同时,这个逻辑处理器就失去了用来运行的线程A。所以,调度器会创建一个新线程B,并将新线程绑定到该逻辑处理器上。之后,调度器会从本地运行队列里选择一个新的 goroutine B来运行,这样就完成了goroutine的切换。当被阻塞的系统调用执行完成并返回 , goroutine A会放回到本地运行队列,而之前的线程也会保存好,以便之后可以继续使用。

Go的CSP模型
CSP 模式, Communicating Sequential Process 的简称,这是一种用于描述两个独立的并发实体之间,通过共享的 Channel(管道)进行通信的并发模型。 Golang,用到了 CSP 的理论为它的并发模型进行了论证,CSP理论中的 Process/Channel(对应到Go语言中的 goroutine/channel):这两个并发原语之间没有从属关系, Process 可以订阅任意个 Channel,Channel 也并不关心是哪个 Process 在利用它进行通信;Process 围绕 Channel 进行读写,形成一套有序阻塞和可预测的并发模型。
所以,其实在Go里面,使用Chan关键字来实现Goroutine之间的通信,和Java不太一样,Java是使用共享内存来通信,Go强调不要使用共享内存来通信,而是使用通信来实现共享内存。
共享内存来通信的缺点是:多个线程共享内存空间,容易导致数据访问混乱,某个线程误操作内存挂掉后,可能搞坏整个线程组, 健壮性不高。
好处是共享内存来通信更快捷。

Goroutine 是实际并发执行的实体,它底层是使用协程(coroutine)实现并发,coroutine是一种运行在用户态的用户线程,类似于 greenthread,go底层选择使用coroutine的出发点是因为,它具有以下特点:

  • 用户空间 避免了内核态和用户态的切换导致的成本
  • 可以由语言和框架层进行调度
  • 更小的栈空间允许创建大量的实例
    Go Chan
    Go的Chan可以用于goroutine之间的通信,Go强调不要使用共享内存来通信,而要使用通信来共享内存。而Chan就是goroutine之间通信的通道。
    Go chan使用场景
    实现退出通知机制:
    在Goroutine中没有父子的关系,每个goroutine是独立被调度的,Go里面常用Chan和select的机制来实现通知goroutine退出的机制
    实现两个Goroutine之间传输数据:
    有缓冲的通道可以实现两个Goroutine之间传输数据
    Go有缓冲的Chan和无缓冲的Chan
    无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何值的通道。这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。如果两个 goroutine 没有同时准备好,通道会导致先执行发送或接收操作的 goroutine 阻塞等待。这种对通道进行发送 和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在。
    在这里插入图片描述

有缓冲的通道(buffered channel)是一种在被接收前能存储一个或者多个值的通道。这种类 型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。通道会阻塞发送和接收动作的 条件也会不同。只有在通道中没有要接收的值时,接收动作才会阻塞。只有在通道没有可用缓冲 区容纳被发送的值时,发送动作才会阻塞。这导致有缓冲的通道和无缓冲的通道之间的一个很大 的不同:无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的 通道没有这种保证。
在这里插入图片描述

Go的Context介绍
Context:
Go语言在语法上处理某个goroutine退出通知机制很简单。但是实际编程中goroutine会拉起新的goroutine,新的goroutine可能还会拉起另一个新的goroutine, 最终形成一个树状的结构,由于goroutine里并没有父子的概念, Go实现了Context通知这个树状上的所有goroutine。Context它提供:退出通知和数据传递的功能。context库的设计目的就是跟踪goroutine调用树,并在这些gouroutine调用树中传递通知和数据。通知和数据可以传递给整个goroutine调用树上的每一个goroutine。

func
Context工作机制:
context包的整体工作机制:第一个创建Context的goroutine
被称为root节点。root节点负责创建一个实现Context接口的具体对象,并将该对象作为参数
传递到其新拉起的goroutine,下游的goroutine可以继续封装该对对象,再传递到更下游的goroutine
Context对象在传递的过程中最终形成一个树状的数据结构,这文样通过位于root节点(树的根节
点)的Context对象就能遍历整个Context对象树,通知和消息就可以通过root节点传递出去,
实现了上游goroutine对下游goroutine的消息传递。
Context中有哪些接口:
Cancel Context:
cancelCtx是一个实现了Context接口的具体类型,同时实现了canceler接口。conceler具有
退出通知方法。注意退出通知机制不但能通知自己,也能逐层通知其children节点。
Timer Context:
timerCtx内部封装了cancelCtx类型,同时有一个deadline变量,用来实现定时退出通知。
Value Context:
valueCtx是一个实现了Context接口的具体类型,内部封装了Context接口类型,同时封装
了一个k/v的存储变量。valueCtx可用来传递通知信息。
Go的WaitGroup
waitgroup为Go的goroutine提供了多个goroutine的同步机制。WaitGroup用来等待多个goroutine完成, 通过调用Add方法设置需要等待goroutine的数目, 之后每一个goroutine结束时调用Done()方法, 最后Wait()方法用来等待所有的goroutine完成。
import (
“net/http”
“sync”
var wg sync.WaitGroup
var urls = []string{
“http://www.golang.org/”,
“http://www.google.com/”,
“http://www.qq.com/”,
func main(){
for_, url := range urls {
//每一个URL启动一个goroutine,同时给wg加1
wg.Add (1)
//Launch a goroutine to fetch the URL.
go func (url string) {
//当前goroutine结束后给wg计数减1,wg.Done()等价于wg…Add (-1)
//defer wg.Add(-1)
defer wg.Done()
//发送HTTP get请求并打印HTTP返回码
resp, err := http.Get (url)
if err == nil {
println (resp.Status)
}
}(url)
//等待所有请求结束
wg.Wait()
//执行结果
200 OK

使用Go和Java分别遇到什么坑
Go的坑:
1.
2. 使用Go内置的Map是无序的Map,并且是不能并发读写的,并发读写会报错
Java的坑:

Go/Java的线上内存泄露问题,如何定位

  1. Go使用pprof工具定位内存泄露问题
  2. Java
    Sync map的原理
    适合读多写少的场景
    https://www.cnblogs.com/457220157-FTD/p/14666852.html
    go垃圾回收机制
    三色标记算法将程序中的对象分成白色、黑色和灰色三类。白色对象表示暂无对象引用的潜在垃圾,其内存可能会被垃圾收集器回收;灰色对象表示活跃的对象,黑色到白色的中间状态,因为存在指向白色对象的外部指针,垃圾收集器会扫描这些对象的子对象;黑色对象表示活跃的对象,包括不存在引用外部指针的对象以及从根对象可达的对象。
  3. 将所有对象标记为白色
  4. 从根节点集合出发,将第一次遍历到的节点标记为灰色放入集合列表中
  5. 遍历灰色集合,将灰色节点遍历到的白色节点标记为灰色,并把灰色节点标记为黑色
  6. 循环这个过程
  7. 直到灰色节点集合为空,回收所有的白色节点

[图片]
operator是什么,原理
在Oprearor编程里面,通过在K8s中定义一个自定义的资源(快照),spec 期望状态,status 实际状态,定义了这个自定义资源的时候,我们会开发对应的Operator对这个K8s自定义资源进行管理,Operator整体的设计思路就是一个Reconcile循环, 会在一个循环中检测我们自定义资源的状态Status是否和自定义资源的Spec是否相符. 若不相符, 则对集群做出操作, 使状态不停向预期靠拢. 这个过程称为Reconcile
通过这个过程:

  1. 实现简单的故障自愈
  2. 定制化开发,我们之所以要使用自定义资源是因为K8s原生的资源无法满足我们的需求,比如k8s的资源无法完整对一个快照进行描述,所以我们会对数据库集群定义一个资源,叫做CRD。因为是自定义的,K8s并没有控制器对这个自定义资源进行管理,而operator就充当了控制器的角色
    原理
    [图片]
    有看过什么源码吗
    阿里开源的OpenKruise框架,在K8s一些场景资源的基础上增强了无损升级等能力,有看过部分源码。
    OpenKruise Kruise-Daemon & CRR 浅析
    Go goroutine调度器的实现原理
    高并发问题:
    https://pdai.tech/md/arch/arch-y-loadbalance.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值