Go语言初尝
Posted 编程大观园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言初尝相关的知识,希望对你有一定的参考价值。
概述
对于语言设计之争, 唯一需要牢记的一句话是: 如果把 C 变成 C++, 那么 C 就消失了。
Go 是一个轻量级的简洁的支持并发的现代语言, 可以用于探索性个人项目, 这是我想学这门语言的主要原因。 对于有一定编程经验的人来说, 学习一种新语言的方式是, 先概览下语言特性, 然后编写一个中等规模的程序, 尽可能地运用到大部分重要特性。
Go 的主要语言特性:
- 静态类型 + 类型推导
- 简化的控制结构: if , for , switch
- 可命名的多返回值的函数
- 内置容器支持: 数组、Slice、Map
- 简化的指针与引用
- 简化的对象特性: 结构体与非侵入式接口
- 内置的并发支持: go, Goroutine & Channel
- 反射、匿名函数与闭包;
- 便捷地 CGo 交互;
- 内置 GC ;
- 包管理。
针对熟练 Java 开发者,快速上手 Go 语言。
学习新语言的最佳方式,就是找一个实际项目来研究,再写一个实用程序来练手。
Go 语法
1. 变量名在前,变量类型在后;变量名(方法名、函数名、类名)采用驼峰形式。比如:
type JsonResp struct
Msg string `json:"msg"`
Data interface `json:"data"`
2. 语句结束不用分号。
3. 条件语句不用括号。
if runMode == engine_common.RUNMODE_TRACE
processor = append(processor, sendMongo.NewSendDetectToMongo(w.mongoDao))
4. 强制类型转换
edgeName := p["type"].(string)
var a int32 = 10
var b int64 = int64(a)
var a int =10
var b *int =&a
var c *int64 = (*int64)(unsafe.Pointer(b))
5. 定义枚举常量,可以用于 switch 语句;常量名全大写。
const
const (
RUNMODE_TRACE = "trace" // 联调模式,日志输出至kafka、日志、控制台
RUNMODE_DEBUG = "debug" // 本地调试模式,日志输出至日志、控制台
RUNMODE_RELEASE = "release" // 发布模式,用于正式发布,仅输出至kafka
RUNMODE_MOCK = "mock" // 测试模式,用于测试相关流程,输出到go channel
)
switch runMode
case engine_common.RUNMODE_TRACE:
processor = append(processor, aggregator.NewIncidentMsgAggregatorWithSink(w.config.AggregationTime,
sink.NewKafkaSink(w.incidentKafka),
sink.NewLogSink()))
case engine_common.RUNMODE_RELEASE:
processor = append(processor, aggregator.NewIncidentMsgAggregatorWithSink(w.config.AggregationTime,
sink.NewKafkaSink(w.incidentKafka)))
case engine_common.RUNMODE_DEBUG:
a.AddSink(sink.NewConsoleSink())
a.AddSink(sink.NewLogSink())
case engine_common.RUNMODE_MOCK:
a.AddSink(sink.NewLogSink())
a.AddSink(sink.NewChannelSink(param.MockChannel))
6. 定义通用对象
type Any interface
type List []Any
7. 遍历列表
Path []map[string]common.Any `json:"path"`
for _, p := range ctx.Detect.Path
if p["type"] == "DETECT_POINT"
detect_id = p["dst_id"].(string)
8. 定义映射
var m = map[int]string
ERROR_SUCCESS: "ok",
ERROR_FAIL: "fail",
ERROR_INVALID_PATH: "请求路径参数不合法",
ERROR_INVALID_QUERY: "请求查询参数错误",
ERROR_RESULT_NOT_EXIST: "没有找到符合条件的记录",
// 多重 map
elements map[string]map[string]common.Any
// 创建 map
edge_prop := make(map[string]common.Any)
9. 定义函数
左括号必须与 func 在同一行。函数名首字母大写。
func 方法名(方法签名)返回对象
func GetMsg(code int) string
msg, ok := m[code]
if ok
return msg
return m[ERROR_SUCCESS]
10. 定义类、构造器、方法
类名首字母大写。
type 类名或结构名 struct
func New类名() *类名
return &类名属性名:属性值
func (m *类名) 方法名(方法签名) 返回值类型
示例:
// EngineConfig 配置项
type EngineConfig struct
GraphEngineComponent // 组件配置
GraphEngineRule // 本地规则配置,初次从本地加载的数据
CustomConfig `toml:"custom_config"` // 自定义配置,控制参数
LogConfig `toml:"log_config"` // 日志配置
ServiceConfig `toml:"service_config"` // 服务配置
MonitorService `toml:"monitor_service"` // 監控
// 合并本地和云端配置后的规则配置参数,云端参数优先
graphEngineUpdatedRule atomic.Value
// 单例nacos客户端,Go 保留指针,但不能对指针运算,只能引用
nacosClient *nacos.Client
type CenterServer struct
servers map[string] ipc.Server players []*Player
rooms []*Room
mutex sync.RWMutex
func NewCenterServer() *CenterServer
servers := make(map[string] ipc.Server) players := make([]*Player, 0)
return &CenterServerservers:servers, players:players
# 定义类的方法, (m *EngineConfig) 是对象的隐藏指针。
func (m *EngineConfig) GetIncidentMaxRange() int64
timeSlice := m.graphEngineUpdatedRule.Load().(GraphEngineRule).TimeSliceData
if timeSlice.MaxRange.Duration == 0
return int64(time.Minute * 5 / time.Millisecond) // 默认间隔5min
else
return int64(timeSlice.MaxRange.Duration / time.Millisecond)
12. 定义接口
只要实现这个接口的方法,就能赋值给这个接口的引用。即无论是黑猫还是白狗,能抓住老鼠的就是鼠的天敌。
type IWorkFlowFactory interface
GetName() string
Clean() bool
Create(name string, params interface) (IWorkFlow, error)
type WorkFlowFactory struct
config *config.EngineConfig
globalContext context.Context
mongoDao *mongo.MongoDao // 仅trace模式时会用到
// code...
func (w *WorkFlowFactory) GetName() string
return "WorkFlowFactory"
func (w *WorkFlowFactory) Clean() bool
// code...
log.Info("workflow factory clean successful")
return true
func (w *WorkFlowFactory) Create(name string, params interface) (define.IWorkFlow, error)
// code
13. 类型自动推导: a := 5
14. 错误处理
if param, ok := params.(*service_define.StartParam); !ok
return nil, errors.New("Input type error ")
nebulaDao := dao.NewNebulaDaoWithSpace(w.nebulaClient, nebulaSpace)
if nil == nebulaDao
return nil, errors.New("Init nebula dao error ")
15. 格式化
fmt.Errorf("detect.path[%d].dst_id=%v missing in detect.element, detectId=%v", j, p["dst_id"].(string), ctx.Detect.Id)
编程实战
下面的程序用于计算一个目录下所有文件或目录的大小。
$ go build -o dirsize dirsize_module.go time2.go
$ dirsize
dirsize_module.go
package main
import (
"fmt"
// "log"
"os"
"sort"
"time"
)
import "C"
type ShortFileInfo struct
fileName string
size int64
type FileInfoSlice []ShortFileInfo
func (fileInfo *ShortFileInfo) Desc() string
return fmt.Sprintf("%s:%s", fileInfo.fileName, readableSize(fileInfo.size))
func (fileInfos FileInfoSlice) Len() int
return len(fileInfos)
func (fileInfos FileInfoSlice) Swap(i, j int)
fileInfos[i], fileInfos[j] = fileInfos[j], fileInfos[i]
func (fileInfos FileInfoSlice) Less(i, j int) bool
return fileInfos[i].size > fileInfos[j].size
type IOError struct
msg string
err error
func (ioe IOError) Error() string
return "Error: " + ioe.msg + "\\nCause: " + ioe.err.Error()
func produceFiles(dirName string) (fileInfos []os.FileInfo, e error)
path, err := os.Open(dirName)
if err != nil
//log.Fatal(err)
return nil , IOError"failed to open " + dirName, err
defer path.Close()
fileInfos, readerr := path.Readdir(0)
if readerr != nil
//log.Fatal(readerr)
return nil, IOError"failed to read " + dirName, readerr
return fileInfos, nil
func procFile(fileInfo os.FileInfo, baseDirName string, channelBuffer chan ShortFileInfo)
var filesize int64
fileName := fileInfo.Name()
if fileInfo.IsDir()
filesize = totalFilesizeInDir(fileInfo, baseDirName)
else
filesize = fileInfo.Size()
shortFileInfo := ShortFileInfofileName, filesize
fmt.Println(time.Now().String() + " store: " + shortFileInfo.Desc())
channelBuffer <- shortFileInfo
func totalFilesizeInDir(fileInfo os.FileInfo, baseDirName string) int64
var filesize int64 = 0
fileInfos, _ := produceFiles(baseDirName + "/" + fileInfo.Name())
for _, subfileInfo := range fileInfos
if subfileInfo.IsDir()
filesize += totalFilesizeInDir(subfileInfo, baseDirName+"/"+fileInfo.Name())
else
filesize += subfileInfo.Size()
return filesize
func sleep(ns int)
time.Sleep(time.Duration(time.Second) * time.Duration(ns))
const (
B int64 = 1
KB int64 = 1024
MB int64 = 1024 * 1024
GB int64 = 1024 * 1024 * 1024
TB int64 = 1024 * 1024 * 1024 * 1024
)
const formatF string = "%8.4f"
func readableSize(sizeInBytes int64) string
switch
case B <= sizeInBytes && sizeInBytes < KB:
return fmt.Sprintf("%dB", sizeInBytes)
case KB <= sizeInBytes && sizeInBytes < MB:
return fmt.Sprintf(formatF+"KB", float64(sizeInBytes)/float64(KB))
case MB <= sizeInBytes && sizeInBytes < GB:
return fmt.Sprintf(formatF+"MB", float64(sizeInBytes)/float64(MB))
case GB <= sizeInBytes && sizeInBytes < TB:
return fmt.Sprintf(formatF+"GB", float64(sizeInBytes)/float64(GB))
case TB <= sizeInBytes:
return fmt.Sprintf(formatF+"TB", float64(sizeInBytes)/float64(TB))
default:
return "0"
//export mainProc
func mainProc()
start_1 := time.Now().Unix()
baseDirName := "/home/lovesqcc"
//baseDirName := "/notExist"
//baseDirName := "/"
fileList, err := produceFiles(baseDirName)
if err != nil
fmt.Println(err.Error())
os.Exit(1)
fileNumber := len(fileList)
channelBuffer := make(chan ShortFileInfo, fileNumber)
fileInfoMap := make(map[string]int64, fileNumber)
for _, fileInfo := range fileList
go procFile(fileInfo, baseDirName, channelBuffer)
var fileInfos = make([]ShortFileInfo, fileNumber+1)
timeout := make(chan int, 1)
go func()
secs := 3
sleep(secs)
timeout <- secs
()
for count := 0; count <= fileNumber;
select
case fileInfo := <-channelBuffer:
fmt.Println(time.Now().String() + " fetch: " + fileInfo.Desc())
fileInfoMap[fileInfo.fileName] = fileInfo.size
fileInfos[count] = fileInfo
count++
case secs := <- timeout:
fmt.Printf("%d s timout ! Exit Loop\\n", secs)
os.Exit(1)
default:
if count == fileNumber
close(channelBuffer)
fmt.Println("Waiting for data ...")
sort.Sort(FileInfoSlice(fileInfos))
for _, fileInfo := range FileInfoSlice(fileInfos)
fmt.Println("File " + fileInfo.fileName + " : " + readableSize(fileInfo.size))
var totalSize int64
totalSize = 0
for _, filesize := range fileInfoMap
totalSize += filesize
fmt.Printf("Total size in %s:%dB %s\\n", baseDirName, totalSize, readableSize(totalSize))
end_1 := time.Now().Unix()
fmt.Printf("start=%d, end=%d\\n",start_1, end_1)
fmt.Printf("Time cost: %dms\\n", (end_1-start_1)*1000)
time2.go
package main
/*
#include<stdio.h>
#include<time.h>
extern int mainProc();
int printTimeCost()
printf("exec printTimeCost");
time_t start = 0, end = 0;
long duration = 0;
start = time(NULL);
mainProc();
end = time(NULL);
duration = (end - start) *1000;
return duration;
*/
import "C"
import "fmt"
func main()
fmt.Printf("cost: %dms\\n", C.int(C.printTimeCost()))
参考资料
- 《Go语言编程》 许式伟著。
- Go 轻松学: https://github.com/jemygraw/TechDoc
- Go 实例学习: https://gobyexample.com/
- Go 并发之美: http://www.cnblogs.com/yuxingfirst/archive/2012/11/28/2792366.html
- Go 文档: https://go-zh.org/doc/
- C-Go 互操作 : http://tonybai.com/2012/09/26/interoperability-between-go-and-c/
- Go 标准库: http://studygolang.com/pkgdoc
- Why Go is Not Good: http://yager.io/programming/go.html
以上是关于Go语言初尝的主要内容,如果未能解决你的问题,请参考以下文章