C++项目 -- 高并发内存池(二)Thread Cache

news2024/11/23 8:14:13

C++项目 – 高并发内存池(二)Thread Cache

文章目录

  • C++项目 -- 高并发内存池(二)Thread Cache
  • 一、高并发内存池整体框架设计
  • 二、thread cache设计
    • 1.整体设计
    • 2.thread cache哈希桶映射规则
    • 3.TLS无锁访问
    • 4.thread cache代码


一、高并发内存池整体框架设计

现代很多的开发环境都是多核多线程,在申请内存的场景下,必然存在激烈的锁竞争问题。malloc本身其实已经很优秀,那么我们项目的原型tcmalloc就是在多线程高并发的场景下更胜一筹,所以这次我们实现的内存池需要考虑以下几方面的问题。

  1. 性能问题。
  2. 多线程环境下,锁竞争问题
  3. 内存碎片问题。

concurrent memory pool主要由以下3个部分构成:
在这里插入图片描述

  1. thread cache线程缓存每个线程独有的,用于小于256KB的内存的分配,线程从这里申请内存不需要加锁,每个线程独享一个cache,这也就是这个并发线程池高效的地方。
  2. central cache中心缓存所有线程所共享,thread cache是按需从central cache中获取的对象。central cache合适的时机回收thread cache中的对象,避免一个线程占用了太多的内存,而其他线程的内存吃紧,达到内存分配在多个线程中更均衡的按需调度的目的。central cache是存在竞争的,所以从这里取内存对象是需要加锁,首先这里用的是桶锁,其次只有thread cache的没有内存对象时才会找central cache,所以这里竞争不会很激烈
  3. page cache页缓存在central cache缓存上面的一层缓存,存储的内存是以页为单位存储及分配的,central cache没有内存对象时,从page cache分配出一定数量的page,并切割成定长大小的小块内存,分配给central cache。当一个span的几个跨度页的对象都回收以后,page cache会回收central cache满足条件的span对象,并且合并相邻的页,组成更大的页,缓解内存碎片的问题。

二、thread cache设计

1.整体设计

thread cache是哈希桶结构每个桶是一个按桶位置映射大小的内存块对象的自由链表。每个线程都会有一个thread cache对象,这样每个线程在这里获取对象和释放对象时是无锁的。

  • 类比定长内存池,一大块内存用来分配空间,freeList用于管理已经分配好的定长内存块
  • thread cache的freeList可以设计多个大小的定长list,如下图,8字节、16字节…;对象小于等于8字节的由8字节的freelist管理,9到16字节的由16字节的freeList管理;
    在这里插入图片描述
  • 但这样设计会造成空间浪费,比如一个对象大小为6字节,为它分配了8字节的空间,那么这剩下的2字节就会成为碎片,这叫做内碎片
  • thread cache整体是一个哈希桶结构,将对象的大小映射到对应大小的freeList中进行管理;

申请内存:

  • 当内存申请size<=256KB,先获取到线程本地存储的thread cache对象,计算size映射的哈希桶自由链表下标i。
  • 如果自由链表_freeLists[i]中有对象,则直接Pop一个内存对象返回。
  • 如果_freeLists[i]中没有对象时,则批量从central cache中获取一定数量的对象,插入到自由链表并返回一个对象。

释放内存:

  • 当释放内存小于256k时将内存释放回thread cache,计算size映射自由链表桶位置i,将对象Push到_freeLists[i]。
  • 当链表的长度过长,则回收一部分内存对象到central cache。

2.thread cache哈希桶映射规则

  • thread cache最大支持一个对象申请256KB的内存空间,则对象的大小范围是1 ~ 256KB,如果以8字节为对齐数,指定freeList,就是1 ~ 8字节大小的对象按照8字节对齐,分配8字节空间,连接到第一个freeList后面;9 ~ 16字节大小的对象分配16字节空间,连接到第二个freeList后面,这就是内存对齐
  • 每一个对齐的freeList都是一个哈希桶,这就是哈希映射
    在这里插入图片描述
  • 如果将所有的哈希桶对齐规则都为8字节,则一共需要32768个哈希桶,数量太多;

因此我们需要制定一个对齐规则:
在这里插入图片描述
分段安排对齐数的大小;

  • 对象大小在1 ~ 128字节之间的,按照8字节对齐,即每个哈希桶大小增长8字节;
  • 那么8字节对齐的哈希桶就有16个,即对应freeLists[0] - freeList[15];
    第一个桶链接对象大小在1 ~ 8字节之间的,第二个链接9 ~ 16字节之间,依次类推;
  • 对象大小在129 - 1024之间的,按照16字节对齐,即每个哈希桶大小增长16字节,以此类推;
  • 最终分配下来,8字节对齐的哈希桶共16个,16字节对齐的哈希桶共56个,128字节对齐的哈希桶共56个,1024字节对齐的哈希桶共56个,8KB字节对齐的哈希桶共56个,所有的哈希桶加起来一共208个,也就是说一共有208个freeList;
  • 根据上面的对齐规则可以将对象的size按照对齐数进行对齐,对齐的逻辑:
    在这里插入图片描述
    如果size不是对齐数alignNum的倍数,就需要根据对齐数调整最终分配空间的大小,否则size就是最终分配空间的大小;
  • 上面的计算过程可以使用下面的位运算进行代替,因为这段代码会被频繁调用,位运算的效率更高:
    在这里插入图片描述

这样分配的好处:

  • 能够减少哈希桶的数量
  • 能够将内碎片浪费控制在10%左右
    • 以16字节对齐为例,16字节对齐数能浪费的空间最大为15字节;如果一个对象分配到了129字节的内存,其对应的对齐数是16,则最终系统会为该对象分配145字节的空间,那么就有15字节的空间浪费,则内碎片为15字节,浪费率 = 15 / 145 = 0.1034 ,后面128字节等的对齐规则类似
    • 前面8字节对齐的部分可能不止10%,但是从16字节开始就能够控制在10%左右;

在制定好字节对齐规则后,还需要制订哈希映射规则,将不同大小的对象映射到对应的freeList中:

  • 根据字节对齐规则,1 ~ 128字节大小的对象,映射在8字节对齐的哈希桶,其映射逻辑如下:
    在这里插入图片描述
    129 ~ 1024字节大小的对象也是这样的规则;
  • 上面的代码可以使用位运算代替,因为这段代码会被频繁调用,位运算的效率更高:
    在这里插入图片描述

3.TLS无锁访问

在多线程环境下,ThreadCache的创建和访问会涉及到锁的问题,我们希望每个线程都有独立的ThreadCache,并且访问自己的ThreadCache都无须加锁,这样就需要使用TLS;
线程局部存储(TLS),是一种变量的存储方法,这个变量在它所在的线程内是全局可访问的,但是不能被其他线程访问到,这样就保持了数据的线程独立性。而熟知的全局变量,是所有线程都可以访问的,这样就不可避免需要锁来控制,增加了控制成本和代码复杂度。

Linux下TLS
win下TLS

TLS —— thread local storage:线程本地存储,我们这里使用静态的TLS:
声明以下代码:

_declspec(thread) DWORD data=0;

声明了_declspec(thread)的变量,会为每一个线程创建一个单独的拷贝。

  • 静态TLS的原理
    在×86CPU上,将为每次引用的静态TLS变量生成3个辅助机器指令。如果在进程中创建了另一个线程,那么系统就要将它捕获并且自动分配另一个内存块,以便存放新线程的静态TLS变量。新线程只拥有对它自己的静态TLS变量的访问权,不能访问属于其他线程的TLS变量。

ThreadCache.h
在这里插入图片描述

  • 在该头文件下定义一个全局的ThreadCache*类型的静态指针pTLSThreadCache使用_declspec(thread)声明后,每一个线程都会为该指针创建一个单独的拷贝,本线程在访问pTLSThreadCache指针时是能够全局访问的,但是其他线程不能访问本线程的pTLSThreadCache指针,这样就实现了多线程环境下的无锁访问;
  • 其实就是每一个线程都有一个独立的pTLSThreadCache,线程之间访问互不干扰

ConcurrentAlloc.h
在这里插入图片描述

  • 上面的代码只是声明了TLS的指针,并没有指向实际的ThreadCache对象,实际上每个线程在运行的时候,都需要将TLS指针指向自己的ThreadCache对象;
  • 这个头文件是对多线程环境下ThreadCache申请和释放内存的功能进行了封装,保证每个线程的ThreadCache都是本线程独有的,
  • ConcurrentAlloc函数会检测pTLSThreadCache是否为空,如果为空,证明初次调用,就需要构建一个新的ThreadCache对象,并将pTLSThreadCache指针指向该对象,这样本线程独有的ThreadCache对象就创建好了,再通过pTLSThreadCache指针去调用Allocate函数开辟空间;

4.thread cache代码

Common.h

  • 公共的头文件,共有部分的代码可以写在这里面;
  • NextObj函数用于获取当前obj对象指向的下一个对象的指针:
  • 将自由链表定义为一个类FreeList,实现链表的基本操作
  • 定义一个管理字节对齐和哈希映射规则的类SizeClass
    • 类中的所有成员函数都定义为静态的内联函数,方便外部直接调用;
    • RoundUp是用来计算当前对象size字节对齐之后对应的size,先判断对象的size在哪个对齐区间,再根据对齐数来计算对齐后的size(调用子函数_RoundUp
    • Index函数用来计算对象size映射到哪一个哈希桶(freelist),根据对象size所属的对齐区间和对齐数,调用_Index函数计算该对象映射到的哈希桶下标;
      注意从第二个对齐区间开始,由于前面部分的对齐数是不同的,因此在计算下标的时候,先要用size减去前面不同对齐数的区间,带入_Index函数计算该对象在当前区间内的相对下标,最后再加上前面所有的哈希桶个数,得到最终的下标;
      例如:如果映射到的是16字节对齐的区域,先要用分配空间减去128,因为前面的128字节是8字节对齐的,要减去,剩下的按照16字节对齐计算,再加上前面减去的桶的数量,以此类推
#pragma once
//公共头文件

#include <iostream>
#include <vector>
#include <assert.h>
#include <thread>
using std::cout;
using std::endl;
using std::vector;

static const size_t MAX_BYTES = 256 * 1024; //ThreadCache能分配对象的最大字节数
static const size_t NFREELIST = 208; // 最大的哈希桶数量

// 访问obj的前4 / 8字节地址空间
static void*& NextObj(void* obj) {
	return *(void**)obj;
}

//自由链表类,用于管理切分好的小内存块
class FreeList {
public:
	void Push(void* obj) {
		assert(obj);
		//头插
		NextObj(obj) = _freeList;
		_freeList = obj;
	}

	void* Pop() {
		assert(_freeList);

		//头删
		void* obj = _freeList;
		_freeList = NextObj(obj);

		return obj;
	}

	bool Empty() {
		return _freeList == nullptr;
	}
private:
	void* _freeList = nullptr;
};


// 管理对齐和哈希映射规则的类
class SizeClass {
public:
	//对齐规则
	// 整体控制在最多10%左右的内碎片浪费
	// [1,128]				8byte对齐			freelist[0,16)
	// [128+1,1024]			16byte对齐			freelist[16,72)
	// [1024+1,8*1024]		128byte对齐			freelist[72,128)
	// [8*1024+1,64*1024]	1024byte对齐			freelist[128,184)
	// [64*1024+1,256*1024] 8*1024byte对齐		freelist[184,208)

	//RoundUp的子函数,根据对象大小和对齐数,返回对象对齐后的大小
	static inline size_t _RoundUp(size_t size, size_t align) {
		//if (size % align == 0) {
		//	return size;
		//}
		//else {
		//	return (size / align + 1) * align;
		//}

		//使用位运算能够得到一样的结果,但是位运算的效率很高
		return ((size + align - 1) & ~(align - 1));
	}

	//计算当前对象size字节对齐之后对应的size
	static inline size_t RoundUp(size_t size) {
		assert(size <= MAX_BYTES);

		if (size <= 128) {
			//8字节对齐
			_RoundUp(size, 8);
		}
		else if (size <= 1024) {
			//16字节对齐
			_RoundUp(size, 16);
		}
		else if (size <= 8 * 1024) {
			//128字节对齐
			_RoundUp(size, 128);
		}
		else if (size <= 64 * 1024) {
			//1024字节对齐
			_RoundUp(size, 1024);
		}
		else if (size <= 256 * 1024) {
			//8KB字节对齐
			_RoundUp(size, 8 * 1024);
		}
		else {
			assert(false);
		}
		return -1;
	}

	//Index的子函数,用于计算映射的哈希桶下标
	static inline size_t _Index(size_t size, size_t alignShift) {
		//if (size % align == 0) {
		//	return size / align - 1;
		//}
		//else {
		//	return size / align;
		//}

		//使用位运算能够得到一样的结果,但是位运算的效率很高
		//使用位运算需要将输入参数由对齐数改为对齐数是2的几次幂、

		return ((size + (1 << alignShift) - 1) >> alignShift) - 1;
	}

	//计算对象size映射到哪一个哈希桶(freelist)
	static inline size_t Index(size_t size) {
		assert(size <= MAX_BYTES);

		//每个区间有多少个哈希桶
		static int groupArray[4] = { 16, 56, 56, 56 };
		if (size <= 128) {
			return _Index(size, 3);
		}
		else if (size <= 1024) {
			//由于前128字节不是16字节对齐,因此需要减去该部分,单独计算16字节对齐的下标
			//再在最终结果加上全部的8字节对齐哈希桶个数
			return _Index(size - 128, 4) + groupArray[0];
		}
		else if (size <= 8 * 1024) {
			return _Index(size - 1024, 7) + groupArray[0] + groupArray[1];
		}
		else if (size <= 64 * 1024) {
			return _Index(size - 8 * 1024, 10) + groupArray[0] + groupArray[1] + groupArray[2];
		}
		else if (size <= 256 * 1024) {
			return _Index(size - 64 * 1024, 13) + groupArray[0] + groupArray[1] + groupArray[2] + groupArray[3];
		}
		else {
			assert(false);
		}
		return -1;
	}
};

ThreadCache.h

  • 用于声明ThreadCache类的头文件
  • ThreadCache类包括一个FreeList类型的数组,这就是哈希桶的数组,还有完成ThreadCache功能的成员函数的声明;
  • 定义了由_declspec(thread)声明的TLS指针,用于实现无锁访问
#pragma once
#include "Common.h"

class ThreadCache {
public:
	//申请和释放对象内存
	void* Allocate(size_t size);
	void Deallocate(void* obj, size_t size);

	//从中心缓存获取对象
	void* FetchFromCentralCache(size_t index, size_t alignSize);

private:
	FreeList _freeLists[];
};

//声明_declspec(thread)后,会为每一个线程创建一个单独的拷贝
//使用_declspec(thread)声明了ThreadCache*指针变量,则该指针在该线程中会创建一份单独的拷贝
//pTLSThreadCache指向的对象在本线程内是能够全局访问的,但是无法被其他线程访问到,这就做到了多线程情景下的无锁访问
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;

ThreadCache.cpp

  • 这个.cpp文件中来实现ThreadCache中的成员函数:
  • Allocate函数为对象申请内存空间
    • 先获取对齐后的size和对应的哈希桶下标
    • 如果该哈希桶的freeList不为空,就Pop一个内存块给该对象,如果为空就需要向CentralCache申请空间
  • Deallocate函数用于归还对象的内存空间
    • 先获取对象对应的freeList的下标
    • 直接将该内存块插入对应的freeList中
  • FetchFromCentralCache用于从中心缓存获取对象空间
#include "ThreadCache.h"

void* ThreadCache::Allocate(size_t size) {
	assert(size <= MAX_BYTES);
	//获取对齐后的大小及对应的哈希桶下标
	size_t alignSize = SizeClass::RoundUp(size);
	size_t index = SizeClass::Index(size);

	if (!_freeLists[index].Empty()) {
		//若对应的freeList桶不为空,直接pop一个内存块给该对象
		return _freeLists[index].Pop();
	}
	else {
		//否则需要从CentralCache获取内存空间
		return ThreadCache::FetchFromCentralCache(index, alignSize);
	}
}

void ThreadCache::Deallocate(void* obj, size_t size) {
	assert(obj);
	assert(size <= MAX_BYTES);

	//找该对象对应的freeList的桶,直接插入
	size_t index = SizeClass::Index(size);
	_freeLists[index].Push(obj);
}

void* ThreadCache::FetchFromCentralCache(size_t index, size_t alignSize) {
	return nullptr;
}

ConcurrentAlloc.h

  • 该头文件用于进一步封装ThreadCache的功能,进而使其能够实现多线程情况下的无锁访问
#pragma once
#include "Common.h"
#include "ThreadCache.h"

static void* ConcurrentAlloc(size_t size) {
	if (pTLSThreadCache == nullptr) {
		//如果pTLSThreadCache指针是空的,就构造一个ThreadCache对象,并指向它
		//则这个ThreadCache对象就是本线程专属的ThreadCache对象
		pTLSThreadCache = new ThreadCache;
	}
	
	//使用pTLSThreadCache访问本线程专属的ThreadCache对象来开辟空间
	return pTLSThreadCache->Allocate(size);
}

static void ConcurrentFree(void* obj, size_t size) {
	assert(pTLSThreadCache);

	pTLSThreadCache->Deallocate(obj, size);
}

UnitTest.cpp

  • 该cpp文件用于测试ThreadCache的功能,重点测试TLS无锁访问的功能
  • 创建两个线程,分别使用ThreadCache申请空间,使用并行监视窗口监视每个进程的内容;
  • c++11的多线程,是将一个线程封装成一个对象
    在这里插入图片描述
    构造thread对象时,传入该线程执行的函数的指针以及参数;
#include "ObjectPool.h"
#include "ConcurrentAlloc.h"
#include "ThreadCache.h"

void Alloc1() {
	for (int i = 0; i < 5; i++) {
		void* obj = ConcurrentAlloc(5);
	}
}

void Alloc2() {
	for (int i = 0; i < 5; i++) {
		void* obj = ConcurrentAlloc(8);
	}
}

void TestTLS() {
	std::thread t1(Alloc1);
	std::thread t2(Alloc2);

	t1.join();
	t2.join();
}


int main() {
	TestTLS();
	return 0;
}

测试结果:
在这里插入图片描述

  • 两个不同的线程都获取和ThreadCache对象,但是通过TLS获取到的是两个不同的ThreadCache,每个线程各一个,两个线程通过pTLSThreadCache指针访问各自的ThreadCache对象

  • 也可以通过输出线程id和pTLSThreadCache指针来观察验证
    在这里插入图片描述
    在这里插入图片描述

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

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

相关文章

CCF迎来新风采:揭晓2024-2026年度执行机构负责人名单!

会议之眼 快讯 中国计算机学会&#xff08;CCF&#xff09;成立于1962年&#xff0c;是一家全国性学会&#xff0c;拥有独立社团法人地位&#xff0c;同时是中国科学技术协会的会员单位。作为中国计算机及相关领域的学术团体&#xff0c;CCF的宗旨在于为该领域专业人士的学术和…

C

extern int a; //同一个项目声明 int r a > b ? a : b; 错误 scanf 不输入‘\n’,getchar()输入\n; printf()返回值 0次 system("cls"); 可以调用命令行函数 time(NULL)时间戳 srand((unsigned)time(NULL)); //随机数种子 int rev rand()%1001; //随…

Linux---yum命令详解

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1.概念2.yum的配置信…

Open CASCADE学习|拓扑变换

目录 平移变换 旋转变换 组合变换 通用变换 平移变换 TopoDS_Shape out;gp_Trsf theTransformation;gp_Vec theVectorOfTranslation(0., 0.125 / 2, 0.);theTransformation.SetTranslation(theVectorOfTranslation);BRepBuilderAPI_Transform myBRepTransformation(out, th…

一篇文章了解区分指针数组,数组指针,函数指针,链表。

最近在学习指针&#xff0c;发现指针有这许多的知识&#xff0c;其中的奥妙还很多&#xff0c;需要学习的也很多&#xff0c;今天那我就将标题中的有关指针知识&#xff0c;即指针数组&#xff0c;数组指针&#xff0c;函数指针&#xff0c;给捋清楚这些知识点&#xff0c;区分…

【Linux取经路】进程控制——程序替换

文章目录 一、单进程版程序替换看现象二、程序替换的基本原理三、程序替换接口学习3.1 替换自己写的可执行程序3.2 第三个参数 envp 验证四、结语一、单进程版程序替换看现象 #include <stdio.h> #

【Funny guys】龙年专属测试鼠标寿命小游戏...... 用Python给大家半年了......

目录 【Funny guys】龙年专属测试鼠标寿命小游戏...... 用Python给大家半年了...... 龙年专属测试鼠标寿命小游戏用Python给大家半年了贪吃龙游戏 文章所属专区 码农新闻 欢迎各位编程大佬&#xff0c;技术达人&#xff0c;以及对编程充满热情的朋友们&#xff0c;来到我们的程…

Spring Boot + flowable 快速实现工作流

背景 使用flowable自带的flowable-ui制作流程图 使用springboot开发流程使用的接口完成流程的业务功能 文章来源&#xff1a;https://blog.csdn.net/zhan107876/article/details/120815560 一、flowable-ui部署运行 flowable-6.6.0 运行 官方demo 参考文档&#xff1a; htt…

SpringBoot整合Flowable最新教程(二)启动流程

介绍 文章主要从SpringBoot整合Flowable讲起&#xff0c;关于Flowable是什么&#xff1f;数据库表解读以及操作的Service请查看SpringBoot整合Flowable最新教程&#xff08;一&#xff09;&#xff1b;   其他说明&#xff1a;Springboot版本是2.6.13&#xff0c;java版本是1…

4. 树(二叉树、二叉查找树/二叉排序树/二叉搜索树、平衡二叉树、平衡二叉B树/红黑树)

树 1. 二叉树1.1 概述1.2 特点1.3 二叉树遍历方式1.3.1 前序遍历(先序遍历)1.3.2 中序遍历1.3.3 后序遍历1.3.4 层序遍历 2. 二叉查找树&#xff08;二叉排序树、二叉搜索树&#xff09;2.1 概述2.2 特点 3. 平衡二叉树3.1 概述3.2 特点3.3 旋转3.3.1 左旋3.3.2 右旋 3.4 平衡二…

指针的学习2

目录 数组名的理解 使用指针访问数组 一维数组传参的本质 冒泡排序 二级指针 指针数组 指针数组模拟二维数组 数组名的理解 数组名是数组首元素的地址 例外&#xff1a; sizeof(数组名),sizeof中单独放数组名&#xff0c;这里的数组名表示整个数组&#xff0c;计算的…

EasyCVR智能视频监控平台云台降低延迟小tips

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

彻底学会系列:一、机器学习之线性回归

1.基本概念 线性回归&#xff1a; 有监督学习的一种算法。主要关注多个因变量和一个目标变量之间的关系。 因变量&#xff1a; 影响目标变量的因素&#xff1a; X 1 , X 2 . . . X_1, X_2... X1​,X2​... &#xff0c;连续值或离散值。 目标变量&#xff1a; 需要预测的值: t…

智慧未来已至:人工智能与数字孪生共筑城市新纪元

随着科技的飞速发展&#xff0c;人工智能与数字孪生技术正逐步成为智慧城市建设的核心驱动力。 这两项技术的结合&#xff0c;不仅将彻底改变城市的传统面貌&#xff0c;更将引领我们走向一个更加高效、便捷、绿色的未来。 一、智慧城市的新内涵 智慧城市&#xff0c;是指在城…

DDoS攻击:分布式拒绝服务攻击的威胁与对策

DDoS攻击&#xff1a;分布式拒绝服务攻击的威胁与对策 随着互联网的快速发展&#xff0c;网络安全威胁也在不断增加。其中&#xff0c;分布式拒绝服务攻击&#xff08;DDoS&#xff09;是一种常见且具有破坏性的攻击方式&#xff0c;给个人用户、企业和组织的网络基础设施带来了…

爬虫工作量由小到大的思维转变---<第四十五章 Scrapyd 关于gerapy遇到问题>

前言: 本章主要是解决一些gerapy遇到的问题,会持续更新这篇! 正文: 问题1: 1400 - build.py - gerapy.server.core.build - 78 - build - error occurred (1, [E:\\项目文件名\\venv\\Scripts\\python.exe, setup.py, clean, -a, bdist_uberegg, -d, C:\\Users\\Administrat…

React进阶 - 15(React 中 ref 的使用)

本章内容 目录 一、e.target 获取事件对应“元素”的DOM节点二、ref三、ref 和 setState 合用 上一节我们了解了 React中的”虚拟DOM“中的”Diff算法““ &#xff0c;本节我们来说一说 React中 ref的使用 一、e.target 获取事件对应“元素”的DOM节点 打开之前工程中的 To…

elasticsearch重置密码操作

安装es的时候需要测试这个url&#xff1a;http://127.0.0.1:9200/ 出现弹窗让我输入账号和密码。我第一次登录&#xff0c;没有设置过账号和密码&#xff0c; 解决方法是&#xff1a;在es的bin目录下打开cmd窗口&#xff0c;敲命令&#xff1a;.\elasticsearch-reset-password…

给mysql设置时区

每次重启MySQL服务器后&#xff0c;使用IDEA的database navigator连接都会出现这种情况 解决方式就是 命令行登录后 set global time_zone 8:00;嘿嘿把之前自家简书文章 给mysql设置时区 搬运过来了&#xff0c;方便查阅

Zookeeper相关面试准备问题

Zookeeper介绍 Zookeeper从设计模式角度来理解&#xff0c;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心的数据&#xff0c;然后接受观察者的注册&#xff0c;一旦这些数据的状态发生了变化&#xff0c;Zookeeper就负责通知已经在Zoo…