<p>Hi folks, I'm pretty new to Golang but having a blast so far. </p>
<p>I'm trying to use <code>go-ethereum</code>'s abigen ( <a href="https://github.com/ethereum/go-ethereum/tree/master/cmd/abigen" rel="nofollow">https://github.com/ethereum/go-ethereum/tree/master/cmd/abigen</a> ) using cmd.Exec in my code, like so: </p>
<p><a href="https://play.golang.org/p/cdbxOF099-" rel="nofollow">https://play.golang.org/p/cdbxOF099-</a> </p>
<p>Now, what this does is generate a <code>*contract*.go</code> file with a bunch of bindings ready for me to use. It is here I am at a loss for how to inject these newly created functions, which look like this: </p>
<p><a href="https://play.golang.org/p/clkBCnyd6p" rel="nofollow">https://play.golang.org/p/clkBCnyd6p</a> </p>
<p>As the normal process for using <code>abigen</code> is to use it in the command line yourself, then build/run your Go app. I will be needing to create these bindings dynamically like this quite often, and am just trying out different approaches to see what works.</p>
<hr/>**评论:**<br/><br/>peterbourgon: <pre><p>There's no concept of "injecting" or "adding" generated code to an existing Go binary. You need to recompile the binary with the new code, which requires invoking the <code>go</code> compiler, terminating the existing process, and re-starting it.</p></pre>koresho: <pre><p>Not the downvoter, but you’re being downvoted because there is literally plug-in support in the standard library: <a href="https://golang.org/pkg/plugin/" rel="nofollow">https://golang.org/pkg/plugin/</a> </p>
<p>(It’s not great yet, and comes with a ton of caveats, but it does exist). </p></pre>ChristophBerger: <pre><p>You are right about the plugin option, but this still requires building the plugin; you cannot have a Go binary execute Go <em>source</em> code on the fly. </p>
<p>Also worth noting is that, at least in its current state, the <code>plugin</code> package allows loading a plugin but not unloading it again. So plugins are probably not the best way of injecting new code on a frequent basis (which, as I understand it, is what the OP wants to do), as they will accumulate over time until the binary gets restarted.</p></pre>itsmontoya: <pre><p>Also you can't reload the plug-in if it updates</p></pre>diabetesjones: <pre><p>I managed to get code injected to an existing Go binary within an hour of posting this topic. I appreciate you posting regardless, but if you don't know your answer is correct and phrase it like an absolute, then your comment hurts more than it helps.</p></pre>bobbafeftta: <pre><p>Peter is indeed correct, but perhaps may have misinterpreted your intentions.</p>
<p>I think the terminology used here may be confusing. Perhaps you mean you have recompiled to dynamically load a library (binary) during run-time?</p></pre>diabetesjones: <pre><p>Yes, exactly. My intention was just to get a dynamically created .go's functions, variables, and structs available to me. I see now I need to package my structs outside in a shared package!</p></pre>albatr0s: <pre><p>If you want to load it from a running instance you may want to take a look at <a href="https://golang.org/pkg/plugin/" rel="nofollow">https://golang.org/pkg/plugin/</a> . In this case it looks like you should run <code>abigen</code>, drop the result in your source tree and then run <code>go build</code>.</p></pre>diabetesjones: <pre><p>So I should run <code>go build -buildmode=plugin -o out/contract.so in/contract.go</code> in my go code using cmd.Exec like I did abigen to generate the contract.go file, and then I can use plugin like <code>plug, err := plugin.Open('out/contract.so')</code> from within my running instance, with the methods available on my <code>plug</code> var, like <code>plug.Method()</code> as long as <code>Method()</code> is exported from my original .go file. </p>
<p>Am I on the right track? </p>
<p>edit: Okay, so I think I need to use <code>m, err := plug.Lookup('Method')</code> first and then I have the <code>m</code> var I can type-check against to make sure I have a function or interface or whatever. </p>
<p>edit2: Okay, I got everything working EXCEPT - only functions and variables are exported in the Plugin. So, if I have <code>func DeployDadToken() (*DadToken, err)</code>, how do i type assert that DadToken in the file I just imported the plugin in to, as I do not have access to it? </p></pre>zevdg: <pre><p>So it depends on your exact use case, but 99 times out of 100, the plugin bulidmode is the wrong tool. It's much simpler to just recompile and bounce if you can, or if you cannot, then compile the new code into a separate process on the fly and then use some kind of <a href="https://golang.org/pkg/net/rpc/" rel="nofollow">standard</a> RPC <a href="https://grpc.io" rel="nofollow">library</a> or <a href="https://github.com/hashicorp/go-plugin" rel="nofollow">framework</a> to communicate with the original process. The plugin package has a ton of additional complexity and disadvantages when compared to separate processes and RPC. </p>
<p>I would very much encourage you to avoid the plugin package unless you fully understand the tradeoffs. For example, consider what happens if your plugin shares a dependency with your main binary, but was built with a different version of that dependency. This is fine with 2 processes communicating via RPC but is a big problem when using the plugin package. What if your plugin needs to be updated? It's trivial to bounce a separate process to an updated version, but there is no way to unload or reload a plugin once it has been loaded.</p>
<p>I strongly recommend checking out <a href="https://youtu.be/x-LhC-J2Vbk" rel="nofollow">this video</a> that explains when it would make sense to use each of the various build modes including plugin.</p></pre>diabetesjones: <pre><p>In a situation where I have a Go service dispatching workers onto jobs from RabbitMQ and dynamically using that <code>abigen</code> function to make my .go files - what is the best way to recompile on-the-fly to load that code in, without stopping my numerous other workers who are doing something similar? They will need to be doing this task many, many times in my app, so I think I'm thinking about this the wrong way. This "bouncing" you mentioned where it's trivial to bounce a separate process to an updated version sounds very confusing to me, if you meant that I can do it dynamically from within my code. </p>
<p>I'm basically attempting proof of concepts here to see what works, and can see that this plugin / reflection method is going to cause many headaches... </p>
<p>EDIT: seems like it's easier to just replicate the code that the abigen command does, in my code itself, by <code>go getting</code> the packages themselves.</p></pre>zevdg: <pre><p>So I'm understanding your edit correctly that there is a way a way you can achieve your goals without generating new go source code on the fly, that's probably the best option.</p>
<p>But to answer your question and clarify what I meant. For the sake of example, lets pick <a href="https://github.com/hashicorp/go-plugin" rel="nofollow">https://github.com/hashicorp/go-plugin</a> to manage your RPC although it may be overkill for your purposes. The <a href="https://gist.github.com/karalabe/5839509295afa4f7e2215bc4116c7a8f" rel="nofollow">output of abigen</a> doesn't contain a main method, so by itself, it cannot be compiled into a process. You would need to have a main method as well in a separate plugin_main.go file. That file method would look something <a href="https://github.com/hashicorp/go-plugin/blob/master/examples/basic/plugin/greeter_impl.go" rel="nofollow">like this</a> but the interface it would expose would be implemented by the generated code instead of that simple GreeterHello type. Also worth noting, <a href="https://github.com/hashicorp/go-plugin/blob/master/examples/basic/commons/greeter_interface.go" rel="nofollow">that exposed interface</a> would need to be in a separate package that is imported in your original binary as well as in plugin_main.go. This interface absolutely must be known in advance. Hopefully, you could design the interface so that your plugin_main.go is exactly the same for every plugin that you load, but you may have to generate a slightly different one for each plugin using a simple template. So putting it all together, your psudocode might look something like this</p>
<pre><code>import (
"github.com/org/project/sharedinterface
"github.com/hashicorp/go-plugin"
)
//to keep the example simple i'm assuming that you only need 1 plugin running at a time
//you could easily keep a map of these instead of a single one
var currentClient *plugin.Client
func newPlugin(<some inputs>) (plugin.Client, error) {
runCmd("abigen", "with", "input");
//generatePluginMain("some", "input") //if necessary
runCmd("go", "build", "-o", "foo_plugin", "/path/to/plugin/dir") //containing plugin_main.go and abigen_output.go
return plugin.NewClient(&plugin.ClientConfig{
Plugins: map[string]plugin.Plugin{ "foo": &sharedinterface.Plugin{}}
Cmd: exec.Command("./foo_plugin"), //external process to start
//other config options here
})
}
func onRabbitMqMessage(<some input>){
if currentClient != nil {
currentClient.Kill() //bring down the old plugin process
}
currentClient, err = newPlugin(<inputs>) //generate and start the new plugin process
//handle error
//dispense foo plugin
//do things
//see https://github.com/hashicorp/go-plugin/blob/master/examples/basic/main.go
}
</code></pre>
<p>I hope that clears some things up.</p></pre>DeedleFake: <pre><p>Put common types in a third package and then have the function in the plugin return that. If you want to be able to use a type defined by the plugin, put an interface in the third package and have the plugin-defined type implement it. For example:</p>
<pre><code>package api
type Example interface {
Something(int) error
}
package callee
func InitPlugin() Example {
return &exampleImpl{}
}
package main
import (
"api"
"plugin"
)
func main() {
p, _ := plugin.Open("callee.so")
ip, _ := p.Lookup("InitPlugin")
ex := ip.(func() Example)
err := ex.Something(3)
if err != nil {
panic(err)
}
}
</code></pre></pre>diabetesjones: <pre><p>This looks good - can I ask you one more question? </p>
<p>My dynamically created .go file requires me to get that struct, DadToken, which will be dynamically named. The struct looks like this:</p>
<pre><code>type DadCoin struct {
DadCoinCaller // Read-only binding to the contract
DadCoinTransactor // Write-only binding to the contract
}
// DadCoinCaller is an auto generated read-only Go binding around an Ethereum contract.
type DadCoinCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// DadCoinTransactor is an auto generated write-only Go binding around an Ethereum contract.
type DadCoinTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
</code></pre>
<p>I obviously won't be able to make an interface with DadcoinCaller and Transactor in my shared library, and the Caller/Transactor each have the same parameter - a contract *bind.BoundContract. These are NOT dynamic and will always be the same, it is simply the name of the Structs that will change. How can I make my shared library interface implement this?</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传