【go项目-geecache】动手写分布式缓存 - day3 - HTTP 服务端

news2024/12/23 2:42:21

收获总结:

  • 了解go函数的可变参数的使用,还有切片展开的方式即…
  • 了解了HTTP通信方式,hinder的使用
  • 了解了go.mod ,import 和modoule的使用

分布式缓存需要实现点对点的通信,我们可以使用HTTP来实现节点之间的通信,如果说某个节点开始了HTTP服务,那么其他节点就可以进行通信

实现HTTP通信数据结构——HTTPPool

实现HTTPPool结构体

package geecache  
  
import (  
	"fmt"  
	"log"  
	"net/http"  
	"strings"  
)  
  
const defaultBasePath = "/_geecache/"  

type HTTPPool struct {  
	self     string  // 自己的地址
	basePath string  // 节点间通讯地址的前缀
}  

  • self 表示自己的地址
  • basePath 表示通讯地址的前缀,默认是 /_geecache/
    比如https://A.com/_geecache/ 就是一个请求

实现实例化函数

func NewHTTPPool(self string) *HTTPPool {  
	return &HTTPPool{  
		self:     self,  
		basePath: defaultBasePath,  
	}  
}

实现HTTPPool通信的接口 http.go

定义Log输出日志

func (p *HTTPPool) Log(format string, v ...interface{}) {  
	log.Printf("[Server %s] %s", p.self, fmt.Sprintf(format, v...))  
}

这里的v …inerface{} 是什么意思 ?

在 Go 语言中,v …interface{} 是一种可变参数的语法,它表示一个包含任意个 interface{} 类型参数的切片。这种语法常常用于函数或方法的定义中,以支持不定数量的参数。比如v … int就表示若干个int类型参数的切片

这里的v… 意思?

…可以用于将切片展开为独立的参数。这种语法通常用于需要将一个切片中的元素传递给函数或方法的调用

实现 http.Handler接口的 ServeHTTP方法

实现ServeHTTP方法是干什么的?

当该 HTTP 服务接收到请求时,会调用 HTTPPoolServeHTTP 方法来处理请求,该方法会解析请求路径中的参数,然后使用这些参数来获取缓存数据并将其写入 HTTP 响应。

具体实现

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> required  
	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())  
}

我们规定访问的路径格式是 /<basepath>/<groupname>/<key>

  • func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request):定义了一个方法,接收两个参数 wr,分别表示 HTTP 响应的写入器和 HTTP 请求的内容和元数据。
  • if !strings.HasPrefix(r.URL.Path, p.basePath) {...}:判断请求的路径是否以 HTTPPool 的 basePath 开头,如果不是,则抛出异常,终止请求处理。
  • p.Log("%s %s", r.Method, r.URL.Path):记录日志,表示收到了一个 HTTP 请求。
  • parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2):将请求路径从 basePath 开始的部分按照 “/” 分割成两个部分,并存储到 parts 变量中。例如,如果请求路径为 /basepath/groupname/key,则 parts 的值为 [“groupname”, “key”]。
  • if len(parts) != 2 {...}:判断 parts 是否为两个元素,如果不是,则返回 HTTP 状态码 400 Bad Request。
  • groupName := parts[0]:从 parts 中获取第一个元素,即缓存组名。
  • key := parts[1]:从 parts 中获取第二个元素,即缓存键。
  • group := GetGroup(groupName):根据组名获取缓存组。
  • if group == nil {...}:如果缓存组不存在,则返回 HTTP 状态码 404 Not Found。
  • view, err := group.Get(key):从缓存组中获取指定键的缓存数据。
  • if err != nil {...}:如果获取缓存数据失败,则返回 HTTP 状态码 500 Internal Server Error。
  • w.Header().Set("Content-Type", "application/octet-stream"):设置 HTTP 响应头部的 Content-Type 字段为 “application/octet-stream”,表示响应数据的 MIME 类型为二进制流。
  • w.Write(view.ByteSlice()):将缓存数据的字节数组写入 HTTP 响应的写入器中,从而将缓存数据返回给客户端。

测试文件

package main  
  
import (  
	"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))  
}

此时我们的文件目录应是这样
请添加图片描述

我们的main文件使用了geecache,但我们开启的go modules,不支持相对路径,所有在go.mod 应声明
require geecache v0.0.0
replace geecache => ./geecache

最后的go.mod文件是

module day3
go 1.20
require geecache v0.0.0
replace geecache => ./geecache

go.mod 到底是干什么的?

简而言之,go.mod就是管理项目依赖、版本控制和构建过程

测试

接下来,运行 main 函数,使用 curl 做一些简单测试:

$ curl http://localhost:9999/_geecache/scores/Tom  
630  
$ curl http://localhost:9999/_geecache/scores/kkk  
kkk not exist

成功截图

  1. 对应目录下运行 go run main(这里我运行过一次,所以已经存在了)
    然后curl http://localhost:9999/_geecache/scores/Tom

请添加图片描述

  1. 然后curl http://localhost:9999/_geecache/scores/kkk
    请添加图片描述

实现代码和测试代码

http.go

package geecache

  

import (

    "fmt"

    "log"

    "net/http"

    "strings"

)

  

const defaultBasePath = "/_geecache/"

  

type HTTPPool struct {

    self     string

    basePath string

}

  

func NewHTTPPool(self string) *HTTPPool {

    return &HTTPPool{

        self:     self,

        basePath: defaultBasePath,

    }

}

  

func (p *HTTPPool) Log(format string, v ...interface{}) {

    log.Printf("[Server %s] %s", p.self, fmt.Sprintf(format, v...))

}

  

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> required

    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())

}

main.go

package main

  

import (

    "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))

}

go.mod

module day3

  

go 1.20

  

require geecache v0.0.0

  

replace geecache => ./geecache

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

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

相关文章

Linux·IO子系统和文件系统读写流程

目录 1.概述 2.linux IO子系统和文件系统读写流程 3.IO之流程与buffer概览 4.IO队列和IO调度 1.概述 分析一下写操作&#xff1a; char *buf malloc(MAX_BUF_SIZE); strncpy(buf, src, , MAX_BUF_SIZE); fwrite(buf, MAX_BUF_SIZE, 1, fp); fclose(fp); 以下图为例&…

九耶丨钛伦特-用深度学习实现垃圾图像分类(二)

3 构建模型 这里大家可以使用自己在项目 2 中寻找到的最好的模型结构&#xff0c;做为示例&#xff0c;这里以实验2-3 中的 snet 模型为基础&#xff0c;并进行细微的调整。 样例代码: 需要注意的是&#xff0c;我们在第一层卷基层之后增加了一层池化层&#xff0c;主要是为了降…

大数据技术之Spark

第1章 Spark概述 1.1 什么是Spark 回顾&#xff1a;Hadoop主要解决&#xff0c;海量数据的存储和海量数据的分析计算。 Spark是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。 Hadoop的Yarn框架比Spark框架诞生的晚&#xff0c;所以Spark自己也设计了一套资源调度…

30天学会《Streamlit》(1)

30学会《Streamlit》是一项编码挑战&#xff0c;旨在帮助您开始构建Streamlit应用程序。特别是&#xff0c;您将能够&#xff1a; 为构建Streamlit应用程序设置编码环境 构建您的第一个Streamlit应用程序 了解用于Streamlit应用程序的所有很棒的输入/输出小部件 第1天 - 建立…

Java面向对象高级【注解和反射】

目录 注解 什么是注解&#xff1f; 自定义注解 元注解 反射 什么是反射 静态语言和动态语言 动态语言 静态语言 对比 Class类 Java内存分析 类加载过程 类加载器 获取运行时类的完整结构 通过Class对象实例化对象 1.调用Class对象的newInstance 2.Constructor…

保姆级教程!如何在 Anolis 8 上构建基于 Nydus 和 Dragonfly 的镜像加速解决方案?

文/云原生 SIG 01 背景 镜像是容器技术的基础之一&#xff0c;在云原生场景下&#xff0c;业务的正常运作离不开对镜像的制作、分发和运行。当前的镜像在使用的过程中&#xff0c;需要将镜像从仓库中全量拉取到本地&#xff0c;再由容器 engine 进行解压&#xff0c;堆叠挂载…

【JUC进阶】详解synchronized锁升级

文章目录 1. synchronized概述2. synchronized 的实现原理2.1 Java对象组成2.2 Monitor2.3 从字节码角度看synchronized 3. 锁升级3.1 偏向锁3.2 轻量级锁 1. synchronized概述 synchronized是一个悲观锁&#xff0c;可以实现线程同步&#xff0c;在多线程的环境下&#xff0c…

信盈达CorexM4核心板STM32F407VGT6电路原理图\电源原理图\USB的工作原理\miniUSB的分类

一、电源部分电路 Micro_USB插座中的5V与GND就是直接提供VCC与GND&#xff0c;其中的D与D-是接在CH340芯片上的实现串口通信的数据口。 电源VCC通过一个自恢复保险丝接在一个自锁开关上,这样就分析完一个miniUSB的提供电源的原理图了。 在原理图的右上角有一个AMS1117-3.3的器件…

RabbitMQ 高级篇 | 黑马

一、消息可靠投递 生产端的 在使用 RabbitMQ的时候&#xff0c;作为消息发送方希望杜绝任何消息丢失或者投递失败场景。 RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。 confirm 确认模式return 退回模式 rabbitmq整个消息投递的路径为: producer--->ra…

HCIP-6.6BGP配置中IGP路由表和BGP路由表、IP路由表关系

BGP配置中IGP路由表和BGP路由表、IP路由表关系1、基础配置2、配置建立IBGP对等体3、配置EBGP对等体4、使用network命令宣告路由5、路由的学习5.1、R2的路由表5.2、R1的BGP路由表5.3、R3的BGP路由表5.4、R5的BGP路由表6、AS200与AS100通信6.1、AS200出接口注入AS100IP路由表6.2、…

Go 汇编详解

动手点关注干货不迷路前言我们知道 Go 语言的三位领导者中有两位来自 Plan 9 项目&#xff0c;这直接导致了 Go 语言的汇编采用了比较有个性的 Plan 9 风格。不过&#xff0c;我们不能因咽废食而放弃无所不能的汇编。1、 Go 汇编基础知识1.1、通用寄存器不同体系结构的 CPU&…

【Docker】使用Docker Compose部署项目

目录 前言 使用 前言 如果你部署项目需要很多个容器, 并且还是在手动一个一个启动的话来试试Docker Compose, 只需要写好Docker Compose文件运行命令就能帮你一次性全启动, 对微服务很友好啊~ 使用 运行以下命令查看是否有版本号, 确保你已经安装了Docker Compose docker…

使用sealos工具部署k8s

为什么使用sealos工具&#xff1a;简单、快、完全兼容 k8s、给100年认证 sealos使用最新版本&#xff1a; 官网&#xff1a;https://www.sealyun.com/ 码&#xff1a;https://github.com/labring/sealos 官方介绍什么是sealos Sealos 是以 kubernetes 为内核的云操作系统发行版…

精彩回顾|4.8 Beijing Rust Meetup

2023年4月8日&#xff0c;达坦科技联合南京大学、CloudWeGo、华为等技术专家成功举办了题为Rust X的meetup。开发者们不仅线下积极报名参与&#xff0c;更在线上直播中踊跃参与互动&#xff0c;一起探讨Rust作为一种强调性能、安全和并发性的编程语言的各种应用和实践。演讲者与…

从EXCEL BOM 描述中提取部分信息---正则表达式使用

从EXCEL BOM描述中提取关键的信息&#xff0c;用于建库填写内容&#xff0c;或者检查BOM等都会用到&#xff0c;如下大概通过两种方式实现信息的提取 1.手动Excel中提取 2.将如上1的方式用python实现&#xff0c;可以实现批量操作&#xff0c;减少操作带来的错误&#xff0c;…

[网络安全]第三次作业

目录 1. 什么是IDS&#xff1f; 2. IDS和防火墙有什么不同&#xff1f; 3. IDS工作原理&#xff1f; 4. IDS的主要检测方法有哪些详细说明&#xff1f; 5. IDS的部署方式有哪些&#xff1f; 6. IDS的签名是什么意思&#xff1f;签名过滤器有什么作用&#xff1f;例外签名…

温故c语言——深度剖析数据在内存中的存储

目录 数据类型详细介绍整形在内存中的存储&#xff1a;原码、反码、补码大小端字节序介绍及判断浮点型在内存中的存储解析 1. 数据类型介绍 基本内置数据类型有&#xff1a; //在内存中占用空间的大小 char //字符数据类型 占用1个字节 short //短整型 占用2个…

linux及openEuler破解root密码

第一步&#xff1a;开机的时候按键盘的字母 E 键&#xff0c; 进入引导模式 第二步&#xff1a;进入引导模式 &#xff1a;找到linux这一行&#xff0c;按键盘上的end 键&#xff0c;跳转到行尾&#xff0c;输入&#xff1a; init/bin/sh 修改完后&#xff0c;按键盘上的 ctr…

电脑开机出现英文字母开不了机U盘重装系统教学

电脑开机出现英文字母开不了机U盘重装系统教学。有用户电脑开机之后出现了错误代码字母&#xff0c;无法正常的开机了。遇到这个问题要怎么去进行系统的重新安装呢&#xff1f;一起来看看以下的具体解决方法教学吧。 准备工作&#xff1a; 1、U盘一个&#xff08;尽量使用8G以上…

基于LS1028 TSN时间敏感网络交换机方案(一)TSN介绍

2.1 时间敏感网络介绍 时间敏感网络小组的前身是 AVB &#xff0c;即以太网音视频桥接技术&#xff08; Ethernet Audio/Video Bridging, 简称 Ethernet AVB &#xff09; [10] 。它在传统以太网络的基础上&#xff0c;通过 精确时钟同步、预留带宽、流量整形&#xff0…