GoCN每日新闻(2017-06-12)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 478 次浏览 • 2017-06-12 10:25 • 来自相关话题

Golang逃逸分析

sheepbao 发表了文章 • 0 个评论 • 2294 次浏览 • 2017-06-11 11:56 • 来自相关话题

Golang逃逸分析

介绍逃逸分析的概念,go怎么开启逃逸分析的log。
以下资料来自互联网,有错误之处,请一定告之。
sheepbao 2017.06.10

什么是逃逸分析

w... 查看全部

Golang逃逸分析


介绍逃逸分析的概念,go怎么开启逃逸分析的log。

以下资料来自互联网,有错误之处,请一定告之。

sheepbao 2017.06.10


什么是逃逸分析


wiki上的定义


In compiler optimization, escape analysis is a method for determining the dynamic scope of pointers - where in the program a pointer can be accessed. It is related to pointer analysis and shape analysis.


When a variable (or an object) is allocated in a subroutine, a pointer to the variable can escape to other threads of execution, or to calling subroutines. If an implementation uses tail call optimization (usually required for functional languages), objects may also be seen as escaping to called subroutines. If a language supports first-class continuations (as do Scheme and Standard ML of New Jersey), portions of the call stack may also escape.


If a subroutine allocates an object and returns a pointer to it, the object can be accessed from undetermined places in the program — the pointer has "escaped". Pointers can also escape if they are stored in global variables or other data structures that, in turn, escape the current procedure.


Escape analysis determines all the places where a pointer can be stored and whether the lifetime of the pointer can be proven to be restricted only to the current procedure and/or threa


大概的意思是在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法,可以分析在程序的哪些地方可以访问到指针。它涉及到指针分析和形状分析。
当一个变量(或对象)在子程序中被分配时,一个指向变量的指针可能逃逸到其它执行线程中,或者去调用子程序。如果使用尾递归优化(通常在函数编程语言中是需要的),对象也可能逃逸到被调用的子程序中。
如果一个子程序分配一个对象并返回一个该对象的指针,该对象可能在程序中的任何一个地方被访问到——这样指针就成功“逃逸”了。如果指针存储在全局变量或者其它数据结构中,它们也可能发生逃逸,这种情况是当前程序中的指针逃逸。
逃逸分析需要确定指针所有可以存储的地方,保证指针的生命周期只在当前进程或线程中。


逃逸分析的用处(为了性能)



  • 最大的好处应该是减少gc的压力,不逃逸的对象分配在栈上,当函数返回时就回收了资源,不需要gc标记清除。

  • 因为逃逸分析完后可以确定哪些变量可以分配在栈上,栈的分配比堆快,性能好

  • 同步消除,如果你定义的对象的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。


go消除了堆和栈的区别


go在一定程度消除了堆和栈的区别,因为go在编译的时候进行逃逸分析,来决定一个对象放栈上还是放堆上,不逃逸的对象放栈上,可能逃逸的放堆上。


开启go编译时的逃逸分析日志


开启逃逸分析日志很简单,只要在编译的时候加上-gcflags '-m',但是我们为了不让编译时自动内连函数,一般会加-l参数,最终为-gcflags '-m -l'


Example:


package main

import (
"fmt"
)

func main() {
s := "hello"
fmt.Println(s)
}

go run -gcflags '-m -l' escape.go

Output:


# command-line-arguments
escape_analysis/main.go:9: s escapes to heap
escape_analysis/main.go:9: main ... argument does not escape
hello

什么时候逃逸,什么时候不逃逸


Example1:


package main

type S struct{}

func main() {
var x S
y := &x
_ = *identity(y)
}

func identity(z *S) *S {
return z
}

Output:


# command-line-arguments
escape_analysis/main.go:11: leaking param: z to result ~r1 level=0
escape_analysis/main.go:7: main &x does not escape

这里的第一行表示z变量是“流式”,因为identity这个函数仅仅输入一个变量,又将这个变量作为返回输出,但identity并没有引用z,所以这个变量没有逃逸,而x没有被引用,且生命周期也在mian里,x没有逃逸,分配在栈上。


Example2:


package main

type S struct{}

func main() {
var x S
_ = *ref(x)
}

func ref(z S) *S {
return &z
}

Output:


# command-line-arguments
escape_analysis/main.go:11: &z escapes to heap
escape_analysis/main.go:10: moved to heap: z

这里的z是逃逸了,原因很简单,go都是值传递,ref函数copy了x的值,传给z,返回z的指针,然后在函数外被引用,说明z这个变量在函数內声明,可能会被函数外的其他程序访问。所以z逃逸了,分配在堆上


对象里的变量会怎么样呢?看下面


Example3:


package main

type S struct {
M *int
}

func main() {
var i int
refStruct(i)
}

func refStruct(y int) (z S) {
z.M = &y
return z
}

Output:


# command-line-arguments
escape_analysis/main.go:13: &y escapes to heap
escape_analysis/main.go:12: moved to heap: y

看日志的输出,这里的y是逃逸了,看来在struct里好像并没有区别,有可能被函数外的程序访问就会逃逸


Example4:


package main

type S struct {
M *int
}

func main() {
var i int
refStruct(&i)
}

func refStruct(y *int) (z S) {
z.M = y
return z
}

Output:


# command-line-arguments
escape_analysis/main.go:12: leaking param: y to result z level=0
escape_analysis/main.go:9: main &i does not escape

这里的y没有逃逸,分配在栈上,原因和Example1是一样的。


Example5:


package main

type S struct {
M *int
}

func main() {
var x S
var i int
ref(&i, &x)
}

func ref(y *int, z *S) {
z.M = y
}

Output:


# command-line-arguments
escape_analysis/main.go:13: leaking param: y
escape_analysis/main.go:13: ref z does not escape
escape_analysis/main.go:10: &i escapes to heap
escape_analysis/main.go:9: moved to heap: i
escape_analysis/main.go:10: main &x does not escape

这里的z没有逃逸,而i却逃逸了,这是因为go的逃逸分析不知道z和i的关系,逃逸分析不知道参数y是z的一个成员,所以只能把它分配给堆。


参考


Go Escape Analysis Flaws

go-escape-analysis

GoCN每日新闻(2017-06-11)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 456 次浏览 • 2017-06-11 11:16 • 来自相关话题

GoCN每日新闻(2017-06-10)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 459 次浏览 • 2017-06-10 10:50 • 来自相关话题

GoCN每日新闻(2017-06-09)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 547 次浏览 • 2017-06-09 10:16 • 来自相关话题

GoCN每日新闻(2017-06-08)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 546 次浏览 • 2017-06-08 09:36 • 来自相关话题

GoCN每日新闻 (2017-06-07)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 498 次浏览 • 2017-06-07 10:02 • 来自相关话题

golang使用服务发现系统consul

changjixiong 发表了文章 • 0 个评论 • 455 次浏览 • 2017-06-06 22:15 • 来自相关话题

  本文的完整代码见 https://github.com/changjixiong/goNotes/tree/master/consulnotes ,如果文中没有显示链接说明链接在被转发的时候被干掉了,请搜索找到原文阅读。


consul是什么


"Consul is a distributed, highly available, datacenter-aware, service discovery and configuration system. It can be used to present services and nodes in a flexible and powerful interface that allows clients to always have an up-to-date view of the infrastructure they are a part of."


  引用一段网上对consul文档的翻译(http://consul.la/intro/what-is-consul)


Consul有多个组件,但是整体来看,它是你基础设施中用于发现和配置服务的一个工具。它提供如下几个关键功能:

* 服务发现: Consul的某些客户端可以提供一个服务,例如api或者mysql,其它客户端可以使用Consul去发现这个服务的提供者。使用DNS或者HTTP,应用可以很容易的找到他们所依赖的服务。
* 健康检查: Consul客户端可以提供一些健康检查,这些健康检查可以关联到一个指定的服务(服务是否返回200 OK),也可以关联到本地节点(内存使用率是否在90%以下)。这些信息可以被一个操作员用来监控集群的健康状态,被服务发现组件路由时用来远离不健康的主机。
* 键值存储: 应用可以使用Consul提供的分层键值存储用于一些目的,包括动态配置、特征标记、协作、leader选举等等。通过一个简单的HTTP API可以很容易的使用这个组件。
* 多数据中心: Consul对多数据中心有非常好的支持,这意味着Consul用户不必担心由于创建更多抽象层而产生的多个区域。
Consul被设计为对DevOps群体和应用开发者友好,他非常适合现代的、可伸缩的基础设施。

范例


  网上关于consul的文档及使用说明有很多,然而却缺少关于使用的范例,接下来的内容将用一个范例来演示如何找到可服务的节点。完整的代码见https://github.com/changjixion ... notes


  假设在一个系统中,节点A需要访问某种服务,该服务有N个节点可提供服务,这些节点位于服务集群groupB,节点A只需要连接上groupB中的任一节点即可获得服务。


启动consul


  consul提供开发模式用于启动单节点服务供开发调试用,运行命令consul agent -dev 启动consul,输出的信息中有一行


Client Addr: 127.0.0.1 (HTTP: 8500, HTTPS: -1, DNS: 8600, RPC: 8400)

  显示了consul运行参数,通过网址http://127.0.0.1:8500/ui/#/dc1/nodes可以查看节点与服务


注册服务并添加健康检查


  下面的代码将向consul注册一个服务


import (
"fmt"
"log"

"net/http"

consulapi "github.com/hashicorp/consul/api"
)

func consulCheck(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "consulCheck")
}

func registerServer() {

config := consulapi.DefaultConfig()
client, err := consulapi.NewClient(config)

if err != nil {
log.Fatal("consul client error : ", err)
}

checkPort := 8080

registration := new(consulapi.AgentServiceRegistration)
registration.ID = "serverNode_1"
registration.Name = "serverNode"
registration.Port = 9527
registration.Tags = []string{"serverNode"}
registration.Address = "127.0.0.1"
registration.Check = &consulapi.AgentServiceCheck{
HTTP: fmt.Sprintf("http://%s:%d%s", registration.Address, checkPort, "/check"),
Timeout: "3s",
Interval: "5s",
DeregisterCriticalServiceAfter: "30s", //check失败后30秒删除本服务
}

err = client.Agent().ServiceRegister(registration)

if err != nil {
log.Fatal("register server error : ", err)
}

http.HandleFunc("/check", consulCheck)
http.ListenAndServe(fmt.Sprintf(":%d", checkPort), nil)

}

  consulapi.DefaultConfig()的源代码显示默认采用的是http方式连接"127.0.0.1:8500",前文中显示consul开发模式默认提供的http服务是在127.0.0.1:8500,在实际使用中需要设置为实际的参数。


  consulapi.AgentServiceCheck中的HTTP指定了健康检查的接口地址即127.0.0.1:8080/check,consulCheck函数响应这个接口调用,返回200状态码及一段字符串"consulCheck",健康检查还有其他几种方式,具体可以参考官方文档。


  consulapi.AgentServiceCheck中的DeregisterCriticalServiceAfter指定检查不通过后多长时间注销本服务,这里设置为30秒。


  向consul注册的服务地址为127.0.0.1:9527,以下是在127.0.0.1:9527上提供的echo服务。


ln, err := net.Listen("tcp", "0.0.0.0:9527")

if nil != err {
panic("Error: " + err.Error())
}

for {
conn, err := ln.Accept()

if err != nil {
panic("Error: " + err.Error())
}

go EchoServer(conn)
}

  服务启动后,访问http://127.0.0.1:8500/ui/#/dc1/nodes 会发现 "2 services",点开后会在页面上看到serverNode 127.0.0.1:9527,表明服务信息已经注册。以下信息显示健康检查通过。


HTTP GET http://127.0.0.1:8080/check: 200 OK Output: consulCheck

使用服务


  服务使用方client通过以下代码向consul查询可用的服务(忽略错误处理)


client, err := consulapi.NewClient(consulapi.DefaultConfig())//非默认情况下需要设置实际的参数
...
services, err = client.Agent().Services()
...
if _, found := services["serverNode_1"]; !found {
log.Println("serverNode_1 not found")
continue
} //查找名为serverNode_1的服务

  查找到服务后连接服务并发送数据


conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", service.Address, service.Port))
...

  client先启动,服务后启动,然后服务关闭,运行日志如下:


serverNode_1 not found
get: EchoServerHello World, 001
get: EchoServerHello World, 002
...
get: EchoServerHello World, 008
Read Buffer Error: EOF
dial tcp 127.0.0.1:9527: getsockopt: connection refused
dial tcp 127.0.0.1:9527: getsockopt: connection refused
...
dial tcp 127.0.0.1:9527: getsockopt: connection refused
serverNode_1 not found
serverNode_1 not found

  服务启动前提示serverNode_1没找到,服务启动后数据交互正常,服务关闭后consul尚未注销服务client提示服务无法连接,稍后consul注销了失效的服务,client显示服务没有找到。


使用场景设想


  假设一个网络游戏有N个副本服务节点提供服务,在生产运行期间,有的节点可能故障,有些节点可能负载过高,有些节点可能故障后自行回复需要能重新上线提供服务。通过consul系统可以随时让网关服务器或者逻辑服务器获取可用的副本服务节点并将请求转发到该节点,保持副本服务的高效可用。


  其他类型的服务也可以采用同样的方式进行水平扩展。进一步的,可以在负载高的时候启动新节点,在负载低的时候关闭部分节点,在云服务器上实现这些非常方便,并且由于是按使用计费,通过负载增加或关闭节点也可以避免云服务器资源的浪费从而降低费用。


一点问题


  服务注册时设置检查失败后30秒注销服务,实际运行中大约80秒才注销服务,原因待查。

GoCN每日新闻(2017-06-06)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 485 次浏览 • 2017-06-06 09:50 • 来自相关话题

GoCN每日新闻(2017-06-05)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 514 次浏览 • 2017-06-05 10:02 • 来自相关话题

GoCN每日新闻(2017-06-04)

taowen 回复了问题 • 3 人关注 • 2 个回复 • 494 次浏览 • 2017-06-05 09:51 • 来自相关话题

GoCN每日新闻(2017-06-03)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 450 次浏览 • 2017-06-03 09:59 • 来自相关话题

Go 动态汇总(持续跟进)

bingohuang 发表了文章 • 0 个评论 • 430 次浏览 • 2017-06-02 17:22 • 来自相关话题

依托 Go team 的 『State of Go』talks,持续跟进 Go 的动态.

Go 动态列表如下:

日期 | 作者 |... 查看全部


依托 Go team 的 『State of Go』talks,持续跟进 Go 的动态.



Go 动态列表如下:


日期 | 作者 | 视频 | 演讲稿


Go1.8|2017年5月 | Francesc Campoy | video | slides


Go1.7-Go1.8|2017年2月 | Francesc Campoy | video | slides


Go1.6|2016年2月 | Francesc Campoy | video | slides


Go1.5|2015年5月 | Andrew Gerrand | video | slides


Go1.4-Go1.5|2015年2月 | Andrew Gerrand | video | slides


Gopher|2014年10月 | Brad Fitzpatrick | video | slides


Go1.3-Go1.4|2014年6月 | Andrew Gerrand | video | slides


Go1.2-Go1.3|2014年2月 | Andrew Gerrand | video | slides


Go1.1|2013年5月 | Andrew Gerrand | 无 | slides


Go1|2012年7月 | Rob Pike & Andrew Gerrand | video | slides


将持续跟进最新的 『State of Go』 talks,更新列表,敬请关注



转自:http://bingohuang.com/the-state-of-go/


GoCN每日新闻(2017-06-02)

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 584 次浏览 • 2017-06-02 10:23 • 来自相关话题

Gopher上海 meetup

astaxie 发表了文章 • 0 个评论 • 376 次浏览 • 2017-06-01 09:08 • 来自相关话题

上海Gopher meetup

时间: 2017-07-01

地点:上海普陀区真北路788号 近铁城市广场(南楼)8楼 饿了么

  1. 张培培 饿了么 《基于amqp实现的golang消息队列MaxQ》... 查看全部

上海Gopher meetup


时间: 2017-07-01


地点:上海普陀区真北路788号 近铁城市广场(南楼)8楼 饿了么



  1. 张培培 饿了么 《基于amqp实现的golang消息队列MaxQ》
    将从架构模型、消息可靠性、容错性、扩展性和高并发介绍MaxQ实现。

  2. 严清 teambition 《Why We Use Go Web Frameworks》
    讲一讲 Go 语言下的 Web 服务框架解决的几个核心问题

  3. 董海冰 VIPABC 《TutorMeet+:用Go打造高品质互动云课堂》
    Go在高品质直播课堂中的应用,如何和视频编解码、WebRTC等集成

  4. 陈叶皓 携程 《Go实现消息推送服务》
    以携程的应用场景为例,消息推送服务器需要同时维持数百万个客户端连接,当用户有消息产生时,可以实时地发送到用户的手机客户端。

  5. 申砾 PingCAP 《Go 搭建大型开源分布式数据库技术内幕》
    TiDB 是用 Go 开发的分布式数据库,是 Spanner/F1 的开源实现。本次 Talk 会介绍 TiDB 的架构设计、技术细节以及我们在使用 Go 语言的一些经验。


报名地址:http://www.bagevent.com/event/569317