Go后端开发 -- Go Modules
Go后端开发 – Go Modules
文章目录
- Go后端开发 -- Go Modules
- 一、什么是Go Modules?
- 二、GOPATH的工作模式
- 1.GOPATH模式
- 2.GOPATH模式的弊端
- 三、Go Modules模式创建项目
- 1.go mod命令
- 2.go mod环境变量
- 3.使用Go Modules初始化项目
- 4.修改模块的版本依赖关系
- 四、Go Modules下import导本地包
- 1.所有包在同一项目目录下
- 2.import导入的包与main包不在同一个路径下
- 3.import导入本地包的格式
一、什么是Go Modules?
Go modules 是 Go 语言的依赖解决方案,发布于 Go1.11,成长于 Go1.12,丰富于 Go1.13,正式于 Go1.14 推荐在生产上使用。
Go moudles 目前集成在 Go 的工具链中,只要安装了 Go,自然而然也就可以使用 Go moudles 了,而 Go modules 的出现也解决了在 Go1.11 前的几个常见争议问题:
- Go 语言长久以来的依赖管理问题。
- “淘汰”现有的 GOPATH 的使用模式。
- 统一社区中的其它的依赖管理工具(提供迁移功能)。
二、GOPATH的工作模式
1.GOPATH模式
Go Modoules的目的之一就是淘汰GOPATH, 那么GOPATH是个什么?
我们输入go env
命令行后可以查看到 GOPATH 变量的结果,我们进入到该目录下进行查看,如下:
GOPATH目录下一共包含了三个子目录,分别是:
- bin:存储所编译生成的二进制文件。
- pkg:存储预编译的目标文件,以加快程序的后续编译速度。
- src:存储所有
.go
文件或源代码。在编写 Go 应用程序,程序包和库时,一般会以$GOPATH/src/github.com/foo/bar
的路径进行存放。
因此在使用 GOPATH 模式下,我们需要将应用代码存放在固定的$GOPATH/src
目录下,并且如果执行go get
来拉取外部依赖会自动下载并安装到$GOPATH
目录下。
2.GOPATH模式的弊端
在 GOPATH 的 $GOPATH/src
下进行 .go
文件或源代码的存储,我们可以称其为 GOPATH 的模式,这个模式拥有一些弊端.
- 无版本控制概念. 在执行
go get
的时候,你无法传达任何的版本信息的期望,也就是说你也无法知道自己当前更新的是哪一个版本,也无法通过指定来拉取自己所期望的具体版本。 - 无法同步一致第三方版本号. 在运行 Go 应用程序的时候,你无法保证其它人与你所期望依赖的第三方库是相同的版本,也就是说在项目依赖库的管理上,你无法保证所有人的依赖版本都一致。
- 无法指定当前项目引用的第三方版本号. 你没办法处理 v1、v2、v3 等等不同版本的引用问题,因为 GOPATH 模式下的导入路径都是一样的,都是
github.com/foo/bar
。
三、Go Modules模式创建项目
我们接下来用Go Modules的方式创建一个项目, 建议为了与GOPATH分开,不要将项目创建在GOPATH/src
下.
1.go mod命令
命令 | 作用 |
---|---|
go mod init | 生成 go.mod 文件 |
go mod download | 下载 go.mod 文件中指明的所有依赖 |
go mod tidy | 整理现有的依赖 |
go mod graph | 查看现有的依赖结构 |
go mod edit | 编辑 go.mod 文件 |
go mod vendor | 导出项目所有的依赖到vendor目录 |
go mod verify | 校验一个模块是否被篡改过 |
go mod why | 查看为什么需要依赖某模块 |
2.go mod环境变量
可以通过 go env
命令来进行查看
$ go env
GO111MODULE="auto"
GOPROXY="https://proxy.golang.org,direct"
GONOPROXY=""
GOSUMDB="sum.golang.org"
GONOSUMDB=""
GOPRIVATE=""
...
- GO111MODULE
Go语言提供了 GO111MODULE这个环境变量来作为 Go modules 的开关,其允许设置以下参数:- auto:只要项目包含了 go.mod 文件的话启用 Go modules,目前在 Go1.11 至 Go1.14 中仍然是默认值。
- on:启用 Go modules,推荐设置,将会是未来版本中的默认值。
- off:禁用 Go modules,不推荐设置。
设置GO111MODULE;
$ go env -w GO111MODULE=on
- GOPROXY
这个环境变量主要是用于设置 Go 模块代理(Go module proxy),其作用是用于使 Go 在后续拉取模块版本时直接通过镜像站点来快速拉取。
GOPROXY 的默认值是:https://proxy.golang.org,direct
proxy.golang.org
国内访问不了,需要设置国内的代理.- 阿里云
https://mirrors.aliyun.com/goproxy/
- 七牛云
https://goproxy.cn,direct
- 阿里云
设置GOPROXY:
$ go env -w GOPROXY=https://goproxy.cn,direct
GOPROXY 的值是一个以英文逗号 “,” 分割的 Go 模块代理列表,允许设置多个模块代理,假设你不想使用,也可以将其设置为 “off” ,这将会禁止 Go 在后续操作中使用任何 Go 模块代理。
$ go env -w GOPROXY=https://goproxy.cn,https://mirrors.aliyun.com/goproxy/,direct
而在刚刚设置的值中,我们可以发现值列表中有 direct
标识,它又有什么作用呢?
实际上 “direct” 是一个特殊指示符,用于指示 Go 回源到模块版本的源地址去抓取(比如 GitHub 等),场景如下:当值列表中上一个 Go 模块代理返回 404 或 410 错误时,Go 自动尝试列表中的下一个,遇见 “direct” 时回源,也就是回到源地址去抓取,而遇见 EOF 时终止并抛出类似 “invalid version: unknown revision…” 的错误。
-
GOSUMDB
它的值是一个 Go checksum database,用于在拉取模块版本时(无论是从源站拉取还是通过 Go module proxy 拉取)保证拉取到的模块版本数据未经过篡改,若发现不一致,也就是可能存在篡改,将会立即中止。
GOSUMDB 的默认值为:sum.golang.org,在国内也是无法访问的,但是 GOSUMDB 可以被 Go 模块代理所代理(详见:Proxying a Checksum Database)。
因此我们可以通过设置 GOPROXY 来解决,而先前我们所设置的模块代理 goproxy.cn 就能支持代理 sum.golang.org,所以这一个问题在设置 GOPROXY 后,你可以不需要过度关心。
另外若对 GOSUMDB 的值有自定义需求,其支持如下格式:- 格式 1:
<SUMDB_NAME>+<PUBLIC_KEY>
。 - 格式 2:
<SUMDB_NAME>+<PUBLIC_KEY> <SUMDB_URL>
。 - 也可以将其设置为“off”,也就是禁止 Go 在后续操作中校验模块版本
- 格式 1:
-
GONOPROXY/GONOSUMDB/GOPRIVATE
这三个环境变量都是用在当前项目依赖了私有模块,例如像是你公司的私有 git 仓库,又或是 github 中的私有库,都是属于私有模块,都是要进行设置的,否则会拉取失败。
更细致来讲,就是依赖了由 GOPROXY 指定的 Go 模块代理或由 GOSUMDB 指定 Go checksum database 都无法访问到的模块时的场景。
一般建议直接设置 GOPRIVATE,它的值将作为 GONOPROXY 和 GONOSUMDB 的默认值,所以建议的最佳姿势是直接使用 GOPRIVATE。
并且它们的值都是一个以英文逗号 “,” 分割的模块路径前缀,也就是可以设置多个,例如:
$ go env -w GOPRIVATE="git.example.com,github.com/eddycjy/mquote"
设置后,前缀为 git.xxx.com
和 github.com/eddycjy/mquote
的模块都会被认为是私有模块。
如果不想每次都重新设置,我们也可以利用通配符,例如:
$ go env -w GOPRIVATE="*.example.com"
这样子设置的话,所有模块路径为 example.com
的子域名(例如:git.example.com
)都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 example.com
本身。
- 以上的所有环境变量配置,如果执行命令后未修改成功,都可以直接在配置文件中修改:
vim ~/.bashrc
修改完后需加载配置文件:
source .bashrc
3.使用Go Modules初始化项目
- 创建项目目录
使用Go Modules创建项目无需在GOPATH
下创建文件夹
- 执行Go modules 初始化
执行完go mod
初始化后,项目文件夹中会出现一个go.mod
文件;
go.mod
文件内容:包括模块名称和go的版本
go mod初始化:
go mod init 模块名称
模块名称是自定义的,决定之后的代码在导入本包的时候import的名称;
- 接下来写一段代码,导入一个现成的包:
package mainimport ("fmt""github.com/aceld/zinx/ziface""github.com/aceld/zinx/znet"
)// ping test 自定义路由
type PingRouter struct {znet.BaseRouter
}// Ping Handle
func (this *PingRouter) Handle(request ziface.IRequest) {//先读取客户端的数据fmt.Println("recv from client : msgId=", request.GetMsgID(),", data=", string(request.GetData()))//再回写ping...ping...pingerr := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping"))if err != nil {fmt.Println(err)}
}func main() {//1 创建一个server句柄s := znet.NewServer()//2 配置路由s.AddRouter(0, &PingRouter{})//3 开启服务s.Serve()
}
我们先不要关注代码本身,我们看当前的main.go
也就是我们的aceld/modules_test
项目,是依赖一个叫github.com/aceld/zinx
库的, znet和ziface只是zinx的两个模块;
- 接下来我们在
$HOME/aceld/modules_test
,本项目的根目录执行go get
指令
go get github.com/aceld/zinx/znet
不主动执行go get
指令,也会自动下载
- 我们会看到 我们的
go.mod
被修改,同时多了一个go.sum
文件. - 查看
go.mod
文件
这里面的关键字:- module: 用于定义当前项目的模块路径
- go:标识当前Go版本.即初始化版本
- require: 当前项目依赖的一个特定的必须版本
- // indirect: 示该模块为间接依赖,也就是在当前应用程序中的 import 语句中,并没有发现这个模块的明确引用,有可能是你先手动 go get 拉取下来的,也有可能是你所依赖的模块所依赖的.我们的代码很明显是依的
"github.com/aceld/zinx/znet"
和"github.com/aceld/zinx/ziface"
,所以就间接的依赖了github.com/aceld/zinx
- 查看
go.sum
文件
在第一次拉取模块依赖后,会发现多出了一个go.sum
文件,其详细罗列了当前项目直接或间接依赖的所有模块版本,并写明了那些模块版本的 SHA-256 哈希值以备 Go 在今后的操作中保证项目所依赖的那些模块版本不会被篡改。
我们可以看到一个模块路径可能有如下两种:
h1:hash
情况:
go.mod hash
情况:
h1 hash 是 Go modules 将目标模块版本的 zip 文件开包后,针对所有包内文件依次进行 hash,然后再把它们的 hash 结果按照固定格式和算法组成总的 hash 值。
而 h1 hash 和 go.mod hash 两者,要不就是同时存在,要不就是只存在 go.mod hash。那什么情况下会不存在 h1 hash 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的 h1 hash,就会出现不存在 h1 hash,只存在 go.mod hash 的情况。
4.修改模块的版本依赖关系
为了作尝试,假定我们现在对zinx版本作了升级, 由zinx v1.2.1
升级到zinx v1.2.2
(注意zinx是一个没有打版本tag打第三方库,如果有的版本号是有tag的,那么可以直接对应v后面的版本号即可)
那么,如果我们想获得1.2.1版本的包,该如何执行命令呢?
- 先回到
$HOME/aceld/modules_test
,本项目的根目录执行
[lmx@lmx-CentOS modules_go]$ go get github.com/aceld/zinx/znet@v1.2.1
go: downloading github.com/aceld/zinx v1.2.1
go: downgraded github.com/aceld/zinx v1.2.2 => v1.2.1
go get package@version
在包后面加上@就可以下载指定版本的包;
如果要升级最新版本,可以执行
go get -u package
会更新所有依赖该包的版本
这样我们,下载了之前版本的zinx, 版本是v1.2.1
- 在
go.mod
中:
我们会看到,当我们执行go get 的时候, 会自动的将本地将当前项目的require更新了.变成了指定版本的依赖. - 我们想用一个最新版本的zinx. 来修改当前zinx模块的依赖版本号
目前我们在$GOPATH/pkg/mod/github.com/aceld
下,已经有了两个版本的zinx库
- 目前,我们的项目依赖的是
zinx@v1.2.1
这个不是最新版, 我们要改成最新版本zinx@v1.2.2
. - 回到
/goProject/modules_go
项目目录下,执行
$ go mod edit -replace=zinx@v1.2.1=zinx@v1.2.2
- 然后我们打开go.mod查看一下
- 这里出现了
replace
关键字.用于将一个模块版本替换为另外一个模块版本
四、Go Modules下import导本地包
1.所有包在同一项目目录下
一个文件夹中只能有一个包,多个包会报错
- 代码结构:
- go.mod
module function_gogo 1.20
- Lib1.go
Initlib1包
package Initlib1import "fmt"// lib1提供的API
func lib1Test() {fmt.Println("lib1Test()...")
}func init() {fmt.Println("lib1")
}
- Lib2.go
Initlib2包
package Initlib2import "fmt"// lib2提供的API
func lib2Test() {fmt.Println("lib2Test()...")
}func init() {fmt.Println("lib2")
}
- main.go
main包- 在使用Go Modules导入本地包的时候,在包名的前面需要指定模块名才可以导入
比如上面的go.mod
中模块名是function_go
,导入包的路径就是function_go/InitLib1
,是模块名/文件夹名
- 在导入了包之后,需要调用包中的方法,可以直接
包名.方法()
- 在使用Go Modules导入本地包的时候,在包名的前面需要指定模块名才可以导入
package mainimport ("function_go/InitLib1""function_go/InitLib2"
)func main() {InitLib1.Lib1Test()InitLib2.Lib2Test()
}
- 运行结果
lib1
lib2
libmain init
libmian main
2.import导入的包与main包不在同一个路径下
- 项目结构:
fun_go
和function_go
是两个不同的模块,如果要在fun_go
下的main.go
中导入function_go/InitLib1
包,也需要指定模块名:
package mainimport ("fmt""function_go/InitLib1" // 指定模块名
)func main() {InitLib1.Lib1Test()fmt.Println("go")
}
3.import导入本地包的格式
项目结构:
- go.mod
module expressiongo 1.20
- switch.go
package Switchimport "fmt"func Switch() {grade := 'B'marks := 90switch marks {case 90:grade = 'A'case 80:grade = 'B'case 70, 60, 50:grade = 'C'default:grade = 'D'}switch {case grade == 'A':fmt.Println("优秀")case grade == 'B':fmt.Println("良好")case grade == 'C':fmt.Println("及格")default:fmt.Println("不及格")}fmt.Println("你的等级是:", grade)
}
- main.go
package mainimport (Switch "expression/switch""fmt"
)func main() {Switch.Switch()fmt.Println("ll")
}
在main包中导入同一模块下的Switch包,导入的格式一定是模块名/文件夹名
,模块名后面跟的是包的路径,不是包名,写包名会导致报错,所以文件夹的名字最好和包名一致,这样方便不容易出错;
- 修改文件夹名之后:
- main.go
package mainimport ("expression/Switch" //模块名/包的文件夹名"fmt"
)func main() {Switch.Switch()fmt.Println("ll")
}
相关文章:

Go后端开发 -- Go Modules
Go后端开发 – Go Modules 文章目录 Go后端开发 -- Go Modules一、什么是Go Modules?二、GOPATH的工作模式1.GOPATH模式2.GOPATH模式的弊端 三、Go Modules模式创建项目1.go mod命令2.go mod环境变量3.使用Go Modules初始化项目4.修改模块的版本依赖关系 四、Go Modules下impo…...
基于det_keypoint_unite的ROS功能包(jetson部署)
文章目录 硬件软件FastDeploy编译CMakeLists.txt头文件源代码硬件 Jetson AGX Orin 64GB 软件 gcc/g++ >= 5.4(推荐8.2)cmake >= 3.10.0jetpack >= 4.6.1opencv=4.2.0FastDeploy编译 git clone https://github.com/PaddlePaddle/FastDeploy.git cd FastDeploy mkdi…...

TS 36.211 V12.0.0-下行(8)-调制和上变频
本文的内容主要涉及TS 36.211,版本是C00,也就是V12.0.0。...

基于SSM酒店后台管理系统【源码】【最详细运行文档】
基于SSM酒店后台管理系统【源码】【最详细运行文档】 功能简介技术描述运行准备♝项目运行访问项目 演示图✅源码获取 💡 「分享」 大家好,最近几年在酒店后台管理系统非常流行,无论是上课的项目或者是一些毕设都会以酒店后台管理系统举例说…...

利用Python实现每日新闻早报推送
本文将介绍如何使用Python编写简单的逻辑,通过调用API接口实现每日新闻推送功能。 步骤: 导入所需的库: 在代码的开头,我们需要导入所需的库。通常,我们会使用requests库来发送HTTP请求,以获取新闻数据。 …...

图像分割-Grabcut法
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 本文的C#版本请访问:图像分割-Grabcut法(C#)-CSDN博客 GrabCut是一种基于图像分割的技术,它可以用于将图像…...

性能测试浅谈
早期的性能测试更关注后端服务的处理能力。 一个用户去访问一个页面的请求过程,如上图。 数据传输时间 当你从浏览器输入网址,敲下回车,开始... 真实的用户场景请不要忽视数据传输时间,想想你给远方的朋友写信,信件需…...
媒体运营常用的ChatGPT通用提示词模板
媒体平台选择:如何选择合适的媒体平台,确保内容的有效传播? 内容策划与创作:如何策划和创作高质量的内容,吸引和留住目标受众? 内容发布与推广:如何有效地发布和推广内容,提高内容…...
Java学习苦旅(二十一)——泛型
本篇博客将详细讲解Java中的泛型。 文章目录 泛型的定义语法示例 泛型类语法示例类型边界语法示例 类型擦除通配符语法示例上界语法示例 下界语法示例 裸类型泛型方法语法示例 泛型的限制结尾 泛型的定义 语法 class 泛型类名称<类型形参列表> {//这里可以使用类型参数…...
具备闭环思维的测试才更充分
测试工作的终极目标是为了保障产品的质量。如果用同一个维度衡量测试人员的业务水平,简单粗暴一些:那就是针对同一款产品,哪个测试人员发现的bug多,哪个测试人员的测试理论与实践水平相对来说还是高一些。 前两天组长在群里分析了…...
flask web学习之模板(一)
文章目录 一、模板基本用法1.1 定界符1.2 模板语法1.3 渲染模板 二、模板辅助工具2.1 上下文2.2 全局对象2.3 过滤器2.4 测试器2.5 模板环境对象 在动态web程序中,视图函数返回的HTML数据往往需要根据相应的变量(比如查询参数)动态生成。当HT…...

RedisInsight - Redis官方可视化工具
一、RedisInsight 简介 RedisInsight 是一个直观高效的 Redis GUI 管理工具,它可以对 Redis 的内存、连接数、命中率以及正常运行时间进行监控,并且可以在界面上使用 CLI 和连接的 Redis 进行交互(RedisInsight 内置对 Redis 模块支持&#…...
Matlab定义函数计算斐波那契数列
以下是使用 MATLAB 定义函数计算并输出斐波那契数列前 200 个数的示例代码: function result fibonacci(n)if n < 1 || n > 200result NaN;elseif n 1 || n 2result 1;elseresult fibonacci(n-1) fibonacci(n-2);end endn 200; result fibonacci(n)…...
计算机网络面试题总结
总结自Network | JavaGuide(Java面试 学习指南) 什么是OSI7层模型? 什么是TCP/IP 四层模型? 为什么网络要分层? 应用层有哪些常见的协议? 传输层有哪些常见的协议? 网络层有哪些常见的协议? 从输入…...

视频转为序列图的软件,让视频批量转为序列图
你是否曾经遇到过这样的困境:需要将一段视频转为一系列的图片,但却没有合适的工具来完成?或许你曾经手动截图,或者用其他方式,但结果往往不尽如人意,图片质量差、色彩失真、画面不清晰。现在,让…...

目标检测中的常见指标
概念引入: TP:True Positive IoU > 阈值 检测框数量 FP: False Positive IoU < 阈值 检测框数量 FN: False Negative 漏检框数量 Precision:查准率 Recall:查全率(召回率) AP&am…...

QT上位机开发(会员充值软件)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 所有的控件当中,除了label、edit、radio、combobox和button之外,另外一个用的比较多的控件就是grid,也可称之为…...

小程序实现绘制图片 保存到手机
HTML <template><view><canvas canvas-id"myCanvas" :style"{height:380px,width:wWidthpx,background:#FFFFFF}"></canvas><view class"textCenter"><button click"saveCanvas">保存图片</b…...

Elasticsearch基本操作之索引操作
本文说下Elasticsearch基本操作之索引操作 文章目录 概述创建索引创建索引示例重复创建索引示例 查看索引查看所有索引查看单个索引 删除索引删除索引 概述 由于是使用命令来操作Elasticsearch,可以使用kibana,postman和apifox等工具 我使用了apifox来执…...

调用Java线程相关的API为什么能够控制操作系统线程?
今天我们解决Java线程的这五个问题: Java线程创建的完整流程 Java的线程是何时与JVM线程绑定的 JVM线程是何时与OS线程绑定的 Java线程对应的OS线程有什么特殊的地方 调用JavaAPI为什么能够操作OS线程 对于任何支持多线程的计算机语言来说,深入理解…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...