手撕分布式缓存---HTTP Server搭建

  经过了前两个章节的学习,分布式缓存的存储与新增我们已经实现了,并且对其做了高可用处理。本章节我们剥离和缓存强相关的逻辑,开始搭建一个HTTP服务器,毕竟缓存数据库搭建完之后别人没法访问也是没有用处的。这一章节我们重点学习go原生的HTTP标准库


前文链接

手撕分布式缓存之一 | 定义缓存结构体与实现底层功能函数
手撕分布式缓存之二 | 互斥锁的优化


系列目录

  • (1)HTTP Server 的搭建
    • (1.1)前言
    • (1.2)HTTP 核心数据结构的构造及初始化函数的实现
    • (1.3)单节点HTTP Server核心Get方法的实现
    • (1.4)代码实现及解释

(1)HTTP Server 的搭建

(1.1)前言

  通过本篇文章可以学习到 go 语言原生的 HTTP Server 的使用,虽然不像成熟的Web框架那样,方法、机制都很完全,但是通过学习底层的功能实现,对于我们整体项目结构搭建是非常重要的。如果将go语言、go衍生技术(gin,MiddleWare等)等一系列相关的技术看做一整个项目结构,那么哪些技术我们要实现哪些功能呢?这些功能做出来实际上使用的人有多少呢?能给我们带来多少价值?这些问题具象来看的话是与我们开发人员息息相关的,因此我们有必要去学习这种看似吃力不讨好实际却会给我们带来很深远的收益的知识。

(1.2)HTTP 核心数据结构的构造及初始化函数的实现

  go语言的HTTP服务为我们提供了 ListenAndServe(addr string, handler Handler) error 这个方法,handler是HTTP服务的主体处理函数,任何实现了 ServeHTTP 方法的对象都可以作为handler的值传递给ListenAndServe 方法。该方法除了handler参数还需要接收一个addr,用来标识服务启动的地址和端口,这个也是服务启动之后其他请求者访问的域名。因此我们可以定义一个数据结构HTTPPool,里面有属性 self string 和 basePath string,self是就是上面说的addr,basePath是用于区分服务器上其他服务的请求Url,避免重复,比如我们可以默认 /_geecache/ 作为我们请求URI(统一资源标识符)第一级的值

(1.3)单节点HTTP Server核心Get方法的实现

  在1.2小节我们解释了HTTP的核心数据结构,这一小节我们讨论下HTTP服务的核心服务ServeHTTP。在前言中我们概括性的讨论了go的HTTP原生包(下称HTTP原生包)与成熟框架是有区别的,从相关的代码中(gin和geeCache的相关实现中)了解到,HTTP原生包对于方法的直接操作只有ServeHTTP方法,也就是说如果只使用HTTP原生包构建HTTP Server,所有的请求都是由 ServeHTTP 方法首先处理的,而例如Gin框架,可以将方法绑定在URI上,在路由注册之后,可以通过URI访问与其绑定的功能方法;从HTTP原生包来看,我能想到的实现Gin这一功能的方法是,在ServeHTTP方法中通过判断请求的URI的值与if语句过滤来实现不同的URI访问不同的功能函数。
  HTTP Get方法的实现并不复杂,主要流程还是调用下层我们封装的Get方法(如何封装的可以阅读 分布式系列 的前两章内容)。

(1.4)代码实现及解释

自定义默认的URI第一级的值 和 定义HTTP的核心数据结构

  • 自定义URI的第一级的值为 _geecache ,使用下划线开头的值用作区分重复的可能性更低,因此我们使用下划线开头。
// this is the defaultPath, user it to distinguish geeCache Project and other Project.
const defaultBasePath = "/_geecache/"type HTTPPool struct {// this peer's base URL, e.g. self is "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,}
}

核心功能Get函数的实现

  • 在通过URI获取GroupName之前,首先判断URI的第一级值是否与defaultPath一致
  • 通过strings.SplitN根据 **字符 \ ** 进行截取,并获得groupName 和 key
  • 调用下层已经实现的函数功能,以从中获取key对应的value
  • 使用 w.Write(view.ByteSlice()),返回给 请求者一个 copy的对象,避免返回给请求者原对象后遭到恶意修改吗,view.ByteSlice() 的具体实现在上一章节有具体说明和实现 。
// ServeHTTP handle all http requests
func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {// 1. judge the first layer of uri is defaultBasePath.if !strings.HasPrefix(r.URL.Path, p.basePath) {panic("HTTPPool serving unexpected path: " + r.URL.Path)}// begin from the defaultPath, then start to split the rest of uri and get groupName from this operationparts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)// 2. judge the structure have two layers, first is groupName, second is key// the structure is  /<basepath>/<groupname>/<key>if len(parts) != 2 {http.Error(w, "bad request", http.StatusBadRequest)return}// 3. get GroupName and keygroupName := parts[0]key := parts[1]// 4. call Underlying interface to get Groupgroup := GetGroup(groupName)if group == nil {http.Error(w, "no such group: "+groupName, http.StatusNotFound)return}// 5. call Underlying interface to get key-valueview, err := group.Get(key)if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/octet-stream")// return key-valuew.Write(view.ByteSlice())
}

HTTP Server 服务的启动(main函数的编写),参数的声明

  • db 用于验证GetterFunc方法是否生效。
  • 2<<10是二进制左移10位,对应的十进制为4096。
  • http.ListenAndServe 是服务的启动代码。
// not necessary, just is a test demo in here
var db = map[string]string{"Tom":  "630","Jack": "589","Sam":  "567",
}func main() {// 2<<10 means Binary Left Shift 10 bits, its value is 2048// GetterFunc can ignore the data's addition, cause it initialize the group scores, and state the maxBytesgeecache.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 the ListenAndServe's errorlog.Fatal(http.ListenAndServe(addr, peers))
}

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

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

相关文章

ElasticSearch应用场景以及技术选型[ES系列] - 第496篇

历史文章&#xff08;文章累计490&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 M…

PDF控件Spire.PDF for .NET【转换】演示:将 PDF 转换为 Excel

PDF是一种通用的文件格式&#xff0c;但它很难编辑。如果您想修改和计算PDF数据&#xff0c;将PDF转换为Excel将是一个理想的解决方案。在本文中&#xff0c;您将了解如何使用Spire.PDF for .NET在 C# 和 VB.NET 中将 PDF 转换为 Excel。 Spire.Doc 是一款专门对 Word 文档进行…

【华为数据之道学习笔记】3-10元数据管理架构及策略

元数据管理架构包括产生元数据、采集元数据、注册元数据和运 维元数据。 产生元数据&#xff1a; 制定元数据管理相关流程与规范的落地方案&#xff0c;在IT产品开发过程中实现业务元数据与技术元数据的连接。 采集元数据&#xff1a; 通过统一的元模型从各类IT系统中自动采集元…

多线程(初阶九:线程池)

目录 一、线程池的由来 二、线程池的简单介绍 1、ThreadPoolExecutor类 &#xff08;1&#xff09;核心线程数和最大线程数&#xff1a; &#xff08;2&#xff09;保持存活时间和存活时间的单位 &#xff08;3&#xff09;放任务的队列 &#xff08;4&#xff09;线程工…

Axure的安装以及简单使用

目录 Axure简介 是什么 有什么用 Axure的优缺点 优点&#xff1a; 缺点&#xff1a; 安装 汉化 Axure的使用 工具栏 页面 ​编辑 添加子页面 ​编辑 Axure简介 是什么 Axure是一款著名的原型设计工具。它允许用户创建交互式线框图、流程图、原型和其他设计文档&…

「Verilog学习笔记」脉冲同步电路

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 timescale 1ns/1nsmodule pulse_detect(input clk_fast , input clk_slow , input rst_n ,input data_in ,output dataout );reg data_level, dat…

第十一章 React 封装自定义组件

一、专栏介绍 &#x1f30d;&#x1f30d; 欢迎加入本专栏&#xff01;本专栏将引领您快速上手React&#xff0c;让我们一起放弃放弃的念头&#xff0c;开始学习之旅吧&#xff01;我们将从搭建React项目开始&#xff0c;逐步深入讲解最核心的hooks&#xff0c;以及React路由、…

【NLP】RAG 应用中的调优策略

​ 检索增强生成应用程序的调优策略 没有一种放之四海而皆准的算法能够最好地解决所有问题。 本文通过数据科学家的视角审视检索增强生成&#xff08;RAG&#xff09;管道。它讨论了您可以尝试提高 RAG 管道性能的潜在“超参数”。与深度学习中的实验类似&#xff0c;例如&am…

关于jinja2高版本api变化导致notebook导出html失败的问题

最新jinja2版本已经到了3.1.2&#xff0c;但是nbconvert引用的应该是老版本&#xff0c;具体代码报错如下 Type "help", "copyright", "credits" or "license" for more information. >>> import nbconvert Traceback (most…

spark从表中采样(随机选取)一定数量的行

在Spark SQL中&#xff0c;你可以使用TABLESAMPLE来按行数对表进行采样。以下是使用TABLESAMPLE的示例&#xff1a; SELECT * FROM table_name TABLESAMPLE (1000 ROWS);在这个示例中&#xff0c;table_name是你要查询的表名。TABLESAMPLE子句后面的(1000 ROWS)表示采样的行数…

axios 基础的 一次封装 二次封装

一、平常axios的请求发送方式 修改起来麻烦的一批 代码一大串 二、axios的一次封装 我们会在src/utils创建一个request.js的文件来存放我们的基地址与拦截器 /* 封装axios用于发送请求 */ import axios from axios/* (1)request 相当于 Axios 的实例对象 (2)为什么要有reque…

VSCode使用Remote-SSH连接服务器时报错:无法与“***”建立连接: XHR failed.

关于VSCode的报错问题&#xff1a;无法与“***”建立连接: XHR failed 问题描述问题理解解决方法手动在本地下载安装包&#xff0c;然后手动传到服务器端 问题描述 是的&#xff0c;我又踩坑了&#xff0c;而且这个弄了好久&#xff0c;也重新装了VSCode软件&#xff0c;好像结…

2024黑龙江省职业院校技能大赛暨国赛选拔赛“GZ031应用软件系统开发”赛项赛题题库

2024黑龙江省职业院校技能大赛暨国赛选拔赛 “GZ031应用软件系统开发”赛项赛题题库 2024黑龙江省职业院校技能大赛暨国赛选拔赛 应用软件系统开发赛项&#xff08;高职组&#xff09; 赛题第1套 目录 竞赛说明 模块一&#xff1a;系统需求分析 任务1&#xff1a;制造执行…

Kotlin之for循环的具体使用说明

我们用java进行Android开发过程中&#xff0c;经常会用到for循环&#xff0c;在Kotlin中也会经常用到&#xff0c;但是在最近使用Kotlin中我发现&#xff0c;在java中使用for循环不会有什么问题&#xff0c;但是在Kotlin中会出现问题&#xff0c;就是循环出出来的结果不一样&am…

前端框架(Front-end Framework)和库(Library)的区别

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

阿里云国际版CDN加速,如何判断网站IP已加速?

将源站接入阿里云CDN服务后&#xff0c;您可以通过IP检测功能&#xff0c;检测客户端请求实际访问的IP是否为CDN加速节点IP&#xff0c;判断加速是否生效。 应用场景 IP检测的应用场景如下&#xff1a; 场景一&#xff1a;成功配置CDN后&#xff0c;您可以检测客户端请求实际…

Android popupwindow在低版本手机上无法显示

所以我开始看各个参数&#xff0c;注意到了在我自定义popupwindow的builder下的&#xff1a;&#x1f447;&#x1f447; .showAsDropDown(mLinMain, 0, 0);就是这个&#xff0c;这时候我想到了屏幕的原点坐标是&#xff08;0&#xff0c; 0&#xff09;&#xff0c;所设置的p…

Postman高级应用——变量、流程控制、调试、公共函数、外部数据文件

Postman 提供了四种类型的变量 环境变量&#xff08;Environment Variable&#xff09; 不同的环境&#xff0c;使用不同的环境变量&#xff0c;例如&#xff1a;测试过程中经常会用到 测试环境&#xff0c;外网环境等 全局变量&#xff08;Global Variable&#xff09; 所有的…

12.使用 Redis 优化登陆模块

目录 1. 使用 Redis 优化登陆模块 1.1 使用 Redis 存储验证码 1.2 使用 Redis 存储登录凭证 1.3 使用 Redis 缓存用户信息 1. 使用 Redis 优化登陆模块 使用 Redis 存储验证码&#xff1a;验证码需要频繁的访问与刷新&#xff0c;对性能要求较高&#xff1b;验证码不需要永…

【计算机网络】序列化,反序列化和初识协议

目录 ​编辑 一、概念 二、 序列化过程&#xff1a; 选择序列化格式&#xff1a; 实现序列化代码&#xff1a; JSON示例&#xff1a; Protocol Buffers示例&#xff1a; JSON编码示例&#xff1a; 传输或存储&#xff1a; 三、反序列化过程&#xff1a; 下面是反序列…