假设我们有一个投标系统(用Java 8编写),根据某些算法(投标策略)和一定的条件,我们会在某一时刻增加或减少投标量。我们有不同的定义算法,但现在我们希望我们的用户定义自己的算法。
用户可以在我们的算法中选择他想要的投标策略。
这个想法是让他们编写代码,然后将这个算法保存为一个新的选项(在我们所有的算法中)用于他们的投标策略。
所以,我考虑了这两个选择:
选项2可能不是一个好主意,因为每个用户可能有数百万的出价,所以这种方法可能有太多的延迟。但我想知道,1和2是否是一个好办法,还是有更好的方法来处理这一要求?
发布于 2018-04-26 13:21:09
这是一个困难的问题:如何安全地执行这些用户提供的代码,以及如何确保良好的用户体验?
如果您代表其他人运行代码,则不能信任该代码。代码可能试图占用计算资源(因此给您带来成本,并拒绝给其他投标人提供计算资源),可能试图黑入您的系统或其他投标人的代码,或者仅仅使用您作为僵尸网络…的一部分提供的计算资源。滥用的可能性是无限的。因此,您需要非常严格地对这段代码进行沙箱。Java安全模型是一个很好的开端,它允许您拒绝反射和其他不受信任代码的API。但是,您可能仍然很难可靠地限制可用的计算资源。
下一个问题是用户体验。用户如何编写、调试和测试他们的代码?你能提供一种IDE吗?您为该代码提供了哪些API?它是怎么记录下来的?是否有一个可以尝试投标策略的测试环境?您将如何收集和显示错误消息?
如果编译Java代码,则受Java安全模型的限制。这可能已经足够了,假设您精通该安全模型(我不是),并且可以适当地配置它。您可以轻松地缓存已编译的代码,这样就不必为每次执行重新编译它。Java有成熟的工具,所以提供一种IDE是很简单的。
如果您使用任何其他基于JVM的语言,如Groovy,那么同样的注意事项也适用。最后,一切都将以JVM字节码的形式结束,因此速度会相当快。
如果您集成了一种非JVM脚本语言(一种现有的语言,如JavaScript或您自己设计的DSL ),那么您可以更多地控制代码和不受信任的用户代码之间的安全边界。然而,这可能会慢一点。特别是,实现您自己的DSL通常是非常容易出错的,而且通常很慢。
如果进程内沙箱不足以满足您的需要,请考虑进程级或操作系统级沙箱:容器化和虚拟化。您将在单独的进程中运行用户代码,并通过套接字进行通信。这不仅提高了安全性,还支持了更大的可伸缩性。但是,任何输入/输出都必须序列化,这会限制性能。
与构建文本DSL不同,您还可以设计一个规则引擎。基本上,您将提供用户可以在GUI中组合的可配置块。对于非程序员来说,这更容易实现。提供或不提供某些块(例如循环、函数声明、…)您可以影响结果程序的成本,同时仍然提供比一组预先配置的策略更灵活的功能。当您控制所有可用的块时,您还可以创建一个清晰的安全边界,而不必使用复杂的隔离特性。
除非您的出价必须在几秒钟内解决,否则还有另一种选择:提供REST。这样,您就不必执行任何不受信任的代码。有非常成熟的工具来创建API。有一个非常明确的安全边界。你可以很容易地限制过度的用户。使用API,用户可以订阅事件提要,并通过将其操作发布到API端点来做出反应。这样,他们就可以完全自由地制定他们的策略。然而,他们必须提供自己的计算资源。这在很大程度上取决于您的市场:您的产品是为消费者设计的,还是面向B2B或开发人员的?
但是,无论您选择什么,提供一个可用的测试环境等其他考虑因素可能比仅仅集成脚本语言或提供API要复杂得多。
我建议自问和/或向利益攸关方澄清:
发布于 2018-04-26 12:51:01
这两个选项都是完全合理的,但是对于您的场景来说,选项1将是更有效的。
编译需要显著的一次性开销(大约为0.1秒),但确实会导致更快的执行。如果您执行了数百万次的策略,那么从长远来看,编译就会有回报。只需确保您没有编译数百万次!
您可能不希望在从DB中提取字符串之后进行编译。您可以预先编译,然后将.class文件保存在DB中。这将允许您开始执行最初的几个出价更快。
https://softwareengineering.stackexchange.com/questions/369981
复制相似问题