东风草堂博客

公众号:开发者来风

单点redis问题

  • 数据丢失,实现redis持久化
  • 并发能力,主从集群,读写分离
  • 故障恢复,哨兵机制,健康监测,自动恢复
  • 存储能力,分片集群,插槽机制实现动态扩容

redis持久化

rdb,redis数据备份文件,实现持久化。
save命令开启,主进程阻塞完成。bgsave后台完成。停机时自动执行rdb。
redis.conf可以配置,save 900 1表示900秒内,如果至少一个key被修改,则执行bgsave,如果是save “”,则表示禁用rdb。dbfilename和dir也可以配置文件名和目录,rdbcompression可以配置是否压缩,但会消耗cpu。


bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据,完成fork后读取内存数据写入rgb文件。fork采用copy-on-write技术,读操作访问共享内存,写操作拷贝数据执行写操作,所以极端情况所有数据都被修改了,可能内存会翻倍。

阅读全文 »

在 CentOS 系统上安装 Google Chrome 浏览器,可以按照以下步骤进行:

添加 Google Chrome 的 YUM 源
在终端中输入以下命令:

sudo tee /etc/yum.repos.d/google-chrome.repo <<-‘EOF’
[google-chrome]
name=google-chrome - $basearch
baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl.google.com/linux/linux_signing_key.pub
EOF
安装 Google Chrome
输入以下命令进行安装:

yum -y install google-chrome-stable –nogpgcheck
启动 Google Chrome
在终端中输入以下命令启动 Google Chrome 浏览器:

google-chrome
这样就可以在 CentOS 系统上安装和使用 Google Chrome 浏览器了,输入法安装:https://www.google.cn/intl/zh-CN/inputtools/chrome/index.html。

阅读全文 »

go server

1
data: something happend!\n\n

第二个 \n 表示这个 event 结束,可以发给客户端了。第一个 \n 主要是为了有时要传输多行数据时,例如传个 json:

1
2
3
data: {\n
data: "foo": "bar"\n
data: }\n\n

上面都属于 message 这个类型,如果想发送别的类型可以通过 event 字段自定义:

阅读全文 »

网络模式

docker的默认网络模式的bridge,有个问题,docker内部无法直接与另外的docker内的端口进行通信,改为host模式即可。如果不改网络模式的话,要么不走http,改为走uds通信就可以。

1
2
3
services:
nginx:
network_mode: "host"

镜像推送

1
2
3
4
docker build . -t REPOSITORY:TAG
docker tag IMAGEID(镜像id) REPOSITORY:TAG(仓库:标签)
docker login
docker push nephen2023/chatgpt-next-web

+——————————————-+
| Server |
| (single-threaded, event-driven etc) |
+——————————————-+
| Processor |
| (compiler generated) |
+——————————————-+
| Protocol |
| (JSON, compact etc) |
+——————————————-+
| Transport |
| (raw TCP, HTTP etc) |
+——————————————-+

Transport

Transport传输层为从网络读取/向网络写入提供了一个简单的抽象。这使 Thrift 能够将底层传输与系统的其余部分分离(例如,序列化/反序列化)
Transport接口:

  • open
  • close
  • read
  • write
  • flush

除了上面的 Transport 接口之外,Thrift 还使用了一个 ServerTransport 接口,用于接受或创建原始传输对象。顾名思义,ServerTransport 主要用于服务器端,为传入连接创建新的传输对象

阅读全文 »

https://docs.midjourney.com/docs/invite-the-bot,可以将机器人拉入到自己的群中,这样消息不会被淹没。

api下载mj生成的图片:https://medium.com/@neonforge/how-to-create-a-discord-bot-to-download-midjourney-images-automatically-python-step-by-step-guide-3e76d3282871
api自动发送mj消息:https://medium.com/@neonforge/how-to-automate-midjourney-image-generation-with-python-and-gui-automation-ac9ca5f747ae

discord生成应用:https://discord.com/developers/applications
discord开发手册:https://discord.com/developers/docs/interactions/application-commands

1
2
3
4
5
Action shot.An Asian little boy sitting on a flying pterosaur .Close-Up.surreal photography. 

A 10 year old Chinese girl wearing a fiery red dress and a large fiery phoenix, 16k, ultra detailed, ultra realistic, captured by a Fuji camera,, --iw 2 --ar 3:4 --v 5.2

A lively and cute Chinese girl interacts with a huge flower in a surreal setting, 6 year old girl, looks like Angelababy, the art of Hitoko Kawauchi, natural poses, dadcore for vacation, energy and pressure of youth, body extension, simulation film in the sky, super detail, dreamlike high photography. A riot of colour, shot on fujifilm XT4,3d, c4d,hd, oc renderer, cinematic lighting, super detail, 8k, super detail --s 350 --ar 3:4

网页重定向

网页迟迟没有被google收录,进入https://search.google.com/发现:

在 Nuxt.js 中,您可以通过设置 generate.subFolders 选项来控制生成的文件是否应该包含子目录。默认情况下,该选项的值为 true,这意味着生成的文件将被放置在相应的路由目录中。

如果您不想要生成的文件包含子目录,并直接生成 HTML 文件,您可以将 generate.subFolders 设置为 false。您可以在 nuxt.config.js 文件中添加以下配置:

1
2
3
4
5
export default {
generate: {
subFolders: false
}
}
阅读全文 »

服务器TNonblockingServer

rpc请求生成task放入到任务队列中,这个任务是Runnable的,但没有依附于某个thread,等于是封装了个run函数,供线程池工作线程调用时运行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
void addTask(stdcxx::shared_ptr<Runnable> task) {
threadManager_->add(task, 0LL, taskExpireTime_);
}

stdcxx::shared_ptr<Runnable> task = stdcxx::shared_ptr<Runnable>(
new Task(processor_, inputProtocol_, outputProtocol_, this));
server_->addTask(task);

// 真正的任务,其实也是一个run函数,这个Runnable是没有thread实体的
class TNonblockingServer::TConnection::Task : public Runnable {
public:
Task(stdcxx::shared_ptr<TProcessor> processor,
stdcxx::shared_ptr<TProtocol> input,
stdcxx::shared_ptr<TProtocol> output,
TConnection* connection)
: processor_(processor),
input_(input),
output_(output),
connection_(connection),
serverEventHandler_(connection_->getServerEventHandler()),
connectionContext_(connection_->getConnectionContext()) {}

void run() {
try {
for (;;) {
if (serverEventHandler_) {
serverEventHandler_->processContext(connectionContext_, connection_->getTSocket());
}
if (!processor_->process(input_, output_, connectionContext_)
|| !input_->getTransport()->peek()) {
break;
}
}
} catch (const TTransportException& ttx) {
GlobalOutput.printf("TNonblockingServer: client died: %s", ttx.what());
} catch (const std::bad_alloc&) {
GlobalOutput("TNonblockingServer: caught bad_alloc exception.");
exit(1);
} catch (const std::exception& x) {
GlobalOutput.printf("TNonblockingServer: process() exception: %s: %s",
typeid(x).name(),
x.what());
} catch (...) {
GlobalOutput.printf("TNonblockingServer: unknown exception while processing.");
}

// Signal completion back to the libevent thread via a pipe
if (!connection_->notifyIOThread()) {
GlobalOutput.printf("TNonblockingServer: failed to notifyIOThread, closing.");
connection_->server_->decrementActiveProcessors();
connection_->close();
throw TException("TNonblockingServer::Task::run: failed write on notify pipe");
}
}

TConnection* getTConnection() { return connection_; }

private:
stdcxx::shared_ptr<TProcessor> processor_;
stdcxx::shared_ptr<TProtocol> input_;
stdcxx::shared_ptr<TProtocol> output_;
TConnection* connection_;
stdcxx::shared_ptr<TServerEventHandler> serverEventHandler_;
void* connectionContext_;
};

线程池管理ThreadManager

线程池的管理,包括增加工作线程worker,这个worker线程是继承自Runnable的,依附于具体的线程thread,还有就是增加任务,上面说到了server是怎么调用这个增加任务的接口的。注意worker和task都是继承自runnable,一个有依附的thread,一个没有,都要实现run函数。

阅读全文 »

thrift编解码过程

Thrift是一种用于定义RPC服务和数据交换格式的框架,其中编解码过程是其重要的组成部分。下面简单介绍一下Thrift的编解码过程:

  1. 定义IDL接口:使用Thrift的IDL语言进行服务接口定义,包括服务方法、参数和返回值的类型及名称等信息。
  2. 生成代码:使用Thrift提供的代码生成器根据IDL文件生成各语言环境下可用的客户端和服务端代码。
  3. 编写客户端和服务端:使用生成的代码进行客户端和服务端的开发。
  4. 序列化请求:客户端将请求参数序列化为二进制格式,以便在网络上传输。
  5. 发送请求:客户端通过网络发送请求消息。
  6. 接收请求:服务端接收请求消息。
  7. 反序列化请求:服务端将接收到的二进制数据反序列化为具体的参数类型。
  8. 处理请求:服务端根据请求消息调用对应的服务方法,并返回处理结果。
  9. 序列化响应:服务端将返回结果序列化为二进制格式,以便在网络上传输。
  10. 发送响应:服务端通过网络发送响应消息。
  11. 接收响应:客户端接收响应消息。
  12. 反序列化响应:客户端将接收到的二进制数据反序列化为具体的返回值类型。

序列化过程

Thrift的序列化过程可以分为两个步骤:写入(Write)和读取(Read)。对于一个需要序列化的对象,Thrift会按照其定义的类型和顺序依次将其转换为二进制数据。
Thrift支持多种序列化协议,常用的有: Binary、Compact、JSON。binary序列化是一种二进制的序列化方式。不可读,但传输效率高。

阅读全文 »

概述

简介

在函数内部,可以省略var关键字,使用更简单的定义模式。

1
2
3
4
fun main() {
x := 100
fmt.Println(x)
}

流程控制可以省略条件判断:

阅读全文 »
0%
客服