【问题标题】:Is it possible to modify Clojure code running as a Spring component on a remote server and download the modified code using the REPL?是否可以修改在远程服务器上作为 Spring 组件运行的 Clojure 代码并使用 REPL 下载修改后的代码?
【发布时间】:2021-09-30 10:35:03
【问题描述】:

假设我有以下设置:

  1. 一个 Spring Boot 应用程序。
  2. 内部是一个 Camunda 工作流引擎。
  3. 该应用程序中有多个组件 (@org.springframework.stereotype.Component) 是用 Clojure 编写的,供 Camunda 工作流引擎使用。

我听说据说可以在不重新启动 Clojure 应用程序的情况下修改它的代码。

所以,我想

  1. 修改这些组件的代码(无需重新启动应用程序),
  2. 向应用程序添加新组件(无需重新启动应用程序),并且
  3. 完成原型设计后,下载所有组件的当前版本。

我的想法是我从 REPL 中对组件进行原型设计,直到它们按设计工作。这意味着 Camunda 工作流引擎将使用我在 REPL 上的操作修改的组件。

然后,我下载应用程序中组件的当前版本(以便在应用程序关闭时它们不会丢失)。然后这些代码会被清理、重构、单元测试覆盖并置于版本控制之下。

问题:

  1. 理论上是否可以使用 Clojure 实现这样的工作流程(不一定是开箱即用)?
  2. 是否有任何已知的限制会使此类工作流程绝对不可能?

更新 1

发现以下项目,这些项目表面上允许您使用 REPL 与 Java 代码进行交互:

  1. spring-boot-bugger
  2. spring-repl

不过,不知道能不能用它们来改代码。

【问题讨论】:

    标签: java spring spring-boot clojure read-eval-print-loop


    【解决方案1】:

    这是一个非常粗略和简单的示例,说明如何执行此操作。我有 在这里放弃了 Camunda,因为我认为它实际上并不相关。

    这里的方法:

    • 创建委托给实际 Clojure 代码的 Spring 服务。
    • 确保将该代码的“当前良好版本”加载到您的 过程
    • 启动“服务器 REPL”以允许覆盖

    完整的项目可以在here找到

    提供一个委托给一些 Clojure 代码的服务:

    @Service
    class ClojureBackedService {
        BigDecimal add(BigDecimal a, BigDecimal b) {
            Clojure.var('net.ofnir.repl', 'add').invoke(a, b)
        }
    }
    

    启动一个 REPL 并加载一些“良好的初始设置”:

    @Service
    class ClojureRepl {
    
        @PostConstruct
        def init() {
            def require = Clojure.var('clojure.core', 'require')
            require.invoke(Clojure.read('net.ofnir.repl'))
            Clojure.var('clojure.core.server', 'start-server').invoke(
                    Clojure.read("{:port 5555 :name spring-repl :accept clojure.core.server/repl}")
            )
        }
    
        @PreDestroy
        def destroy() {
            Clojure.var('clojure.core.server', 'stop-server').invoke(
                    Clojure.read("{:name spring-repl}")
            )
        }
    
    }
    

    为了展示它是如何工作的,提供一个网络端点,它增加了两个 数字:

    @RestController
    class MathController {
        private final ClojureBackedService backend
    
        MathController(ClojureBackedService backend) {
            this.backend = backend
        }
    
        @PostMapping
        def add(BigDecimal a, BigDecimal b) {
            backend.add(a, b)
        }
    }
    

    运行应用程序并进行第一次测试:

    $ curl -da=5 -db=5 localhost:8080                                     
    10
    

    看起来不错。现在连接到 REPL 并替换 add 函数:

    $ telnet localhost 5555
    Trying ::1...
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    user=> (ns net.ofnir.repl)
    nil
    net.ofnir.repl=> (defn add [a b] (* a b))
    #'net.ofnir.repl/add
    

    不再调用端点:

    $ curl -da=5 -db=5 localhost:8080
    25
    

    你的 spring 服务在运行时发生了变化。

    【讨论】:

    • 关于“下载”方面,如果您在 IDE 中进行编辑,您可以连接到远程 repl。您正在评估的代码随身携带,无需下载。最后,如果您的 clojure 代码正在实现 Java 接口,通常最好使您创建的对象只是普通旧函数的适配器。否则你会遇到麻烦——当你编辑一个 defrecord 或其他东西时,它不会自动改变现有的实例。
    【解决方案2】:

    如果您需要更新作为例如一部分执行的代码无需重新启动流程引擎的服务任务,那么您可能需要考虑将流程引擎与此代码完全解耦。外部服务任务模式允许您这样做: https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/

    您无需将代码部署到 Camunda 运行时,而是在单独的运行时(例如 JVM)中启动外部工作者。一些好处包括:

    • 流程引擎和工作人员已解耦。引擎不需要知道工人的位置。工作人员在可用时调用引擎。
    • 线程管理已外部化到工作人员。引擎线程池不需要扩展。
    • worker 可以用选择的编程语言实现
    • worker 可以独立升级(甚至不同版本的 worker 可以同时运行,例如在滚动升级期间)
    • worker 可以独立于流程引擎进行扩展

    另请参见:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-20
      相关资源
      最近更新 更多