这是我参与「第五届青训营 」伴学笔记创作活动的第3天
从现在起go语言学习正式进入工程实践环节
在进一步实战前我们先来回忆以下之前学过的基础语法,完成几个相关的小demo
guessing-game
经典的猜数字游戏,有几个关注点
- 在使用rand之前要设置随机数种子,否则每一次都会生成相同的随机数序列。一般使用程序启动的时间戳来初始化。
- 目标:实现用户输入输出,并理解析成数字。
- 每个程序执行的时候都会打开几个文件,stdin stdout stderr等,stdin文件可以用os.Stdin来得到。然后直接操作这个文件很不方便,我们会用bufio.NewReader把一个文件转换成一个reader变量。reader变量上会有很多用来操作一个流的操作,我们可以用它的ReadString方法来读取一行。
- ReadString返回的结果包含结尾的换行符,我们把它去掉,再转换成数字。
最终代码:
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is ", secretNumber)
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin)
for {
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
continue
}
input = strings.Trim(input, "\r\n")
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
continue
}
fmt.Println("You guess is", guess)
if guess > secretNumber {
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
break
}
}
}
在线词典
curl命令是一个利用URL规则在命令行下工作的文件传输工具。使用一种受支持的协议,从远程服务器传输数据,或将数据传输到远程服务器。默认情况下,已安装在macOS和大多数Linux发行版上。curl支持包括HTTP、HTTPS、ftp等众多协议,还支持POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。在进行web后台程序开发测试过程中,常常会需
bash和cmd的curl不一样
两个很好用的网站用于自动代码生成:
curlconverter.com/go/(由bash curl 生成
oktools.net/json2go (由JSON 生成
核心代码:
func query(word string) {
client := &http.Client{}
request := DictRequest{TransType: "en2zh", Source: word}
buf, err := json.Marshal(request)//序列化成json
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
//req.Header.Set
resp, err := client.Do(req)//响应
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)//反序列化
if err != nil {
log.Fatal(err)
}
fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
}
SOCKS5代理
作用:
某些企业的内网为了确保安全性,有很严格的防火墙策略,副作用就是访问某些资源很麻烦,
socks5相当于在防火墙上开了个口子,让授权的用户可以通过单个端口去访问内部的所有资源。
在爬虫的时候,爬取过程中很容易会遇到IP访问频率超过限制,这个时候很多人就会去上网找一些代理IP池,这些代理IP池里的很多代理协议就是socks5。
正常浏览器访问一个网站,如果不经过代理服务器的:
- 先和对方的网站建立TCP连接
- 然后三次握手
- 然后发起HTTP请求
- 然后服务返回HTTP响应。
如果设置代理服务器,流程变复杂,变为:
- 首先浏览器和socks5代理建立TCP连接,代理再和真正的服务器建立TCP连接,分为四个阶段
- 握手阶段:浏览器会向socks5代理发送请求,内容包括一个协议的版本号,还有支持的认证的种类,socks5服务器会选中一个认证方式,返回给浏览器。如果返回的是00的话就代表不需要认证,返回其他类型的话会开始认证流程。
- 认证阶段:有点复杂暂略。
- 请求阶段:认证通过后浏览器会向socks5服务器发起请求,主要信息还是包括版本号和请求的类型,一般主要是connection请求,就代表代理服务器和要某个域名或者某个IP地址某个端口建立TCP连接。代理服务器收到响应后,会真正和后端服务器建立连接,然后返回一个响应。
- relay阶段:此时浏览器会发送正常请求,然后代理服务器收到请求后,会直接把请求转换到真正的服务器上。然后如果真正的服务器以后返回响应的话,那么也会把请求转发到浏览器这边。然后实际上代理服务器并不关心流量的细节,可以是HTTP流量,也可以是其他TCP流量。这个就是socks5协议的工作原理。
青训营社区话题页
青训营社区话题页forum.juejin.cn/youthcamp/p…
需求描述:
- 展示话题(标题,文字描述)和回帖列表
- 仅实现一个本地web服务
- 话题和回帖数据用文件存储(暂时不用数据库
具体分析:
E-R图也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。
分层结构
熟悉的MVC模式
- 数据层:数据Model,外部数据的增删改查
- 逻辑层:业务Entity,处理核心业务逻辑输出
- 视图层:视图view,处理和外部的交互逻辑
组件及技术点
-
web框架:Gin - github.com/gin-gonic/g…
- 了解go web框架的简单使用
-
分层结构设计:github.com/bxcodec/go-…
- 了解分层设计的概念
-
文件操作:读文件pkg.go.dev/io
-
数据查询:索引www.baike.com/wikiid/5527…
代码开发
Repository(数据层)
以topic为例:
package repository
import (
"sync"
)
type Topic struct {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
CreateTime int64 `json:"create_time"`
}
type TopicDao struct {
}
var (
topicDao *TopicDao
topicOnce sync.Once//适用于高并发场景下只执行一次的场景,减少存储的浪费
)
func NewTopicDaoInstance() *TopicDao {
topicOnce.Do(//单例
func() {
topicDao = &TopicDao{}
})
return topicDao
}
func (*TopicDao) QueryTopicById(id int64) *Topic {
return topicIndexMap[id]//模仿索引操作
}
Service层(逻辑层)
func (f *QueryPageInfoFlow) Do() (*PageInfo, error) {
if err := f.checkParam(); err != nil {
return nil, err
}
if err := f.prepareInfo(); err != nil {
return nil, err
}
if err := f.packPageInfo(); err != nil {
return nil, err
}
return f.pageInfo, nil
}
func (f *QueryPageInfoFlow) checkParam() error {
if f.topicId <= 0 {
return errors.New("topic id must be larger than 0")
}
return nil
}
Controller(视图层)
- 构建View对象
- 业务错误码