Golang plugin implementation
This repository explores the plugin feature introduced in Go 1.8, providing a example to help you understand and utilize this powerful functionality.
About plugin feature in Golang
Starting from Go 1.8, developers have the ability to write plugins and load them dynamically at runtime. This feature adds a new level of flexibility to Go programs, allowing for modular and extensible designs. With plugins, you can extend your application's functionality without the need to recompile or redeploy the entire codebase.
Key benefit
- Dynamic Loading: Plugins can be loaded and unloaded dynamically at runtime, enabling the addition or removal of functionality without disrupting the main program.
- Flexibility: Unlike traditional library imports, which are resolved at build time, plugins offer a more flexible approach. They can be swapped or updated independently, giving you the freedom to iterate and experiment with different implementations.
- Modularity: The plugin feature promotes a modular design pattern by encapsulating specific functionalities in separate components. This enhances code organization, reusability, and maintainability.
- Encapsulation: Plugins run in their own isolated namespaces, preventing conflicts between different plugins or the main program. This allows for secure and reliable extensibility.
How to run this example:
- Build plugins
go build -buildmode=plugin -o ./plugins/eng/eng.so ./plugins/eng/speaker.go
You can use the Makefile to quickly build all plugins:
make build-plugins
- Run
go run main.go english
# Alice says "hello" in English
go run main.go vietnamese
# Anh Thư says "xin chào" in Vietnamese
How to implement a plugin
- A plugin package must be identified as main.
- Exported package functions and variables become shared library symbols. In the above, variable Speaker will be exported as a symbol in the compiled shared library.
Example implementation:
package main
type speaker struct {
}
func (s *speaker) Speak() string {
return "hello"
}
// Exported
var Speaker speaker
var SpeakerName = "Alice"
In this example, we are exporting both the Speaker
type and the SpeakerName
variable, which are referred to as symbols
in the context of plugins. By exporting these symbols, we enable their accessibility and visibility to other packages and modules that import them.
How to open plugin
plugin, err := plugin.Open("path/to/plugin.so")
if err != nil {
return err
}
How to look up symbol
You can look up the symbol that has been exported in plugin implementation by the following syntax:
symSpeaker, err := plugin.Lookup("Speaker")
if err != nil {
return err
}
var speaker Speaker
speaker, ok := symSpeaker.(Speaker)
if !ok {
return errors.New("unexpected type from module symbol")
}
speaker.Speak()
Top comments (2)
I made a plugin system using this tutorial that allows any custom gofiber http handler to be attached from the plugin, if anyone is interested on a real-world example.
The conversion of types received from the plugin is the tricky part, but i got it working.
You can also see in this repo a local docker-compose setup with separate dockerfiles for the main program and the plugin, with
air
for rebuilding on save.github.com/AndreiTelteu/hestia-go/...
That's amazing ! So useful.
If adopted by k6 for example will be super easy to install plugins like github.com/szkiba/xk6-dashboard or k6-browser without having to rebuild k6 every time you want to add a plugin. Just download the plugin and place it in the plugins folder :D
Also they can make it even simpler by having a command like
k6 plugin add k6-browser