东风草堂博客

公众号:开发者来风

分布式锁

在分布式系统中,如何保证多个进程或线程之间对共享资源的访问互斥性和并发性?
在分布式锁的情况下,还是需要单机锁的,可以减少对同时加锁的并发量,减轻中间件的压力。
没有抢到锁怎么处理?自旋,性能差。事件通知回调最好。

如何保证多个进程或线程之间的互斥性和并发性?

在一个分布式系统中,多个进程或线程需要对共享资源进行读写操作,如果不加限制,可能会导致数据不一致或者出现竞态条件。因此,需要引入分布式锁来控制对共享资源的访问。

  1. MySQL实现分布式锁的一种常见方式是使用InnoDB的行级锁和SELECT … FOR UPDATE语句。简单,使用方便,不需要引入Redis、zookeeper等中间件。但不适合高并发的场景,db操作性能较差,没有设置超时时间,有锁表的风险。具体步骤如下:
阅读全文 »

如何确认光猫是桥接还是路由模式?

当数据包进入桥接器时,桥接器会查看源MAC地址,并将其存储在一个表格中。然后,桥接器会检查目的MAC地址,并将其与该表中的所有MAC地址进行比较。如果该目的MAC地址已保存在表格中,桥接器就将数据包切换到与目的MAC地址相应的端口上。如果目的MAC地址不在表格中,则桥接器广播数据包到所有端口。

方法一: 我们用一台电脑的网口使用网线直接连接光猫的LAN口(也就是上网口),若是电脑连上之后能直接上网,那么就说明,光猫为:路由模式。 反之,若是电脑不能直接上网,那么说明光猫是桥接模式,需要电脑拨号,输入上网宽带账号、密码才能拨号上网。
方法二: 我们还可以直接登录光猫的后台管理界面查看,查看网络-网络设置里面的连接模式是路由还是桥接。如下:

常见组网模式

方法一:跑量设备,直接接入光猫LAN口,光猫开启DMZ或者UNPN(推荐);
方法二:光猫连接模式改成桥接、路由器上网模式改成拨号上网。

阅读全文 »

自主部署代理

不用搭环境,自己有境外的vps就行,最好是openai支持的地区的vps,下载bin里面的执行文件直接就能跑,最简单的api proxy方式,最重要的是支持SSE,让客户端请求时响应得更加迅速,也提供了golang的源码,需要定制的可以自行完善。

1
./api_proxy -daemon -port 9000 # 最好开启daemon守护进程模式

docker的部署方式,直接拉镜像部署,不用自己配置环境,这种自主部署的代理可以支持sse,使用nodejs的可以参考:

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
sudo yum remove docker
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# https://mirrors.tuna.tsinghua.edu.cn/help/docker-ce/
sed -i 's+https://download.docker.com+https://mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker # 这个不要忘了
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
yum install libxcrypt-compat # 报错dlopen: libcrypt.so.1: cannot open shared object file: No such file or directory

# 代理配置,参考https://docs.docker.com/engine/daemon/proxy/#httphttps-proxy,HTTPS_PROXY配成一样的就行
mkdir -p /etc/systemd/system/docker.service.d
touch /etc/systemd/system/docker.service.d/proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://proxy.example.com:443"
systemctl daemon-reload
systemctl restart docker

docker pull python:3-slim # 可以用来跑python脚本
docker run -dit --name proxy -p 9000:9000 easychen/ai.level06.com:latest # Proxy地址为 http://${IP}:9000
# 可用环境变量
# PORT: 服务端口
# PROXY_KEY: 代理访问KEY,用于限制访问
# TIMEOUT:请求超时时间,默认5秒

# 新方法
wget https://github.com/Yidadaa/ChatGPT-Next-Web/archive/refs/tags/v1.7.1.tar.gz
tar -xzvf v1.7.1.tar.gz
cd ChatGPT-Next-Web-1.7.1
docker build .
docker tag IMAGEID(镜像id) REPOSITORY:TAG(仓库:标签)
docker login
docker push nephen2023/chatgpt-next-web
阅读全文 »

参考

消息检查是否违规:https://platform.openai.com/docs/guides/moderation/quickstart
怎么开启流式传输:https://platform.openai.com/docs/api-reference/chat/create#chat/create-stream

1
2
stream boolean Optional Defaults to false
Whether to stream back partial progress. If set, tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message.

怎么做到连续上下文:https://platform.openai.com/docs/guides/chat/introduction,可以用https://platform.openai.com/playground?mode=chat 进行测试,入下图:刚开始问的时候assistant内容是为空的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 当用户指令引用先前的消息时,包括对话历史记录会有所帮助。
messages=[
# 主要的输入是消息参数。消息必须是一个消息对象的数组,其中每个对象都有一个角色("系统"、"用户 "或 "助手")和内容(消息的内容)。对话可以短到1条消息,也可以写满很多页。
# 通常,对话首先使用系统消息进行格式化,然后是交替的用户和助理消息。
{"role": "system", "content": "You are a helpful assistant."}, # 系统信息有助于设置助手的行为
{"role": "user", "content": "Who won the world series in 2020?"}, # 用户消息有助于指导助手。它们可以由应用程序的最终用户生成,或由开发人员设置为指令
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
{"role": "user", "content": "Where was it played?"} # 用户消息有助于指导助手。它们可以由应用程序的最终用户生成,或由开发人员设置为指令
]

completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=messages
)

print(completion.choices[0].message)
阅读全文 »

在 Zeabur 上部署

推荐使用 Zeabur,具体操作如下

  1. Fork 这个仓库为你自己的仓库
fork 2. 在 [Zeabur](https://zeabur.com) 部署你的仓库 3. 在 [Zeabur](https://zeabur.com) 控制台新增一个服务 步骤 1
阅读全文 »

考虑到seo,使用nuxtjs建立了自己的产品官网,但是在部署上线后出现了问题,用户说我收藏了某个网页,但是后面请求时都跑到首页去了,我仔细看了一下,刷新网页时确实会先闪现首页,后面再跑到指定的页面。

看了下nginx的请求,是请求的/about,但返回的是首页的页面。

再查了下站点的文件,也没有about文件呀,只有About。

想起来nginx的配置方式,请求不到对应的文件时,会跑到首页去,原因就明确了。

于是我改了文件夹的名字,把大写改为小写,再次请求验证ok。


那nuxtjs为什么会生成大写字母开头的文件夹?看了下源码,是因为vue文件命名成了大写,于是我重新把相关的文件名改为小写,再npm run generate生成静态部署文件就好了。

至于说是坑,可能是自己理解的问题,之前以为是不能使用static静态部署,要使用server模式才行,但是我这种小网站不想这么折腾,网上有人说用server模式加nginx反向代理就好了,其实不是这个问题,所以写篇文章记录一下。
贴一下相关的配置。

1
2
3
4
5
6
7
8
9
10
11
// nuxt.config.js
// npm run generate,再部署dist文件夹
ssr: true,
target: 'static',
// nginx.conf
index index.html index.htm index.nginx-debian.html;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ /index.html;
}

定制小程序和星级用户有什么关系?

  1. 定制小程序和星级用户都拥有小程序的最高级别权限,都可以长期使用。
  2. 定制小程序的主体是你自己,因为这个小程序是你自己申请注册的,等于这个小程序就是属于你自己的了,所以小程序的名字可以自己命名。
  3. 定制小程序后,这个小程序就只有你自己一个人使用,留言内容可以由你自己把控,而星级用户使用的公共的小程序,有很多公众号使用,存在评论管理相关的风险。

定制开发流程

  1. 微信公众平台注册一个小程序,激活后使用注册好的邮箱/密码登录进入小程序后台。
  2. 补充小程序的基本信息,如名称、图标、描述等。名称、图标、描述先和自己的公众号名字保持一致。
  3. 加客服进入后续的开发工作。
  4. 开发完成,提交微信审核。
  5. 审核通过,上线即可使用。
  6. 客服联系你,教你怎么使用留言板小程序。

定制开发价格

阅读全文 »

守护进程简介

守护进程(deamon)是生存期长的一种进程。它们常常在系统引导装入时启动(如果需要守护进程随系统自启动,需要在/etc/init.d目录下放置响应的启动脚本,或者利用systemctl来控制,还有一些其他方法如supervisor等,读者可自行网上搜索相关用法),仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的。

从daemon的启动和管理方式区分,可以将daemon分为两大类:可独立启动的daemon(stand alone)和由一个超级daemon(super daemon)来统一管理的daemon。

  • stand alone:可单独自行启动的daemon。这种daemon启动后会一直占用内存和系统资源,最大的优点是响应速度快,多用于能够随时接受远程请求的服务,如WWW的daemon(httpd)、FTP的daemon(vsftpd)等。
  • super daemon:由一个特殊的daemon来统一管理。这种服务通过一个统一的daemon在需要时负责唤醒,当没有远程请求时,这些服务都是未启动的,等到有远程请求过来时,super daemon才唤醒相应的服务。当远程请求结束后,被唤醒的服务会关闭并释放系统资源。早期的super daemon是inetd,后来被xinetd替代了。super daemon本身是一个stand alone的服务,因为它需要管理后续的其他服务,所以它自己本身当然需要常驻内存中。

守护进程创建步骤

阅读全文 »

Windows Subsystem for Linux(简称WSL)是一个在Windows 10上能够运行原生Linux二进制可执行文件(ELF格式)的兼容层。它是由微软与Canonical公司合作开发,其目标是使纯正的Ubuntu、Debian等映像能下载和解压到用户的本地计算机,并且映像内的工具和实用工具能在此子系统上原生运行。

使用了wsl可以丢弃VMware。

安装wsl

参考:https://docs.microsoft.com/en-us/windows/wsl/install-manual

PowerShell执行下面命令安装wsl:

阅读全文 »

网络编程的痛点

reactor是基于异步事件的网络模型,但io操作都是同步的:非阻塞io,io多路复用,拷贝数据时还是需要等待。
同步和异步的区别是结果是否在发起调用处返回。阻塞与非阻塞的区别是io没有就绪的情况下是否立刻返回。
reactor的事件处理是异步的,回调太多会影响业务逻辑的编写,一个业务逻辑在不同的回调中完成,需要保存和传递上下文。需要通过协程捏合回调。

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
void on_login(struct bufferevent *bev, struct Arg *args) {
if (args->eles < 2) {
reply_error(bev, "on_login 登陆参数个数不对\n");
return;
}
Arg *params = args->next;
const char *name = params->str;
size_t name_len = params->len;

params = params->next;
const char *password = params->str;
size_t password_len = params->len;

replyContext *rc = (replyContext *)malloc(sizeof(replyContext));
rc->bev = bev;
rc->args = args;

redisAsyncCommand(redis_conn, redis_login_hget, rc, "hgetall roll: %s", name); // redis_login_hget为回调函数,需要传递上下文
}

// redisAsyncCommand实现思路
// 1. 准备socket
// 2. connect
// 3. 准备好协议
// 4. send
// 5. fd、callback加入epoll,使用yield,跑到下方的2.read部分

// callback处理,这是另外一个线程(协程要想办法改为同一个线程)
// 1. epoll_wait
// 2. read fd
// 3. 解析对应的数据,使用resume,接着跑上面的第五步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function CMD.login(fd, name, password)
if not name or not password then
socket.write(fd, "没有设置账号或密码\n")
return
end
if clients[fd] then
socket.write(fd, ("%s 用户已登陆\n"):format(name))
return
end
local ok, err = rds:exists("role:"..name)
if ok == 0 then
rds:hmset("role:"..name,
"name", name,
"password", password) -- 同步处理
local cli = {}
cli.fd = fd
cli.name = name
cli.password = password
clients[fd] = cli
socket.write(fd, ("%s 注册成功\n"):format(name))
return
end
end

协程

阅读全文 »
0%