Tsuko

  • 注册于 2018年5月24日
  • 补充一下,无WSGI的视角:

      http            http
    -------->  Nginx --------> webserver

    WSGI:

      http            http                  WSGI\ASGI
    -------->  Nginx --------> web server ------------> Python Object
  • 0x0001
    gq老哥的回答有很大帮助!其实主要是WSGI的粒度与我的经验有差异。

    WSGI的文章老是提到:

    WSGI is an interface between web server and web app.

    我觉得很奇怪:web server 和 web app不是同一个东西吗?后来看多几篇文章,大概了解这个server是接受请求和返回请求的程序,web app指的是处理具体业务的Python对象。从uvicorn的”奇怪“用法可以看出:

    # main.py
    from fastapi import FastAPI
    app = FastAPI()

    通过uvicorn main:app --reload 启动服务器。这里uvicorn(web server)直接找到main.py的app这个全局变量作为web app,不是整个main.py。在我接触过的框架(gin, actix-web)没有这样做法,所以一时间感到很奇怪。

    • 看了一些资料,WSGI的作用主要是:定义了什么样的Python对象可以作为WebApp:

      这说明它是一个跟Python语言密切相关的应用接口。
      另外,WSGI、LB、App的关系大概功能如下:

      uWSGI是一个WSGI的实现,除了socket管理外,还负责进程管理(健康检查、重启)。

    • 不太懂为什么会产生WSGI这个东西?
      现在是: Browser -> Nginx -> uvicorn -> WebApp.
      直接 Browser -> Nginx -> WebApp不行吗?

      是不是将某些网络功能从APP offload到WSGI服务器上了?APP就负责业务逻辑?

      有什么比较好的资料、文章讲讲这个东西的必要性\没有会产生什么问题?

    • 需求:有一个不断生长的有向无环图,有向无环图的生长依赖外部事件源,生长规律是不可计算的。

      请问有什么实现方案吗?语言、库、软件都可以,动画效果其次,简单易上手最好(:

    • 可以啊!建议去掉“后端”定语。期待来个数据库内核开发路线图?@iosmanthus 😃😃

    • Miigon 当时也有往VM和容器方面想,但是考虑到机器做深度学习,比较吃资源,一台机器应该不能放太多重容器。目前只有三台机器,没足够样本去判断。。

    • 知识点+1.

      突然想起同学在极客云上租了几台机器,发现不同机器的内网ip一样,不同机器通过预留的23XXX端口进行通信,比如A与B通信:172.0.0.1:23001 -> 172.0.0.1:23002, 看来是用了IP anycast?

      有点好奇极客云为啥要这样做,为啥不是创建一个私有网络然后一台机器一个ip,有大佬了解这方面吗?

      • 如题,有没有云原生相关的工作?

      • woc!尴尬地发现包发错,发给自己了。。
        把:

        .map(|(self_addr, target)| async move {
                            let ((addr, listener), tar) = (self_addr, target);
                            let d = Duration::from_secs(delay_secs);
                            // ... 
        }

        改为

        .map(|((target, listener), self_addr)| async move {
        // ... 
        }

        果然是“先把问题复述一遍,然后就能自己找到自己的愚蠢错误了”。。。

      • 最近用Rust写了个pinger和ponger,pinger每5s向ponger发一个udp包, ponger会把这个消息打印到终端。

        但是发现pinger一直在自顾自地ping,ponger那里一条日志都没收到。

        先用tcpdump抓包sudo tcpdump -i lo port 8888:

        tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
        listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
        10:50:34.290607 IP localhost.8888 > localhost.8888: UDP, length 24
        10:50:39.291118 IP localhost.8888 > localhost.8888: UDP, length 24
        10:50:44.291684 IP localhost.8888 > localhost.8888: UDP, length 24
        10:50:49.292292 IP localhost.8888 > localhost.8888: UDP, length 24
        10:50:54.292864 IP localhost.8888 > localhost.8888: UDP, length 24

        sudo tcpdump -i lo port 6666抓ponger,发现没有收到包消息。

        nc -z -u -v localhost 6666同时抓ponger,发现ponger可以正常接发包。
        不知道哪里出问题了,请教各位。。

        最后附上代码:

        // Pinger
        use async_std::net::{SocketAddr, UdpSocket};
        use futures_timer::Delay;
        use log::{error, info};
        use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode};
        
        use futures::future::join_all;
        use std::{io::Read, sync::Arc, time::Duration};
        pub struct UdpPinger {
            // targets:
            pub targets: Vec<SocketAddr>,
        
            pub delay_secs: u64,
        }
        
        impl UdpPinger {
            pub async fn run(self) -> std::io::Result<()> {
                let listener = async_std::net::UdpSocket::bind("127.0.0.1:8888")
                    .await
                    .expect("couldn't bind");
                let listener: Arc<UdpSocket> = Arc::new(listener);
                let self_addr = listener.local_addr().unwrap().to_string();
                let delay_secs = self.delay_secs;
                join_all(
                    self.targets
                        .into_iter()
                        .zip([listener].iter().cloned())
                        .zip([self_addr].iter().cloned())
                        .map(|(self_addr, target)| async move {
                            let ((addr, listener), tar) = (self_addr, target);
                            let d = Duration::from_secs(delay_secs);
                            loop {
                                // std::thread::sleep(std::time::Duration::from_secs(1));
                                match listener
                                    .send_to(format!("ping from {}", &addr).as_bytes(), &tar)
                                    .await
                                {
                                    Err(e) => error!("{:?}", e.to_string()),
                                    Ok(_) => info!("ping {}", &addr),
                                }
        
                                Delay::new(d).await;
                            }
                        }),
                )
                .await;
        
                // println!("done");
                info!("pinger done");
                Ok(())
            }
        }
        
        fn main() {
            let _ = CombinedLogger::init(vec![TermLogger::new(
                LevelFilter::Info,
                Config::default(),
                TerminalMode::Mixed,
            )]);
        
            info!(
                "working at {}",
                std::env::current_dir().unwrap().to_str().unwrap()
            );
        
            match std::fs::File::open("./conf/targets") {
                Ok(mut f) => {
                    let mut s = String::new();
                    match f.read_to_string(&mut s) {
                        Ok(_) => {
                            async_std::task::block_on(
                                UdpPinger {
                                    targets: s
                                        .split_whitespace()
                                        .map(|tar| tar.parse().unwrap())
                                        .collect(),
                                    delay_secs: 5,
                                }
                                .run(),
                            )
                            .unwrap();
                        }
                        Err(e) => error!("{}", e.to_string()),
                    }
                }
                Err(e) => error!("{}", e.to_string()),
            }
        }
        // Ponger
        use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode};
        
        use async_std::net::SocketAddr;
        
        use log::info;
        
        pub struct FakeHost {
            pub addr: SocketAddr,
        }
        
        impl FakeHost {
            pub async fn run(self) -> std::io::Result<()> {
                let listener = async_std::net::UdpSocket::bind(self.addr).await?;
                let quit = false;
                let mut buf = [0u8; 512];
        
                info!("ponger up {}", listener.local_addr().unwrap().to_string());
        
                while !quit {
                    let (sz, peer) = listener.recv_from(&mut buf).await?;
                    info!(
                        "from {}: {}",
                        peer.to_string(),
                        String::from_utf8_lossy(&buf[..sz])
                    );
                    // echo
                    //listener.send_to(&buf[..sz], peer).await?;
                }
                info!("ponger done");
                Ok(())
            }
        }
        
        fn main() {
            let _ = CombinedLogger::init(vec![TermLogger::new(
                LevelFilter::Info,
                Config::default(),
                TerminalMode::Mixed,
            )]);
        
            async_std::task::block_on(
                FakeHost {
                    addr: "127.0.0.1:6666".parse().unwrap(),
                }
                .run(),
            )
            .unwrap();
        }
      • xiao
        Map强引用ThreadLocal, 那么只有先回收了Map才能回收ThreadLocal. 会影响其他ThreadLocal.

      • Thread通过中间层ThreadLocal去访问ThreadLocalMap, Map弱引用了ThreadLocal作为key。
        ThreadLocal被GC但是Map中对应的Slot没有被释放。可以通过:

        • 定期删除
        • 延迟到某事件发生时删除

        Map采用后者. 在set()/get()/remove()中会删除无效的slot. 这也是大多数文章提到的,然后点到为止了(??)
        我猜应该是其他threadLocal调用 remove()会告诉Map去遍历删除无效的key value.

      • 对Java不熟悉,但是在Golang中通过条件变量Cond。看了一下这篇文章,发现Condition使用方式跟go的sync.Cond十分类似。由此推断条件变量的用法应该是较为通用的。

        首先,条件变量是跟某个锁关联的,在Java里面类似这样:

        public Lock lock = new ReentrantLock();
        public Condition condition = lock.newCondition();

        在Go中类似这样:

        var mx sync.Cond
        cond := sync.NewCond(&mx)

        其次,假设要实现:p()为真时通知所有等待变量,那么sync.Cond的用法应该是这样:

        func f1(){
            mx.Lock()
            for ! p(){
                 cond.Wait()
            }
            mx.Unlock()
        }

        也就是条件变量被mx的加锁解锁包住。这是因为Wait()的底层实现:

        1. 先解锁mx, 所以你在Wait之间先要加锁
        2. 阻塞
        3. 被唤醒
        4. 再加锁
        5. 再检查 P()是否成立

        如果P()成立,那么将不会进入循环,而此时已经加锁了,所以你要解锁。
        所以我推测你的问题在:await()已经获得锁了,但是在下一次loop时还要获得锁,因此导致了死锁。用条件变量最好按照这个范式来。

        © 2018-2025 0xFFFF