数科院博客园 - 摇光
type: channel
link: https://blog.icsc.vip/index.php/author/16/
description: 摇光
items: raft算法理论
items: golang实现发送邮箱随机六位验证码
items: golang中Redis的基本使用
items: go语言下的nats streaming
items: go语言下的深广度遍历
items: Golang实现MySQL数据库备份
items: 后端数据交互二：消息队列
items: Python心得和数据结构实现
items: 后端数据交互一：Golang模拟前端请求
items: Xshell和Xftp的初步使用

raft算法理论
type: item
date: 2020-05-20T16:04:00+00:00
description: raft算法理论前言这几天看了一下raft算法，在这里着重讲解一下raft算法的理论介绍raft 是一种分布式的共识算法，其目的是要实现多个节点集群的容错性,一致性从而能够构建大规模的软件系统。在raft之前，比较有名的是Paxos。但是paxos难于理解。raft的诞生是为了让共识算法更容易理解,在工程上更容易实现。特点leader：raft中会有一个领导者具有超级权限，可以把自己的log 复制到其他节点中。leader election： raft每隔一段随机的时间就会进行leader的选举raft允许集群配置变化时正常运行。状态机状态机是分布式系统中的一个重要概念，任何一个系统的最终状态都可以看成是每一个操作的集合。因此，算法会维护一份replicated log，将每一份操作都存储起来。每一个节点只要按顺序执行log中的命令，就会到达相同的最终状态。这样，即便是系统奔溃也可以快速的恢复。共识算法需要保证relicated log的一致性,服务器收到客户端发出来的执行命令Command后，会将其加入到log中。服务器之间会相互的交流，保证最后的log的一致性（即便服务器奔溃），即Command 会复制到其他服务器的log中，所有服务器的log是相同的，有序的。其他服务器会执行此log，即会执行此命令。最后，所有的服务器都会到达同一个状态。基础raft集群一般保持奇数个数量（5个节点比较普遍). 从而只要大部分节点存活，即可用。raft中的节点有3种状态。 leader, Candidates, follower。一般的状态只会存在一个leader，其余的节点都是follower。leader会处理所有的客户端请求， 如果是客户端请求follower，也会被转发到leader处理。Candidates 是一种选举时候的过渡状态，用于自身拉票选举leader。在raft中会有一个叫做term的时间周期。term是以选举leader开始的，如果Candidates选举成为了leader，那么其会成为这个term剩下时间的leader。有时候，在整个term周期都没有选举出leader。这时一个新的选举会在不久后开始。Terms 在raft中类似于一种时间戳，后一个一定比前一个后发生，这一点和比特币中的区块链很类似。每一个服务器会存储一个当前的term，其会随着时间的增加而增长。如果某一个节点的当前term小于其他节点，那么节点会更新自己的term为最大的term。如果一个candidate 发现自己当前的term 过时了，那么其会立即变为follower。raft节点之间通过RPC（远程过程调用）来进行通信。 RequestVote 方法用于candidate在选举时候使用，AppendEntries用于leader在通知其他节点复制log时使用。同时也用于心跳检测。RPC 是并发的，并支持失败重试。选举在raft中会有一套心跳检测，只要follower收到来自leader或者Candidates的数据，那么其会保持follower的状态。如果follower一段时间内没有收到RPC请求，这意味着选举超时（ election timeout ）。这时follower会将current term 加1，过渡到Candidates状态。其会给自己投票，并发送RequestVote RPC请求给其他的节点，拉票！Candidates状态会持续，直到下面的3种情况发生:当其获得了大部分节点的支持后，其赢得了选举，变为了leader。一旦其变为了leader，其会向其他节点发送 AppendEntries RPC， 确认其leader的地位，阻止选举。其他节点成为了leader。如果其收到了其他节点的AppendEntries RPC. 并发现其他节点的current term比自己的大,则其变为follower状态。一段时间过去任然没有参与者。如果有许多的节点同时变为了candidate,则可能会出现一段时间内都没有节点能够选举成功的情况。在raft中，为了快速解决并修复这个问题，规定了每一个candidate在选举前会重置一个随机的选举超时（ election timeout ）时间，此随机时间会在一个区间内（eg.150-300ms）随机时间保证了在大部分的情况下，有一个唯一的节点首先选举超时，其在大部分节点选举超时前发送心跳检测，赢得了选举。当一个leader在心跳检测中发现另一个节点有更高的term时，会转变为follower。否则其将一直保持leader状态。

golang实现发送邮箱随机六位验证码
type: item
date: 2020-04-27T03:17:00+00:00
description: golang实现发送邮箱随机六位验证码有时我们需要给用户发送验证码，但是手机短信验证会有成本，所以今天我在这里分享一下邮箱发送验证码的方法依赖包go get gopkg.in/gomail.v2详细代码下面的代码附有详细注释package main import ( "fmt" "log" "math/rand" "time" "gopkg.in/gomail.v2" ) // MailboxConf 邮箱配置 type MailboxConf struct { // 邮件标题 Title string // 邮件内容 Body string // 收件人列表 RecipientList []string // 发件人账号 Sender string // 发件人密码，QQ邮箱这里配置授权码 SPassword string // SMTP 服务器地址， QQ邮箱是smtp.qq.com SMTPAddr string // SMTP端口 QQ邮箱是25 SMTPPort int } func main() { var mailConf MailboxConf mailConf.Title = "验证" //这里就是我们发送的邮箱内容，但是也可以通过下面的html代码作为邮件内容 // mailConf.Body = "坚持才是胜利，奥里给" //这里支持群发，只需填写多个人的邮箱即可，我这里发送人使用的是QQ邮箱，所以接收人也必须都要是 //QQ邮箱 mailConf.RecipientList = []string{"邮箱账号1","邮箱账号2"} mailConf.Sender = `邮箱账号` //这里QQ邮箱要填写授权码，网易邮箱则直接填写自己的邮箱密码，授权码获得方法在下面 mailConf.SPassword = "这里填写自己QQ邮箱授权码" //下面是官方邮箱提供的SMTP服务地址和端口 // QQ邮箱：SMTP服务器地址：smtp.qq.com（端口：587） // 雅虎邮箱: SMTP服务器地址：smtp.yahoo.com（端口：587） // 163邮箱：SMTP服务器地址：smtp.163.com（端口：25） // 126邮箱: SMTP服务器地址：smtp.126.com（端口：25） // 新浪邮箱: SMTP服务器地址：smtp.sina.com（端口：25） mailConf.SMTPAddr = `smtp.qq.com` mailConf.SMTPPort = 25 //产生六位数验证码 rnd := rand.New(rand.NewSource(time.Now().UnixNano())) vcode := fmt.Sprintf("%06v", rnd.Int31n(1000000)) //发送的内容 html := fmt.Sprintf(` 尊敬的用户，您好！ 你本次的验证码为%s,为了保证账号安全，验证码有效期为5分钟。请确认为本人操作，切勿向他人泄露，感谢您的理解与使用。 此邮箱为系统邮箱，请勿回复。 `, vcode) m := gomail.NewMessage() // 第三个参数是我们发送者的名称，但是如果对方有发送者的好友，优先显示对方好友备注名 m.SetHeader(`From`, mailConf.Sender, "小铃铛官方") m.SetHeader(`To`, mailConf.RecipientList...) m.SetHeader(`Subject`, mailConf.Title) m.SetBody(`text/html`, html) // m.Attach("./Dockerfile") //添加附件 err := gomail.NewDialer(mailConf.SMTPAddr, mailConf.SMTPPort, mailConf.Sender, mailConf.SPassword).DialAndSend(m) if err != nil { log.Fatalf("Send Email Fail, %s", err.Error()) return } log.Printf("Send Email Success") } QQ邮箱获取授权码的方法打开我们的邮箱，左上角点击设置然后点击账户在下面找到SMTP并开启，在这里我已经开启过了，开启后他会给你一个授权码，如果授权码忘了，可以关闭再打开他会给你一个 新的授权码

golang中Redis的基本使用
type: item
date: 2020-04-12T04:10:00+00:00
description: 今天在这里就简单的说一下golang中操作redis数据库在这里需要用到golang中优秀的第三方包开源库redigo的使用github地址：https://github.com/garyburd/redigo获取的redigo包的命令（一般通过git来操作）go get github.com/garyburd/redigo链接redispackage main import( "github.com/garyburd/redigo/redis" ) func main(){ //链接redis redisConn,err :=redis.Dial("tcp","127.0.0.1:6379") if err != nil{ fmt.Println("连接失败:",err) return } defer redisConn.Close() }在这里tcp可以是"tcp4"、"tcp6"、"tcp"中的任意一个，分别表示TCP(IPv4-only),TCP(IPv6-only)或者TCP(IPv4,IPv6的任意一个)。127.0.0.1:6379是端口号，redis默认为6379.redis.Dial()返回的是一个redis.conn的指针.读写package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { redisConn, err := redis.Dial("tcp", "127.0.0.1:6379") if err != nil { fmt.Println("连接失败:", err) return } defer redisConn.Close() _, err = redisConn.Do("SET", "mykey", "hello,world") if err != nil { fmt.Println("redis set failed:", err) } data, err := redis.String(c.Do("GET", "mykey")) if err != nil { fmt.Println("redis get failed:", err) } else { fmt.Println("Get mykey:", data) } }输出:hello,world在c.Do()函数里写入对应的redis操作指令就可以执行该指令。第21行加了一个redis.String()函数是因为执行get命令时返回的是一个[]uint8类型的数据，可以把他转化为string类型.如果想设置过期时间，可以使用SET的附加参数：_, err = redisConn.Do("SET", "mykey", "hello,world", "EX", "5") if err != nil { fmt.Println("redis set failed:", err) }第一行是让他存活5秒，然后会delete掉检测值是否存在EXISTS keyjudge, err := redis.Bool(c.Do("EXISTS", "mykey1")) if err != nil { fmt.Println("error:", err) } else { fmt.Println("exists or not:", judge) }输出:exists or not: false删除 _, err = redisConn.Do("DEL", "mykey") if err != nil { fmt.Println("redis delelte failed:", err) }会找到键名为key的删除掉，如果没有，返回一个错误读写json到redispackage main import ( "encoding/json" "fmt" "github.com/garyburd/redigo/redis" ) func main() { redisConn, err := redis.Dial("tcp", "127.0.0.1:6379") if err != nil { fmt.Println("Connect to redis error", err) return } defer redisConn.Close() key := "user" //创建集合 mymap := map[string]string{"userName": "hello", "passWord": "world"} //通过json包把json数据封装 value, _ := json.Marshal(mymap) n, err := redisConn.Do("S…

go语言下的nats streaming
type: item
date: 2020-04-08T04:25:00+00:00
description: 列表项目简介基本的NATS服务器专为高性能和简洁而设计，它不会为通过NATS发布的消息提供持久性存储。缺乏对消息的持久存储对于许多分布式系统来说都是一个问题。例如，假设你的一个用户系统在你发布消息时发生故障，则该用户系统不会收到该消息，因此你必须提供处理此类情况的架构方法。NATS Streaming附带一个持久性存储，用于为通过 NATS 服务器发布的消息提供日志。如果你需要持久性消息传输和交付保证，则可以使用 NATS Streaming 而不是核心 NATS 平台。NATS Streaming是一个非常高性能，轻量级和可靠的流式平台，它构建在核心NATS平台的顶层，提供持久性日志。NATS Streaming 是用 Go 语言编写的。它可以用来添加事件流，交付保证和将历史数据重放到NATS。然而NATS Streaming 不是一个独立的服务器，但它使用 NATS 服务器（gnatsd）。简而言之，NATS Streaming 内嵌 NATS 服务器作为消息传输服务器，并提供了一个额外的功能，可以为事件流系统提供持久性日志使用。安装首先我们先使用go get获得NATS流go get github.com/nats-io/nats-streaming-server当我们下载好之后，打开我们的github.com/nats-io/nats-streaming-server包，在里面有个nats-streaming-server.go文件，把这个文件复制一份到我们go工作目录下的bin目录里为了用 Go 语言创建 NATS 客户端应用程序，请使用_go get_命令下载并安装 Go 语言包：go get github.com/nats-io/go-nats-streaming要运行NATS流，请运行我们放在bin目录下的 _nats-streaming-server_二进制文件：nats-streaming-server默认情况下，NATS Streaming使用内存存储来存储消息，因此如果NATS服务器关闭，你将丢失消息。下面是我运行的结果：发布事件首先我们先在这里创建一个事件的发布者，nats streaming一般都是用在集群的发布订阅。package main import ( "encoding/json" "log" //stan "github.com/nats-io/go-nats-streaming" // 如果这个包导入不成功就,导入下面的包 "github.com/nats-io/stan.go" ) const ( clusterID = "test-cluster" //集群ID：自定义 clientID = "event-store1" //客户端ID：自己这个发布者的ID，自定义 channel = "order-notification" //发布的频道：把自己 产生的事件要发布的频道ID ) //创建一个学生结构体 type student struct { Name string `json:"name,omitempty"` Age int `json:"age,omitempty"` Gender string `json:"gender,omitempty"` } func main() { //建立nats streaing连接 sc, err := stan.Connect( clusterID, clientID, stan.NatsURL(stan.DefaultNatsURL), //这里为默认， ) if err != nil { log.Print(err) } defer sc.Close() //创建实例 var stu student stu.Name = "小明" stu.Age = 19 stu.Gender = "男" //把结构体实例转化为json数据 data, err := json.Marshal(stu) if err != nil { log.Println(err, " nats-streaming 解析json数据失败") } //通过推送函数，把[]byte类型的json数据推送到该集群下的"order-notification"频道 sc.Publish(channel, data) } 订阅事件基本的NATS服务器没有提供持久性日志，订阅消息的能力非常有限。当你发布消息时，如果订阅者客户端关闭，它将无法接收来自服务器的消息。由于NATS Streaming 服务器有持久化日志功能，它提供了很多从 NATS 服务器订阅消息的功能。在这里我们讲两种订阅方式：队列组订阅订阅者客户端可以通过指定一个_队列组_来创建。具有相同队列名称的同一频道的…

go语言下的深广度遍历
type: item
date: 2020-03-24T07:01:00+00:00
description: 深广度优先遍历一般情况下我们都是用在复杂的图结构，以方便我们进行必要的检索操作。之前我用C++，Python写过图的深广度优先遍历，今天在这里想用go语言来实现这一操作，并希望对各位有帮助邻接表图的存贮结构很多，在这里我们选择中等难度的邻接表作为我们图的储存结构，下面的图是我模拟的一个图结构，是一个有向非连通图（F点未相连）来作为这次深广度遍历的实验图。下面附上邻接表的代码（含注释）package main import "fmt" //邻接表我们需要把顶点和边都看成是一个结构节点 //所以我们在下面创建这两种结构 //VertexNode 顶点节点结构体 type VertexNode struct { data string //存放顶点数据 firstarc *ArcNode //顶点的第一条边 } //ArcNode 边节点结构体 type ArcNode struct { adjvex int //存放顶点的下标 next *ArcNode //存放下一条边节点 } //GraphAdjacencyList 图的邻接表结构体 type GraphAdjacencyList struct { vertexNode []VertexNode //存放顶点的数组 visted []bool //是否遍历过的bool类型数组 } // 添加顶点 func (g *GraphAdjacencyList) addVertexNode(data string) bool { //定义一个顶点节点实例，并储存顶点的数据 temp := VertexNode{} temp.data = data //判断这个顶点是否已经存在 for _, v := range g.vertexNode { if v.data == data { return false } } //把顶点节点存入切片中，方便我们后面遍历使用 g.vertexNode = append(g.vertexNode, temp) //初始化访问切片为false，代表这个顶点没有被访问过 g.visted = append(g.visted, false) return true } //添加边节点 func (g *GraphAdjacencyList) addArcNode(verNode1, verNode2 string) bool { //获得该边节点两头顶点在切片中的下标 index1 := g.verNodeIndex(verNode1) index2 := g.verNodeIndex(verNode2) //判断顶点是否存在或重复 if index1 == -1 || index2 == -1 || index1 == index2 { return false } //进行顶点和边节点的逻辑连接 temp := new(ArcNode) //创建边节点实例 temp.adjvex = index2 //对边节点和顶点进行绑定 temp.next = g.vertexNode[index1].firstarc //设置该边指向的下一条边 g.vertexNode[index1].firstarc = temp //同理设置另一个顶点 temp = new(ArcNode) temp.adjvex = index1 temp.next = g.vertexNode[index2].firstarc g.vertexNode[index2].firstarc = temp return true } // 获得顶点的下标 func (g *GraphAdjacencyList) verNodeIndex(data string) int { for i, v := range g.vertexNode { if v.data == data { return i } } return -1 } //初始化访问切片visted func (g *GraphAdjacencyList) initVisted() { for i, _ := range g.visted { g.visted[i] = false } } func main() { g := GraphAdjacencyList{} //定义一个邻接表实例 //添加四个顶点 topData := [5]string{"A", "B", "C", "D", "E"} for _, v := range topData { if g.addVertexNode(v) == false { fmt.Println("顶点数据重复") } } //添加五条边 g.addArcNode("A", "B") g…

Golang实现MySQL数据库备份
type: item
date: 2019-12-16T08:39:00+00:00
description: 目的在我们后端写项目时，数据一般都是保存在MySQL数据库里。但是有时我们不能只是简单的把数据保存在MySQL数据库里（删库跑路）。在这里我们就要经常备份一下我们的数据库或者是数据库里面的某一个表。Navicat在这里我们使用的是MySQL可视化工具里最棒的navicat，所以先安装：Navicat下载连接先点击左上角的连接，然后连接名是自己随便取的，主机名填想要连接数据库的IP，自己用一般默认为localhost，用户名和密码填自己MySQL的用户名和密码。Go代码实现导入包import ( "io/ioutil" "log" "os/exec" "time" )在这里我们要使用一个平常很少使用的包os/exex, exec包执行外部命令，详情可以去参考Go语言标准文档。全代码package main import ( "io/ioutil" "log" "os/exec" "time" ) func main() { BackupMySqlDb("localhost", "3306", "root", "123456", "school", "", "E:/") } /** * * 备份MySql数据库 * @param host: 数据库地址: localhost * @param port: 端口: 3306 * @param user: 用户名: root * @param password: 密码: root * @param databaseName: 需要被分的数据库名: test * @param tableName: 需要备份的表名: user * @param sqlPath: 备份SQL存储路径: D:/backup/test/ * @return backupPath * */ func BackupMySqlDb(host, port, user, password, databaseName, tableName, sqlPath string) (error,string) { //定义Cmd结构体对象指针 var cmd *exec.Cmd //在这里如果没有传输表名，那么将会备份整个数据库，否则将只备份自己传入的表 if tableName == "" { cmd = exec.Command("mysqldump", "--opt", "-h"+host, "-P"+port, "-u"+user, "-p"+password, databaseName) } else { cmd = exec.Command("mysqldump", "--opt", "-h"+host, "-P"+port, "-u"+user, "-p"+password, databaseName, tableName) } //StdinPipe方法返回一个在命令Start后与命令标准输入关联的管道。 stdout, err := cmd.StdoutPipe() if err != nil { log.Println(err) return err,"" } if err := cmd.Start(); err != nil { log.Println(err) return err,"" } bytes, err := ioutil.ReadAll(stdout) if err != nil { log.Println(err) return err,"" } //获得一个当前的时间戳 now := time.Now().Format("20060102150405") var backupPath string //设置我们备份文件的名字 if tableName == "" { backupPath = sqlPath+databaseName+"_"+now+".sql" } else { backupPath = sqlPath+databaseName+"_"+tableName+"_"+now+".sql" } //写入文件并设置文件权限 err = ioutil.WriteFile(backupPath, bytes, 0644) if err != nil { log.Println(err) return err,"" } return nil,backupPath } 示例上述代码以school数据库为例，由于表名为空所以在这里是备份一整个数据库，运行程序即可然后会在我们设置的路径下找到我们代码生成的sql文件运行sql文件右键点击我们的数据库或者表里面有个运行SQL文件，然后选中自己生成的SQL文件即可，但是导入SQL文件会把相同表名的表数据替换，所以导入时一般选择一个额外的数据库显示…

后端数据交互二：消息队列
type: item
date: 2019-12-13T13:41:00+00:00
description: 简介RabbitMQ是一个开源的，使用最广的消息转发器。提供匿名消息类型，可以使用多种消息协议，消息队列等，支持集群部署。它的轻量级能够让你很轻易地在私有云或公有云环境中部署，同时提供丰富的插件工具，能够让你轻易的进行扩展，且可使用HTTP-API、命令行工具和UI界面以便于管理和监控。专业术语生产者：产生并发送消息的程序队列：类似于一个存在RabbitMQ中的邮筒，虽然消息是在应用程序和RabbitMQ中进行传递，但队列才是唯一能够存储消息的的地方。队列的大小取决于寄主机器的内存和磁盘容量，他本质上是一个巨大的消息缓存池。多个生产者可以发送消息给同一个队列，多个消费者也可以从同一个队列中读取消息消费者：等待接收消息的程序Hello World最开始接触消息队列，我们先来一个简单传输 Hello World的消息队列。其中包含两个小程序，一个是用于发送消息send.go, 一个用来接收消息的reveive.go在使用Go语言操作RabbitMQ之前我们需要先从github上拉取amqp包：go get github.com/streadway/amqpSending首先我们要把需要用到的包导入我们的程序package main import ( "fmt" "log" "github.com/streadway/amqp" )创建请求连接在这里我们使用的消息队列将会基于TCP协议进行数据传输//guest为消息队列网站注册的（可以修改），端口号默认为5672 conn, err := amqp.Dial("amqp://guest:guest@127.0.0.1:5672") if err!=nil{ log.Println("conn err:",err) } defer conn.Close()上面代码会建立一个socket连接，处理一些协议转换及版本对接和登录授权的问题。建立连接之后，我们需要创建一个通道channel，之后我们的大多数API操作都是围绕通道来实现的创建通道ch, err := conn.Channel() if err!=nil{ log.Println("create Channel err:",err) } defer ch.Close()创建队列通道创建完毕后，我们要在通道的基础上创建一个队列q, err := ch.QueueDeclare( "hello", //通道名称 false, //持久的 false, //使用后是否删除 false, // 唯一性 false, //不允许等待 nil, //命令参数 )在这里q就为我们创建的队列，这个队列的名字叫做hello生产当我们创建好一个消息队列后就可以向消息队列中推送消息了body:="hello worldd" err = ch.Publish( //发布 "", //exchange 交换器名，这里为空 q.Name, //routing key 路由 key，这里写消息队列的名字 false, //mandatory 托管的 false, //immediate 直接的，立即的 amqp.Publishing{ //发布中 ContentType: "text/plain", //内用类型 Body: []byte(body), //消息队列传输时传的是byte字节型的切片 }, )Receiving首先，我们还是要导入同样的包package main import ( "fmt" "log" "github.com/streadway/amqp" )创建请求连接接着与生产者一样创建连接，注意这里的参数必须与send中的queue name相一致，这样才能实现发送/接受的配对。//guest为消息队列网站注册的（可以修改），端口号默认为5672 conn, err := amqp.Dial("amqp://guest:guest@127.0.0.1:5672") if err!=nil{ log.Println("conn err:",err) } defer conn.Close()创建通道ch, err := conn.Channel() if err!=nil{ log.Println("create Channel err:",err) } defer ch.Close()创建队列通道创建完毕后，创建的队列里的属性必须要与生产者队列属性一致，不然会不匹配q, err := ch.QueueDeclare( "hello", //通道名称 false, //持久的 false, //使用后是否删除 false, // 唯一性 false, //不允许等待 nil, //命令参数 )消费紧接着我们就可以从队列里消费信息了msgs,…

Python心得和数据结构实现
type: item
date: 2019-12-11T09:36:00+00:00
description: 目的之前学过一段时间的python，感觉受益匪浅，个人觉得python语言能火起来也有它一定的道理，所以在这里想总结一下之前所学习的知识，做一篇博客，希望其他人能从中学到知识。简介Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。Python 的设计具有很强的可读性，相比其他语言经常使用英文关键字，其他语言的一些标点符号，它具有比其他语言更有特色语法结构，废话不多说，直接开始。基础知识序列是Python中最基本的数据结构。序列中的每个元素都分配一个数字 - 它的位置，或索引，第一个索引是0，第二个索引是1，依此类推。（类似数组）Python有6个序列的内置类型，但最常见的是列表和元组。序列都可以进行的操作包括索引，切片，加，乘，检查成员。此外，Python已经内置确定序列的长度以及确定最大和最小的元素的方法。列表是最常用的Python数据类型，它可以作为一个方括号内的逗号分隔值出现。列表的数据项不需要具有相同的类型。1.列表Name_list["王梅"，“大傻”，“二傻”]定义一个列表，名字是Name_list,初始化赋值三个属性Name_list.index("大傻")获取数据在列表中的索引值Name_list.append("三傻")向列表的末尾追加数据Name_list.insert(1,"四傻")向指定位置插入数据Name_list.extend(temp_list)把其他列表内容追加到末尾.remove("大傻")从列表中删除指定数据.pop().pop(3)默认条件下删除最后一个元素（第一个）,也可根据索引删除（第二个）.clear()清空列表del name_list[1]清空列表len(Name_list)获取列表的长度Name_list.count("二傻")统计数据出现的次数.sort()对列表进行升序排序.sort(reverse=True)对列表进行降序排序.reverse()把列表中的数据翻转（逆序）下面为定义三个列表L1，L2，L3，然后把L1和L2放到L3里，并且去重L1 = [] L2 = [] L3 = [] print("请输入两个数分别代表要录入列表L1和列表L2的元素个数") num1 = int(input()) num2 = int(input()) print("请输入列表L1的数据") while num1 != 0: L1.append(int(input())) num1 -= 1 print("请输入列表L2的数据") while num2 != 0: L2.append(int(input())) num2 -= 1 print("L1:",L1) print("L1:",L2) for i in L1: if not i in L3: L3.append(i) for i in L2: if not i in L3: L3.append(i) print("L3:",L3) 2.元组元组操作与列表相似，但元组定义好之后无法修改元组定义好之后无法修改空元组Info_tuple(5,)单个数据元组3.字典一个字典变量最好记录一个物品的信息zidian={"name":小明, "age":18, "gender":True} 4.字符与ASCII码之间的转化Python提供了ord和chr两个内置的函数，用于字符与ASCII码之间的转换。print ord('a') #97 print chr(97) #a面向对象Python是一门面向对象的语言，所以它有类这个结构，接下来将会介绍一下类的定义格式（对比其他面向对象语言）1.构造函数def __init__(self): print("这是构造函数")2.析构函数def __del__(self): print("这是个析构函数")3.自定义函数def __del__(self): return"这是个自定义函数"4.对象赋值为空new_data=None5.身份运算符用于判断两个对象的内存地址是否相同id(x) is id(y) 类似 id(x) == id(y) id(x) is not id(y) 类似 id(x) != id(y)is用来判断 两个变量 引用对象是 否为同一个==用来判断 引用变量的值 是否为同一个6.定义私有属性定义私有属性或私有函数时在变量或函数名前面加 两个下划线 __name（但是Python是没有绝对的私有的）class TreeNode:#树节点 def __init__(self,data): self.__data = data self.__lchild = None self.__rchild = None7.类的继承狗类继承动物类class Animal…

后端数据交互一：Golang模拟前端请求
type: item
date: 2019-11-17T05:01:00+00:00
description: 我们在后端服务中有时要进行数据之间的交流，所以在这里我说一下用后端模拟http请求去访问其他后端服务的方法。创建请求连接 //生成要访问的url url := "http://192.168.235.128:8080" //http请求 request, err := http.NewRequest("POST", url, "nil") // request, err := http.NewRequest("POST", url, body)第二行：我们定义一个url用来储存要请求服务的地址第四行：我们用http包的NewRequest()函数来创建连接，第一个参数是请求方式，第二个参数是访问地址，第三个放http请求的body（如第五行）,在这里我们用不到，所以我们设置为空访问连接//生成clint 参数为默认 client := &http.Client{} //处理返回结果 response, err := client.Do(request)第二行：我们模拟一个用户对象client。第四行：用client对象去访问要访问的服务，然后返回一个*http.Request对象解析数据// 将结果定位到标准输出 也可以直接打印出来 或者定位到其他地方进行相应的处理 jsonData, err = ioutil.ReadAll(response.Body) //获取post的数据 fmt.Println(string(jsonData)) if err != nil { log.Println("response.Body err:", err) } err = json.Unmarshal(jsonData, &redisStatus) if err != nil { log.Println("fixedRefresh Unmarshal json err 2:", err) pb.ResponseServe(w, &returnArg, 404, "error") }当我们接收到其他服务返回来的响应对象时，我们就可以把他body里面的json数据解析出来。完整代码这个例子是从前端接收到请求然后再拿前端请求里的数据去访问其他服务，最终获得想要的数据然后再返回给前端。package ssf import ( "bytes" "encoding/json" "fmt" "io/ioutil" "log" "net/http" "studentStatus/pb" ) func fixedRefresh(w http.ResponseWriter, r *http.Request) { returnArg := pb.ReturnArg{} //响应对象 jsonData, err := ioutil.ReadAll(r.Body) //获取post的数据 if err != nil { log.Println("fixedRefresh readAll json err:", err) pb.ResponseServe(w, &returnArg, 404, "error") return } defer r.Body.Close() var redisStatus pb.RedisStatus //定义初始化化redis数据的 err = json.Unmarshal(jsonData, &redisStatus) if err != nil { log.Println("fixedRefresh Unmarshal json er:", err) pb.ResponseServe(w, &returnArg, 404, "error") return } redisStatus.Fid = redisStatus.BuildingNo data, err := json.Marshal(redisStatus) if err != nil { log.Println("fixedRefresh Marshal json err:", err) } body := bytes.NewBuffer(data) //转化[]byte //生成clint 参数为默认 client := &http.Client{} //生成要访问的url url := "http://192.168.235.128:9090/returnSno" //http请求 // request, err := http.NewRequest("POST", url, body) request, err := http.NewRequest("POST", url, body) if err != nil { log.Println("request er…

Xshell和Xftp的初步使用
type: item
date: 2019-11-06T11:56:00+00:00
description: Xshell和Xftp的初步使用XshellXshell是一个强大的安全终端模拟软件，它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议。Xshell 通过互联网到远程主机的安全连接以及它创新性的设计和特色帮助用户在复杂的网络环境中享受他们的工作。Xshell可以在Windows界面下用来访问远端不同系统下的服务器，从而比较好的达到远程控制终端的目的。除此之外，其还有丰富的外观配色方案以及样式选择。我在这里将会介绍Xshell和linux虚拟机建立连接。建立连接1.点击左上角的文件然后点击新键会出现一个弹窗。2.名称自定义，一般写连接虚拟机的名字，协议选择SSH协议，端口号选22，主机填写你要建立连接的虚拟机的IP地址。获得虚拟机的IP（1）打开要建立连接的虚拟机，在这里我要与CentOS7建立连接。（2）输入ip addr命令，会出现该虚拟机的ip地址，在这里我们取192.168.10.63。获得ip地址后我们把ip地址填入Xshell弹窗的主机栏里，并点击用户身份验证，把你虚拟机的用户名和该用户名的密码填上（在这里推荐填root用户），点击确定即可。3.找到自己建立的连接，点击连接即可。!连接成功使用Xshell的一个好用的地方就是他能把windows上的文字粘贴复制到虚拟机上1.我们先创建一个文件 a.2.打开文件a,并去其他地方复制一段文字，右键点击会出现粘贴。3.反正功能很多，随便试。Xftp建立连接1.点击左上角的文件然后点击新键会出现一个弹窗。名称自己定义，主机还是建立连接的linuxIP，协议选择SFTP，端口号选择22，下面登录栏的方法选择Password，用户名和密码选择虚拟机的root用户和该用户的密码。点击选项可以设置默认连接的文件夹2.点击确定后，左上角的文件有个打开点击一下，然后选择你要建立的连接。连接成功后可以直接拖拽文件到彼此的系统上