Go 的 Http 请求系统指南

在这里插入图片描述

文章目录

  • 快速体验
  • 请求方法
  • URL参数
  • 响应信息
    • Body
    • StatusCode
    • Header
    • Encoding
  • 图片下载
  • 定制请求头
  • 复杂的POST请求
    • 表单提交
    • 提交文件
  • Cookie
    • Client 上设置 Cookie
    • 请求上设置 Cookie
  • 重定向和请求历史
  • 超时设置
    • 总超时
    • 连接超时
    • 读取超时
  • 请求代理
  • 错误处理
  • 总结

前几天在 “知乎想法” 谈到了一个话题,如何模仿学习,举了通过 net/http client 模仿 Pyhton 的requests的例子。但并未实践,难道想法真的只能是想法吗?当然不是,于是我决定先暂停一周 GO 笔记,来实践下自己的想法。

有些新的知识,我们可以通过模仿学习

本文将通过 GO 实现 requests 的 quick start 文档中的所有例子,系统学习http client的使用。虽然标题是 quick start,但其实内容挺多的。

快速体验

首先,我们来发起一个 GET 请求,代码非常简单。如下:

func get() {r, err := http.Get("https://api.github.com/events")if err != nil {panic(err)}defer func() { _ = r.Body.Close() }()body, _ := ioutil.ReadAll(r.Body)fmt.Printf("%s", body)
}

通过 http.Get 方法,获取到了一个 Response 和一个 error ,即 r 和 err。通过 r 我们能获取响应的信息,err 可以实现错误检查。

r.Body 被读取后需要关闭,可以defer来做这件事。内容的读取可通过 ioutil.ReadAll实现。

请求方法

除了GET,HTTP还有其他一系列方法,包括POST、PUT、DELETE、HEAD、OPTIONS。快速体验中的GET是通过一种便捷的方式实现的,它隐藏了很多细节。这里暂时先不用它。

我们先来介绍通用的方法,以帮我们实现所有HTTP方法的请求。主要涉及两个重要的类型,Client 和 Request。

Client 即是发送 HTTP 请求的客户端,请求的执行都是由 Client 发起。它提供了一些便利的请求方法,比如我们要发起一个Get请求,可通过 client.Get(url) 实现。更通用的方式是通过 client.Do(req) 实现,req 属于 Request 类型。

Request 是用来描述请求信息的结构体,比如请求方法、地址、头部等信息,我们都可以通过它来设置。Request 的创建可以通过 http.NewRequest 实现。

接下来列举 HTTP 所有方法的实现代码。

GET

r, err := http.DefaultClient.Do(http.NewRequest(http.MethodGet, "https://api.github.com/events", nil))

POST

r, err := http.DefaultClient.Do(http.NewRequest(http.MethodPost, "http://httpbin.org/post", nil))

PUT

r, err := http.DefaultClient.Do(http.NewRequest(http.MethodPut, "http://httpbin.org/put", nil))

DELETE

r, err := http.DefaultClient.Do(http.NewRequest(http.MethodDelete, "http://httpbin.org/delete", nil))

HEAD

r, err := http.DefaultClient.Do(http.NewRequest(http.MethodHead, "http://httpbin.org/get", nil))

OPTIONS

r, err := http.DefaultClient.Do(http.NewRequest(http.MethodOptions, "http://httpbin.org/get", nil))

上面展示了HTTP所有方法的实现。这里还几点需要说明。

DefaultClient,它是 net/http 包提供了默认客户端,一般的请求我们无需创建新的 Client,使用默认即可。

GET、POST 和 HEAD 的请求,GO提供了更便捷的实现方式,Request 不用手动创建。

示例代码,每个 HTTP 请求方法都有两种实现。

GET

r, err := http.DefaultClient.Get("http://httpbin.org/get")
r, err := http.Get("http://httpbin.org/get")

POST

bodyJson, _ := json.Marshal(map[string]interface{}{"key": "value",
})
r, err := http.DefaultClient.Post("http://httpbin.org/post","application/json",strings.NewReader(string(bodyJson)),
)
r, err := http.Post("http://httpbin.org/post","application/json",strings.NewReader(string(bodyJson)),
)

这里顺便演示了如何向 POST 接口提交 JSON 数据的方式,主要 content-type 的设置,一般JSON接口的 content-type 为 application/json。

HEAD

r, err := http.DefaultClient.Head("http://httpbin.org/get")
r, err := http.Head("http://httpbin.org/get")

如果看了源码,你会发现,http.Get 中调用就是 http.DefaultClient.Get,是同一个意思,只是为了方便,提供这种调用方法。Head 和 Post 也是如此。

URL参数

通过将键/值对置于 URL 中,我们可以实现向特定地址传递数据。该键/值将跟在一个问号的后面,例如 http://httpbin.org/get?key=val。 手工构建 URL 会比较麻烦,我们可以通过 net/http 提供的方法来实现。

举个栗子,比如你想传递 key1=value1 和 key2=value2 到 http://httpbin.org/get。代码如下:

req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil)
if err != nil {panic(err)
}params := make(url.Values)
params.Add("key1", "value1")
params.Add("key2", "value2")req.URL.RawQuery = params.Encode()// URL 的具体情况 http://httpbin.org/get?key1=value1&key2=value2
// fmt.Println(req.URL.String())r, err := http.DefaultClient.Do(req)

url.Values 可以帮助组织 QueryString,查看源码发现 url.Values 其实是 map[string][]string。调用 Encode 方法,将组织的字符串传递给请求 req 的 RawQuery。通过 url.Values也可以设置一个数组参数,类似如下的形式:

http://httpbin.org/get?key1=value1&key2=value2&key2=value3

怎么做呢?

params := make(url.Values)
params.Add("key1", "value1")
params.Add("key2", "value2")
params.Add("key2", "value3")

观察最后一行代码。其实,只要在 key2 上再增加一个值就可以了。

响应信息

执行请求成功,如何查看响应信息。要查看响应信息,可以大概了解下,响应通常哪些内容?常见的有主体内容(Body)、状态信息(Status)、响应头部(Header)、内容编码(Encoding)等。

Body

其实,在最开始的时候已经演示Body读取的过程。响应内容的读取可通过 ioutil 实现。

body, err := ioutil.ReadAll(r.Body)

响应内容多样,如果是 json,可以直接使用 json.Unmarshal 进行解码,JSON知识不介绍了。

r.Body 实现了 io.ReadeCloser 接口,为减少资源浪费要及时释放,可以通过 defer 实现。

defer func() { _ = r.Body.Close() }()

StatusCode

响应信息中,除了 Body 主体内容,还有其他信息,比如 status code 和 charset 等。

r.StatusCode
r.Status

r.StatusCode 是 HTTP 返回码,Status 是返回状态描述。

Header

响应头信息通过 Response.Header 即可获取,要说明的一点是,响应头的 Key 是不区分大小写。

r.Header.Get("content-type")
r.Header.Get("Content-Type")

你会发现 content-type 和 Content-Type 获取的内容是完全一样的。

Encoding

如何识别响应内容编码呢?我们需要借助 http://golang.org/x/net/html/charset 包实现。先来定义一个函数,代码如下:

func determineEncoding(r *bufio.Reader) encoding.Encoding {bytes, err := r.Peek(1024)if err != nil {fmt.Printf("err %v", err)return unicode.UTF8}e, _, _ := charset.DetermineEncoding(bytes, "")return e
}

怎么调用它?

bodyReader := bufio.NewReader(r.Body)
e := determineEncoding(bodyReader)
fmt.Printf("Encoding %v\n", e)decodeReader := transform.NewReader(bodyReader, e.NewDecoder())

利用 bufio 生成新的 reader,然后利用 determineEncoding 检测内容编码,并通过 transform 进行编码转化。

图片下载

如果访问内容是一张图片,我们如何把它下载下来呢?比如如下地址的图片。

https://pic2.zhimg.com/v2-5e8b41cae579722bd6b8a612bf1660e6.jpg

其实很简单,只需要创建新的文件并把响应内容保存进去即可。

f, err := os.Create("as.jpg")
if err != nil {panic(err)
}
defer func() { _ = f.Close() }()_, err = io.Copy(f, r.Body)
if err != nil {panic(err)
}

r 即 Response,利用 os 创建了新的文件,然后再通过 io.Copy 将响应的内容保存进文件中。

定制请求头

如何为请求定制请求头呢?Request 其实已经提供了相应的方法,通过 req.Header.Add 即可完成。

举个例子,假设我们将要访问 http://httpbin.org/get,但这个地址针对 user-agent 设置了发爬策略。我们需要修改默认的 user-agent。

示例代码:

req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil)
if err != nil {panic(err)
}req.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0)")

如上便可完成任务。

复杂的POST请求

前面已经展示过了向 POST 接口提交 JSON 数据的方式。接下来介绍下另外几种向 POST 接口提交数据的方式,即表单提交和文件提交。

表单提交

表单提交是一个很常用的功能,故而在 net/http 中,除了提供标准的用法外,还给我们提供了简化的方法。

我们先来介绍个标准的实现方法。

举个例子,假设要向 http://httpbin.org/post 提交 name 为 poloxue 和 password 为 123456 的表单。

payload := make(url.Values)
payload.Add("name", "poloxue")
payload.Add("password", "123456")
req, err := http.NewRequest(http.MethodPost,"http://httpbin.org/post",strings.NewReader(payload.Encode()),
)
if err != nil {panic(err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")r, err := http.DefaultClient.Do(req)

POST 的 payload 是形如 name=poloxue&password=123456 的字符串,故而我们可以通过 url.Values 进行组织。

提交给 NewRequest 的内容必须是实现 Reader 接口的类型,所以需要 strings.NewReader转化下。

Form 表单提交的 content-type 要是 application/x-www-form-urlencoded,也要设置下。

复杂的方式介绍完了。接着再介绍简化的方式,其实表单提交只需调用 http.PostForm 即可完成。示例代码如下:

payload := make(url.Values)
payload.Add("name", "poloxue")
payload.Add("password", "123456")
r, err := http.PostForm("http://httpbin.org/post", form)

竟是如此的简单。

提交文件

文件提交应该是 HTTP 请求中较为复杂的内容了。其实说难也不难,区别于其他的请求,我们要花些精力来读取文件,组织提交POST的数据。

举个例子,假设现在我有一个图片文件,名为 as.jpg,路径在 /Users/polo 目录下。现在要将这个图片提交给 http://httpbin.org/post。

我们要先组织 POST 提交的内容,代码如下:

filename := "/Users/polo/as.jpg"f, err := os.Open(filename)
if err != nil {panic(err)
}
defer func() { _ = f.Close() }()uploadBody := &bytes.Buffer{}
writer := multipart.NewWriter(uploadBody)fWriter, err := writer.CreateFormFile("uploadFile", filename)
if err != nil {fmt.Printf("copy file writer %v", err)
}_, err = io.Copy(fWriter, f)
if err != nil {panic(err)
}fieldMap := map[string]string{"filename": filename,
}
for k, v := range fieldMap {_ = writer.WriteField(k, v)
}err = writer.Close()
if err != nil {panic(err)
}

我认为,数据组织分为几步完成,如下:

  • 第一步,打开将要上传的文件,使用 defer f.Close() 做好资源释放的准备;
  • 第二步,创建存储上传内容的 bytes.Buffer,变量名为 uploadBody;
  • 第三步,通过 multipart.NewWriter 创建 writer,用于向 buffer中写入文件提供的内容;
  • 第四步,通过writer.CreateFormFile 创建上传文件并通过 io.Copy 向其中写入内容;
  • 最后,通过 writer.WriteField 添加其他的附加信息,注意最后要把 writer 关闭;

至此,文件上传的数据就组织完成了。接下来,只需调用 http.Post 方法即可完成文件上传。

r, err := http.Post("http://httpbin.org/post", writer.FormDataContentType(), uploadBody)

有一点要注意,请求的content-type需要设置,而通过 writer.FormDataContentType() 即能获得上传文件的类型。

到此,文件提交也完成了,不知道有没有非常简单的感觉。

Cookie

主要涉及两部分内容,即读取响应的 cookie 与设置请求的 cookie。响应的 cookie 获取方式非常简单,直接调用 r.Cookies 即可。

重点来说说,如何设置请求 cookie。cookie设置有两种方式,一种设置在 Client 上,另一种是设置在 Request 上。

Client 上设置 Cookie

直接看示例代码:

cookies := make([]*http.Cookie, 0)cookies = append(cookies, &http.Cookie{Name:   "name",Value:  "poloxue",Domain: "httpbin.org",Path:   "/cookies",
})
cookies = append(cookies, &http.Cookie{Name:   "id",Value:  "10000",Domain: "httpbin.org",Path:   "/elsewhere",
})url, err := url.Parse("http://httpbin.org/cookies")
if err != nil {panic(err)
}jar, err := cookiejar.New(nil)
if err != nil {panic(err)
}
jar.SetCookies(url, cookies)client := http.Client{Jar: jar}r, err := client.Get("http://httpbin.org/cookies")

代码中,我们首先创建了 http.Cookie 切片,然后向其中添加了 2 个 Cookie 数据。这里通过 cookiejar,保存了 2 个新建的 cookie。

这次我们不能再使用默认的 DefaultClient 了,而是要创建新的 Client,并将保存 cookie 信息的 cookiejar 与 client 绑定。接下里,只需要使用新创建的 Client 发起请求即可。

请求上设置 Cookie

请求上的 cookie 设置,通过 req.AddCookie即可实现。示例代码:

req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/cookies", nil)
if err != nil {panic(err)
}req.AddCookie(&http.Cookie{Name:   "name",Value:  "poloxue",Domain: "httpbin.org",Path:   "/cookies",
})r, err := http.DefaultClient.Do(req)

挺简单的,没什么要介绍的。

cookie 设置 Client 和 设置在 Request 上有何区别?一个最易想到的区别就是,Request 的 cookie 只是当次请求失效,而 Client 上的 cookie 是随时有效的,只要你用的是这个新创建的 Client。

重定向和请求历史

默认情况下,所有类型请求都会自动处理重定向。

Python 的 requests 包中 HEAD 请求是不重定向的,但测试结果显示 net/http 的 HEAD 是自动重定向的。

net/http 中的重定向控制可以通过 Client 中的一个名为 CheckRedirect 的成员控制,它是函数类型。定义如下:

type Client struct {...CheckRedirect func(req *Request, via []*Request) error...
}

接下来,我们来看看怎么使用。

假设我们要实现的功能:为防止发生循环重定向,重定向次数定义不能超过 10 次,而且要记录历史 Response。

示例代码:

var r *http.Response
history := make([]*http.Response, 0)client := http.Client{CheckRedirect: func(req *http.Request, hrs []*http.Request) error {if len(hrs) >= 10 {return errors.New("redirect to many times")}history = append(history, req.Response)return nil},
}r, err := client.Get("http://github.com")

首先创建了 http.Response 切片的变量,名称为 history。接着在 http.Client 中为 CheckRedirect 赋予一个匿名函数,用于控制重定向的行为。CheckRedirect 函数的第一个参数表示下次将要请求的 Request,第二个参数表示已经请求过的 Request。

当发生重定向时,当前的 Request 会保存上次请求的 Response,故而此处可以将 req.Response 追加到 history 变量中。

超时设置

Request 发出后,如果服务端迟迟没有响应,那岂不是很尴尬。那么我们就会想,能否为请求设置超时规则呢?毫无疑问,当然可以。

超时可以分为连接超时和响应读取超时,这些都可以设置。但正常情况下,并不想有那么明确的区别,那么也可以设置个总超时。

总超时

总的超时时间的设置是绑定在 Client 的一个名为 Timeout 的成员之上,Timeout 是 time.Duration。

假设这是超时时间为 10 秒,示例代码:

client := http.Client{Timeout:   time.Duration(10 * time.Second),
}

连接超时

连接超时可通过 Client 中的 Transport 实现。Transport 中有个名为 Dial 的成员函数,可用设置连接超时。Transport 是 HTTP 底层的数据运输者。

假设设置连接超时时间为 2 秒,示例代码:

t := &http.Transport{Dial: func(network, addr string) (net.Conn, error) {timeout := time.Duration(2 * time.Second)return net.DialTimeout(network, addr, timeout)},
}

在 Dial 的函数中,我们通过 net.DialTimeout 进行网络连接,实现了连接超时功能。

读取超时

读取超时也要通过 Client 的 Transport 设置,比如设置响应的读取为 8 秒。

示例代码:

t := &http.Transport{ResponseHeaderTimeout: time.Second * 8,
}
综合所有,Client 的创建代码如下:t := &http.Transport{Dial: func(network, addr string) (net.Conn, error) {timeout := time.Duration(2 * time.Second)return net.DialTimeout(network, addr, timeout)},ResponseHeaderTimeout: time.Second * 8,
}
client := http.Client{Transport: t,Timeout:   time.Duration(10 * time.Second),
}

除了上面的几个超时设置,Transport 还有其他一些关于超时的设置,可以看下 Transport 的定义,还有发现三个与超时相关的定义:

// IdleConnTimeout is the maximum amount of time an idle
// (keep-alive) connection will remain idle before closing
// itself.
// Zero means no limit.
IdleConnTimeout time.Duration// ResponseHeaderTimeout, if non-zero, specifies the amount of
// time to wait for a server's response headers after fully
// writing the request (including its body, if any). This
// time does not include the time to read the response body.
ResponseHeaderTimeout time.Duration// ExpectContinueTimeout, if non-zero, specifies the amount of
// time to wait for a server's first response headers after fully
// writing the request headers if the request has an
// "Expect: 100-continue" header. Zero means no timeout and
// causes the body to be sent immediately, without
// waiting for the server to approve.
// This time does not include the time to send the request header.
ExpectContinueTimeout time.Duration

分别是 IdleConnTimeout (连接空闲超时时间,keep-live 开启)、TLSHandshakeTimeout (TLS 握手时间)和 ExpectContinueTimeout(似乎已含在 ResponseHeaderTimeout 中了,看注释)。

到此,完成了超时的设置。相对于 Python requests 确实是复杂很多。

请求代理

代理还是挺重要的,特别对于开发爬虫的同学。那 net/http 怎么设置代理?这个工作还是要依赖 Client 的成员 Transport 实现,这个 Transport 还是挺重要的。

Transport 有个名为 Proxy 的成员,具体看看怎么使用吧。假设我们要通过设置代理来请求谷歌的主页,代理地址为 http://127.0.0.1:8087。

示例代码:

proxyUrl, err := url.Parse("http://127.0.0.1:8087")
if err != nil {panic(err)
}
t := &http.Transport{Proxy:           http.ProxyURL(proxyUrl),TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := http.Client{Transport: t,Timeout:   time.Duration(10 * time.Second),
}r, err := client.Get("https://google.com")

主要关注 http.Transport 创建的代码。两个参数,分时 Proxy 和 TLSClientConfig,分别用于设置代理和禁用 https 验证。我发现其实不设置 TLSClientConfig 也可以请求成功,具体原因没仔细研究。

错误处理

错误处理其实都不用怎么介绍,GO中的一般错误主要是检查返回的error,HTTP 请求也是如此,它会视情况返回相应错误信息,比如超时、网络连接失败等。

示例代码中的错误都是通过 panic 抛出去的,真实的项目肯定不是这样的,我们需要记录相关日志,时刻做好错误恢复工作。

总结

本文以 Python 的 requests 文档为指导方向,整理了 requests 快速入门文档中的案例在 GO 的是如何实现的。要说明的是, GO 其实也提供了对应于 requests 的克隆版本,github地址。暂时我也还没有看,有兴趣的朋友可以去研究一下。

博文地址:Go 的 Http 请求系统指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/640866.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【力扣】记录一下竞赛分上 Knight

记录一下力扣上 Knight 力扣的题还是相对来说比较简单的,前两个月写的题多一点,后面几乎都是只做了每日一题,感觉正常来说刷个两三个月的题水平就差不多够了,甚至在我才刷半个月的时候就可以做三题了,排名和现在差不多…

自然语言处理的崛起:从初步分析到深度理解

自然语言处理(NLP)是计算机科学、人工智能和语言学的交叉领域,旨在让计算机能够理解和生成人类语言。随着时间的推移,NLP 经历了一系列革命性的变化,从简单的规则和模式匹配到如今的深度学习模型,它们使计算…

宝塔面板SRS音视频TRC服务器启动失败

首先,查找原因 1.先看srs服务在哪 find / -type f -name srs 2>/dev/null运行结果: /var/lib/docker/overlay2/5347867cc0ffed43f1ae24eba609637bfa3cc7cf5f8c660976d2286fa6a88d2b/diff/usr/local/srs/objs/srs /var/lib/docker/overlay2/5347867…

STL——vector模拟实现

在学习过程种 ,学习看源码是很有必要的,它可以让你更清楚的知道一些代码的细节,你也会发现源码将效率利用到了极致,不得不佩服写出源码的人。 下面我将配合源码来实现一个简单的vector,下面请看源码 看源码我们首先要…

React16源码: React中的completeUnitOfWork的源码实现

completeUnitOfWork 1 )概述 各种不同类型组件的一个更新过程对应的是在执行 performUnitOfWork 里面的 beginWork 阶段它是去向下遍历一棵 fiber 树的一侧的子节点,然后遍历到叶子节点为止,以及 return 自己 child 的这种方式在 performUni…

Kotlin程序设计 扩展篇(一)

Kotlin程序设计(扩展一) **注意:**开启本视频学习前,需要先完成以下内容的学习: 请先完成《Kotlin程序设计》视频教程。请先完成《JavaSE》视频教程。 Kotlin在设计时考虑到了与Java的互操作性,现有的Ja…

像素图片在网页中很模糊怎么办?输入这个样式

像素图片在网页中很模糊怎么办?输入这个样式 image-rendering: pixelated;输入前 输入后

业务逻辑漏洞—验证码绕过

验证码绕过第一关: 前端验证码绕过: 打开pikachu靶场: 输入错误的验证码时会出现弹窗(alert)此时我们猜测这可能存在着前端限制 如果验证码有前端限制(只在前端有作用),不影响后…

(十一)Head first design patterns状态模式(c++)

状态模式 如何去描述状态机? 假设你需要实例化一台电梯,并模仿出电梯的四个状态:开启、关闭、运行、停止。也许你会这么写 class ILift{ public:virtual void open(){}virtual void close(){}virtual void run(){}virtual void stop(){} }…

基于SpringBoot Vue二手闲置物品交易系统

大家好✌!我是Dwzun。很高兴你能来阅读我,我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结,还为大家分享优质的实战项目,本人在Java项目开发领域有多年的经验,陆续会更新更多优质的Java实战项目&#x…

4小时精通MyBatisPlus框架

目录 1.介绍 2.快速入门 2.1.环境准备 2.2.快速开始 2.2.1引入依赖 2.2.2.定义Mapper ​编辑 2.2.3.测试 2.3.常见注解 ​编辑 2.3.1.TableName 2.3.2.TableId 2.3.3.TableField 2.4.常见配置 3.核心功能 3.1.条件构造器 3.1.1.QueryWrapper 3.1.2.UpdateWra…

Flowable 加签和减签

一:示例 Deployment deploy repositoryService.createDeployment().name("会签流程").addClasspathResource("processes/CounterSignProcess.bpmn").deploy();ProcessInstance processInstance runtimeService.startProcessInstanceByKey(&qu…

【操作系统】同步和互斥详细讲解(算法+源码)

博主介绍:✌全网粉丝喜爱、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦! 🍅附上相关C语言版源码讲解🍅 &#x1f44…

记录一次从有道云笔记迁移到语雀笔记

推荐阅读 智能化校园:深入探讨云端管理系统设计与实现(一) 智能化校园:深入探讨云端管理系统设计与实现(二) 1、安装git,python3等准备工作 文章中标注python3,为避免与python2 冲…

【Java-框架-SpringSecurity】单点登录(认证和授权)- 随笔

项目文件; 【1】 【2】 【3】 【4】 【5】 【6】 【7】 【8】

【小白学机器学习3】关于最简单的线性回归,和用最小二次法评估线性回归效果, 最速下降法求函数的最小值

目录 1 什么是回归分析 1.1 什么是线性回归 1.2非线性回归 2 数据和判断方法 2.1 原始数据 2.2 判断方法:最小二乘法 3 关于线性回归的实测 3.1 用直线模拟 3.2 怎么判断哪个线性模拟拟合更好呢? 3.2.1 判断标准 3.2.2 最小二乘法 3.2.3 高维…

【征服redis16】收官-redis缓存一致性问题解决方案

今天我们来写redis最后一篇:redis作为缓存时如何与数据库实现数据一致的问题。 最近看redis看得有点麻了,这篇就简单描述吧 目录 1.什么是缓存与数据库一致性问题 1.1 缓存一致性的概念 1.2 缓存不一致的场景 2.缓存不一致的解决思路 1.什么是缓存…

HarmonyOS 通过Web组件嵌套网络应用

我们今天来说说 在程序中嵌套一个网址地址 HarmonyOS中是通过一个简单的WEB组件来实现 网络应用就是相当于网址地址 通过链接将应用嵌入到手机当中 WEB组件需要两个参数 一个是 src 地址 要嵌套的网址 另一个是 控制器 我们可以先编写代码如下 import webview from "o…

力扣hot100 环形链表 快慢指针 计步器

Problem: 141. 环形链表 文章目录 思路💖 快慢指针法💖 计步器法 思路 👨‍🏫 参考题解 💖 快慢指针法 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) /*** Definition for singly-linked list…