您的位置:首页 > 教育 > 锐评 > 企业网站建设解决方案_小程序搜索排名帝搜sem880官网_市场营销策划方案3000字_茂名seo快速排名外包

企业网站建设解决方案_小程序搜索排名帝搜sem880官网_市场营销策划方案3000字_茂名seo快速排名外包

2025/7/23 16:40:58 来源:https://blog.csdn.net/weixin_51147313/article/details/143646945  浏览:    关键词:企业网站建设解决方案_小程序搜索排名帝搜sem880官网_市场营销策划方案3000字_茂名seo快速排名外包
企业网站建设解决方案_小程序搜索排名帝搜sem880官网_市场营销策划方案3000字_茂名seo快速排名外包

参考资料

前置知识

在 Go 的 HTTP 服务器开发中,ServeHTTP 方法的参数 w http.ResponseWriterr *http.Request 用于处理 HTTP 请求和构建响应。以下是它们的详细解释:

1. w http.ResponseWriter

  • w 是一个 http.ResponseWriter 类型,用于构建并发送 HTTP 响应。
  • 它提供了一组方法,用于向客户端写入响应数据或设置 HTTP 响应头。
  • 常见的操作包括:
    • 写入响应内容:使用 w.Write([]byte("response content")) 将字节内容写入响应。
    • 设置响应状态码:使用 w.WriteHeader(http.StatusCode),例如 w.WriteHeader(http.StatusNotFound) 设置状态为 404。
    • 设置响应头:通过 w.Header().Set("Content-Type", "application/json") 设置响应头,比如设置 Content-Typeapplication/json

2. r *http.Request

  • r 是一个指向 http.Request 的指针,包含了关于 HTTP 请求的所有信息。
  • http.Request 是一个结构体,包含许多字段和方法,帮助开发者获取请求的详细信息。
  • 常用字段和方法包括:
    • r.Method:获取请求方法,例如 GETPOST 等。
    • r.URL:包含请求的 URL 和路径信息,如 r.URL.Path 可以获取请求的路径部分。
    • r.Header:包含请求头信息,可以通过 r.Header.Get("Header-Name") 获取指定的请求头。
    • r.Body:包含请求的主体数据(通常用于 POST 请求),可以通过 io.ReadAll(r.Body) 读取内容。
    • r.Form:包含解析后的表单数据,适用于 POST 表单提交的数据。

示例说明

func (h *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {// 打印请求的路径log.Println("Request URL Path:", r.URL.Path)// 设置响应头w.Header().Set("Content-Type", "text/plain")// 写入响应内容w.Write([]byte("Hello World!"))
}

在这个示例中:

  • r.URL.Path 获取请求路径并打印出来。
  • 使用 w.Header().Set 设置 Content-Type 响应头为 text/plain
  • w.Write([]byte("Hello World!")) 向客户端写入响应内容 Hello World!

这样一来,w http.ResponseWriterr *http.Request 就构成了一个完整的 HTTP 请求-响应流程,服务器能够根据请求内容生成并返回相应的响应。

http.ListenAndServeServeHTTP 的隐式调用

http.ListenAndServe("localhost:9999", &s) 中,ServeHTTP 的调用是由 Go 的 HTTP 服务器框架隐式完成的。虽然代码中没有直接调用 ServeHTTP,但当服务器接收到 HTTP 请求时,Go 的 HTTP 服务器会自动调用 ServeHTTP 方法来处理该请求。这是通过接口机制实现的。以下是具体的过程:

1. http.Handler 接口

  • Go 的 net/http 包定义了一个 http.Handler 接口,其中只有一个方法:

    type Handler interface {ServeHTTP(ResponseWriter, *Request)
    }
    
    • 任何实现了 ServeHTTP 方法的类型都满足 http.Handler 接口。
    • 在这个例子中,server 类型实现了 ServeHTTP 方法,因此 server 类型满足 http.Handler 接口。

2. ListenAndServe 函数

  • http.ListenAndServe 函数的签名如下:

    func ListenAndServe(addr string, handler Handler) error
    
    • 第二个参数是一个 Handler 接口(即 http.Handler)。
    • 传递给 ListenAndServe&sserver 类型的一个指针,而 server 类型实现了 ServeHTTP 方法,因此 &s 是一个有效的 http.Handler 实例。

3. 隐式调用 ServeHTTP

  • ListenAndServe 启动服务器并监听 localhost:9999 后,每当收到一个 HTTP 请求,它会检查传入的 handler(即 &s)并调用其 ServeHTTP 方法。
  • 这个调用过程是 Go 的 HTTP 服务器框架自动处理的,因此不需要在代码中显式调用 ServeHTTP

运行流程概述

  1. http.ListenAndServe("localhost:9999", &s) 启动服务器并监听端口 9999
  2. 当收到一个请求时,服务器会自动调用 &sServeHTTP 方法来处理该请求。
  3. ServeHTTP 中,使用 wr 参数构建响应。

因此,ServeHTTP 的调用是 http.ListenAndServe 函数在处理请求时自动完成的。

http 标准库

Go 语言提供了 http 标准库,可以方便地搭建 HTTP 服务端和客户端。

实现服务端

type server intfunc (h *server) ServeHTTP(w http.ResponseWriter, r *http.Request){log.Println(r.URL.Path)w.Write([]byte("Hello World!"))
}func main(){var s serverhttp.ListenAndServe("localhost:9999", &s)
}
  • ServeHTTPhttp.Handler 接口的一个方法,用于处理 HTTP 请求。
  • 参数 w http.ResponseWriter:用于构造 HTTP 响应。
  • 参数 r *http.Request:包含了 HTTP 请求的信息。
  • log.Println(r.URL.Path):将请求的 URL 路径记录到控制台。
  • w.Write([]byte("Hello World!")):向客户端返回一个简单的 Hello World! 字符串作为响应内容。

http.ListenAndServe 接收 2 个参数,第一个参数是服务启动的地址,第二个参数是 Handler,任何实现了 ServeHTTP 方法的对象都可以作为 HTTP 的 Handler。

GeeCache HTTP服务器

分布式缓存需要实现节点间通信,==建立基于 HTTP 的通信机制是比较常见和简单的做法。如果一个节点启动了 HTTP 服务,那么这个节点就可以被其他节点访问。==今天我们就为单机节点搭建 HTTP Server。

首先我们创建一个结构体 HTTPPool,作为承载节点间 HTTP 通信的核心数据结构(包括服务端和客户端,今天只实现服务端)。

const defaultBasePath = "/_geecache/"type HTTPPool struct{self stringbasePath string
}func NewHTTPPool(self string) *HTTPPool{return &HTTPPool{self: self,basePath: defaultBasePath,}
}
  • HTTPPool 只有 2 个参数,一个是 self,用来记录自己的地址,包括主机名/IP 和端口。
  • 另一个是 basePath,作为节点间通讯地址的前缀,默认是 /_geecache/,那么 http://example.com/_geecache/ 开头的请求,就用于节点间的访问。因为一个主机上还可能承载其他的服务,加一段 Path 是一个好习惯。比如,大部分网站的 API 接口,一般以 /api 作为前缀。
func (p *HTTPPool) Log(format string, v ...interface{}){// 自定义日志方法,使用特定的格式来输出日志信息// fmt.Sprintf(format, v...) 格式化输出,将所有参数 v 格式化成字符串log.Printf("[Server %s] %s", p.self, fmt.Sprintf(format, v...))
}func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {// 检查请求的路径是否以 basePath 开头,确保请求路径正确if !strings.HasPrefix(r.URL.Path, p.basePath) {panic("HTTPPool serving unexpected path: " + r.URL.Path)  // 如果路径不匹配,触发 panic}// 使用自定义的 Log 方法记录请求方法和路径p.Log("%s %s", r.Method, r.URL.Path)// 将路径去除 basePath 前缀部分后按 '/' 分割为两部分,以获取 <groupname> 和 <key>// 格式要求为 /<basepath>/<groupname>/<key>parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)if len(parts) != 2 {// 如果分割后的部分长度不为 2,说明格式不正确,返回 400 错误http.Error(w, "bad request", http.StatusBadRequest)return}// 提取分割后的第一个部分作为 groupName,第二个部分作为 keygroupName := parts[0]key := parts[1]// 获取 group 对象,使用 groupName 在缓存中查找对应的 Groupgroup := GetGroup(groupName)if group == nil {// 如果没有找到对应的 group,返回 404 错误http.Error(w, "no such group: " + groupName, http.StatusNotFound)return}// 尝试在 group 中查找对应的 key 值,获取缓存数据view, err := group.Get(key)if err != nil {// 如果查找过程中出现错误,返回 500 错误http.Error(w, err.Error(), http.StatusInternalServerError)return}// 设置响应头的内容类型为 "application/octet-stream"// 这表示响应的内容是二进制数据w.Header().Set("Content-Type", "application/octet-stream")// 将缓存的内容写入响应体,发送给客户端w.Write(view.ByteSlice())
}
  • ServeHTTP 的实现逻辑是比较简单的,首先判断访问路径的前缀是否是 basePath,不是返回错误。
  • 我们约定访问路径格式为 /<basepath>/<groupname>/<key>,通过 groupname 得到 group 实例,再使用 group.Get(key) 获取缓存数据。
  • 最终使用 w.Write() 将缓存值作为 httpResponse 的 body 返回。

到这里,HTTP 服务端已经完整地实现了。接下来,我们将在单机上启动 HTTP 服务,使用 curl 进行测试。

package geecacheimport ("fmt""log""net/http""strings"
)const defaultBasePath = "/_geecache/"// HTTPPool implements PeerPicker for a pool of HTTP peers.
type HTTPPool struct {// this peer's base URL, e.g. "https://example.net:8000"self     stringbasePath string
}// NewHTTPPool initializes an HTTP pool of peers.
func NewHTTPPool(self string) *HTTPPool {return &HTTPPool{self:     self,basePath: defaultBasePath,}
}// Log info with server name
func (p *HTTPPool) Log(format string, v ...interface{}) {log.Printf("[Server %s] %s", p.self, fmt.Sprintf(format, v...))
}// ServeHTTP handle all http requests
func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {if !strings.HasPrefix(r.URL.Path, p.basePath) {panic("HTTPPool serving unexpected path: " + r.URL.Path)}p.Log("%s %s", r.Method, r.URL.Path)// /<basepath>/<groupname>/<key> requiredparts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)if len(parts) != 2 {http.Error(w, "bad request", http.StatusBadRequest)return}groupName := parts[0]key := parts[1]group := GetGroup(groupName)if group == nil {http.Error(w, "no such group: "+groupName, http.StatusNotFound)return}view, err := group.Get(key)if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/octet-stream")w.Write(view.ByteSlice())
}

1. 常量定义

const defaultBasePath = "/_geecache/"
  • 定义了 defaultBasePath 常量,表示缓存服务的默认路径前缀,所有请求都应以该路径前缀开头(如 /_geecache/)。

2. 结构体定义:HTTPPool

type HTTPPool struct {self     stringbasePath string
}
  • HTTPPool 是一个结构体,代表一组 HTTP 节点组成的缓存池。它实现了 PeerPicker 接口,可以在集群中选择适当的节点。
  • 字段解释:
    • self string:表示当前节点的基本 URL,例如 https://example.net:8000
    • basePath string:当前节点路径的前缀。默认是 defaultBasePath,用于区分普通的 HTTP 请求和缓存服务的请求。

3. 构造函数:NewHTTPPool

func NewHTTPPool(self string) *HTTPPool {return &HTTPPool{self:     self,basePath: defaultBasePath,}
}
  • NewHTTPPool 是一个构造函数,用于初始化一个 HTTPPool 实例。
  • 接收 self string 参数作为当前节点的 URL,并将默认路径前缀 defaultBasePath 赋给 basePath
  • 返回值类型为指针 *HTTPPool,可以有效避免数据拷贝,便于共享该实例。

4. 方法:Log

func (p *HTTPPool) Log(format string, v ...interface{}) {log.Printf("[Server %s] %s", p.self, fmt.Sprintf(format, v...))
}
  • Log 是一个日志方法,用于记录日志信息,便于调试。
  • 接收 format string 和可变参数 v ...interface{},通过 fmt.Sprintf 格式化后输出到日志中。
  • 日志信息前包含 [Server <self>],用来标记日志属于哪个节点,方便排查分布式系统中的问题。

5. 方法:ServeHTTP

func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {// 检查请求路径是否符合预期格式if !strings.HasPrefix(r.URL.Path, p.basePath) {panic("HTTPPool serving unexpected path: " + r.URL.Path)}p.Log("%s %s", r.Method, r.URL.Path)// 解析路径为 <groupname>/<key>parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)if len(parts) != 2 {http.Error(w, "bad request", http.StatusBadRequest)return}groupName := parts[0]key := parts[1]group := GetGroup(groupName)if group == nil {http.Error(w, "no such group: "+groupName, http.StatusNotFound)return}view, err := group.Get(key)if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/octet-stream")w.Write(view.ByteSlice())
}
  • ServeHTTP 方法用于处理所有 HTTP 请求。它使 HTTPPool 结构体实现了 http.Handler 接口,这样它可以作为 HTTP 处理器。
方法流程
  1. 路径验证

    • 检查请求路径是否以 basePath 开头。如果不符合,则触发 panic,说明请求路径异常。
  2. 日志记录

    • 调用 p.Log 记录请求的 HTTP 方法和路径。
  3. 路径解析

    • 去除 basePath 前缀后,将路径按 / 分割成两部分,期望格式为 <groupname>/<key>
    • 如果解析失败(如路径格式不正确),返回 400 Bad Request 错误。
  4. 缓存分组获取

    • 使用 GetGroup(groupName) 获取指定的缓存分组 group
    • 如果找不到对应的缓存分组,返回 404 Not Found 错误。
  5. 获取缓存数据

    • 调用 group.Get(key) 获取指定 key 的缓存数据 view
    • 如果获取数据时出现错误,返回 500 Internal Server Error
  6. 返回数据

    • 设置响应头 Content-Typeapplication/octet-stream 表示二进制数据。
    • 将缓存数据写入响应体,返回给客户端。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com