0. 写在前面

大家如果有去看过nodejs所支持的官方库的话,应该会惊讶于它所提供了非常完善的网络库,不仅是应用层,传输层,等等基础的协议,我们可以按照事件驱动的逻辑编写清晰易懂的网络应用,网络服务。这也是本文为什么选择Nodejs编写的原因。

1. 背景映入

大家在使用一些数据库软件的时候常常会使用远程连接

mysql -h xxx.xxx.xxx.xx -u xzzz -p

这里也指明了ip地址,但是很明显这里可不是http协议在服务,而是更加底层的协议 - 传输层协议,具体来说是TCP协议(Transmission Control Protocol)。通信的示意图如下:



所以很自然的想到,数据库的客户端一定经过如下流程,从而与远程相连接:

graph TB
身份验证 --> 运输层连接建立
运输层连接建立 --> 客户端服务端输入输出绑定_通道
客户端服务端输入输出绑定_通道 --> 连接中断
连接中断 --> 双方退出释放资源

所以我们可以尝试向服务端发送这样的请求消息,建立与服务端的连接,发送一些数据,接受一些数据,最后断开连接。

2. 数据库选择

这里为了简单起见,我们考虑不需要身份验证的redis数据库来作为此次实验的服务端。

如果大家是mac,或者linux倒是可以直接安装,如果是windows的话,推荐使用docker进行安装,这里给出一行docker命令。

docker run  --name redis-server -p 6379:6379 -d redis:latest

3. Nodejs TCP连接

在nodejs中支持TCP连接的是net模块, 其中使用createConnection(config)或者直接new Socket(config)来初始化一个TCP连接。

上面两个函数不论哪一个都会返回socket实例,如果连接正常的话,就可以通过这个socket发送消息了。





当服务端redis接收到消息之后也会返回相应的消息,在本机客户端通过对数据的校验,检查后,触发相应的操作(是拒绝还是接受服务端的响应)。

3. 代码编写

知道了原理之后,我这里直接把代码贴出来

  • RedisSocket: 继承自Socket
class RedisSocket extends Socket {
constructor(config: RedisClientConfig) {
super();
this.connect(config.port, config.host);
}
// Set
public set(key: string, value: string | number): Promise<Buffer> {
return new Promise((resolve, reject) => {
this.write(`SET ${key} ${value}\n`);
const fetchAns = (chunk: Buffer) => {
if (chunk.toString().includes("OK")) {
resolve(chunk);
this.off("data", fetchAns);
// 在交付完成之后使用off 把函数取消绑定
} else {
reject("error! can't set data");
}
}
this.on("data", fetchAns);
})
}
// Get
public get(key: string): Promise<Buffer> {
return new Promise((resolve, reject) => {
try {
this.write(`GET ${key}\n`);
const fetchAns = (chunk: Buffer) => {
resolve(chunk);
this.off("data", fetchAns);
// 在交付完成之后使用off 把函数取消绑定
}
this.on("data", fetchAns);
} catch(err) {
reject(err);
}
})
}
// 断开TCP
public close() {
this.end();
}
}

这个类将用来处理建立好后的连接的

  • RedisClient
class RedisClient {

    private config: RedisClientConfig;

    constructor(config: RedisClientConfig) {
this.config = config; // 配置项
} // 获取redis实例
getConnection(): Promise<RedisSocket> {
return new Promise((resolve, reject) => {
const socket = new RedisSocket(this.config); socket.on("connect", () => {
resolve(socket);
}); socket.on("error", (err) => {
reject(err);
});
});
}
}

这个类用来建立与服务端的连接,使用getConnection()方法,将会交付一个redisSocket,使用这个Socket可以直接向server发送和接受数据。

4. 实验

import { RedisClient, RedisSocket } from "./src/Client";

const Redis = new RedisClient({
host: "localhost",
port: 6379
}); Redis.getConnection().then((socket: RedisSocket) => {
socket.set("Mushroom", "Cookie");
socket.set("Mici", "Icmi").then( () => {
socket.get("Mushroom").then((data: Buffer) => {
console.log(data.toString());
socket.close();
})
});
})

这里使用RedisClient建立与本地redis的连接,随后通过getConnection()获取到连接实例,并通过这个连接实例设置了两个数据,以及获取了一数据并打印了出来。

> pnpm dev
> $6 // 这里的$6你也许会感到奇怪,不过我们很快就会知道这是什么
> Cookie

5. wireshark 抓包分析



这一次请求就是一整个完整的TCP流程,

在这其中TCP保证数据的可靠传输,而RESP(REdis Serialization Protocol)把数据封装成一个fragment段,发送到下面的TCP

服务端相应的时候也是如此,会把数据封装起来发送到TCP中转发出去。

看看发送方的RESP



看看响应的RESP





所以知道了吗?没错,6其实就是长度那一部分强行转化为字符串的结果,所以在现在很多流行的redis客户端中如ioredis都对RESP报文做了非常完备的解析,这使得开发者能够非常丝滑的与redis服务端交互。(感谢这些开发者做的一切!)

6. 杂与代码

Github 仓库

希望大家都对世界保持好奇!

用Nodejs 实现一个简单的 Redis客户端的更多相关文章

  1. 用nodejs搭建一个简单的服务器

    使用nodejs搭建一个简单的服务器 nodejs优点:性能高(读写文件) 数据操作能力强 官网:www.nodejs.org 验证是否安装成功:cmd命令行中输入node -v 如果显示版本号表示安 ...

  2. 用nodejs搭建一个简单的服务监听程序

    作为一个从业三年左右的,并且从事过半年左右PHP开发工作的前端,对于后台,尤其是对以js语言进行开发的nodejs,那是比较有兴趣的,虽然本身并没有接触过相关的工作,只是自己私下做的一下小实验,但是还 ...

  3. 利用 nodeJS 搭建一个简单的Web服务器(转)

    下面的代码演示如何利用 nodeJS 搭建一个简单的Web服务器: 1. 文件 WebServer.js: //-------------------------------------------- ...

  4. Go语言之从0到1实现一个简单的Redis连接池

    Go语言之从0到1实现一个简单的Redis连接池 前言 最近学习了一些Go语言开发相关内容,但是苦于手头没有可以练手的项目,学的时候理解不清楚,学过容易忘. 结合之前组内分享时学到的Redis相关知识 ...

  5. nodejs实现一个简单的爬虫

    nodejs是js语言,实现一个爬出非常的方便. 步骤 1. 使用nodejs的request模块,获取目标页面的html代码:https://github.com/request/request 2 ...

  6. nodejs创建一个简单的web服务

    这是一个突如其来的想法,毕竟做web服务的框架那么多,为什么要选择nodejs,因为玩前端时,偶尔想调用接口获取数据,而不想关注业务逻辑,只是想获取数据,使用java或者.net每次修改更新后还要打包 ...

  7. nodeJS搭建一个简单的(代理)web服务器

    前端获取数据时经常遇见跨域问题,以前一直用nginx做反向代理.最近在用vuejs,发现webpack-dev-server的代理简单好用.于是仿照写了一个简单的web服务器,用于非webpack的项 ...

  8. 演示一个简单的Redis队列

    0.Windows Service版下载 https://github.com/rgl/redis/downloads 1.新建一个Console项目 打开Nuget控制台,执行以下命令 Instal ...

  9. 一个简单的Redis结合Spring MVC架构以及实现过程

    为了加快开发人员对公司项目的理解.更加容易入手和对公司项目的整体把控. 整体框架 首先介绍公司项目的整体框架,闲话少说,直接上图 整体性能分析 这就是公司的一个整体的架构,为了开发人员对架构的侧重点的 ...

随机推荐

  1. APT 安装 MySQL 提示错误:dpkg: error: dpkg frontend lock is locked by another process

    在安装 MySQL 的时候提示错误: ubuntu@VM-0-6-ubuntu:/opt$ sudo dpkg -i mysql-apt-config_0.8.22-1_all.deb dpkg: e ...

  2. PLSQL 与 PLPGSQL

    KingbaseES 为了更好地适应用户的oracle 应用,实现了对 plsql 的支持,用户可以根据需要使用 plsql 或 plpgsql. 以下简要介绍下二者的差异 一.格式差异 1.plpg ...

  3. 一个包搞定中文数据集: datasetstore

    工作中,总是要使用各种中文数据集,每次使用数据集都要花费不少的时间进行寻找,写预处理代码,结合不同的模型和框架做出相应的处理.有的时候好不容易找到合适的数据集,但是却因为网络问题,无法下载,下载了很长 ...

  4. e1000e网卡驱动在麒麟3.2.5上编译安装

    一.清空原驱动 因为系统安装完毕后系统中自带了e1000e的网卡驱动,会影响后面自行编译的驱动 所以先用find命令找出并删除掉所有关于e1000e的驱动文件 find / -name "* ...

  5. 《!--suppress ALL --> 在Android XML 文件中的用途是什么?

    <!--suppress ALL --> 在Android XML 文件中的用途是什么? 警告一次又一次地出现在谷歌地图的 XML 文件中,但是当我使用时,所有警告都被禁用.那么压制所有评 ...

  6. 我的Vue之旅、01 深入Flexbox布局完全指南

    花了几个小时整合的"A Complete Guide to Flexbox"最新版本,介绍了flexbox的所有属性,外带几个实用的例子. 传统布局.Flexbox 布局的传统解决 ...

  7. Windows 2012 R2上搭建IIS管理用户的隔离模式FTP

    Windows 2012 R2上搭建IIS管理用户的隔离模式FTP Windows自带的FTP现在可以提供基于非OS用户的管理,这提高了安全性.即使FTP用户名和密码泄露,也不会对操作系统造成进一步的 ...

  8. 宝塔搭建的nginx如何只允许指定IP访问--nginx如何允许指定IP访问,nginx开发者调试模式

    我的博客,向来都是简洁.有用为主,转载请注明出处. 说白了就是往nginx配置文件中加两句话 allow 127.0.0.1; deny all; 允许127.0.0.1访问 然后拒绝其他连接,返回4 ...

  9. 【gRPC】C++异步服务端客户端API实例及代码解析

    对于同步API而言,程序的吞吐量并不高.因为在每次发送一个gRPC请求时,会阻塞整个线程,必须等待服务端的ack回到客户端才能继续运行或者发送下一个请求,因此异步API是提升程序吞吐量的必要手段. g ...

  10. 基于electron+vue+element构建项目模板之【自定义标题栏&右键菜单项篇】

    1.概述 开发平台OS:windows 开发平台IDE:vs code 本篇章将介绍自定义标题栏和右键菜单项,基于electron现有版本安全性的建议,此次的改造中主进程和渲染进程彼此语境隔离,通过预 ...