如何在Clojure中使用Stuart Sierra的组件库

monch1962

我正在努力了解如何在Clojure应用程序中使用Stuart Sierra的组件库通过观看他的Youtube视频,我认为我可以很好地理解导致他创建图书馆的问题。但是,我正在努力研究如何在新的,相当复杂的项目中实际使用它。

我意识到这听起来很模糊,但是感觉好像我缺少一些关键概念,一旦理解了它,我就会对如何使用组件有很好的了解。换句话说,Stuart的文档和视频非常详细地介绍了组件的功能和原因,但是我没有找到操作方法。

是否有任何种类的详细教程/演练可用于:

  • 为什么在非平凡的Clojure应用中完全使用组件
  • 一种如何在非平凡的Clojure应用程序中分解功能的方法,以便可以以合理的最佳方式实现组件。当您拥有的只是一个数据库,一个应用程序服务器和一个Web服务器层时,这相当简单,但是我正在努力了解如何将其用于具有许多不同层次的系统的系统,这些系统都需要一起工作连贯地
  • 开发/测试/故障转移/等方法 在使用组件构建的非平凡Clojure应用中

提前致谢

杰夫

简而言之,Component是一个专门的DI框架它可以通过给定两个映射来设置注入的系统:系统映射和依赖关系映射。

让我们看一个虚构的Web应用程序(免责声明,我在不实际运行的情况下以一种形式键入了此内容):

(ns myapp.system
  (:require [com.stuartsierra.component :as component]
            ;; we'll talk about myapp.components later
            [myapp.components :as app-components]))

(defn system-map [config] ;; it's conventional to have a config map, but it's optional
  (component/system-map
    ;; construct all components + static config
    {:db (app-components/map->Db (:db config))
     :handler (app-components/map->AppHandler (:handler config))
     :server (app-components/map->Server (:web-server config))}))

(defn dependency-map
  ;; list inter-dependencies in either:
  ;;    {:key [:dependency1 :dependency2]} form or
  ;;    {:key {:name-arg1 :dependency1
  ;;           :name-arg2 :dependency2}} form
  {:handler [:db]
   :server {:app :handler})

;; calling this creates our system
(def create-system [& [config]]
  (component/system-using
    (system-map (or config {})
    (dependency-map)))

这使我们可以(create-system)在需要时调用以创建整个应用程序的新实例。

使用(component/start created-system),我们可以运行它提供的系统服务。在这种情况下,是Web服务器在侦听端口和开放的db连接。

最后,我们可以通过停止它(component/stop created-system)来停止系统运行(例如-停止Web服务器,从数据库断开连接)。

现在让我们components.clj看看我们的应用程序:

(ns myapp.components
  (:require [com.stuartsierra.component :as component]
            ;; lots of app requires would go here
            ;; I'm generalizing app-specific code to
            ;; this namespace
            [myapp.stuff :as app]))

(defrecord Db [host port]
   component/Lifecycle
   (start [c]
      (let [conn (app/db-connect host port)]
        (app/db-migrate conn)
        (assoc c :connection conn)))
   (stop [c]
      (when-let [conn (:connection c)]
        (app/db-disconnect conn))
      (dissoc c :connection)))

(defrecord AppHandler [db cookie-config]
   component/Lifecycle
   (start [c]
      (assoc c :handler (app/create-handler cookie-config db)))
   (stop [c] c))

;; you should probably use the jetty-component instead
;; https://github.com/weavejester/ring-jetty-component
(defrecord Server [app host port]
   component/Lifecycle
   (start [c]
      (assoc c :server (app/create-and-start-jetty-server
                        {:app (:handler app)
                         :host host 
                         :port port})))
   (stop [c]
      (when-let [server (:server c)]
         (app/stop-jetty-server server)
      (dissoc c :server)))

那我们刚才做了什么?我们获得了一个可重装的系统。我认为一些使用figwheel的clojurescript开发人员开始看到相似之处。

这意味着我们可以在重新加载代码后轻松地重新启动系统。继续user.clj

(ns user
    (:require [myapp.system :as system]
              [com.stuartsierra.component :as component]
              [clojure.tools.namespace.repl :refer (refresh refresh-all)]
              ;; dev-system.clj only contains: (def the-system)
              [dev-system :refer [the-system]])

(def system-config
  {:web-server {:port 3000
                :host "localhost"}
   :db {:host 3456
        :host "localhost"}
   :handler {cookie-config {}}}

(def the-system nil)

(defn init []
  (alter-var-root #'the-system
                  (constantly system/create-system system-config)))

(defn start []
  (alter-var-root #'the-system component/start))

(defn stop []
  (alter-var-root #'the-system
                  #(when % (component/stop %))))

(defn go []
  (init)
  (start))

(defn reset []
  (stop)
  (refresh :after 'user/go))

要运行系统,我们可以在repl中输入以下内容:

(user)> (reset)

这将重新加载我们的代码,并重新启动整个系统。如果启动,它将关闭正在运行的现有系统。

我们还有其他好处:

  • 端到端测试很容易,只需编辑配置或替换组件以指向进程内服务(我已使用它指向进程内kafka服务器进行测试)。
  • 从理论上讲,您可以为同一个JVM多次生成应用程序(实际上并不像第一点那样实用)。
  • 更改代码并重新启动服务器时,无需重新启动REPL。
  • 与ring reload不同,我们可以采用统一的方式来重新启动应用程序,而无需考虑其用途:后台工作程序,微服务或机器学习系统都可以以相同的方式进行架构。

值得注意的是,由于一切都在进行中,因此Component不处理与故障转移,分布式系统或错误代码有关的任何事情;

Component可以帮助您在服务器中管理许多“资源”(又称有状态对象):

  • 与服务的连接(队列,数据库等)
  • 时间的流逝(计划程序,计划程序等)
  • 日志记录(应用程序日志记录,异常日志记录,指标等)
  • 文件IO(blob存储,本地文件系统等)
  • 传入的客户端连接(Web,套接字等)
  • 操作系统资源(设备,线程池等)

如果您只有Web服务器+数据库,则该组件看起来像是多余的。但是,如今只有少数Web应用程序可用。

边注:移动the-system到另一个命名空间减小刷新的可能性the-system开发时变种(如-调用refresh代替reset)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何在Clojure中使用jMusic(Java库)?

来自分类Dev

如何在Clojure中使用JDBC创建数据库?

来自分类Dev

如何在组件库中使用React钩子?

来自分类Dev

初始开发期间如何在Clojure库中使用本地存储库?

来自分类Dev

Clojure-如何在多个项目中使用我的个人调试库?

来自分类Dev

如何在Oracle数据库中使用骆驼SQL组件插入Blob

来自分类Dev

如何在vue.js组件中使用导入的JavaScript库构造函数?

来自分类Dev

如何在Dart中使用Bower组件?

来自分类Dev

如何在组件的value属性中使用“ <”

来自分类常见问题

如何在Class组件中使用useEffect()?

来自分类Dev

如何在功能组件中使用PanResponder?

来自分类Dev

如何在类组件中使用useMediaQuery

来自分类Dev

如何在代码中使用Blazor组件?

来自分类Dev

如何在Vue组件中使用Bootstrap?

来自分类Dev

如何在组件中使用<ValidationMessage>

来自分类Dev

如何在页面中使用组件

来自分类Dev

如何在 JavaScript 中使用 JSF 组件

来自分类Dev

如何在 ElectronJs 中使用 StencilJs 组件

来自分类Dev

Vue 如何在组件中使用 id?

来自分类Dev

在功能组件中使用外部js库

来自分类Dev

如何在Typescript中使用js库?

来自分类Dev

如何在virtualenv中使用外部库?

来自分类Dev

如何在Livecode中使用C ++库

来自分类Dev

如何在AIDE中使用库

来自分类Dev

如何在Progressbar中使用改造库?

来自分类Dev

如何在nativeScript中使用FusionAuth库?

来自分类Dev

如何在C ++中使用CPR库?

来自分类Dev

如何在Webpack中使用emojionearea库

来自分类Dev

如何在typeorm中使用存储库?