avatar


《我在同福》开发中的一些小事

二维码

前言

最近开发了一款 基于大模型的小游戏 《我在同福》,整体的技术架构如下:

  • 前端:React + Taro
  • 后端:Spring系列 + MyBatis
  • 数据:MySQL
  • 其他:Linux(Rocky)、NGINX

本文记录一些在开发过程中的"小事"。

这些"小事"应该说都比较冷门,不会是热点问题。但并不是不重要。

MySQL数据目录

背景

数据盘挂在/data目录下。

基于合理分配利用磁盘的考虑,考虑把MySQL的数据目录也修改到数据盘下。

安装MySQL

这次用的是Rocky这个发行版的Linux,采用的是dnf 安装,整体过程和CentOS中的yum 安装没有太大区别。

安装命令如下:

  1. 更新软件包
    1
    dnf upgrade --refresh -y
  2. 从Appstream中安装
    1
    dnf -y install mysql mysql-server
  3. 查看MySQL版本。示例代码:
    1
    mysql --version
    运行结果:
    1
    mysql  Ver 8.0.36 for Linux on x86_64 (Source distribution)

修改数据目录

取消SELINUX

在修改数据目录之前,我们需要取消SELINUX。

这是一个坑,否则之后修改数据目录,会报各种权限错误。

关于"取消SELINUX",可以参考《ClickHouse应用实践:1.基本操作》中"CentOS取消SELINUX",在Rocky中,没有任何区别。

修改目录

修改配置文件/etc/my.cnf。如果/etc/my.cnf的内容如下,则修改/etc/my.cnf.d的内容。

1
2
3
4
5
6
7
8
9
10
#
# This group is read both both by the client and the server
# use it for options that affect everything
#
[client-server]

#
# include all files from the config directory
#
!includedir /etc/my.cnf.d

在本文修改的是/etc/my.cnf.d/mysql-server.cnf

1
2
3
4
5
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
log-error=/var/log/mysql/mysqld.log
pid-file=/run/mysqld/mysqld.pid
  • datadir=/data/mysql,指定了MySQL数据文件存放的目录,所有的数据库文件、表定义文件等都将存放在这个目录下。
  • socket=/data/mysql/mysql.sock,定义了MySQL服务器与客户端之间进行本地通信时使用的套接字文件的路径。
  • log-error=/var/log/mysql/mysqld.log:指定了MySQL错误日志的文件位置。
  • pid-file=/run/mysqld/mysqld.pid:设置了MySQL服务器进程ID(PID)文件的位置。

在本文,我们不但修改了datadir,还修改了socket

最后,注意权限问题!示例代码:

1
chown -R mysql:mysql /data/mysql/

连接MySQL

修改完成后,启动MySQL,连接。

1
mysql -uroot -p -S/data/mysql/mysql.sock

解释说明:

  • 如果使用-S参数,通过指定实例下的socket文件来登录到指定的实例。
  • 如果使用-h参数,则使用"TCP/IP"的方式,这时候不能是"localhost",因为"localhost"会使用默认的socket文件登录。

NGINX相关

SSL

FreeSSL

通过FreeSSL,申请证书。
官网:https://freessl.cn

在本文,我们选择浏览器生成文件验证

浏览器生成-文件验证-1

之后会得到我们需要设置的文件内容。

浏览器生成-文件验证-2

文件验证

–with-http_ssl_module

在服务器上安装NGINX,安装方法可以参考《NGINX入门与提高:1.安装、基础操作和配置》

需要注意的是,如果采用编译安装,需要在安装的时候配置SSL模块。示例代码:

1
2
3
./configure --with-http_ssl_module
make
sudo make install
  • 注意参数--with-http_ssl_module

修改server块中的location

我们添加或修改server块中的location指令如下:

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
server {
listen 80;
server_name kaka.ac.cn;

【部分内容略】

location /.well-known/pki-validation/5D3505A7F4CDF9AB39B863519463CA3F.txt {
alias /root/temp/5D3505A7F4CDF9AB39B863519463CA3F.txt;
}

【部分内容略】
}

【部分内容略】

server {
listen 443 ssl;
server_name www.kaka.ac.cn;

【部分内容略】

location /.well-known/pki-validation/5D3505A7F4CDF9AB39B863519463CA3F.txt {
alias /root/temp/5D3505A7F4CDF9AB39B863519463CA3F.txt;
}

}
  • 正如配置文件,我们需要新建/root/temp/5D3505A7F4CDF9AB39B863519463CA3F.txt,内容是证书发行方提供的验证:
    1
    2
    3
    06967BE234FEB6DEF786A60075EE80F005F04ED0AC56A453E3D24FA5F568FB4F
    trust-provider.com
    cmcdt3xs4rp8we

配置SSL证书

验证通过后,FreeSSL会给我们发放证书。

配置SSL证书的方法,可以参考《一种基于nginx的反向代理策略》,本文不赘述。

反向代理SpringBoot

最后,我们还需要反向代理到一个SpringBoot应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {

【部分内容略】

# 反向代理配置
location / {
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

【部分内容略】
}

解释说明:

  • proxy_pass http://localhost:8080/,目标地址(SpringBoot)。
  • proxy_set_header:设置转发请求的头部信息,确保SpringBoot应用能够获取正确的客户端信息。

小程序相关

跳转

背景

《我在同福》有点卡充值功能,因为个人小程序不允许有支付功能,所以考虑采取点卡的方式。

可以通过淘口令的方式,将购买点卡的地址给客户。如果能直接跳转到其他小程序就更好。

微信官方和Taro,都提供了跳转到其他小程序的方法,具体参加官方的文档:

在实践中,考虑如下因素:

  • 太冷门的网上商城不要,我不可能为了让客户买点卡,再去注册账号。
    所以,实际上只有拼多多、京东、闲鱼这些小程序。
  • 在网上商城开店卖点卡(或类似商品),需要企业资质,需要高额保证金的不要。
    所以,最后只有闲鱼。

获取任意小程序的AppID和页面路径

两个参数

小程序跳转中,两个关键参数是:

  • AppID,标明需要跳转到哪一个小程序。
  • path,目标页面的路径。

AppID

AppID可以通过小程序的资料页获取:

AppID

path

目标页面的路径需要有一个微信公众号。
步骤如下:

  1. 新建图文
  2. 插入小程序
  3. 点击去搜索
    点击去搜索
  4. 输入小程序的AppID
  5. 获取更多页面路径
    点击去搜索
  6. 输入自己的微信号,点击开启。
    点击去搜索
  7. 手机微信打开目标小程序的目标页面,复制页面路径。
    点击去搜索

最后,需要注意的是虽然我们复制的地址形如:

1
pages/personal/index.html?userId=【XXX】

但是,我们在代码中填写的参数如下,没有.html后缀。

1
2
3
4
Taro.navigateToMiniProgram({
appId: 'wx9882f2a891880616',
path: 'pages/personal/index?userId=【XXX】'
})

在实践中发现,闲鱼小程序功能不全,有些功能如"再次购买",需要通过闲鱼APP进行操作。最后还是选择了淘宝口令的方式。

useEffect

原因

在React中,useEffect钩子用于执行副作用操作,如数据获取、订阅或手动更改DOM等。
当组件首次渲染或者依赖项发生变化时,useEffect中定义的函数会被执行。

对于微信小程序(特别是使用Taro框架开发的小程序),页面的生命周期与React组件的生命周期有所不同。
在微信小程序中,页面的显示和隐藏并不会导致页面组件的卸载和重新挂载。
这意味着当你从A页面跳转到B页面,再从B页面返回A页面时,A页面实际上并没有被销毁和重建,而是被隐藏和重新显示了。
因此,A页面的组件实例仍然存在,其useEffect钩子中的清理函数(如果有)可能会执行,但是由于页面没有重新渲染,useEffect钩子的初始化部分不会再次执行。

解决方案

使用onShow生命周期方法。

  • 在Taro中,我们可以利用页面的onShow生命周期方法来模拟useEffect的行为。onShow方法会在页面每次显示时调用,包括首次加载和从其他页面返回时。你可以在onShow方法中调用你需要在页面显示时执行的逻辑。
  • onShow方法修改一个useState变量,而这个变量作为useEffect的第二个参数。

不校验域名

默认小程序只能调用合法域名(我们自己配置)的接口。
这样在开发过程会很麻烦。
我们可以设置,不校验域名。

在微信开发者工具,依次点击:

  1. 详情
  2. 本地设置
  3. 不校验合法域名...

不校验域名

调整样式

可以直接在微信开发者工具上,调整样式。

调整样式

安卓手机的BUG

现象

在部分安卓手机上,会有如下的BUG。

输入框展示不全。

部分安卓手机的BUG

探索

复现过程

  1. 进入"开始页",或"新开页"。
  2. 输入"自我介绍"(或输入之后删除,或不输入只点了一下输入框)
  3. 点击"确认"之后。

估计有50%的概率,会出现上述现象。

不会复现的情况

  1. 通过"继续"页,进入聊天框。
  2. 进入"开始页",或"新开页"。不输入自我介绍,直接点"确认"。

试图解决

强制重新渲染

怀疑是"聊天页"的页面渲染问题,尝试各种修改聊天页,没有解决问题。

甚至强制React重新渲染,还是没有解决问题。

React自动渲染机制

React依靠虚拟DOM来实现组件的自动渲染。当状态(state)或属性(props)发生变化时,React会自动检测这些变化,并重新渲染组件以反映最新的状态。

通常情况下,React的自动渲染机制能够很好地工作,但在某些情况下,可能需要手动强制组件重新渲染。

强制更新函数组件

类组件可以通过调用this.forceUpdate()方法来强制重新渲染。

函数组件没有this.forceUpdate()方法,但可以通过创建一个空的状态变量并在需要时更新它来达到类似的效果。

下文的例子展示了如何使用useStateuseCallback钩子来创建一个强制更新函数:

1
2
const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);
  • 调用forceUpdate函数,即可完成强制重新渲染。

注意

强制重新渲染组件并不是最佳实践,应当尽量避免,因为过度使用会导致性能问题。
只有在必要时,例如React未能自动更新组件的情况下,才考虑使用这种方法。在尝试强制重新渲染之前,应仔细检查代码,确保状态和属性的更新是正确的。

解决

最后,发现,这是一个"坑"。
在进入"聊天页"之前,输入法需要不能处于弹出状态,否则会有一定的机率影响页面渲染。

  1. 复现的情况都是输入法都处于弹出状态,直接进入聊天页。
  2. 没复现的情况,包括进入"开始页",或"新开页"。不输入自我介绍,直接点"确认",这种情况下输入法都没有处于弹出状态。

所以,解决问题的方法是:在"进入聊天页"之前 收起输入法

文章作者: Kaka Wan Yifan
文章链接: https://kakawanyifan.com/19913
版权声明: 本博客所有文章版权为文章作者所有,未经书面许可,任何机构和个人不得以任何形式转载、摘编或复制。

留言板