【网络编程】实现一个简单多线程版本TCP服务器(附源码)

00

TCP多线程

  • 🌵预备知识
    • 🎄 Accept函数
    • 🌲字节序转换函数
    • 🌳listen函数
  • 🌴代码
    • 🌱Log.hpp
    • 🌿Makefile
    • ☘️TCPClient.cc
    • 🍀TCPServer.cc
    • 🎍 util.hpp

🌵预备知识

🎄 Accept函数

accept 函数是在服务器端用于接受客户端连接请求的函数,它在监听套接字上等待客户端的连接,并在有新的连接请求到来时创建一个新的套接字用于与该客户端通信。

  • 下面是 accept 函数的详细介绍以及各个参数的意义:
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd: 是服务器监听套接字的文件描述符,通常是使用 socket 函数创建的套接字。accept 函数在该套接字上等待连接请求。

addr: 是一个指向 struct sockaddr 类型的指针,用于存储客户端的地址信息。当新连接建立成功后,客户端的地址信息将会被填充到这个结构体中。

addrlen: 是一个指向 socklen_t 类型的指针,它指示 addr 结构体的长度。在调用 accept 函数之前,需要将其初始化为 addr 结构体的大小,函数执行后会更新为实际的客户端地址长度。

返回值:如果连接成功建立,accept 函数将返回一个新的文件描述符,该文件描述符用于与客户端进行通信。如果连接失败,函数将返回 -1,并设置 errno 以指示错误原因。

  • accept 函数的工作原理如下:

当服务器的监听套接字接收到一个新的连接请求时,accept 函数会创建一个新的套接字用于与该客户端通信。
新的套接字会继承监听套接字的监听属性,包括 IP 地址、端口等。
accept 函数会填充 addr 结构体,以便获取客户端的地址信息。
服务器可以使用返回的新套接字与客户端进行通信。

  • 注意事项:

accept 函数在没有连接请求时会阻塞,直到有新的连接请求到来。
如果希望设置非阻塞模式,可以使用 fcntl 函数设置 O_NONBLOCK 属性。
在多线程或多进程环境下,需要注意 accept 函数的线程安全性,可以使用互斥锁等机制来保护。
综上所述,accept 函数在构建服务器程序时非常重要,它使服务器能够接受客户端的连接请求并创建新的套接字与客户端进行通信。

🌲字节序转换函数

在网络编程中,字节序问题很重要,因为不同的计算机体系结构可能使用不同的字节序,这可能导致在通信过程中的数据解释错误。为了在不同体系结构之间正确传递数据,需要进行字节序的转换。

  • 以下是一些常用的字节序转换函数:

ntohl 和 htonl: 这些函数用于 32 位整数的字节序转换。ntohl 用于将网络字节序转换为主机字节序,htonl 则相反,将主机字节序转换为网络字节序。

ntohs 和 htons: 这些函数用于 16 位整数的字节序转换。ntohs 用于将网络字节序转换为主机字节序,htons 则相反,将主机字节序转换为网络字节序。

这些函数通常用于在网络编程中处理套接字通信中的数据转换,以确保在不同平台上的正确数据交换。

  • 示例
#include <arpa/inet.h>int main() {uint32_t networkValue = 0x12345678;uint32_t hostValue = ntohl(networkValue); // 0x78563412 on a little-endian hostuint32_t convertedValue = htonl(hostValue); // 0x12345678 on a little-endian hostuint16_t networkPort = 0x1234;uint16_t hostPort = ntohs(networkPort); // 0x3412 on a little-endian hostuint16_t convertedPort = htons(hostPort); // 0x1234 on a little-endian hostreturn 0;
}

请注意,在使用这些函数时,需要包含 <arpa/inet.h> 头文件。这些函数通常在网络编程中用于正确处理字节序问题,以确保不同平台之间的数据传输正确。

🌳listen函数

在TCP通信中,服务端需要使用 listen 函数来监听连接请求。这是因为TCP是一种面向连接的协议,它采用客户端-服务端模型进行通信,通信双方需要先建立连接,然后进行数据的传输。监听的过程是为了等待客户端发起连接请求。

  • 具体原因如下:

建立连接: 在TCP通信中,通信双方需要通过三次握手建立连接。客户端通过 connect 函数向服务器发起连接请求,而服务端则需要通过 listen 函数来准备接收连接请求。

处理并发连接: 服务端可能会同时接收多个客户端的连接请求,而每个连接都需要为其分配一个独立的套接字。通过监听连接请求,服务端可以在一个循环中接受多个连接,为每个连接创建对应的套接字,从而实现并发处理多个客户端。

连接队列: listen 函数将连接请求存储在一个队列中,等待服务端逐个接受。这个队列称为“未完成连接队列”(backlog queue)。如果连接请求过多,超出了队列的长度,那么新的连接请求可能会被拒绝或被丢弃。

连接参数: listen 函数还可以指定一个参数,表示在未完成连接队列中可以容纳的连接请求数量。这个参数可以影响服务端处理并发连接的能力。

总之,TCP监听是为了等待客户端发起连接请求,建立连接,然后实现双方的数据传输。这种机制允许服务器处理多个客户端连接,实现高并发的网络服务。

  • 函数原型:
int listen(int sockfd, int backlog);
  • 参数说明:

sockfd:要进行监听的套接字描述符。
backlog:表示在未完成连接队列中可以容纳的连接请求数量。这个参数可以影响服务器处理并发连接的能力。通常情况下,系统会为这个值设置一个默认的最大值,但你也可以根据你的需求进行适当调整。
返回值:
如果函数调用成功,返回 0。
如果出现错误,返回 -1,并设置全局变量 errno 来指示错误类型。

使用步骤:

创建套接字并绑定地址。
调用 listen 函数将套接字标记为被动套接字,开始监听连接请求。
使用 accept 函数接受客户端连接请求,建立实际的连接。

  • 示例用法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>int main() {int listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock == -1) {perror("socket");exit(EXIT_FAILURE);}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8080);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("bind");exit(EXIT_FAILURE);}if (listen(listen_sock, 5) == -1) { // 开始监听,最多允许5个未完成连接perror("listen");exit(EXIT_FAILURE);}// 现在可以使用 accept 函数接受连接请求并建立连接close(listen_sock);return 0;
}

注意:listen 后的套接字仅能用于接受连接请求,不能用于读写数据。接收到的连接请求将在一个队列中等待,直到使用 accept 函数从队列中取出并建立连接。

🌴代码

🌱Log.hpp

#pragma once#include <cstdio>
#include <ctime>
#include <cstdarg>
#include <cassert>
#include <cstring>
#include <cerrno>
#include <stdlib.h>#define DEBUG 0
#define NOTICE 1
#define WARINING 2
#define FATAL 3const char *log_level[]={"DEBUG", "NOTICE", "WARINING", "FATAL"};// logMessage(DEBUG, "%d", 10);
void logMessage(int level, const char *format, ...)
{assert(level >= DEBUG);assert(level <= FATAL);char *name = getenv("USER");char logInfo[1024];va_list ap; // ap -> char*va_start(ap, format);vsnprintf(logInfo, sizeof(logInfo)-1, format, ap);va_end(ap); // ap = NULLFILE *out = (level == FATAL) ? stderr:stdout;fprintf(out, "%s | %u | %s | %s\n", \log_level[level], \(unsigned int)time(nullptr),\name == nullptr ? "unknow":name,\logInfo);// char *s = format;// while(s){//     case '%'://         if(*(s+1) == 'd')  int x = va_arg(ap, int);//     break;// }
}

🌿Makefile

.PHONY:all
all:TCPClient TCPServerTCPClient: TCPClient.ccg++ -o $@ $^ -std=c++11 -lpthread
TCPServer:TCPServer.ccg++ -o $@ $^ -std=c++11 -lpthread.PHONY:clean
clean:rm -f TCPClient TCPServer

☘️TCPClient.cc

#include"util.hpp"
volatile bool quit=false;
static void Usage(std::string proc)
{std::cerr<<"Usage:\n\t"<<proc<<"serverip serverport "<<std::endl;std::cerr<<"Example:\n\t"<<proc<<"127.0.0.1 8080\n"<<std::endl;
}int main(int argc,char *argv[])
{if(argc!=3){Usage(argv[0]);exit(USAGE_ERR);}std::string serverip=argv[1];uint16_t serverport=atoi(argv[2]);//1.创建socket SOCK_STREAMint sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){std::cerr<<"socket :"<<strerror(errno)<<std::endl;exit(SOCKET_ERR);}//2.链接 //向服务器发起链接请求struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family=AF_INET;server.sin_port=htons(server.sin_port);inet_aton(serverip.c_str(),&server.sin_addr);//2.2发起请求 connect自动会进行bindif(connect(sock,(const struct sockaddr*)&server,sizeof(server))!=0){//链接失败std::cerr<<"connect :"<<strerror(errno)<<std::endl;exit(CONN_ERR);}//链接成功std::cout<<" info :connect success :"<<sock<<std::endl;std::string message;while(!quit){message.clear();std::cout<<"请输入您的消息>>>>"<<std::endl;std::getline(std::cin,message);if(strcasecmp(message.c_str(),"quit")==0){//如果输入的是quit 直接退出程序quit=true; //设置成true 会把当前信息先执行发送到服务器 再进入while循环时条件不满直接退出}//从服务器接收到的消息ssize_t s=write(sock,message.c_str(),message.size());if(s>0){message.resize(1024);ssize_t s=read(sock,(char *)(message.c_str()),1024);if(s>0)message[s]=0;std::cout<<"Server Echo>>>"<<"message"<<std::endl;}else if (s <= 0){break;}}close(sock);return 0;
}

🍀TCPServer.cc

#include "util.hpp"
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
class ServerTcp;//先声明class ThreadData
{public:uint16_t clientPort_;//客户端端口号std::string clientip_;//客户端ipint sock_;ServerTcp *this_;ThreadData(uint16_t port, std::string ip, int sock,  ServerTcp *ts): clientPort_(port), clientip_(ip), sock_(sock),this_(ts){}};class ServerTcp
{public://构造和和析构函数ServerTcp(uint16_t port,const std::string &ip=""):port_(port),ip_(ip),listenSock_(-1){}~ServerTcp(){}public://初始化函数void init(){//第一步:创建套接字listenSock_=socket(PF_INET,SOCK_STREAM,0);if(listenSock_<0){//创建失败logMessage(FATAL,"socket:%s",strerror(errno)); //用日志打印错误信息exit(SOCKET_ERR);}//创建成功logMessage(DEBUG,"sockt:%s,%d",strerror(errno),listenSock_);//第二步 bind绑定//2.1填充服务器信息struct sockaddr_in local;memset(&local,0,sizeof(local));//设置0?/*可以确保将所有这些字段初始化为零,以避免在实际使用过程中出现未定义行为或不可预测的结果。*/local.sin_family=AF_INET;   /*如果 ip_ 为空,服务器将绑定到任意可用的本地IP地址。如果 ip_ 不为空,服务器将绑定到 ip_ 所代表的具体IP地址。*/ip_.empty()?(local.sin_addr.s_addr)=htons(INADDR_ANY):(inet_aton(ip_.c_str(),&local.sin_addr));//2.2if(bind(listenSock_,(const struct sockaddr*)&local,sizeof local)<0)//{//bind绑定失败logMessage(FATAL,"bind:%s",strerror(errno));exit(BIND_ERR);}//绑定成功logMessage(DEBUG,"bind:%S,%d",strerror(errno),listenSock_);//3.监听socketif(listen(listenSock_,5)<0){logMessage(FATAL,"listen:%s",strerror(errno));exit(LISTEN_ERR);}//监听成功logMessage(DEBUG,"listen:%S,%d",strerror(errno),listenSock_);//到这一步就等待运行 等待客户端链接}static void *threadRoutine(void *args){pthread_detach(pthread_self()); //设置线程分离ThreadData *td = static_cast<ThreadData*>(args);td->this_->tranService(td->sock_, td->clientip_, td->clientPort_);delete td;return nullptr;}//加载void loop(){while(true){struct sockaddr_in peer;socklen_t len=sizeof(peer);//获取链接 accept返回值??int serviceSock=accept(listenSock_,(struct sockaddr*)&peer,&len);if(serviceSock<0){//获取连接失败logMessage(WARINING,"Accept :%S[%d]",strerror(errno),serviceSock);continue;//获取失败 继续接收....}//获取客户端的基本信息 存储起来uint16_t peerPort=ntohs(peer.sin_port);std::string peerip=inet_ntoa(peer.sin_addr);//打印一下获取的客户端信息logMessage(DEBUG,"Aceept :%s|%s[%d],socket fd :%d",strerror(errno),peerip.c_str(),peerPort,serviceSock);// 5 提供服务, echo -> 小写 -> 大写// 5.0 v0 版本 -- 单进程 -- 一旦进入transService,主执行流,就无法进行向后执行,只能提供完毕服务之后才能进行accept// transService(serviceSock, peerIp, peerPort);// 5.1 v1 版本 -- 多进程版本 -- 父进程打开的文件会被子进程继承吗?会的// pid_t id = fork();// assert(id != -1);// if(id == 0)// {//     close(listenSock_); //建议//     //子进程//     transService(serviceSock, peerIp, peerPort);//     exit(0); // 进入僵尸// }// // 父进程// close(serviceSock); //这一步是一定要做的!// 5.1 v1.1 版本 -- 多进程版本  -- 也是可以的// 爷爷进程// pid_t id = fork();// if(id == 0)// {//     // 爸爸进程//     close(listenSock_);//建议//     // 又进行了一次fork,让 爸爸进程//     if(fork() > 0) exit(0);//     // 孙子进程 -- 就没有爸爸 -- 孤儿进程 -- 被系统领养 -- 回收问题就交给了系统来回收//     transService(serviceSock, peerIp, peerPort);//     exit(0);// }// // 父进程// close(serviceSock); //这一步是一定要做的!// // 爸爸进程直接终止,立马得到退出码,释放僵尸进程状态// pid_t ret = waitpid(id, nullptr, 0); //就用阻塞式// assert(ret > 0);// (void)ret;// 5.2 v2 版本 -- 多线程// 这里不需要进行关闭文件描述符吗??不需要啦// 多线程是会共享文件描述符表的!ThreadData *td = new ThreadData(peerPort, peerip, serviceSock, this);pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)td);// waitpid(); 默认是阻塞等待!WNOHANG// 方案1// logMessage(DEBUG, "server 提供 service start ...");// sleep(1);}}//提供服务函数 -----> 大小写转换void tranService(int sock,const std::string &clientip,uint16_t clientPort){assert(sock>=0);assert(!clientip.empty());assert(clientPort>=1024); //1~~1024端口为系统端口 不可轻易更改char inbuffer[BUFFER_SIZE];while(true){ssize_t s=read(sock,inbuffer,sizeof(inbuffer)-1); //-1是给\0留出一个位置if(s>0){inbuffer[s]='0';if(strcasecmp(inbuffer,"quit")==0){logMessage(DEBUG,"client quit----------%s[%d]",clientip.c_str(),clientPort);break;}logMessage(DEBUG,"Treans Before:%s[%d]>>>%s",clientip.c_str(),clientPort,inbuffer);//进行大小写转换for(int i=0;i<s;i++){if(isalpha(inbuffer[i])&&islower(inbuffer[i])){inbuffer[i]=toupper(inbuffer[i]);}}logMessage(DEBUG,"Trans after:%s[%d]>>>>%s",clientip.c_str(),clientPort,inbuffer);write(sock,inbuffer,strlen(inbuffer));//给客户端发送回去}else if(s==0){// pipe: 读端一直在读,写端不写了,并且关闭了写端,读端会如何?s == 0,代表对端关闭// s == 0: 代表对方关闭,client 退出logMessage(DEBUG, "client quit -- %s[%d]", clientip.c_str(), clientPort);break;}else{logMessage(DEBUG, "%s[%d] - read: %s", clientip.c_str(), clientPort, strerror(errno));break;}}// 只要走到这里,一定是client退出了,服务到此结束close(sock); // 如果一个进程对应的文件fd,打开了没有被归还,文件描述符泄漏!logMessage(DEBUG, "server close %d done", sock);}private:// sockint listenSock_;// portuint16_t port_;// ipstd::string ip_;
};static void Usage(std::string proc)
{std::cerr << "Usage:\n\t" << proc << " port ip" << std::endl;std::cerr << "example:\n\t" << proc << " 8080 127.0.0.1\n" << std::endl;}// ./ServerTcp local_port local_ip
int main(int argc, char *argv[])
{if(argc != 2 && argc != 3 ){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);std::string ip;if(argc == 3) ip = argv[2];ServerTcp svr(port, ip);svr.init();svr.loop();return 0;
}

🎍 util.hpp

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <ctype.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"#define SOCKET_ERR 1
#define BIND_ERR   2
#define LISTEN_ERR 3
#define USAGE_ERR  4
#define CONN_ERR   5#define BUFFER_SIZE 1024

大家可以拉下来自行测试…

🎋 🍃 🍂 🍁 🍄 🐚 💐 🌷 🌹 🥀 🌺 🌸 🌼 🌻

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

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

相关文章

Nevron Vision for .NET Crack

Nevron Vision for .NET Crack NET Vision是一个用于创建具有数据可视化功能的强大数据表示应用程序的套件。该套件具有用于.NET的Nevron Chart、用于.NET的Nevron Diagram和用于.NET的Nevron User Interface。精心设计的对象模型、众多功能和高质量的演示使复杂数据的可视化变…

【Linux操作系统】深入理解Linux系统编程中的open函数

在Linux系统编程中&#xff0c;open函数是一个非常重要的系统调用函数&#xff0c;它用于打开或创建一个文件&#xff0c;并返回一个文件描述符。本文将详细介绍open函数的用法&#xff0c;并给出具体的代码示例。 文章目录 1. 函数原型2. 函数参数2.1 flags参数2.2 mode参数 …

编译Linux的时候出现 Restart config...

环境 buildroot 问题 在buildroot路径下执行 make linux-rebuild 出现了Restart config… 的问题 原因 我在修改linux源码的时候&#xff0c;在没有指定ARCH架构的情况下&#xff0c; 直接去linux源码目录(output/build/linux/)下执行了 make menuconfig并覆盖了原来的.c…

使用jasypt对Spring Boot配置文件中的配置项加密

在Spring Boot中&#xff0c;有很多口令需要加密&#xff0c;如数据库连接密码、访问第三方接口的Token等。常见的方法就是用jasypt对口令进行加密。 实际上&#xff0c;jasypt可以对配置文件中任意配置项的值进行加密&#xff0c;不局限于对密码的加密。 1.在pom.xml中添加ja…

wpf 3d 坐标系和基本三角形复习

wpf 3d 坐标系的描述见此&#xff0c; WPF 3d坐标系和基本三角形_wpf 坐标系_bcbobo21cn的博客-CSDN博客 X轴正向向右&#xff0c;Y轴正向向上&#xff1b;Z轴&#xff0c;正向是从屏幕里边出来&#xff0c;负向是往屏幕里边去&#xff1b;坐标原点是在呈现区域的中心&#x…

元宇宙核能发电VR模拟仿真实训教学为建设新型电力系统提供重要支撑

随着“碳达峰、碳中和”目标与建设新型能源体系的提出&#xff0c;在元宇宙环境下建设电力系统是未来发展的趋势。以物联网、区块链、数字孪生、混合现实等技术为主要代表的元宇宙技术体系及其在电力和能源系统中的应用&#xff0c;将会促进智能电网的发展&#xff0c;为建设新…

编码技巧——Sentinel的blockHandler与fallback

本文介绍Sentinel的blockHandler与fallback的区别&#xff0c;背景是&#xff1a;发生限流时&#xff0c;配置的sentinel的blockhandler没有生效而fallback生效了&#xff1b;排查原因&#xff0c;从而给出Sentinel配置异常降级和限流降级的代码写法&#xff1b; 在查看源码前…

【Java】数据交换 Json 和 异步请求 Ajax

&#x1f384;欢迎来到边境矢梦的csdn博文&#xff0c;本文主要讲解Java 中 数据交换和异步请求 Json&Ajax 的相关知识&#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一下&#…

端口输入的数据为什么要打拍?

一次作者在开发图像时候&#xff0c;对输入的图像没有打拍&#xff0c;直接输出给显示终端&#xff0c;时好时坏&#xff0c;或者图像颜色不正确&#xff0c;最终经过打拍解决了此问题。 //配置为16-Bit SDR ITU-R BT.656模式时pixel_data[23:16]为高阻。always (posedge pixe…

相关C语言易错点

四区 我们想来介绍一下内存四区 栈区 局部变量&#xff0c;局部常量 空间自动开辟和释放&#xff0c;只能作用于局部&#xff0c;函数不能返回局部变量的空间地址 堆区 malloc,realloc,free 手动开辟&#xff0c;手动释放&#xff0c;如果不手动释放&#xff0c;那么会在程…

【图论】单源最短路

算法提高课笔记。&#xff08;本篇还未更新完… 目录 单源最短路的建图方式例题热浪题意思路代码 信使题意思路代码 香甜的黄油题意思路代码 最小花费题意思路代码 最优乘车题意思路代码 昂贵的聘礼题意思路代码 单源最短路的建图方式 最短路问题可以分为以下两类&#xff1a…

Linux命令200例:tree用于以树状结构显示文件和目录

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

【前端】CSS垂直居中的7种方法

文章目录 line-height绝对定位margin:autoflex绝对定位margin:负值定位transformvertical-align:middledisplay:table-cell思维导图 前文&#xff1a;【前端】CSS水平居中的6种方法_karshey的博客-CSDN博客 有很多相似的部分。 line-height 适用于单行的行内元素设置line-he…

2005-2020年280个地级市绿色全要素生产率测算原始数据

2005-2020年280个地级市绿色全要素生产率测算原始数据 1、时间&#xff1a;2005-2020年 2、来源&#xff1a;中国城市统计年鉴、中国区域统计年鉴、中国能源年鉴、中国环境年鉴等 3、范围&#xff1a;280个地级市 4、指标&#xff1a;年末单位从业人员数、规模以上工业企业…

9.1网络通信基础

一.基础概念: 1)IP地址:描述网络上的一个设备所在的位置. 2)端口号(port):区分一个主机上不同的进程,和pid一样的作用,但两者不同. 3)协议:网络通信传输数据的含义,协议表示一种约定,这种约定可以是任意的.协议分层之后,上层不需要知道下层协议的细节,可以灵活地调整,替换某…

LVS/DR+Keepalived负载均衡实战(一)

引言 负载均衡这个概念对于一个IT老鸟来说再也熟悉不过了&#xff0c;当听到此概念的第一反应是想到举世闻名的nginx&#xff0c;但殊不知还有一个大名鼎鼎的负载均衡方案可能被忽略了&#xff0c;因为对于一般系统来说&#xff0c;很多应用场合中采用nginx基本已经满足需求&a…

【Kubernetes】Kubernetes之kubectl详解

kubectl 一、陈述式资源管理1. 陈述式资源管理方法2. 基本信息查看3. 项目周期管理3.1 创建 kubectl create 命令3.2 发布 kubectl expose命令3.3 更新 kubectl set3.4 回滚 kubectl rollout3.5 删除 kubectl delete 4. kubectl 的发布策略4.1 蓝绿发布4.2 红黑发布4.3 灰度发布…

Docker安装 Kibana

目录 前言安装Kibana步骤1&#xff1a;准备1. 安装docker2. 搜索可以使用的镜像。3. 也可从docker hub上搜索镜像。4. 选择合适的redis镜像。 步骤2&#xff1a;拉取 kibana 镜像拉取镜像查看已拉取的镜像 步骤3&#xff1a;创建容器创建容器方式1&#xff1a;快速创建容器 步骤…

Mongodb:业务应用(2)

需求&#xff1a; 1、获取保存到mongodb库中的搜索记录列表 2、实现删除搜索记录接口 保存搜索记录数据参考上篇Mongodb&#xff1a;业务应用&#xff08;1&#xff09;_Success___的博客-CSDN博客 获取记录列表 1、创建controller package com.heima.search.controller.v1;…

【软考】2023系统架构设计师考试

目录 1 软考资格设置 2 考试报名 3 考试准备 4 参加考试 5 考试感受 6 其他 1 软考资格设置 2 考试报名 报名网址&#xff1a;https://www.ruankao.org.cn/ 3 考试准备 4 参加考试 2023年下半年系统架构设计师考试时间为11月4、5日。 5 考试感受 6 其他 最近好像有地区…