golang中json反序列化可能遇到的问题,rsync只同步指定目录的方法

前言

前言

rsync同步指定目录 rsync使用–include参数与–exclude参数来实现同步指定目录,并且–exclude目录放在后面。
例如:
linux上文件目录

这是一个轮子。

在golang中,当浮点数超过一定数值的时候,golang会把它弄成科学计数法的形式进行显示(好像只要大于七位数就变成科学计数法了)

复制代码 代码如下:

大家都知道
Ansible是功能超级强大的自动化运维工具,十分的高大上。太高大上了以至于在低端运维有点水土不服,在于三点:

var val float64
val = 1000000
fmt.Println(val) // ==> 1e+06

ls /usr/local/apache/htdocs/site
aa
bb
cc
dd
ee
..
zz

  1. Ansible 是基于 Python 的,而 Python
    下的安装是有一堆依赖的。。。不要笑!对于很多使用 Win
    的用户而言,光是装 Python, 装 pip 就够喝一壶的了。
  2. Ansible 的 paybook 所使用的 yaml
    语法当然非常强大了。然而对于新人而言,刚入手是玩不转的,需要学习。虽然
    Ansible
    相比其他的自动化运维工具,它的学习曲线已经非常平易近人了,但毕竟还是要学一下的不是么
  3. Ansible 自动化运维 Linux 服务器得益于 Linux 上 python
    的默认支持,功能非常强大。然而如果拿来跑交换机的话,因为交换机上通常没有
    python
    环境,功能就要打很多折扣了。基本上也就是执行一系列的命令组合。而我们这种有大片园区网的传统单位,运维的大头正式是交换机~

而在日常开发中,我们经常遇到这样一个问题,就是要反序列化前端传递来的json,因为数据结构未知,所以我们便会使用map[string]interface{}来接收反序列化的结果。由于golang将json解析到interface{}类型的时候,遵循如下规则

只想同步aa,bb,ee这三个目录,其他的不同步。

所以造这个轮子的出发点是基于以下考虑的:

  • bool 代表 JSON booleans,
  • float64 代表 JSON numbers,
  • string 代表 JSON strings,
  • nil 代表 JSON null.

实现代码:

  1. 要跨平台,木有依赖,开箱即用。用 Go
    来撸一个就能很好的满足这个需求。你看 Open-Falcon 的 agent,ELK 的
    beats ,都选择用 Go 来实现,就是这个原因。
  2. 简单无脑,无需学习。直接堆砌命令行就行,就像我们初始化交换机的那种命令行组合模板。只要
    cli 会玩,直接照搬过来就行。
  3. 要支持并发。这个是 Go 的强项了,无需多言。
  4. 最后当然是学习 Go 啦。

所以如果我们接收到的json中存在一个比较大的数字,那么它就会被解析成float64类型的,并且有可能显示为科学计数法的形式,比如下面这个例子

复制代码 代码如下:

一点都没有黑 Ansible 的意思。我们也有在用 Ansible
来做自动化运维的工作,我觉得所有运维最好都学习下
Ansible,将来总是要往自动化的方向走的。这个轮子的目的在于学习 Ansible
之前,先有个够简单无脑的工具解决下眼前的需求~

package main
import (
 "encoding/json"
 "fmt"
)

func main() {
 //Create the Json string
 var data = `
 {
 "id": 12423434, 
 "Name": "Fernando"
 }
 `

 //Marshal the json to a map
 var result map[string]interface{}
 err := json.Unmarshal([]byte(data), &result)
 if err != nil {
 fmt.Println(err.Error())
 return
 }
 fmt.Println(result)
}

/usr/local/rsync/bin/rsync -azv  –include “aa/” –include “bb/”
–include “ee”  –exclude “/*”  –progress rsync://127.0.0.1/site
/work/backup/htdocs_bak/site

建立 ssh 会话

输出如下结果

说明:

Go 自身不带 ssh 包。他的 ssh 包放在了
这里。import 他就好

map[id:1.2423434e+07 Name:Fernando]

–include “aa/” –include “bb/” –include “ee” 包含了你要同步的目录
–exclude包含了不同步的目录所有用“/*”指定。
–exclude “/*” 除了–include同步的目录外,排除其他目录
rsync://127.0.0.1/site 为你rsync服务端要同步的目录
/work/backup/htdocs_bak/site 为你客户端的目录

import "golang.org/x/crypto/ssh"

这个时候如果把它传递给前端使用的话,那么前端是有可能会报错的。所以我们最好还是保持这个数字的原始字符串表现形式。即使用json.Number类型来表示

rsync同步指定正则文件(含子目录)

首先我们需要建立一个 ssh 会话,比如这样。

所以换成如下代码就可以了

前阵子做日志备份,把各个服务器上的日志同步到日志服务器上做备份,刚开始比较暴力,把所有压缩好的*gz文件全部传送到日志备份服务器上。后来,发现这种方式非常不好,一旦备份的目录发生改变,服务器上又会把它所有的*gz文件全部再传一遍,即浪费时间又给服务i/o产生不必要的压力,后来想只同步某天的*gz文件。

func connect(user, password, host, key string, port int, cipherList []string) (*ssh.Session, error) {
  var (
    auth     []ssh.AuthMethod
    addr     string
    clientConfig *ssh.ClientConfig
    client    *ssh.Client
    config    ssh.Config
    session   *ssh.Session
    err     error
  )
  // get auth method
  auth = make([]ssh.AuthMethod, 0)
  if key == "" {
    auth = append(auth, ssh.Password(password))
  } else {
    pemBytes, err := ioutil.ReadFile(key)
    if err != nil {
      return nil, err
    }

    var signer ssh.Signer
    if password == "" {
      signer, err = ssh.ParsePrivateKey(pemBytes)
    } else {
      signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(password))
    }
    if err != nil {
      return nil, err
    }
    auth = append(auth, ssh.PublicKeys(signer))
  }

  if len(cipherList) == 0 {
    config = ssh.Config{
      Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},
    }
  } else {
    config = ssh.Config{
      Ciphers: cipherList,
    }
  }

  clientConfig = &ssh.ClientConfig{
    User:  user,
    Auth:  auth,
    Timeout: 30 * time.Second,
    Config: config,
    HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
      return nil
    },
  }

  // connet to ssh
  addr = fmt.Sprintf("%s:%d", host, port)

  if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
    return nil, err
  }

  // create session
  if session, err = client.NewSession(); err != nil {
    return nil, err
  }

  modes := ssh.TerminalModes{
    ssh.ECHO:     0,   // disable echoing
    ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
    ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
  }

  if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
    return nil, err
  }

  return session, nil
}
package main
import (
 "encoding/json"
 "fmt"
 "strings"
)

func main() {
 //Create the Json string
 var data = `
 {
 "id": 12423434, 
 "Name": "Fernando"
 }
 `
 //Marshal the json to a map
 var result map[string]interface{}
 d := json.NewDecoder(strings.NewReader(data))
 d.UseNumber()
 err := d.Decode(&result)
 if err != nil {
 fmt.Println(err.Error())
 return
 }
 // 此刻result["id"]的类型就是json.Number了 它的底层类型其实就是string
 fmt.Println(result)
}

 需求描述:
有一个类似于这样的目录结构,子目录的个数是不确定的

ssh.AuthMethod 里存放了 ssh 的认证方式。使用密码认证的话,就用
ssh.Password()来加载密码。使用密钥认证的话,就用 ssh.ParsePrivateKey()
或 ssh.ParsePrivateKeyWithPassphrase() 读取密钥,然后通过
ssh.PublicKeys() 加载进去。

输出如下结果

复制代码 代码如下:

ssh.config 这个 struct 存了 ssh
的配置参数,他有以下几个配置选项,以下引用自GoDoc。

map[id:12423434 Name:Fernando]

[root@IND_ tlbb]# tree
.
|– ReadMe.txt
| |– Audit
| `– Money_2013-09-10.tgz
|– Audit_2013-09-10.tgz
`– Install

type Config struct {
  // Rand provides the source of entropy for cryptographic
  // primitives. If Rand is nil, the cryptographic random reader
  // in package crypto/rand will be used.
  // 加密时用的种子。默认就好
  Rand io.Reader

  // The maximum number of bytes sent or received after which a
  // new key is negotiated. It must be at least 256. If
  // unspecified, a size suitable for the chosen cipher is used.
  // 密钥协商后的最大传输字节,默认就好
  RekeyThreshold uint64

  // The allowed key exchanges algorithms. If unspecified then a
  // default set of algorithms is used.
  // 
  KeyExchanges []string

  // The allowed cipher algorithms. If unspecified then a sensible
  // default is used.
  // 连接所允许的加密算法
  Ciphers []string

  // The allowed MAC algorithms. If unspecified then a sensible default
  // is used.
  // 连接允许的 MAC (Message Authentication Code 消息摘要)算法,默认就好
  MACs []string
}

总结

我想同步*.py结尾的东东

基本上默认的就好啦。但是 Ciphers 需要修改下,默认配置下 Go 的 SSH
包提供的 Ciphers 包含以下加密方式

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

同步完成后

复制代码 代码如下:

您可能感兴趣的文章:

  • 利用Golang解析json数据的方法示例
  • Golang中使用JSON的一些小技巧分享
  • Golang
    map如何生成有序的json数据详解
  • golang实现sql结果集以json格式输出的方法
  • golang json性能分析详解
  • golang如何自定义json序列化应用详解

复制代码 代码如下:

aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com arcfour256
arcfour128

[root@IND_ tlbb]# tree
.
|– Audit
| `– Money_2013-09-10.tgz
`– Audit_2013-09-10.tgz

连 linux 通常没有问题,但是很多交换机其实默认只提供 aes128-cbc 3des-cbc
aes192-cbc aes256-cbc 这些。因此我们还是加全一点比较好。

方式1:(失败)
rsync -auvz –include=”*${YDate}*gz ” ./ 192.168.137.22::tlbb

这里有两个地方要提一下

结果:会把所有的文件都同步过去了,连Install都同步过去了

1、在 clientConfig 里有这么一段

方式2:
rsync -aruvz –include=”*${YDate}*gz” –include=”*/” –exclude=”*” ./*
192.168.137.22::tlbb

HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
  return nil
},

结果:能把今天所有的gz文件都正常传过去喽,先允许所有的匹配的gz文件,再允许所有子目录,最后拒绝所有其他的。

这是因为默认密钥不受信任时,Go 的 ssh 包会在 HostKeyCallback
里把连接干掉(1.8
之后加的应该)。但是我们使用用户名密码连接的时候,这个太正常了不是么,所以让他
return nil 就好了。

看到这里的朋友大家肯定发现了如果文件夹过多,肯定比较累,直接弄个txt文件将需要同步的与不需要同步都分别弄下就更好了,下面的方法在4.10版本中测试正常

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签:
网站地图xml地图