热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Skynet服务器框架(一)Linux下的安装和启动

引言:一直都是从事客户端的开发工作,最近抽了点时间想了解一下服务器开发的相关知识,一番博客瞎逛之后,发现了一个不错的框架,云风大神的skynet开源服务器框架,这不仅仅是针对于游戏服务器开发

引言:

一直都是从事客户端的开发工作,最近抽了点时间想了解一下服务器开发的相关知识,一番博客瞎逛之后,发现了一个不错的框架,云风大神的 skynet开源服务器框架,这不仅仅是针对于游戏服务器开发的框架,更是一个通用的服务器基础框架。

Skynet简介:

Skynet 主要工作是管理注册服务,并开启多线程协调服务之间的调用和通讯。

1.框架核心:

根据云风博客的描述,Skynet 的核心功能就是解决一个问题:

把一个符合规范的 C 模块,从 动态库(so文件)中启动起来,绑定一个永不重复(即使模块退出)的数字id做为其 handle模块 被称为 服务(Service),服务间可以自由发送消息。

  • 每个 模块 可以向 Skynet 框架注册一个 callback 函数,用来接收发给它的消息;
  • 每个服务都是被一个个 消息包 驱动,当没有包到来的时候,它们就会处于 挂起状态,此状态对 CPU 资源零消耗。如果需要自主逻辑,则可以利用 Skynet 系统提供的 timeout消息,定期触发。

名字服务:
Skynet 提供了 名字服务,还可以给特定的服务起一个易读的名字,而不是用 id 来指代它。id 和运行时态相关,无法保证每次启动服务,都有一致的 id ,但名字可以。

简而言之,这个框架完成的功能大概如下:
Skynet 只负责把一个数据包从一个服务内发送出去,让同一进程内的另一个服务收到,调用对应的callback 函数处理。它保证,模块的初始化过程,每个独立的 callback 调用,都是 相互线程安全 的。编写服务的人不需要特别的为多线程环境考虑任何问题,专心处理发送给它的一个个数据包。


2.框架优点:

  • 高低级语言配合:
    Skynet 是一个融合了低级语言(C)消息框架和高级动态语言(lua)的混合体,这种结构称为 hybrid framework。选择运行高效的C来写服务节点,也可以选择同样开发高效而且安全隔离的lua来写上层业务。Skynet的主要核心包括两部分:

    • C语言 实现的消息循环和组件加载机制;
    • lua 实现的以消息为中心的进入退出 coroutine(协程)的包装层。
  • 组件化能力:
    Skynet 内核(C部分)自身支持加载模块(.so 文件),你可以使用C语言去写性能有要求的服务节点,通过消息与其他节点配合。lua又是一个对C语言极为友好的动态语言,所以你可以找到很多现成的lua的C扩展,skynet/3rd 路径下可以放置你需要的各种组件,比如:CJSONsqliteOpenSSL等。

3.单进程:

很多服务器框架在构建之初,就设想着用多进程的方式来解决高并发的问题,但是所带来的问题就是多进程不可避免的进程安全锁,这样的框架经常会因为部分代码的报错而导致死锁或者内存占用不释放等问题。很多优秀的服务器框架都是使用单进程,然后通过线程池来做消息轮询和任务执行的方式来实现的,这样能够避开锁所带来的诸多问题。

Skynet也是单进程的服务器框架,在单一进程上启动一个线程池,其中包括多个 worker 线程 、一个 socket 网络线程和一个 timer 时间线程。当创建了多个 lua服务,每个服务都相当于Erlang中的一个 Actor (可以简单理解为:可以并行运行的对象),每个服务都有自己的消息队列,skynet也有一个全局的消息队列,线程池中的 worker 线程会随机从消息队列中取出消息来执行直到消息队列为空。此外,每个 lua服务 中又可以通过启动多个 coroutine (携程)来实现异步操作的目的。


Skynet下载配置:

要学习开源框架,第一步肯定是先拿来试用一下,然后再取剖析源码,接下来我们就尝试下载Skynet框架,并尝试使用这套开源的框架来搭建一个测试服务器:

1.资源下载:

Github源码地址:cloudwu/skynet
假如当前是在Linux环境下,并已经安装有 git 工具,则可以直接使用 git 指令 git clone 来拉取Github仓库的 Skynet 最新源码:

sudo git clone https://github.com/cloudwu/skynet.git

假如执行正常,输出如下:

linsh@ubuntu:/mnt/Windows$ sudo git clone https://github.com/cloudwu/skynet.git
正克隆到 'skynet'...
remote: Counting objects: 8079, done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 8079 (delta 1), reused 0 (delta 0), pack-reused 8057
接收对象中: 100% (8079/8079), 2.72 MiB | 24.00 KiB/s, done.
处理 delta 中: 100% (5442/5442), done.
检查连接... 完成。

假如还没安装git工具,可以通过以下指令安装(我的操作系统是 Ubuntu14.04.4):

sudo apt-get install git

2.源码目录结构:

关于源码主要目录及其作用如下:

skynet-master
--3rd //第三方代码,主要生产一些给lua用的so动态库
--example //示例工程
--lualib //lua库
--lualib-src //luaclib:给lua用的c库
--service //lua服务
--service-src //csservice:c服务
--skynet-src //skynet核心c源码主程序
--test //一些类库和接口调用的客户端用例
--HISTORY.md //版本更新日志
--LICENSE
--Makefile //编译脚本
--platform.mk //运行平台相关(支持Linux、MacOSX、freebsd操作系统)

3.源码编译:

  • 工具安装:
    在编译前还需要安装两个工具,不然会出现报错:

    • 安装autoconf

      sudo apt-get install autoconf

      否则会报如下错误:

      cd 3rd/jemalloc && ./autogen.sh --with-jemalloc-prefix=je_ --disable-valgrind
      autoconf
      ./autogen.sh: line 5: autoconf: command not found
      Error 0 in autoconf
      make[1]: *** [3rd/jemalloc/Makefile] Error 1
      make[1]: Leaving directory `/data/skynet'
      make: *** [linux] Error 2
    • 安装readline-devel

      sudo apt-get install libreadline-dev

      否则会报如下错误:

      lua.c:83:31: fatal error: readline/readline.h: 没有那个文件或目录

      #include

      ^
      compilation terminated.
      make[3]: *** [lua.o] 错误 1
      make[3]:正在离开目录 `/application/skynet/3rd/lua'
      make[2]: *** [linux] 错误 2
      make[2]:正在离开目录 `/application/skynet/3rd/lua'
      make[1]: *** [3rd/lua/liblua.a] 错误 2
      make[1]:正在离开目录 `/application/skynet'
      make: *** [linux] 错误 2
  • 编译操作:
    由于下载的是源码,需要进过编译才能运行,编译过程就是:

    • 进入 clone 到本地的项目目录,执行make指令编译源码:

      cd skynet
      sudo make linux

      假如编译过程正常,编译完成后如下:

      linsh@ubuntu:/application/skynet$ sudo make linux
      make all PLAT=linux SKYNET_LIBS="-lpthread -lm -ldl -lrt" SHARED="-fPIC --shared" EXPORT="-Wl,-E" MALLOC_STATICLIB="3rd/jemalloc/lib/libjemalloc_pic.a" SKYNET_DEFINES=""
      make[1]: 正在进入目录 `/application/skynet'
      cc -g -O2 -Wall -I3rd/lua -o skynet skynet-src/skynet_main.c skynet-src/skynet_handle.c skynet-src/skynet_module.c skynet-src/skynet_mq.c skynet-src/skynet_server.c skynet-src/skynet_start.c skynet-src/skynet_timer.c skynet-src/skynet_error.c skynet-src/skynet_harbor.c skynet-src/skynet_env.c skynet-src/skynet_monitor.c skynet-src/skynet_socket.c skynet-src/socket_server.c skynet-src/malloc_hook.c skynet-src/skynet_daemon.c skynet-src/skynet_log.c 3rd/lua/liblua.a 3rd/jemalloc/lib/libjemalloc_pic.a -Iskynet-src -I3rd/jemalloc/include/jemalloc -Wl,-E -lpthread -lm -ldl -lrt
      mkdir cservice
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared service-src/service_snlua.c -o cservice/snlua.so -Iskynet-src
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared service-src/service_logger.c -o cservice/logger.so -Iskynet-src
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared service-src/service_gate.c -o cservice/gate.so -Iskynet-src
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared service-src/service_harbor.c -o cservice/harbor.so -Iskynet-src
      mkdir luaclib
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared lualib-src/lua-skynet.c lualib-src/lua-seri.c -o luaclib/skynet.so -Iskynet-src -Iservice-src -Ilualib-src
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared lualib-src/lua-socket.c -o luaclib/socketdriver.so -Iskynet-src -Iservice-src
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -Iskynet-src lualib-src/lua-bson.c -o luaclib/bson.so -Iskynet-src
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared lualib-src/lua-mongo.c -o luaclib/mongo.so -Iskynet-src
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -I3rd/lua-md5 3rd/lua-md5/md5.c 3rd/lua-md5/md5lib.c 3rd/lua-md5/compat-5.2.c -o luaclib/md5.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared lualib-src/lua-netpack.c -Iskynet-src -o luaclib/netpack.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared lualib-src/lua-clientsocket.c -o luaclib/clientsocket.so -lpthread
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -Iskynet-src lualib-src/lua-memory.c -o luaclib/memory.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared lualib-src/lua-profile.c -o luaclib/profile.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -Iskynet-src lualib-src/lua-multicast.c -o luaclib/multicast.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -Iskynet-src lualib-src/lua-cluster.c -o luaclib/cluster.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared lualib-src/lua-crypt.c lualib-src/lsha1.c -o luaclib/crypt.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -Iskynet-src lualib-src/lua-sharedata.c -o luaclib/sharedata.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -Iskynet-src lualib-src/lua-stm.c -o luaclib/stm.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -Ilualib-src/sproto lualib-src/sproto/sproto.c lualib-src/sproto/lsproto.c -o luaclib/sproto.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -I3rd/lpeg 3rd/lpeg/lpcap.c 3rd/lpeg/lpcode.c 3rd/lpeg/lpprint.c 3rd/lpeg/lptree.c 3rd/lpeg/lpvm.c -o luaclib/lpeg.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared lualib-src/lua-mysqlaux.c -o luaclib/mysqlaux.so
      cc -g -O2 -Wall -I3rd/lua -fPIC --shared -Iskynet-src lualib-src/lua-debugchannel.c -o luaclib/debugchannel.so
      make[1]:正在离开目录 `/application/skynet'

4.错误集:

  • 错误一:
    由于系统时间设置错误,导致编译进入死循环,报错如下:

    make[2]: *** 警告:文件“Makefile.in”的修改时间在将来1.8e+06

    解决方案:

    • 假如当前系统时间是错误的,修正系统时间即可;
    • 假如是文件时间戳有误,可以使用 ind ./* -exec touch {} + 修正文件的时间戳。
  • 错误二:
    在虚拟机的共享目录下安装 skynet,由于虚拟机共享目录不能设置软连接:

    ln -sf libjemalloc.so.2 lib/libjemalloc.so
    ln: 无法创建符号链接"lib/libjemalloc.so": 不支持的操作
    make[2]: *** [lib/libjemalloc.so] 错误 1
    make[2]:正在离开目录 `/mnt/Windows/skynet/3rd/jemalloc'
    make[1]: *** [3rd/jemalloc/lib/libjemalloc_pic.a] 错误 2
    make[1]:正在离开目录 `/mnt/Windows/skynet'
    make: *** [linux] 错误 2

    解决方案:
    选择其他非共享的目录进行安装,例如直接复制:

    sudo cp -r skynet /application/

启动流程:

skynet 由一个或多个进程构成,每个进程被称为一个 skynet 节点。接下来尝试实现 skynet 节点 的启动流程。

1.配置文件Config

上面完成了源码编译,但是运行启动指令的时候,需要传入一个 Config文件 名称作为启动参数,skynet 会读取这个 Config文件 获取一些启动的必要参数,所以在运行程序之前,还需要根据要求修改配置文件,可以参考 example/config 或直接对其进行修改:

root = "./"
thread = 8
logger = nil
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main" -- main script
bootstrap = "snlua bootstrap" -- The service for bootstrap
standalOne= "0.0.0.0:2013"
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua"
lualoader = "lualib/loader.lua"
snax = root.."examples/?.lua;"..root.."test/?.lua"
cpath = root.."cservice/?.so"

不难看出,这个配置文件内存其实是一个lua代码,以 key-value 形式进行赋值,skynet 启动时读取必要配置项,其他项即便用不到也会以字符串的形式存入 env 表中,所有配置项都可通过 skynet.getenv 获取。

  • 必要的配置项有:

    • thread 启动多少个工作线程。通常不要将它配置超过你实际拥有的 CPU 核心数。
    • bootstrap skynet 启动的第一个服务以及其启动参数。默认配置为 snlua bootstrap ,即启动一个名为 bootstrap 的 lua 服务。通常指的是 service/bootstrap.lua 这段代码。
    • cpath 用 C 编写的服务模块的位置,通常指 cservice 下那些 .so 文件。如果你的系统的动态库不是以 .so 为后缀,需要做相应的修改。这个路径可以配置多项,以 ; 分割。
  • 在默认的 bootstrap 代码中还会进一步用到一些配置项:

    • logger 它决定了 skynet 内建的 skynet_error 这个 C API 将信息输出到什么文件中。如果 logger 配置为 nil ,将输出到标准输出。你可以配置一个文件名来将信息记录在特定文件中。
    • logservice 默认为 "logger" ,你可以配置为你定制的 log 服务(比如加上时间戳等更多信息)。可以参考 service_logger.c 来实现它。注:如果你希望用 lua 来编写这个服务,可以在这里填写 snlua ,然后在 logger 配置具体的 lua 服务的名字。在 examples 目录下,有 config.userlog 这个范例可供参考。
    • logpath 配置一个路径,当你运行时为一个服务打开 log 时,这个服务所有的输入消息都会被记录在这个目录下,文件名为服务地址。
      standalone 如果把这个 skynet 进程作为主进程启动(skynet 可以由分布在多台机器上的多个进程构成网络),那么需要配置 standalone 这一项,表示这个进程是主节点,它需要开启一个控制中心,监听一个端口,让其它节点接入。
    • master 指定 skynet 控制中心的地址和端口,如果你配置了 standalone 项,那么这一项通常和 standalone 相同。
    • address 当前 skynet 节点的地址和端口,方便其它节点和它组网。注:即使你只使用一个节点,也需要开启控制中心,并额外配置这个节点的地址和端口。
    • harbor 可以是 1-255 间的任意整数。一个 skynet 网络最多支持 255 个节点。每个节点有必须有一个唯一的编号。
      如果 harbor 为 0 ,skynet 工作在单节点模式下。此时 masteraddress 以及 standalone 都不必设置。
    • start 这是 bootstrap 最后一个环节将启动的 lua 服务,也就是你定制的 skynet 节点的主程序。默认为 main ,即启动 main.lua 这个脚本。这个 lua 服务的路径由下面的 luaservice 指定。
  • 集群服务用到的配置项:

    • cluster 它决定了集群配置文件的路径。
  • lua 服务由 snlua 提供,它会查找一些配置项以加载 lua 代码:

    • lualoader 用哪一段 lua 代码加载 lua 服务。通常配置为 lualib/loader.lua ,再由这段代码解析服务名称,进一步加载 lua 代码。snlua 会将下面几个配置项取出,放在初始化好的 lua 虚拟机的全局变量中。具体可参考实现。
    • SERVICE_NAME 第一个参数,通常是服务名。
    • LUA_PATH config 文件中配置的 lua_path
    • LUA_CPATH config 文件中配置的 lua_cpath
    • LUA_PRELOAD config 文件中配置的 preload
    • LUA_SERVICE config 文件中配置的 luaservice
    • luaservice lua 服务代码所在的位置。可以配置多项,以 ; 分割。 如果在创建 lua 服务时,以一个目录而不是单个文件提供,最终找到的路径还会被添加到 package.path 中。比如,在编写 lua 服务时,有时候会希望把该服务用到的库也放到同一个目录下。
    • lua_path 将添加到 package.path 中的路径,供 require 调用。
    • lua_cpath 将添加到 package.cpath 中的路径,供 require 调用。
    • preload 在设置完 package 中的路径后,加载 lua 服务代码前,loader 会尝试先运行一个 preload 制定的脚本,默认为空。
    • snaxsnax 框架编写的服务的查找路径。
    • profile 默认为 true, 可以用来统计每个服务使用了多少 cpu 时间。在 DebugConsole 中可以查看。会对性能造成微弱的影响,设置为 false 可以关闭这个统计。

    另外,你也可以把一些配置选项配置在环境变量中。比如,你可以把 thread 配置在 SKYNET_THREAD 这个环境变量里。你可以在 config 文件中写:

    thread=$SKYNET_THREAD

    这样,在 skynet 启动时,就会用 SKYNET_THREAD 这个环境变量的值替换掉 config 中的 $SKYNET_THREAD 了。

2.启动Skynet服务:

编译完成后,查询根目录的文件列表,发现生成了一个 skynet 可执行文件:

linsh@ubuntu:/application/skynet$ ls 
3rd HISTORY.md lualib platform.mk service-src test
cservice LICENSE lualib-src README.md skynet
examples luaclib Makefile service skynet-src

在skynet的根目录运行以下指令 ./skynet examples/config 启动skynet服务:

linsh@ubuntu:/application/skynet$ ./skynet examples/config
[:01000001] LAUNCH logger
[:01000002] LAUNCH snlua bootstrap
[:01000003] LAUNCH snlua launcher
[:01000004] LAUNCH snlua cmaster
[:01000004] master listen socket 0.0.0.0:2013
[:01000005] LAUNCH snlua cslave
[:01000005] slave connect to master 127.0.0.1:2013
[:01000004] connect from 127.0.0.1:34760 4
[:01000006] LAUNCH harbor 1 16777221
[:01000004] Harbor 1 (fd=4) report 127.0.0.1:2526
[:01000005] Waiting for 0 harbors
[:01000005] Shakehand ready
[:01000007] LAUNCH snlua datacenterd
[:01000008] LAUNCH snlua service_mgr
[:01000009] LAUNCH snlua main
[:01000009] Server start
[:0100000a] LAUNCH snlua protoloader
[:0100000b] LAUNCH snlua console
[:0100000c] LAUNCH snlua debug_console 8000
[:0100000c] Start debug console at 127.0.0.1:8000
[:0100000d] LAUNCH snlua simpledb
[:0100000e] LAUNCH snlua watchdog
[:0100000f] LAUNCH snlua gate
[:0100000f] Listen on 0.0.0.0:8888
[:01000009] Watchdog listen on 8888
[:01000009] KILL self
[:01000002] KILL self

其他资料:

  • 云风对Skynet的介绍视频:
    云风:基于 Actor 模式的开源框架

参考:

  • Skynet 设计综述
  • 云风skynet服务端框架研究
  • 云风的 BLOG: Skynet 开源
  • 云风的 BLOG: Skynet 集群及 RPC
  • skynet/wiki
    • GettingStarted
    • Config

推荐阅读
  • 字节跳动深圳研发中心安全业务团队正在火热招募人才! ... [详细]
  • 作为140字符的开创者,Twitter看似简单却异常复杂。其简洁之处在于仅用140个字符就能实现信息的高效传播,甚至在多次全球性事件中超越传统媒体的速度。然而,为了支持2亿用户的高效使用,其背后的技术架构和系统设计则极为复杂,涉及高并发处理、数据存储和实时传输等多个技术挑战。 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • Python多线程编程技巧与实战应用详解 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • Java并发机制详解及其在数据安全性保障中的应用方案 ... [详细]
  • 在PHP多线程扩展开发中,面临的主要挑战之一是多线程调用PHP用户类方法时可能出现的内存错误。具体表现为当多个线程同时调用同一个类实例的同一方法时,系统会抛出内存错误。为了解决这一问题,本文深入分析了PHP多线程扩展的实现机制,并提出了几种有效的解决方案和技术思路,包括线程安全的类设计、内存管理优化以及线程同步机制的改进。通过这些方法,可以显著提升PHP多线程扩展的稳定性和性能。 ... [详细]
  • 并发编程入门:初探多任务处理技术
    并发编程入门:探索多任务处理技术并发编程是指在单个处理器上高效地管理多个任务的执行过程。其核心在于通过合理分配和协调任务,提高系统的整体性能。主要应用场景包括:1) 将复杂任务分解为多个子任务,并分配给不同的线程,实现并行处理;2) 通过同步机制确保线程间协调一致,避免资源竞争和数据不一致问题。此外,理解并发编程还涉及锁机制、线程池和异步编程等关键技术。 ... [详细]
  • 在Python网络编程中,多线程技术的应用与优化是提升系统性能的关键。线程作为操作系统调度的基本单位,其主要功能是在进程内共享内存空间和资源,实现并行处理任务。当一个进程启动时,操作系统会为其分配内存空间,加载必要的资源和数据,并调度CPU进行执行。每个进程都拥有独立的地址空间,而线程则在此基础上进一步细化了任务的并行处理能力。通过合理设计和优化多线程程序,可以显著提高网络应用的响应速度和处理效率。 ... [详细]
  • 在Linux系统中,为了提高安全性,可以通过设置命令执行超时和用户超时注销来防止因用户长时间未操作而带来的安全隐患。具体而言,可以通过编辑 `/etc/profile` 文件,添加或修改相关参数,确保用户在指定时间内无操作后自动注销。此外,还可以利用 `timeout` 命令来限制特定命令的执行时间,进一步增强系统的稳定性和安全性。 ... [详细]
  • 在使用 PHP 通过 SSL 安全连接到 MySQLi 数据库服务器时,遇到了一些技术难题。我的环境包括一个 Web 服务器和一个数据库服务器,两者均使用 OpenSSL 生成了证书。尽管证书内容一致,但在尝试从 Web 服务器使用 `mysql` 命令进行连接时,仍然遇到了问题。为了确保连接的安全性和稳定性,需要进一步检查证书配置和 PHP 的 SSL 设置,以排除潜在的配置错误或兼容性问题。 ... [详细]
author-avatar
小伊果果_679
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有