当前位置: 首页 > news >正文

golang分布式缓存项目 Day3 HTTP服务端

注:该项目原作者:https://geektutu.com/post/geecache-day1.html。本文旨在记录本人做该项目时的一些疑惑解答以及部分的测试样例以便于本人复习。

HTTP基础

package mainimport ("log""net/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)
}
  • 创建任意类型 server,并实现 ServeHTTP 方法。
  • 调用 http.ListenAndServe 在 9999 端口启动 http 服务,处理请求的对象为 s server。

在网页:http://localhost:9999 中会显示“hello world”

在标准库中,http.Handler 接口的定义如下:

package httptype Handler interface {ServeHTTP(w ResponseWriter, r *Request)
}

ListenAndServe 方法里面会去自动调用 handler.ServeHTTP() 方法
(在ListenAndServe 方法中传入Handler接口就会自动实现其ServeHTTP方法)

例如:

package mainimport ("fmt""log""net/http"
)// Engine is the uni handler for all requests
type Engine struct{}func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {switch req.URL.Path {case "/":fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)case "/hello":for k, v := range req.Header {fmt.Fprintf(w, "Header[%q] = %q\n", k, v)}default:fmt.Fprintf(w, "404 NOT FOUND: %s\n", req.URL)}
}func main() {engine := new(Engine)log.Fatal(http.ListenAndServe(":9999", engine))
}

GeeCache HTTP服务端

代码结构
在这里插入图片描述

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,}
}
  • HTTPPool 只有 2 个参数,一个是 self,用来记录自己的地址,包括主机名/IP 和端口。
  • 另一个是 basePath,作为节点间通讯地址的前缀,默认是 /_geecache/,那么 http://example.com/_geecache/ 开头的请求,就用于节点间的访问。因为一个主机上还可能承载其他的服务,加一段 Path 是一个好习惯。比如,大部分网站的 API 接口,一般以 /api 作为前缀。

接下来,实现最为核心的 ServeHTTP 方法。

// 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())
}
  • ServeHTTP 的实现逻辑是比较简单的,首先判断访问路径的前缀是否是 basePath,不是返回错误。
  • 我们约定访问路径格式为 /basepath/groupname/key,通过 groupname 得到 group 实例,再使用 group.Get(key) 获取缓存数据。
  • 最终使用 w.Write() 将缓存值作为 httpResponse 的 body 返回。

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

测试


HTTP服务只能在main package中的main函数启动,不能在其它包中的main函数启动!

package mainimport ("fmt""geecache""log""net/http"
)var db = map[string]string{"Tom":  "630","Jack": "589","Sam":  "567",
}func main() {geecache.NewGroup("scores", 2<<10, geecache.GetterFunc(func(key string) ([]byte, error) {log.Println("[SlowDB] search key", key)if v, ok := db[key]; ok {return []byte(v), nil}return nil, fmt.Errorf("%s not exist", key)}))addr := "localhost:9999"peers := geecache.NewHTTPPool(addr)log.Println("geecache is running at", addr)log.Fatal(http.ListenAndServe(addr, peers))
}
  • 同样地,我们使用 map 模拟了数据源 db。
  • 创建一个名为 scores 的 Group,若缓存为空,回调函数会从 db 中获取数据并返回。
  • 使用 http.ListenAndServe 在 9999 端口启动了 HTTP 服务。

测试结果:重复访问http://localhost:9999/_geecache/score/Tom
在这里插入图片描述

http.go的文件中下述代码已被对注释,防止结果输出过多报错,因为在请求时会多出一个/favicon.ico的访问请求(不知道为什么?)

	if !strings.HasPrefix(r.URL.Path, p.basePath) {panic("HTTPPool serving unexpected path: " + r.URL.Path)}

http://www.mrgr.cn/news/70182.html

相关文章:

  • 小面馆叫号取餐流程 佳易王面馆米线店点餐叫号管理系统操作教程
  • 基于Python的网上银行综合管理系统
  • 翼鸥教育:从OceanBase V3.1.4 到 V4.2.1,8套核心集群升级实践
  • 华为私有接口类型hybrid
  • 华为交换机配置默认路由
  • ECharts 实现大屏地图功能
  • 如何让 AI 更懂你:提示词的秘密
  • 海康Android面试题及参考答案
  • 基于SSM超市管理系统的设计与实现(源码+lw+调试)
  • 提取神经网络数学表达式
  • CST如何计算CMA中的模式加权系数MWC
  • 信息安全工程师题
  • 在不久的未来,AI大模型将会如何重塑软件开发流程,会对现在的工作产生什么样的冲击
  • 大模型分布式训练并行技术(五)混合并行
  • Python金融大数据分析概述
  • 量化交易系统开发-实时行情自动化交易-3.4.1.2.A股交易数据
  • BLIP/BLIP-2模型全解析
  • Java基础Day-Sixteen
  • 智能量化模型在大数据下的中阳策略发展
  • 测试中的精准校验:Levenshtein库的应用与实践
  • 第三百一十八节 Java线程教程 - Java线程优先级、Java守护线程
  • 鸿蒙NEXT开发笔记(十三)仿微信聊天App的BASE64转像素图
  • kafka(启动集群,使用spring方法,自定义配置)
  • webpack 性能优化方案 - 代码分离(分包)
  • 部署Apollo 9.0-1 安装docker(阿里源安装)
  • 解读Nature:Larger and more instructable language models become less reliable