C++负载均衡远程调用学习之TCP连接封装与TCPCLIENT封装
目录
1.LARSV0.3回顾
2.解决粘包问题的message结构体定义
3.LARSV0.4链接对象的方法和属性的定义
4.LARSv0.4 TCP_conn链接的初始化
5.LARV0.4-tcp_conn处理读事件方法do_read
6.LARV0.4-tcp_conn模块回顾
7.LARV0.4-tcp_send_message主动发包实现
8.LARV0.4-tcp_conn处理写时间方法do_write
9.LARV0.4-tcp_server创建tcp_conn对象
10.LARV0.4-tcp_conn连接封装流程总结
11.LARV0.4-tcp_client属性和方法分析
12.LARV0.4-tcp_client非阻塞客户端套接字创建
13.LARV0.4-tcp_client读写方法实现
14.LARV0.4-tcp_client模块客户端实现问题
15.LARV0.4-tcp_client_send_message无效问题解决
16.LARV0.3-event_loop添加时间buf修复
17.LARV0.4复习
1.LARSV0.3回顾
2.解决粘包问题的message结构体定义
接下来是客户端,客户端我们创建一个Qps结构体,来记录每秒,服务端成功回显数据的次数,来做qps统计,客户端我们可以指定开多少个线程去压测服务端。
> example/qps_test/client.cpp
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <string>
#include "tcp_client.h"
#include "echoMessage.pb.h"
struct Qps
{
Qps() {
last_time = time(NULL);
succ_cnt = 0;
}
long last_time;//最后一次发包时间 ms为单位
int succ_cnt; //成功收到服务器回显的次数
};
//客户端业务
void busi(const char *data, uint32_t len, int msgid, net_connection *conn, void *user_data)
{
Qps *qps = (Qps*)user_data; //用户参数
qps_test::EchoMessage request, response;
//解析服务端传来的pb数据
if (response.ParseFromArray(data, len) == false) {
printf("server call back data error\n");
return;
}
//判断数据内容是否回显一致
if (response.content() == "Hello Lars!!!") {
//服务器请求响应成功一次
qps->succ_cnt ++;
}
long current_time = time(NULL);
if (current_time - qps->last_time >= 1) {
//如果当前时间比最后记录时间大于1秒,那么我们进行记录
printf("---> qps = %d <----\n", qps->succ_cnt);
qps->last_time = current_time;//记录最后时间
qps->succ_cnt = 0;//清空成功次数
}
//给服务端发送新的请求
request.set_id(response.id() + 1);
request.set_content(response.content());
std::string requestString;
request.SerializeToString(&requestString);
conn->send_message(requestString.c_str(), requestString.size(), msgid);
}
3.LARSV0.4链接对象的方法和属性的定义
//创建链接成功之后
void connection_start(net_connection *client, void *args)
{
qps_test::EchoMessage request;
request.set_id(1);
request.set_content("Hello Lars!!!");
std::string requestString;
request.SerializeToString(&requestString);
int msgid = 1;//与server端的消息路由一致
client->send_message(requestString.c_str(), requestString.size(), msgid);
}
void *thread_main(void *args)
{
//给服务端发包
event_loop loop;
tcp_client client(&loop, "127.0.0.1", 7777, "qps client");
Qps qps;
//设置回调
client.add_msg_router(1, busi, (void*)&qps);
//设置链接创建成功之后Hook
client.set_conn_start(connection_start);
loop.event_process();
return NULL;
}
int main(int argc, char **argv)
{
if (argc == 1) {
printf("Usage: ./client [threadNum]\n");
return 1;
}
//创建N个线程
int thread_num = atoi(argv[1]);
pthread_t *tids;
tids = new pthread_t[thread_num];
for (int i = 0; i < thread_num; i++) {
pthread_create(&tids[i], NULL, thread_main, NULL);
}
for (int i = 0; i < thread_num; i++) {
pthread_join(tids[i], NULL);
}
return 0;
}
```
4.LARSv0.4 TCP_conn链接的初始化
接下来我们的Makefile
```makefile
CXX=g++
CFLAGS=-g -O2 -Wall -fPIC -Wno-deprecated
INC=-I../../include
LIB=-L../../lib -llreactor -lpthread -lprotobuf
OBJS = $(addsuffix .o, $(basename $(wildcard *.cc)))
all:
$(CXX) -o server $(CFLAGS) server.cpp echoMessage.pb.cc $(INC) $(LIB)
$(CXX) -o client $(CFLAGS) client.cpp echoMessage.pb.cc $(INC) $(LIB)
clean:
-rm -f *.o server client
```
记住编译加上`-lprotobuf` 编译的文件加上`echoMessage.pb.cc`文件。
5.LARV0.4-tcp_conn处理读事件方法do_read
## 14.2 并发测试结果
启动服务端,再启动客户端,(注意问了方便看结果,将之前reactor的一些stdout的调试日志都关闭)
看客户端运行结果
```bash
$ ./client 1
msg_router init...
do_connect EINPROGRESS
add msg cb msgid = 1
connect 127.0.0.1:7777 succ!
---> qps = 6875 <----
---> qps = 8890 <----
---> qps = 8354 <----
---> qps = 9078 <----
---> qps = 8933 <----
---> qps = 9043 <----
---> qps = 8743 <----
---> qps = 9048 <----
---> qps = 8987 <----
---> qps = 8742 <----
---> qps = 8720 <----
---> qps = 8795 <----
---> qps = 8889 <----
---> qps = 9034 <----
---> qps = 8669 <----
---> qps = 9001 <----
---> qps = 8810 <----
---> qps = 8850 <----
---> qps = 8802 <----
---> qps = 9072 <----
---> qps = 8853 <----
---> qps = 8804 <----
---> qps = 8574 <----
---> qps = 8645 <----
---> qps = 8085 <----
---> qps = 8720 <----
...
```
这里我们客户端是开启一个线程进行测试,平均每秒服务端会响应8700次左右。
这里我简单用两个主机,分别测试了一些数据
**主机1:**
> CPU个数:2个 , 内存: 2GB , 系统:Ubuntu18.04虚拟机
| 线程数 | QPS |
| ------ | ------- |
| 1 | 0.85w/s |
| 2 | 1.96w/s |
| 10 | 4.12w/s |
| 100 | 4.23w/s |
| 500 | 3.65w/s |
**主机2:**
> CPU个数: 24个 , 内存:128GB, 系统: 云主机
| 线程数 | QPS |
| ------ | -------- |
| 1 | 10.86w/s |
| 3 | 31.06w/s |
| 5 | 48.12w/s |
| 8 | 59.79w/s |
现在我们的Lars-Reactor模块基本已经开完完成了,后续可能会再添加一些模块,为了其他模块方便使用Lars-Reactor的一些接口,我们可以对外提供一个公共的头文件,方便一次性导入
> lars_reactor/include/lars_reactor.h
```c
#pragma once
#include "io_buf.h"
#include "buf_pool.h"
#include "reactor_buf.h"
#include "net_connection.h"
#include "tcp_conn.h"
#include "tcp_server.h"
#include "tcp_client.h"
#include "udp_server.h"
#include "udp_client.h"
#include "message.h"
#include "task_msg.h"
#include "event_loop.h"
#include "thread_pool.h"
#include "thread_queue.h"
#include "config_file.h"
```
6.LARV0.4-tcp_conn模块回顾
## 15) 异步消息任务机制
我们之前在`include/task_msg.h`中, 其中task的消息类型我们只是实现了`NEW_CONN`,目的是`thread_pool`选择一个线程,让一个线程里的`thread_queue`去创建一个连接对象。但是并没有对`NEW_TASK`的任务类型进行定义。这种类型是允许服务端去执行某项具体的业务。并不是根据客户端来消息去被动回复的业务,而是服务端主动发送的业务给到客户端。
### 15.1 任务函数类型
我们先定义task的回调函数类型
> lars_reactor/include/event_loop.h
```c
//...
//定义异步任务回调函数类型
typedef void (*task_func)(event_loop *loop, void *args);
//...
```
为了防止循环头文件引用,我们把typedef定义在`event_loop.h`中。
> lars_reactor/include/task_msg.h
```c
#pragma once
#include "event_loop.h"
//定义异步任务回调函数类型
typedef void (*task_func)(event_loop *loop, void *args);
struct task_msg
{
enum TASK_TYPE
{
NEW_CONN, //新建链接的任务
NEW_TASK, //一般的任务
};
TASK_TYPE type; //任务类型
//任务的一些参数
union {
//针对 NEW_CONN新建链接任务,需要传递connfd
int connfd;
//针对 NEW_TASK 新建任务,
//可以给一个任务提供一个回调函数
struct {
task_func task_cb; //注册的任务函数
void *args; //任务函数对应的形参
};
};
};
```
`task_func`是我们定义的一个任务的回调函数类型,第一个参数当然就是让哪个loop机制去执行这个task任务。很明显,一个loop是对应一个thread线程的。也就是让哪个thread去执行这个task任务。args是`task_func`的函数形参。
7.LARV0.4-tcp_send_message主动发包实现
### 15.2 event_loop模块添加task任务机制
我们知道,task绑定一个loop,很明显,一个`event_loop`应该拥有需要被执行的task集合。
在这里,我们将event_loop加上已经就绪的task任务的属性
> lars_reactor/include/event_loop.h
```c
#pragma once
/*
*
* event_loop事件处理机制
*
* */
#include <sys/epoll.h>
#include <ext/hash_map>
#include <ext/hash_set>
#include <vector>
#include "event_base.h"
#include "task_msg.h"
#define MAXEVENTS 10
// map: fd->io_event
typedef __gnu_cxx::hash_map<int, io_event> io_event_map;
//定义指向上面map类型的迭代器
typedef __gnu_cxx::hash_map<int, io_event>::iterator io_event_map_it;
//全部正在监听的fd集合
typedef __gnu_cxx::hash_set<int> listen_fd_set;
//定义异步任务回调函数类型
typedef void (*task_func)(event_loop *loop, void *args);
class event_loop
{
public:
//构造,初始化epoll堆
event_loop();
//阻塞循环处理事件
void event_process();
//添加一个io事件到loop中
void add_io_event(int fd, io_callback *proc, int mask, void *args=NULL);
//删除一个io事件从loop中
void del_io_event(int fd);
//删除一个io事件的EPOLLIN/EPOLLOUT
void del_io_event(int fd, int mask);
// ===========================================
//获取全部监听事件的fd集合
void get_listen_fds(listen_fd_set &fds) {
fds = listen_fds;
}
//=== 异步任务task模块需要的方法 ===
//添加一个任务task到ready_tasks集合中
void add_task(task_func func, void *args);
//执行全部的ready_tasks里面的任务
void execute_ready_tasks();
// ===========================================
private:
int _epfd; //epoll fd
//当前event_loop 监控的fd和对应事件的关系
io_event_map _io_evs;
//当前event_loop 一共哪些fd在监听
listen_fd_set listen_fds;
//一次性最大处理的事件
struct epoll_event _fired_evs[MAXEVENTS];
// ===========================================
//需要被执行的task集合
typedef std::pair<task_func, void*> task_func_pair;
std::vector<task_func_pair> _ready_tasks;
// ===========================================
};
```
添加了两个属性:
`task_func_pair`: 回调函数和参数的键值对.
`_ready_tasks`: 所有已经就绪的待执行的任务集合。
8.LARV0.4-tcp_conn处理写时间方法do_write
同时添加了两个主要方法:
`void add_task(task_func func, void *args)`: 添加一个任务到_ready_tasks中.
`void execute_ready_tasks()`:执行全部的_ready_tasks任务。
将这两个方法实现如下:
> lars_reactor/src/event_loop.cpp
```c
//...
//添加一个任务task到ready_tasks集合中
void event_loop::add_task(task_func func, void *args)
{
task_func_pair func_pair(func, args);
_ready_tasks.push_back(func_pair);
}
//执行全部的ready_tasks里面的任务
void event_loop::execute_ready_tasks()
{
std::vector<task_func_pair>::iterator it;
for (it = _ready_tasks.begin(); it != _ready_tasks.end(); it++) {
task_func func = it->first;//任务回调函数
void *args = it->second;//回调函数形参
//执行任务
func(this, args);
}
//全部执行完毕,清空当前的_ready_tasks
_ready_tasks.clear();
}
//...
```
那么`execute_ready_tasks()`函数需要在一个恰当的时候被执行,我们这里就放在每次event_loop一次`epoll_wait()`处理完一组fd事件之后,触发一次额外的task任务。
> lars_reactor/src/event_loop.cpp
```c
//阻塞循环处理事件
void event_loop::event_process()
{
while (true) {
io_event_map_it ev_it;
int nfds = epoll_wait(_epfd, _fired_evs, MAXEVENTS, 10);
for (int i = 0; i < nfds; i++) {
//...
//...
}
//每次处理完一组epoll_wait触发的事件之后,处理异步任务
this->execute_ready_tasks();
}
}
```
9.LARV0.4-tcp_server创建tcp_conn对象
这里补充一下,因为在task的回调函数中,有形参`event_loop *loop`,可能会使用当前loop中监控的fd信息,所以我们应该给event_loop补充一个获取当前loop监控的全部fd信息的方法
```c
class event_loop{
//...
//获取全部监听事件的fd集合
void get_listen_fds(listen_fd_set &fds) {
fds = listen_fds;
}
//...
};
```
### 15.3 thread_pool模块添加task任务机制
接下来我们就要用thread_pool来想每个thread所绑定的event_pool中去发送task任务,很明显thread_pool应该具备能够将task加入到event_pool中的_ready_task集合的功能。
> lars_reactor/include/thread_pool.h
```c
#pragma once
#include <pthread.h>
#include "task_msg.h"
#include "thread_queue.h"
class thread_pool
{
public:
//构造,初始化线程池, 开辟thread_cnt个
thread_pool(int thread_cnt);
//获取一个thead
thread_queue<task_msg>* get_thread();
//发送一个task任务给thread_pool里的全部thread
void send_task(task_func func, void *args = NULL);
private:
//_queues是当前thread_pool全部的消息任务队列头指针
thread_queue<task_msg> ** _queues;
//当前线程池中的线程个数
int _thread_cnt;
//已经启动的全部therad编号
pthread_t * _tids;
//当前选中的线程队列下标
int _index;
};
```
`send_task()`方法就是发送给线程池中全部的thread去执行task任务.
> lars_reactor/src/thread_pool.cpp
```c
void thread_pool::send_task(task_func func, void *args)
{
task_msg task;
//给当前thread_pool中的每个thread里的pool添加一个task任务
for (int i = 0; i < _thread_cnt; i++) {
//封装一个task消息
task.type = task_msg::NEW_TASK;
task.task_cb = func;
task.args = args;
//取出第i个thread的消息队列
thread_queue<task_msg> *queue = _queues[i];
//发送task消息
queue->send(task);
}
}
```
`send_task()`的实现实际上是告知全部的thread,封装一个`NEW_TASK`类型的消息,通过`task_queue`告知对应的thread.很明显当我们进行 `queue->send(task)`的时候,当前的thread绑定的loop,就会触发`deal_task_message()`回调了。
> lars_reactor/src/thread_pool.cpp
```c
/*
* 一旦有task消息过来,这个业务是处理task消息业务的主流程
*
* 只要有人调用 thread_queue:: send()方法就会触发次函数
*/
void deal_task_message(event_loop *loop, int fd, void *args)
{
//得到是哪个消息队列触发的
thread_queue<task_msg>* queue = (thread_queue<task_msg>*)args;
//将queue中的全部任务取出来
std::queue<task_msg> tasks;
queue->recv(tasks);
while (tasks.empty() != true) {
task_msg task = tasks.front();
//弹出一个元素
tasks.pop();
if (task.type == task_msg::NEW_CONN) {
//是一个新建链接的任务
//并且将这个tcp_conn加入当当前线程的loop中去监听
tcp_conn *conn = new tcp_conn(task.connfd, loop);
if (conn == NULL) {
fprintf(stderr, "in thread new tcp_conn error\n");
exit(1);
}
printf("[thread]: get new connection succ!\n");
}
else if (task.type == task_msg::NEW_TASK) {
//===========是一个新的普通任务===============
//当前的loop就是一个thread的事件监控loop,让当前loop触发task任务的回调
loop->add_task(task.task_cb, task.args);
//==========================================
}
else {
//其他未识别任务
fprintf(stderr, "unknow task!\n");
}
}
}
```
10.LARV0.4-tcp_conn连接封装流程总结
我们判断task.type如果是`NEW_TASK`就将该task加入到当前loop中去.
通过上面的设计,可以看出来,thread_pool的`send_task()`应该是一个对外的开发者接口,所以我们要让服务器的`tcp_server`能够获取到`thread_pool`属性.
> lars_reactor/include/tcp_server.h
```c
class tcp_server {
//...
//获取当前server的线程池
thread_pool *thread_poll() {
return _thread_pool;
}
//...
};
```
ok,这样我们基本上完成的task异步处理业务的机制. 下面我们来测试一下这个功能.
### 15.4 完成Lars Reactor V0.11开发
> server.cpp
```c
#include "tcp_server.h"
#include <string>
#include <string.h>
#include "config_file.h"
tcp_server *server;
void print_lars_task(event_loop *loop, void *args)
{
printf("======= Active Task Func! ========\n");
listen_fd_set fds;
loop->get_listen_fds(fds);//不同线程的loop,返回的fds是不同的
//可以向所有fds触发
listen_fd_set::iterator it;
//遍历fds
for (it = fds.begin(); it != fds.end(); it++) {
int fd = *it;
tcp_conn *conn = tcp_server::conns[fd]; //取出fd
if (conn != NULL) {
int msgid = 101;
const char *msg = "Hello I am a Task!";
conn->send_message(msg, strlen(msg), msgid);
}
}
}
//回显业务的回调函数
void callback_busi(const char *data, uint32_t len, int msgid, net_connection *conn, void *user_data)
{
printf("callback_busi ...\n");
//直接回显
conn->send_message(data, len, msgid);
}
//打印信息回调函数
void print_busi(const char *data, uint32_t len, int msgid, net_connection *conn, void *user_data)
{
printf("recv client: [%s]\n", data);
printf("msgid: [%d]\n", msgid);
printf("len: [%d]\n", len);
}
11.LARV0.4-tcp_client属性和方法分析
//新客户端创建的回调
void on_client_build(net_connection *conn, void *args)
{
int msgid = 101;
const char *msg = "welcome! you online..";
conn->send_message(msg, strlen(msg), msgid);
//创建链接成功之后触发任务
server->thread_poll()->send_task(print_lars_task);
}
//客户端销毁的回调
void on_client_lost(net_connection *conn, void *args)
{
printf("connection is lost !\n");
}
int main()
{
event_loop loop;
//加载配置文件
config_file::setPath("./serv.conf");
std::string ip = config_file::instance()->GetString("reactor", "ip", "0.0.0.0");
short port = config_file::instance()->GetNumber("reactor", "port", 8888);
printf("ip = %s, port = %d\n", ip.c_str(), port);
server = new tcp_server(&loop, ip.c_str(), port);
//注册消息业务路由
server->add_msg_router(1, callback_busi);
server->add_msg_router(2, print_busi);
//注册链接hook回调
server->set_conn_start(on_client_build);
server->set_conn_close(on_client_lost);
loop.event_process();
return 0;
}
```
我们在每次建立连接成功之后,触发任务机制。其中`print_lars_task()`方法就是我们的异步任务。由于是全部thead都出发,所以该方法会被每个thread执行。但是不同的thread中的pool所返回的fd是不一样的,这里在`print_lars_task()`中,我们给对应的客户端做了一个简单的消息发送。
> client.cpp
```c
#include "tcp_client.h"
#include <stdio.h>
#include <string.h>
//客户端业务
void busi(const char *data, uint32_t len, int msgid, net_connection *conn, void *user_data)
{
//得到服务端回执的数据
char *str = NULL;
str = (char*)malloc(len+1);
memset(str, 0, len+1);
memcpy(str, data, len);
printf("recv server: [%s]\n", str);
printf("msgid: [%d]\n", msgid);
printf("len: [%d]\n", len);
}
//客户端销毁的回调
void on_client_build(net_connection *conn, void *args)
{
int msgid = 1;
const char *msg = "Hello Lars!";
conn->send_message(msg, strlen(msg), msgid);
}
//客户端销毁的回调
void on_client_lost(net_connection *conn, void *args)
{
printf("on_client_lost...\n");
printf("Client is lost!\n");
}
int main()
{
event_loop loop;
//创建tcp客户端
tcp_client client(&loop, "127.0.0.1", 7777, "clientv0.6");
//注册消息路由业务
client.add_msg_router(1, busi);
client.add_msg_router(101, busi);
//设置hook函数
client.set_conn_start(on_client_build);
client.set_conn_close(on_client_lost);
//开启事件监听
loop.event_process();
return 0;
}
```
12.LARV0.4-tcp_client非阻塞客户端套接字创建
客户端代码无差别。
编译并运行
服务端:
```bash
$ ./server
msg_router init...
ip = 127.0.0.1, port = 7777
create 0 thread
create 1 thread
create 2 thread
create 3 thread
create 4 thread
add msg cb msgid = 1
add msg cb msgid = 2
begin accept
begin accept
[thread]: get new connection succ!
callback_busi ...
======= Active Task Func! ========
======= Active Task Func! ========
======= Active Task Func! ========
======= Active Task Func! ========
======= Active Task Func! ========
```
客户端:
```c
$ ./client
msg_router init...
do_connect EINPROGRESS
add msg cb msgid = 1
add msg cb msgid = 101
connect 127.0.0.1:7777 succ!
recv server: [welcome! you online..]
msgid: [101]
len: [21]
recv server: [Hello Lars!]
msgid: [1]
len: [11]
recv server: [Hello I am a Task!]
msgid: [101]
len: [18]
```
task机制已经集成完毕,lars_reactor功能更加强大了。
## 16) 链接属性设置功能
### 16.1 测试链接属性
我们在基于lars_reactor开发的时候,可能有时候需要在写消息回调的时候,希望conn绑定一些属性。现在我们可以配置这种功能到`net_connection`上。
> lars_reactor/include/net_connection.h
```c
#pragma once
#include <stdio.h>
/*
*
* 网络通信的抽象类,任何需要进行收发消息的模块,都可以实现该类
*
* */
class net_connection
{
public:
net_connection():param(NULL) {}
//发送消息的接口
virtual int send_message(const char *data, int datalen, int msgid) = 0;
virtual int get_fd() = 0;
void *param; //TCP客户端可以 通过该参数传递一些自定义的参数
};
//创建链接/销毁链接 要触发的 回调函数类型
typedef void (*conn_callback)(net_connection *conn, void *args);
```
这里我们给`net_connection`类添加了一个`param`属性,这样,我们就可以绑定一些开发者自定义的参数和当前链接进行绑定。注意,这里也提供了一个`get_fd()`的纯虚函数,目的是提供conn获取当前fd的数据。
13.LARV0.4-tcp_client读写方法实现
14.LARV0.4-tcp_client模块客户端实现问题
15.LARV0.4-tcp_client_send_message无效问题解决
16.LARV0.3-event_loop添加时间buf修复
17.LARV0.4复习
相关文章:
C++负载均衡远程调用学习之TCP连接封装与TCPCLIENT封装
目录 1.LARSV0.3回顾 2.解决粘包问题的message结构体定义 3.LARSV0.4链接对象的方法和属性的定义 4.LARSv0.4 TCP_conn链接的初始化 5.LARV0.4-tcp_conn处理读事件方法do_read 6.LARV0.4-tcp_conn模块回顾 7.LARV0.4-tcp_send_message主动发包实现 8.LARV0.4-tcp_conn处…...
Python TensorFlow库【深度学习框架】全面讲解与案例
一、TensorFlow 基础知识 1. 核心概念 张量 (Tensor): 多维数组,是 TensorFlow 的基本数据单位(标量、向量、矩阵等)。计算图 (Graph): 早期版本中的静态图机制(TF2.x 默认启用动态图)。会话 (Session): 在 TF1.x 中…...
日志之ClickHouse部署及替换ELK中的Elasticsearch
文章目录 1 ELK替换1.1 Elasticsearch vs ClickHouse1.2 环境部署1.2.1 zookeeper 集群部署1.2.2 Kafka 集群部署1.2.3 FileBeat 部署1.2.4 clickhouse 部署1.2.4.1 准备步骤1.2.4.2 添加官方存储库1.2.4.3 部署&启动&连接1.2.4.5 基本配置服务1.2.4.6 测试创建数据库和…...
Git 基本操作(一)
目录 git add git commit git log git status git diff git 版本回退 git reset git add git add 指令为添加工作区中的文件到暂存区中。 git add file_name; //将工作区名称为file_name的文件添加进暂存区 git add .; //将工作区中的所有文件添加进暂存区 git comm…...
加密解密记录
一、RSA 加密解密 密钥对生成 1.前端加密解密 (1).vue页面引入 npm install jsencrypt(2)工具 jsencrypt.js import JSEncrypt from jsencrypt/bin/jsencrypt.min// 密钥对生成 http://web.chacuo.net/netrsakeypairconst p…...
Playwright MCP 入门实战:自动化测试与 Copilot 集成指南
什么是 MCP? MCP(Model Context Protocol) 是一种为大语言模型(LLM)设计的协议,MCP充当 LLM 与实际应用之间的桥梁或“翻译器”,将自然语言转化为结构化指令,使得模型可以更精确、高…...
存算一体架构下的新型AI加速范式:从Samsung HBM-PIM看近内存计算趋势
引言:突破"内存墙"的物理革命 冯诺依曼架构的"存储-计算分离"设计正面临根本性挑战——在GPT-4等万亿参数模型中,数据搬运能耗已达计算本身的200倍。存算一体(Processing-In-Memory, PIM)技术通过在存储介…...
为 Unity 项目添加自定义 USB HID 设备支持 (适用于 PC 和 Android/VR)-任何手柄、无人机手柄、摇杆、方向盘
这是一份关于如何在 Unity 中为特定 USB HID 设备(如 Phoenix SM600 手柄)添加支持,并确保其在打包成 APK 安装到独立 VR 设备后仍能正常工作的教程。 目标: 使 Unity 能够识别并处理特定 USB HID(Human Interface Device&#x…...
恒流源电路
常见的是上面这几种, 运放恒流电路一般搭配三极管使用 比赛用的模块可以用这种,会准一点...
python2反编译部分
文章目录 1、所需环境2、确认打包工具(没成功)3、 解包.exe文件(以PyInstaller为例) - useful【***总的来说这一步对我有用】4、定位关键文件 - useful5、 修复.pyc文件头(关键步骤!)- maybe-ig…...
Selenium3自动化测试,Python3测试开发教程视频测试用例设计
Selenium3自动化测试,Python3测试开发教程视频测试用例设计25套高级软件测试,性能测试,功能测试,自动化测试,接口测试,移动端测试,手机测试,WEB测试,渗透测试,…...
PyTorch 2.0编译器技术深度解析:如何自动生成高性能CUDA代码
引言:编译革命的范式转移 PyTorch 2.0的torch.compile不仅是简单的即时编译器(JIT),更标志着深度学习框架从解释执行到编译优化的范式跃迁。本文通过逆向工程编译过程,揭示PyTorch如何将动态图转换为高性能CU…...
ctfshow web入门 web44
信息收集 依旧是把所有输出丢弃,这一次多了flag的过滤,没啥好说的,用*或者?代替就可以了 if(isset($_GET[c])){$c$_GET[c];if(!preg_match("/;|cat|flag/i", $c)){system($c." >/dev/null 2>&1");} }else{h…...
三生原理的离散生成逻辑如何与复分析结合?
AI辅助创作: 三生原理离散生成逻辑与复分析结合路径分析 一、生成规则与解析延拓的协同 参数化联动机制向复数域延伸 三生原理的离散素数生成公式(如p=3(2n+1)+2(2n+m+1))通过引入复数参数 n,m∈C,可扩展为复平面上的解析函数,从而建立与黎曼ζ函数的关联通道。…...
数据升降级:医疗数据的“时空穿梭“系统工程(分析与架构篇)
一、核心挑战与量化分析 1. 版本演化困境的深度解析 (1) 格式断层的结构化危机 数据转换黑洞:某医疗信息平台(2021-2023)统计显示: 数据类型CDA R1→R2转换失败率R2→FHIR转换失败率关键失败点诊断记录28.4%19.7%ICD编码版本冲突(18.7%)用药记录15.2%12.3%剂量单位标准化…...
简单句练习--语法基础
文章目录 谓语和非谓语及物与不及物动词及物不及物主语必须由名词性质的成分充当谓语和非谓语 与中文不同,英语中的动词分为谓语形式和非谓语形式。 以“do”为例, 可以充当谓语的形式有:do,does,did, 以及其他各种时态,如:have done,is doing等。不可独立充当谓语的有…...
基于若依RuoYi-Vue3-FastAPI 的 Docker 部署记录
最近在参与导师项目开发过程中,我选择基于若依 FastAPI Vue3 模板作为系统框架,通过 Docker 实现前后端、数据库和缓存环境的容器化部署。 RuoYi-Vue3-FastAPI的github地址:https://github.com/insistence/RuoYi-Vue3-FastAPI 🛠…...
基于开源AI智能名片链动2+1模式S2B2C商城小程序的电商直播流量转化路径研究
摘要:在电商直播单场GMV突破2.28亿元的流量狂欢背后,传统直播模式正面临"流量过载而转化低效"的困境。本文提出以开源AI智能名片链动21模式S2B2C商城小程序重构流量转化路径,通过智能内容引擎、动态激励体系、供应链协同三大技术模…...
【Linux系统】Linux进程信号(产生,保存信号)
1. 信号快速认识 1-1 基本结论 如何识别信号?识别信号是内置的,进程识别信号,是内核程序员写的内置特性。信号产生之后,是知道怎么处理的,同理,如果信号没有产生,也是知道怎么处理信号的。所以…...
llamafactory-cli webui启动报错TypeError: argument of type ‘bool‘ is not iterable
一、问题 在阿里云NoteBook上启动llamafactory-cli webui报错TypeError: argument of type ‘bool’ is not iterable This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run gradio deploy from the terminal in the working directory t…...
工 厂 模 式
冷知识,当我们需要使用平底锅时,我们并不需要知道平底锅是怎么造的,坏了只需要再买就好了。至于造平底锅,全部交给我们的生产工厂就好。 蕴含这种创建对象思路的设计方法,我们称为“工厂模式”。 核心思想 工厂模式&…...
synchronized与Lock深度对比
Java并发编程:synchronized与Lock深度对比 基本概念 1.1 synchronized synchronized是Java内置的关键字,属于JVM层面的锁机制。它通过对象监视器(Monitor)实现同步,具有自动获取和释放锁的特性。 // 同步方法 public synchronized void sy…...
LeetCode —— 94. 二叉树的中序遍历
94. 二叉树的中序遍历 题目:94. 二叉树的中序遍历 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) :…...
【无标题】四色拓扑收缩模型中环形套嵌结构的颜色保真确定方法
#### **1. 环形嵌套结构的局部保真机制** - **零点虚边与环形嵌套**:在顶点 \( v \) 处引入环形嵌套结构(如环面 \( T^2 \)),通过虚边连接形成闭合路径。该结构作为“颜色记忆单元”,存储相邻区域的色彩信息࿰…...
Curl 全面使用指南
Curl(Client URL)是一个跨平台命令行工具,支持多种协议(HTTP/HTTPS/FTP/SFTP等),用于数据传输、API调试、文件上传/下载等场景。以下从 核心功能、用户疑问解答、高级技巧 三方面系统总结,并整合…...
vscode 的空格和 tab 设置 与 Rime 自建词库
自动保存(多用于失去焦点时保存) Files: Auto Save 推荐不勾 保存时格式化(Pritter 插件的功能,自动使用 Pritter 的格式) Editor: Format On Save 推荐不勾 tab 的空格数量,2 或 4 Editor: Tab Size 推荐…...
Spark-小练试刀
任务1:HDFS上有三份文件,分别为student.txt(学生信息表)result_bigdata.txt(大数据基础成绩表), result_math.txt(数学成绩表)。 加载student.txt为名称为student的RDD…...
Python爬虫实战:获取jd商城最新5060ti 16g显卡销量排行榜商品数据并做分析,为显卡选购做参考
一、引言 1.1 研究目的 本研究旨在利用 Python 爬虫技术,从京东商城获取 “5060ti 16g” 型号显卡的商品数据,并对这些数据进行深入分析。具体目标包括: 实现京东商城的模拟登录,突破登录验证机制,获取登录后的访问权限。高效稳定地爬取按销量排名前 20 的 “5060ti 16g…...
【Vue bug】:deep()失效
vue 组件中使用了 element-plus 组件 <template><el-dialog:model-value"visible":title"title":width"width px":before-close"onClose"><div class"container" :style"{height:height px}"&g…...
基于数字图像处理的裂缝检测与识别系统(Matlab)
【优化】Matlab裂缝检测与识别系统 基于数字图像处理的裂缝检测与识别系统(Matlab) (基本常在线秒回,有兴趣可以随时联系博主) 系统主要的内容包括: 1.图像加载与初始化 选择图像文件并加载:…...
day12:遗传算法及常见优化算法分享
遗传算法这些常见优化算法简直是 “宝藏素材”!用好了,轻轻松松就能填满论文一整节内容;要是研究透彻,甚至能独立撑起一整个章节。今天不打算深入展开,有个基础认知就行。等之后写论文真要用到这些算法了,咱…...
【计算机视觉】语义分割:MMSegmentation:OpenMMLab开源语义分割框架实战指南
深度解析MMSegmentation:OpenMMLab开源语义分割框架实战指南 技术架构与设计哲学系统架构概览核心技术特性 环境配置与安装指南硬件配置建议详细安装步骤环境验证 实战全流程解析1. 数据集准备2. 配置文件定制3. 模型训练与优化4. 模型评估与推理 核心功能扩展1. 自…...
25_04_30Linux架构篇、第1章_02源码编译安装Apache HTTP Server 最新稳定版本是 2.4.62
Linux_基础篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:源码编译安装Apache HTTP Server 最新稳定版本是 2.4.62 版本号: 1.0,0 作者: 老王要学习 日期: 2025.05.01 适用环境: Centos7 文档说明 本文…...
【重走C++学习之路】25、特殊类设计
目录 一、不能被拷贝的类 二、堆上创建对象的类 三、栈上创建对象的类 四、不能被继承的类 五、单例模式 结语 一、不能被拷贝的类 如何实现一个不能被拷贝的类?在看到这个要求的第一反应就是禁掉类的拷贝构造函数和赋值运算符重载函数,再往深了探…...
基于Redis实现-用户签到
基于Redis实现-用户签到 这个功能将使用到Redis中的BitMap来实现。 我们按照月来统计用户签到信息,签到记录为1,未签到则记录为0 把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路称为位图(BitMap)。…...
利用Redisson分布式锁解决多服务器数据刷新问题
利用Redisson分布式锁解决多服务器数据刷新问题 一、业务背景二、代码实现1、引入Redisson依赖2、配置Redisson,实际项目中Redis为集群配置3、自定义拒绝策略4、异步刷新网元服务 三、项目结构及源码 一、业务背景 最近有个需求需要自动刷新网元服务,由…...
25.4.30数据结构|并查集 路径压缩
前言 在QuickUnion快速合并的过程中,每次都要找根ID,而路径压缩让找根ID变得更加迅速直接。 路径压缩 针对的是findRootIndex()【查找根ID】进行的压缩。 需要实现的是: 在找根节点的过程中,记录这条路径上的所有信息,…...
react学习笔记3——基于React脚手架
React路由 相关理解 SPA的理解 单页Web应用(single page web application,SPA)。整个应用只有一个完整的页面。点击页面中的链接不会刷新页面,只会做页面的局部更新。数据都需要通过ajax请求获取, 并在前端异步展现。 路由的理…...
C#中的LINQ:简化数据查询与操作
引言 在现代软件开发中,处理和操作数据是不可避免的任务。无论是从数据库读取信息,还是对内存中的集合进行筛选、排序等操作,开发者都需要一种高效且易于使用的方法。C#中的LINQ(Language Integrated Query)正是为此而…...
OkHttp3.X 工具类封装:链式调用,支持HTTPS、重试、文件上传【内含常用设计模式设计示例】
OkHttp3.X 工具类封装:链式调用,支持HTTPS、重试、文件上传 基于OkHttp3.X封装,提供链式调用API,简化GET/POST请求,支持HTTPS、自动重试、文件上传等功能,提升开发效率。 在 Android 和 Java 开发中&#x…...
Unity SpriteEditor(精灵图片编辑器)
🏆 个人愚见,没事写写笔记 🏆《博客内容》:Unity3D开发内容 🏆🎉欢迎 👍点赞✍评论⭐收藏 🔎SpriteEditor: 精灵图片编辑器 📌用于编辑2D游戏开发中使用的Sp…...
雅思写作--70个高频表达
文章目录 1. learn new skills学生通过户外活动学到很多新技2. take immediate action to do各国采取有效行动以保护环境政府采取了必要行动以减少失业。你应该立即采取行动来解3. communication skills4. grow significantly5. have many advantages1. learn new skills “lea…...
Anaconda中配置Pyspark的Spark开发环境
Anaconda中配置Pyspark的Spark开发环境 目录 1.在控制台中测试ipython是否启动正常2.安装好Java3.安装Spark并配置环境变量4.PySpark配置5.修改spark\conf下的spark-env文件6.测试Pyspark是否安装成功 1.在控制台中测试ipython是否启动正常 anaconda正常安装 这里先检查ipyt…...
Spring 提供了多种依赖注入的方式
构造器注入(Constructor Injection) 构造器注入是通过类的构造函数来注入依赖项。这是 Spring 推荐的方式,因为它提供了不可变性和更好的可测试性。 import org.springframework.stereotype.Component;Component public class ServiceA {pub…...
面经-计算机网络——OSI七层模型与TCP/IP四层模型的对比详解
OSI七层模型与TCP/IP四层模型的对比详解 一、图示解析:分层封装结构 你提供的图清晰展示了网络通信中从应用层到物理层的封装过程,每一层都会对上层的数据加上自己的头部信息(Header): 应用层: 应用…...
网络安全知识问答微信小程序的设计与实现
网络安全知识问答微信小程序的设计与实现,说白了,就是搭建一款网络安全知识问答微信小程序,类似网络安全百科直通车。三步走。 需求沟通 进行需求沟通,此处省略1000字。 画草图 根据沟通的需求,进行整理,…...
Canvas特效实例:黑客帝国-字母矩阵(字母雨)
黑客帝国-字幕矩阵(字母雨) 效果预览代码实现思路解析遗留问题 效果预览 话不多说,我们直接上效果:当页面加载完成,屏幕上会落下如瀑布般的绿色字母流,不断向下滑动,仿佛进入了黑客帝国的数字世…...
「Mac畅玩AIGC与多模态11」开发篇07 - 使用自定义名言插件开发智能体应用
一、概述 本篇介绍如何在 macOS 环境下,通过编写自定义 OpenAPI Schema,将无需认证的名言服务接入 Dify 平台,并开发基于外部公共数据的智能体应用。本案例继续实践 GET 请求型 API 的实际调用技巧。 二、环境准备 1. 确认本地开发环境 macOS 系统Dify 平台已部署并可访问…...
快速上手非关系型数据库-MongoDB
简介 MongoDB 是一个基于文档的 NoSQL 数据库,由 MongoDB Inc. 开发。 NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。 MongoDB 的设计理念是为了应对大数据量、…...
响应式布局,在飞帆平台中如此简单
这些控件都是可以自己动手去实现的。也可以将这些控件复制到自己名下进行修改。 响应式布局https://fvi.cn/782...