Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to solve the problem of registration and loading mechanism of Golang library plug-ins

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces how to solve the problem of Golang library plug-in registration loading mechanism, the article is very detailed, has a certain reference value, interested friends must read it!

Recently I saw a plug-in loading mechanism for an internal project, which is very good. Of course, the plug-in here does not refer to the golang native loading mechanism that can load specified so files in buildmode. It is a "plug-in" in software design. If your software is a framework, or a platform product, and you want to improve scalability, you can have third parties develop third-party libraries and eventually assemble them like building blocks. Then you may need this library loading mechanism.

What is our goal? Carry on some kind of library specification to the third-party library, as long as it is developed according to this library specification, the library can be loaded into the framework.

Let's first define the data structure of a plug-in, which certainly needs to be regulated by interfaces, which can be played freely according to your project. For example, I want the plug-in to have a Setup method to load at startup. Then I define the following Plugin structure.

Type Plugin interface {Name () string Setup (config map [string] string) error}

When the framework starts, I start a global variable like this:

Var plugins map [string] Plugin registration

One might ask, there is the loader function setup, but why is there no registration logic?

The answer is that the registered logic is placed in the library's init function.

That is, the framework also provides a registration function.

/ / package pluginRegister (plugin Plugin)

This register implements putting a third-party plugin into the plugins global variable.

So the third-party plugin library is roughly implemented as follows:

Package MyPlugintype MyPlugin struct {} func (m * MyPlugin) Setup (config map [string] string) error {/ / TODOfunc (m * MyPlugin) Name () string {return "myPlugin" func init () {plugin.Register (& MyPlugin)

So the logic of registration becomes that if you want to load a plug-in, you can simply introduce it in main.go in the form of _ import.

Package main_ import "github.com/foo/myplugin" func main () {}

On the whole, the registration of the plug-in is "hidden" into the import.

Load

The logic of registration actually seems mediocre, but the logic of loading tests the details.

First of all, there are two things to consider when loading a plug-in:

Configuration

Dependence

Configuration means that the plug-in must have some configuration that exists as the path to plugins.myplugin in the configuration file yaml.

Plugins: myplugin: foo: bar

In fact, I have reservations about this realization. Configuration files exist in the form of configuration items in a file, which seems to be better than configuration files, that is, files in config/plugins/myplugin.yaml.

This does not have the problem of a large configuration file. After all, each configuration file is itself an DSL language. If you complicate the logic of the configuration file, there must be a lot of accompanying bug caused by configuration file errors.

The second one is about dependence. Plug-in A depends on plug-in B, so there is the order in which the functions Setup are loaded. If this order depends solely on the user's "experience", it is very painful to put the Setup call of a plug-in before the Setup call of a plug-in. Although there must be a way to do it. A better approach is to rely on the loading mechanism of the framework itself to load.

First, we define an interface in the plugin package:

Type Depend interface {DependOn () [] string}

If my plug-in relies on a plug-in named "fooPlugin", then my plug-in MyPlugin will implement this interface.

Package MyPlugintype MyPlugin struct {} func (m * MyPlugin) Setup (config map [string] string) error {/ / TODOfunc (m * MyPlugin) Name () string {return "myPlugin" func init () {plugin.Register (& MyPlugin) func (m * MyPlugin) DependOn () [] string {return [] string {"fooPlugin"}

When we finally load all the plug-ins, we do not simply call all the plug-ins Setup, but use a channel, put all the plug-ins in channel, and then call Setup one by one. When we encounter other plug-ins in Depend and the dependent plug-ins have not been loaded, we put the current plug-in at the end of the queue (re-insert channel).

Var setupStatus map [string] bool// get all registered plug-ins func loadPlugins () (plugin chan Plugin, setupStatus map [string] bool) {/ / A queue of length 10 is defined here var sortPlugin = make (chan Plugin, 10) var setupStatus = make [string] bool// all plug-ins for name, plugin: = range plugins {sortPlugin 0 {plugin

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report