状态管理

在 Zino 框架中,应用状态是由State类型提供的:

  1. #[derive(Debug, Clone)]
  2. pub struct State<T = ()> {
  3. env: Env,
  4. config: Table,
  5. data: T,
  6. }

它包含有运行环境Env、TOML格式的配置文件Table以及自定义数据类型T

不同于很多Web框架,我们的应用状态管理是基于惰性初始化的全局变量来实现的,主要考量如下:

  1. 应用状态通常存在于整个运行期间,也就是说它的生命周期为'static
  2. 应用状态通常只需要初始化加载一次,并且在运行期间并不会被修改;
  3. 尽可能避免在controllerservice方法中传递应用状态参数。

回到具体实现上,最核心的几行代码就是

  1. static SHARED_STATE: LazyLock<State> = LazyLock::new(|| {
  2. let mut state = State::default();
  3. state.load_config();
  4. state
  5. });

这定义了一个全局共享的静态变量,通过State::shared方法可以得到它的一个&'static引用, 然后就可以在整个应用中到处使用。

示例:如何使用Redis?

  1. # config/config.dev.toml
  2. [redis]
  3. host = "127.0.0.1"
  4. port = 6379
  5. database = "dbnum"
  6. username = "some_user"
  7. password = "hsfU4Y3aRbxVNuLpVG5T+wb9jIDdQyaUIiPgeQrP0ZRM1g"
  1. //! src/extension/redis.rs
  2. use redis::{Client, Connection};
  3. use zino_core::{error::Error, state::State, LazyLock};
  4. #[derive(Debug, Clone, Copy)]
  5. pub struct Redis;
  6. impl Redis {
  7. #[inline]
  8. pub async fn connect() -> Result<Connection, Error> {
  9. REDIS_CLIENT.get_async_connection().await.map_err(Error::from)
  10. }
  11. }
  12. static REDIS_CLIENT: LazyLock<Client> = LazyLock::new(|| {
  13. let config = State::shared()
  14. .get_config("redis")
  15. .expect("the `redis` field should be a table");
  16. let database = config
  17. .get_str("database")
  18. .expect("the `database` field should be a str");
  19. let authority = State::format_authority(config, Some(6379));
  20. let url = format!("redis://{authority}/{database}");
  21. Client::open(url)
  22. .expect("fail to create a connector to the redis server")
  23. });

这是Zino框架中推荐的使用模式:在配置文件里编写Redis连接信息^password,通过Lazy的全局变量初始化Client; 然后定义一个空结构体,进而封装一些自定义方法。仔细想想,这不就类似于其他语言中的“单例模式”吗?