Go命令行工具cobra

cobra是go下的一个命令行解析库。写livermore的时候,需要用到命令行解析,就找到了它。进入项目主页看了一下,发现蛮多高大上的项目也在使用,比如docker,还有本博客程序hugo,和hugo是同一个作者。

好吧,开始了解一下cobra是如何使用的。项目主页上文档已经很完备,使用非常简单。这里再简单介绍一下。

基本概念

这里使用livermore做例子来介绍。例如
livermore -v fetch sh600036 -s codes.txt

livermore 应用程序的名称
fetch 具体的command,这里是获取股票基本信息
sh600036 fetch的参数 arg, 股票代码
-v flag,是否开启verbose模式,打印debug信息
-s fetch的一个flag,具体值: “codes.txt””

实现

文件结构如下

  ▾ livermore/
    ▾ cmd/
        version.go
        fetch.go
        root.go
      main.go

整个程序的入口,main

整个程序入口main.go, 具体的命令都定义在cmd中,每个命令一个文件,这样比较清晰。 在main.go中,只需要加载cobra命令的配置等信息就可以了。

package main

import "github.com/wuyq101/livermore/cmd"

func main() {
	if err := cmd.RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

定义根命令 livermore

刚刚在main中加载的root cmd, 在cmd/root.go中定义livermore这个命令.

package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
)

var Verbose bool

var RootCmd = &cobra.Command{
	Use:   "livermore",
	Short: "Livermore is a command line tool for stock data fetch and display",
	Long: `Livermore is a command line tool for stock data fetch and display.
		   It was written in Go. 
	       More detail please visit : https://github.com/wuyq101/livermore
		`,
	Run: func(cmd *cobra.Command, args []string) {
		// Do Stuff Here
		fmt.Println("This is test from cobra")
	},
}

func init() {
	RootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
}

定义子命令 version

在cmd/version.go中定义version命令, 并且将它加入到livermore中。

package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
)

func init() {
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print version numbre of Livermore",
	Long:  "All software has versions. This is Livermore's",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("Livermore v0.1 --head")
	},
}

定义子命令fetch

在cmd/fetch.go中定义fetch命令,并且将它加入到livermore中。

package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
)

func init() {
	fetchCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
	RootCmd.AddCommand(fetchCmd)
}

var Source string

var fetchCmd = &cobra.Command{
	Use:   "fetch",
	Short: "Fetch stock data by code",
	Long: `Fetch stock data by code
	
	`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("Start to fetch stock data ...")
		fmt.Printf("args %q \n", args)
		fmt.Println("Version ", Verbose)
		fmt.Println("Source", Source)
	},
}

Flag

Flag是另一种类型的参数,可以方便的控制command的具体行为。

全局范围的flag

如果是全局范围内的flag,如上例-v -verbose, 这个是全局性的,我们就加入到root cmd中。

RootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")

子命令的flag

如果是某个子命令特有的flag, 如上例中-s -source, 这个只在fetch命令中有效。

fetchCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")

其他

集成cobra之后,cobra自动支持 –help等参数。help的输出默认就提供了,你也可以在自己重新定义。

防刷机制的一些思考

现在很多产品,为吸引新用户,往往会有一些激励措施,比如赠送一些免费的资源等。像理财类的app赠送的就更多,都是真金白银。因此也吸引了网上许多的人,反复注册,使劲薅羊毛,俗称羊毛党。如何才能防止被刷,又不降低真实用户的用户体验呢?一般的做法,是增加薅羊毛的成本,提升验证的难度。在我们的产品中,可以区分用户的有手机号、身份证、银行卡、设备唯一号(必须是App才有),Ip地址等。

  1. 设备唯一号
    现在有很多的模拟器,可以修改设备参数,所以设备唯一号,只对那些使用真实手机的羊毛党有效。如果是使用模拟器的技术流羊毛党,也比较难。不过这里还有改进的空间,可以在app中增加识别模拟器的功能,可以防止一部分比较弱的模拟器,高端的还是防止不了。
  2. IP地址
    这个可以换。而且真实的app用户,也是多个用户对应同一个IP地址,容易引起错误封杀。
  3. 手机号
    现在手机号的成本已经很低了,那些羊毛党基本上手里都有一大批的虚拟号或者真实号。自从有了阿里小号之后,虚拟号就更加容易获取。现在手机号基本上防止不了。目前也没有好的办法可以区分真实号码和虚拟号码。
  4. 身份证和银行卡
    办身份证和办银行卡的成本应该最高的。无奈羊毛党还是可以收集到很多的身份证和银行卡,他们的资料都是实打实的。所以即使我们限制一个身份证和一个银行卡只能领取一次奖励,那也只能增加我们的黑名单长度,第一次还是被薅走了。

从上面可以看出单独利用某一个特征来做防刷都比较难处理。在用户提取奖励的时候,利用上面的信息做一些限制,设置一段时间内同一个设备号的提现次数,设置相同IP的注册次数,设置手机号的接收验证码的次数。还有就是利用收集到的信息,事后分析。将定位到的羊毛党的手机号、身份证、银行卡等信息列入黑名单。

关于livermore的一些说明

关于livermore

准备写一个命令行工具:livermore。看名字应该知道是要做什么了吧,如果你也听说过Jesse Lauriston Livermore。非常有名的一本书《股票作手回忆录》, 这个版本是我看过翻译最喜欢的。好吧,我想写一个命令livermore,能抓取公开的股票数据,然后做一些简单的数据统计和根据指标筛选股票。

初步计划

  1. 创建数据库模型
  2. 抓取网页内容-爬虫
  3. 命令行参数支持和终端输出展示
  4. 数据统计和指标筛选

暂时先想到这些,接下来慢慢实现吧。

Go Map源码阅读和分析

golang中的map是如何实现的,为什么map支持并发读,而不支持并发写?map中key和value是如何组织的,扩容机制是什么?带着这些问题,来阅读以下golang的map源代码。

开篇说明

2015年的最后几天,借github宝地,搭建个blog。这里会记录一些工作上的内容和技术上的思考。

然后就是晒娃了,恩,是的,我也要变成晒娃狂魔了。