做网站全包,外包服务管理制度,深圳签网站,做蛋糕的英文网站目录 1. golang 动态库2. golang 语言使用动态库、调用动态链接库2.1. Go 插件系统2.2. 动态加载的优劣2.3. Go 的插件系统#xff1a;Plugin2.4. 插件开发原则2.4.1. 插件独立2.4.2. 使用接口类型作为边界2.4.3. Unix 模块化原则2.4.4. 版本控制 2.5. 插件开发示例2.5.1. 编写… 目录 1. golang 动态库2. golang 语言使用动态库、调用动态链接库2.1. Go 插件系统2.2. 动态加载的优劣2.3. Go 的插件系统Plugin2.4. 插件开发原则2.4.1. 插件独立2.4.2. 使用接口类型作为边界2.4.3. Unix 模块化原则2.4.4. 版本控制 2.5. 插件开发示例2.5.1. 编写插件2.5.2. 使用插件 1. golang 动态库
2. golang 语言使用动态库、调用动态链接库
2.1. Go 插件系统
通过使用插件在运行时扩展程序的功能而无需重新编译程序这是一个很常见的功能需求特别是在模块化设计的程序里面比如 Nginx 的模块系统。 在 C/C中通过使用动态库的方式可以实现动态加载但是 Go 直到 1.8 官方才开始支持下面将介绍 Go 如何基于动态链接库来实现动态加载。
2.2. 动态加载的优劣
优点
动态加载也称热加载每次升级时不用重新编译整个工程重新部署服务而是添加插件时进行动态更新。这对于很多比较重型的服务来说非常重要。
缺点
带来一定的安全风险如果一些非法模块被注入如何防范给系统带来一定的不稳定的因素如果模块有问题没有经过良好的测试容易导致服务崩溃为版本管理带来了难题特别是在微服务的今天同一个服务加载了不同的插件应该怎么管理版本插件版本应该如何管理
因此请慎重考虑是使用动态插件还是在源码里面进行插件化。
2.3. Go 的插件系统Plugin
从 1.8 版开始官方提供了这种插件化的手段plugin. 此功能使程序员可以使用动态链接库构建松散耦合的模块化程序可以在运行时动态加载和绑定。
Go 插件是使用 -buildmode plugin 标记编译的一个包用于生成一个共享对象 (.so) 库文件。 Go 包中的导出的函数和变量被公开为 ELF 符号可以使用 plugin 包在运行时查找并绑定 ELF 符号。Go 编译器能够使用 build flag -buildmode c-shared 创建 C 风格的动态共享库。
1.8 版本插件功能只能在 Linux 上使用。 1.10 也可以在 Mac 上运行。
下面将介绍使用 Go 插件系统创建模块化软件的一些开发原则并提供一个功能齐全的示例。
2.4. 插件开发原则
使用 Go 插件创建模块化程序需要遵循与常规 Go 软件包一样严格的软件实践。然而插件引入了新的设计问题因为它们的解耦性质被放大了。因此我们在设计可插拔系统时有一些原则需要关注
2.4.1. 插件独立
应该将插件视为与其他组件分离的独立组件。这允许插件独立于他们的消费者并拥有自己的开发和部署生命周期。注意插件的可用性很重要因为它有肯能为整个系统带来不稳定的因素因此系统必须为插件集成提供一个简单的封装层插件开发人员将系统视为黑盒不作为所提供的合约以外的假设从而保证插件自身的可用性。
2.4.2. 使用接口类型作为边界
Go 插件可以导出任何类型的包函数和变量。您可以设计插件来将其功能解耦为一组松散的函数。缺点是您必须单独查找和绑定每个函数符号。 然而更为简单的方法是使用接口类型。创建导出功能的接口提供了统一简洁的交互并具有清晰的功能划分。解析到接口的符号将提供对该功能的整个方法集的访问而不仅仅是一个方法。
2.4.3. Unix 模块化原则
插件代码应该设计成只关注一个功能点。
2.4.4. 版本控制
插件是不透明而独立的实体应该进行版本控制以向用户提示其支持的功能。这里的一个建议是在命名共享对象文件时使用语义版本控制。例如上面的文件编译插件可以命名为 eng.so.1.0.0。
2.5. 插件开发示例
我以我遇到的一个实际需求为例在开发物联网接入组件的时候需要动态支持物解析下面就开发一个物解析的插件系统。
下面是项目结构parser.go 是接口规约main.go 是主程序plugins 存放多个插件包
├── main.go
├── parser.go
└── plugins├── car│ └── car.go└── phone└── phone.go2.5.1. 编写插件
编写主程序接口规约main.go
package main// Parser use to parse things
type Parser interface {
byte) (meta map[string]string, data map[string]float64, err error)
}根据接口规约编写插件car.go
package maintype car stringfunc (c *car) Parse([]byte) (meta map[string]string, data map[string]float64, err error) {
map[string]string{key1: a}
map[string]float64{key1: 1}return meta, data, nil
}var Car car根据接口规约编写插件phone.go
package maintype phone stringfunc (p *phone) Parse([]byte) (meta map[string]string, data map[string]float64, err error) {
map[string]string{key1: b}
map[string]float64{key1: 2}return meta, data, nil
}var Phone phone编译插件插件写完后将在 plugins 目录下编译插件
$ cd plugins
$ go build -buildmodeplugin -o car.so car/car.go
$ go build -buildmodeplugin -o phone.so phone/phone.go最终在 plugins 目录下会生成好我们编译好的插件
$ ls *.so
car.so phone.so2.5.2. 使用插件
插件的使用很简单大概步骤如下
用 plugin.Open() 打开插件文件用 plguin.Lookup(“Export-Variable-Name”) 查找导出的符号”Car”或者”Phone”。 请注意符号名称与插件模块中定义的变量名称相匹配使用该变量
主程序使用插件main.go
package mainimport (
fmt
plugin
)// Parser use to parse things
type Parser interface {
byte) (meta map[string]string, data map[string]float64, err error)
}func pa() {
./plugins/car.so)
if err ! nil {
panic(err)}Car)
if err ! nil {
panic(err)}p, ok : car.(Parser)
if ok {
byte(a))
if err ! nil {
panic(err)}
meta: %v, data: %v \n, meta, data)}
}func pb() {
./plugins/phone.so)
if err ! nil {
panic(err)}Phone)
if err ! nil {
panic(err)}p, ok : phone.(Parser)
if ok {
byte(a))
meta: %v, data: %v \n, meta, data)}
}func main() {pa()pb()
}测试是否正常运行
$ go run main.go
meta: map[key1:a], data: map[key1:1]
meta: map[key1:b], data: map[key1:2]