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 &CenterServer{servers: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 := ShortFileInfo{fileName, 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()))

}


参考资料

posted @ 2023-02-26 09:27  琴水玉  阅读(57)  评论(0编辑  收藏  举报