Golang实践:so插件plugin

发布于 2020-05-28 · 本文总共 4332 字 · 阅读大约需要 13 分钟

go Plugin介绍

Package plugin implements loading and symbol resolution of Go plugins.

A plugin is a Go main package with exported functions and variables that has been built with:

go build -buildmode=plugin When a plugin is first opened, the init functions of all packages not already part of the program are called. The main function is not run. A plugin is only initialized once, and cannot be closed.

Currently plugins are only supported on Linux, FreeBSD, and macOS. Please report any issues.

main函数执行前plugin的init函数不会执行,插件只被初始化一次,不能被关闭

只支持Linux, FreeBSD, and macOS,不支持Windows

plugin库接口文档

https://golang.google.cn/pkg/plugin/#pkg-overview

使用场景

  • 需要加密的核心算法,核心业务逻辑可以可以编译成plugin插件

  • 对于不同功能加载相应的模块并调用相关的模块

  • 编译出的文件给不同的编程语言用(C/java/python/等)

注意

Go plugin判断两个插件是否相同是通过比较pluginpath实现的,如果没有指定pluginpath,则由内部的算法生成, 生成的格式为plugin/unnamed-“ + root.Package.Internal.BuildID 。 这种情况下,如果两个插件的文件名不同,引用包不同,或者引用的cgo不同,则会生成不同的插件,同时加载不会有问题。 但是如果两个插件的文件名相同,相关的引用包也相同,则可能生成相同的插件,即使插件内包含的方法和变量不同,实现也不同。 判断插件相同,热加载不会成功,也就意味着老插件不支持覆盖更新。

最好在编译的指定pluginpath,同时方便版本跟踪。 目前生产环境建议一些公共库无服务依赖的函数,例如算法库之类的。

实战

plugin1.go

package main

import (
	"fmt"
	"log"
)

func init() {
	log.Println("plugin1 init ....")
}

type Plugin1 struct {
	Name string
}

func (p Plugin1) DoServiceTask() error {
	log.Println(fmt.Sprintf("Plugin %s working ...", p.Name))
	return nil
}

//go build -buildmode=plugin -o=plugin1.so plugin1.go

// PluginService
var PluginService = Plugin1{"Service"}

plugin2.go

package main

import (
	"fmt"
	"log"
)

func init() {
	log.Println("plugin2 init ....")
}

type Plugin2 struct {
	Name string
}

func (p Plugin2) DoLabelTask() error {
	log.Println(fmt.Sprintf("Plugin %s working ...", p.Name))
	return nil
}

//go build -buildmode=plugin -o=plugin1.so plugin1.go

// exported as symbol named "PluginLabel"
var PluginLabel = Plugin2{"label"}

plugin3.go

package main

import (
	"fmt"
	"log"
)

func init() {
	log.Println("plugin3 init ....")
}

type Plugin3 struct {
	Name string
}

func (p Plugin3) DoRiskPortTask() error {
	log.Println(fmt.Sprintf("Plugin %s working ...", p.Name))
	return nil
}

//go build -buildmode=plugin -o=plugin1.so plugin1.go

// PluginRiskPort exported as symbol named "PluginRiskPort"
var PluginRiskPort = Plugin3{"riskport"}

use_plugin.go

package main

import (
	"fmt"
	"log"
	"plugin"
)

type PluginInf interface {
	DoServiceTask() error
}

type PluginInf2 interface {
	DoLabelTask() error
}

type PluginInf3 interface {
	DoRiskPortTask() error
}

func UsePlugin1() error {

	plug, err := plugin.Open("./plugin1.so")
	if err != nil {
		log.Println("error", err.Error())
		return err
	}
	log.Println("plugin opened")

	s, err := plug.Lookup("PluginService")
	if err != nil {
		log.Println(err.Error())
		return err
	}

	service, ok := s.(PluginInf)
	if !ok {
		msg := "unexpected type from module symbol"
		log.Println(msg)
		return fmt.Errorf("error: %s", msg)
	}

	if err := service.DoServiceTask(); err != nil {
		log.Println("use plugin service failed, ", err)
		return err
	}

	return nil

}

func UsePlugin2() error {

	plug, err := plugin.Open("./plugin2.so")
	if err != nil {
		log.Println("error", err.Error())
		return err
	}
	log.Println("plugin opened")

	s, err := plug.Lookup("PluginLabel")
	if err != nil {
		log.Println(err.Error())
		return err
	}

	service, ok := s.(PluginInf2)
	if !ok {
		msg := "unexpected type from module symbol"
		log.Println(msg)
		return fmt.Errorf("error: %s", msg)
	}

	if err := service.DoLabelTask(); err != nil {
		log.Println("use plugin label failed, ", err)
		return err
	}

	return nil

}

func UsePlugin3() error {

	plug, err := plugin.Open("./plugin3.so")
	if err != nil {
		log.Println("error", err.Error())
		return err
	}
	log.Println("plugin opened")

	s, err := plug.Lookup("PluginRiskPort")
	if err != nil {
		log.Println(err.Error())
		return err
	}

	service, ok := s.(PluginInf3)
	if !ok {
		msg := "unexpected type from module symbol"
		log.Println(msg)
		return fmt.Errorf("error: %s", msg)
	}

	if err := service.DoRiskPortTask(); err != nil {
		log.Println("use plugin3 failed, ", err)
		return err
	}

	return nil

}

server.go

package main

import (
	"fmt"
	"net/http"
	"time"
)

func ServerHandle1(w http.ResponseWriter, r *http.Request) {

	UsePlugin1()

	w.Write([]byte(fmt.Sprintf("use plugin1, time now: %+v\n", time.Now())))
}

func ServerHandle2(w http.ResponseWriter, r *http.Request) {

	UsePlugin2()

	w.Write([]byte(fmt.Sprintf("use plugin2, time now: %+v\n", time.Now())))
}

func ServerHandle3(w http.ResponseWriter, r *http.Request) {

	UsePlugin3()

	w.Write([]byte(fmt.Sprintf("use plugin3, time now: %+v\n", time.Now())))
}

func StartServer() {
	mux := http.NewServeMux()
	mux.HandleFunc("/api/v1/mss/service", ServerHandle1)
	mux.HandleFunc("/api/v1/mss/label", ServerHandle2)
	mux.HandleFunc("/api/v1/mss/port", ServerHandle3)
	fmt.Println("Start server....")
	http.ListenAndServe("", mux)
}

编译

go build main.go use_plugin.go plugin1.go plugin2.go plugin3.go server.go

运行




本博客所有文章采用的授权方式为 自由转载-非商用-非衍生-保持署名 ,转载请务必注明出处,谢谢。
声明:
本博客欢迎转发,但请保留原作者信息!
博客地址:邱文奇(qiuwenqi)的博客;
内容系本人学习、研究和总结,如有雷同,实属荣幸!
阅读次数:

文章评论

comments powered by Disqus


章节列表