Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

希望 NamingServiceConfigService trait 提供基于Vec<u8>类型的序列化/反序列化函数 #219

Closed
boholder opened this issue Mar 16, 2024 · 10 comments

Comments

@boholder
Copy link

感谢各位提供这个优秀的rust库!
下面我来解释一下,为什么我希望的改动必须在本项目中实现。

是这样的,我在使用nacos-sdk-rust-binding-py这个利用PyO3实现的库(只用命名,不用配置)。
我需要以Python异步的形式使用它,目前nacos-sdk-rust-binding-py库只提供了同步使用的方式。
为了实现我的目的,有两个实现思路:

一是用pyo3-asyncio库,把对本nacos-sdk库的异步调用包装成Python的异步方法。
这个因为个人能力不足,实验了下没能实现,实在不好意思。

二是用Python的multiprocessing库包装一下对nacos-sdk-rust-binding-py库的调用。
我是这样调用的:

import nacos_sdk_rust_binding_py as nacos
...
_NACOS_CLIENT: nacos.NacosNamingClient
_NACOS_SERVICE_INSTANCE: nacos.NacosServiceInstance
...
async def initialize_nacos():
    ...
    await asyncio.get_event_loop().run_in_executor(
        config.PROCESS_EXECUTOR, register, _NACOS_CLIENT, _NACOS_SERVICE_INSTANCE
    )

此时,Pythonmultiprocessing库要求NacosNamingClientNacosServiceInstance是可序列化的。
opc-source/nacos-sdk-rust-binding-py#2
还好PyO3的issue里有相关解决方案和示例,是需要在nacos-sdk-rust-binding-py的rust代码上动手。
核心思路是要实现struct与Vec<u8>间的序列化反序列化,issue里的示例是serde库实现的。

我照葫芦画瓢实现了nacos-sdk-rust-binding-py库里三个...Data Object的序列化反序列化。
这三个struct都是在nacos-sdk-rust-binding-py库中定义的,字段都是普普通通的OptionStringbool啥的,serde库已经实现了对这些类型的操作,直接用就行:

use serde::{Serialize, Deserialize}; ///<-------引用serde库
use bincode::{serialize, deserialize}; ///<-------serde库的底层好像是这个bincode库,总之也要引入这个库
...
#[pyclass(module = "nacos_sdk_rust_binding_py")]
#[derive(Clone, Serialize, Deserialize)]  ///<------- 这里让struct扩展序列化反序列化能力
pub struct ClientOptions {
    ...

    pub fn __setstate__(&mut self, state: &PyBytes) -> PyResult<()> {
        *self = deserialize(state.as_bytes()).unwrap();   ///<-------反序列化
        Ok(())
    }

    pub fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult<&'py PyBytes> {
        Ok(PyBytes::new(py, &serialize(&self).unwrap()))    ///<-------序列化
    }

    pub fn __getnewargs__(&self)
        -> PyResult<(...)> {
            Ok((self.server_addr.clone(),   ///<-------这里需要用到clone函数,Arc<>在这里调用clone应该也没问题吧
                ...
            ))
    }
}

剩下两个Client strust

/// Client api of Nacos Config.
#[pyclass]
pub struct NacosConfigClient {
    inner: Arc<dyn nacos_sdk::api::config::ConfigService + Send + Sync + 'static>,
}

/// Client api of Nacos Naming.
#[pyclass]
pub struct NacosNamingClient {
    inner: Arc<dyn nacos_sdk::api::naming::NamingService + Send + Sync + 'static>,
}

好,终于到重点了。
目前咱们这个nacos-sdk库,只暴露了ConfigServiceNamingService这两个trait,没把底层的struct暴露出来(这个实现没毛病,毕竟同时提供了同步和异步调用且必须在Cargo.toml里二选一引入):

pub(crate) struct NacosNamingService { /// pub(crate) 仅crate内部可见
...
}

因此如上文所示,nacos-sdk-rust-binding-py库是用dyn nacos_sdk::api::naming::NamingService这种形式引用的。
我想实现对两个Client的序列化反序列化,首先serde库会抱怨说,哎呀我不知道怎么处理这个Arc<dyn nacos_sdk::api::naming::NamingService + Send + Sync + 'static>类型。
抱怨的对,那就手动实现,但是这个NamingService是在nacos-sdk库定义的,我没办法直接写impl Serialize, Deserialize NamingService: ...
还好serde提供了实现非本crate中定义的结构的方式,我就试着写了
再build,serde库抱怨说:

error[E0574]: expected struct, variant or union type, found trait `NamingService`                 
 --> src\nacos_sdk_def.rs:8:18                                                                    
  |                                                                                               
8 | #[serde(remote = "NamingService")]

我心想这下完了,只能在本nacos-sdk库中实现了
说了这么一大串,感谢耐心看到这里。
我看命名和配置两个Client的field类型还怪复杂的,不知道序列化反序列化好不好实现。
工作上急用,我们用的是Nacos 2.x,要让Python服务正常和Nacos交互。。。
周末打扰各位实在不好意思。

@CherishCai
Copy link
Collaborator

哈哈,还是我。

一是用pyo3-asyncio库,把对本nacos-sdk库的异步调用包装成Python的异步方法。

建议还是以上这种方案,毕竟较为通用,我今天看了下 pyo3-asyncio 的 examples 也暂时不知道怎么搞,不过我觉得可以参考博客 nacos-sdk-rust binding for Python 所关联的 为 Databend Rust Driver 实现 Python Binding,其做的 bindings/python 有引入 pyo3-asyncio


二是用Python的multiprocessing库包装一下对nacos-sdk-rust-binding-py库的调用。

这个是不会改动的哈,如果想尝试可以自己 fork 独立的代码,然后 nacos-sdk-rust-binding-py 自行指定
#nacos-sdk = { git = "https://github.com/your-group/nacos-sdk-rust.git", features = ["default"] }

@boholder
Copy link
Author

boholder commented Mar 16, 2024

或者希望各位解答这个问题:

如果我在Python中的多个进程里,每个进程初始化独立的NacosNamingClient实例(该struct是由nacos-sdk-rust-binding-py库里定义的),会在rust这边多浪费资源或者造成业务逻辑状态机进入错误状态(比如不同的client实例被调用做不同操作)吗?

(提示:PyO3会在每次初始化时调用这么一大段代码,注意里面会调用NamingServiceBuilder::newNamingServiceBuilder::build:)

        let naming_service_builder = if is_enable_auth {
            nacos_sdk::api::naming::NamingServiceBuilder::new(props).enable_auth_plugin_http()
        } else {
            nacos_sdk::api::naming::NamingServiceBuilder::new(props)
        };

        let naming_service = naming_service_builder
            .build()
            .map_err(|nacos_err| PyRuntimeError::new_err(format!("{:?}", &nacos_err)))?;

参见上文,我看它引用本nacos-sdk库的NamingServicetrait时,用的是Arc<dyn nacos_sdk::api::config::ConfigService + Send + Sync + 'static>这么长一串。
我不太了解Rust,问了下AI,它说:

代表了一个指向特质对象(dyn NamingService)的 Arc(智能指针),它可以安全地跨线程共享,并具有静态生命周期。

是不是就没有我担心的问题了?那这样的话,我继续用多进程方案也是没有问题的。

@CherishCai
Copy link
Collaborator

Rust 的 trait 限定是有点长 😂

@boholder
Copy link
Author

boholder commented Mar 16, 2024

我今天看了下 pyo3-asyncio 的 examples 也暂时不知道怎么搞

尝试过了,主要是这个方案我能力确实有限做不到,要是有能力,我急用肯定弄出来了。。要不花自己周末时间倒腾呢,实在不好意思,打扰你了。
opc-source/nacos-sdk-rust-binding-py#1 (comment)

@CherishCai
Copy link
Collaborator

CherishCai commented Mar 16, 2024

尝试下模仿它 bindings/python

我之前也是模仿来实现的,之前也不太懂 Python。
实在不行,我明天再认真看看哈,不好意思暂时在看些其它东西。

@boholder
Copy link
Author

尝试下模仿它 bindings/python

我之前也是模仿来实现的,之前也不太懂 Python

要不。。方便回答下上面那个问题吗,没问题的话不实现也能用。😭

@CherishCai
Copy link
Collaborator

一般大部分情况下,应用下仅需一个 NamingService 作为客户端,而且需要长期持有直至应用停止。

因为它内部会初始化与服务端的长链接,后续的数据交互及服务变更等订阅,都是实时地通过长链接告知客户端的。

@boholder
Copy link
Author

boholder commented Mar 16, 2024

一般大部分情况下,应用下仅需一个 NamingService 作为客户端,而且需要长期持有直至应用停止。

感谢解答,因为看到每次new都会调NamingServiceBuilder::build,我就心想生成多个NacosNamingClient实例应该不行。。我再在python这边想点多进程的小把戏吧,多进程共享全局变量之类的。。

@onewe
Copy link
Collaborator

onewe commented Mar 16, 2024

@boholder 你说的是这种实现方式吗? 我也不是很懂 py

image

@onewe onewe reopened this Mar 16, 2024
@boholder
Copy link
Author

boholder commented Mar 16, 2024

@onewe
对的,Python异步实现的话是这样的: client提供的api应该是返回coroutine类型的async def异步函数,这样就能用await来避免把event loop本身block住了。
我看 @CherishCai 发了一版异步实现,晚会我试试。

更新:可以用Python异步形式调用nacos-sdk-rust-binding-py库了,感谢 @CherishCai
opc-source/nacos-sdk-rust-binding-py#1 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants