Tornado 实现的 WebSocket 简单例子

Server 部分,主要就是继承 WebSocketHandler 实现了个 WebSocket Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import logging
import tornado.web
import tornado.websocket
import tornado.ioloop
import tornado.options

from tornado.options import define, options

define("port", default=3000, help="run on the given port", type=int)


class Application(tornado.web.Application):
def __init__(self):
handlers = [(r"/", MainHandler)]
settings = dict(debug=True)
tornado.web.Application.__init__(self, handlers, **settings)


class MainHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True

def open(self):
logging.info("A client connected.")

def on_close(self):
logging.info("A client disconnected")

def on_message(self, message):
logging.info("message: {}".format(message))


def main():
tornado.options.parse_command_line()
app = Application()
app.listen(options.port)
tornado.ioloop.IOLoop.instance().start()


if __name__ == "__main__":
main()

Client 部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tornado.ioloop import IOLoop, PeriodicCallback
from tornado import gen
from tornado.websocket import websocket_connect


class Client(object):
def __init__(self, url, timeout):
self.url = url
self.timeout = timeout
self.ioloop = IOLoop.instance()
self.ws = None
self.connect()
PeriodicCallback(self.keep_alive, 20000).start()
self.ioloop.start()

@gen.coroutine
def connect(self):
print("trying to connect")
try:
self.ws = yield websocket_connect(self.url)
except Exception as e:
print("connection error")
else:
print("connected")
self.run()

@gen.coroutine
def run(self):
while True:
msg = yield self.ws.read_message()
if msg is None:
print("connection closed")
self.ws = None
break

def keep_alive(self):
if self.ws is None:
self.connect()
else:
self.ws.write_message("keep alive")


if __name__ == "__main__":
client = Client("ws://localhost:3000", 5)

运行 Server 和 Clinet 之后输出如下:

  • Server 部分
1
2
3
4
5
[I 220805 10:49:12 server:27] A client connected.
[I 220805 10:49:32 server:33] message: keep alive
[I 220805 10:49:52 server:33] message: keep alive
[I 220805 10:50:12 server:33] message: keep alive
[I 220805 10:50:13 server:30] A client disconnected
  • Client 部分
1
2
trying to connect
connected

初识 Casdoor

最近工作需要研究了下Casdoor:

Casdoor is a UI-first Identity Access Management (IAM) / Single-Sign-On (SSO) platform based on OAuth 2.0, OIDC, SAML and CAS.

简介

Casdoor 是一个开源的单点登录系统,单点登录系统的好处就是集中管理用户,使的我们开发的应用只需要关心业务逻辑而不用每个应用都是去实现一套用户系统。

Casdoor 使用了 OAuth2 的方式来完成单点认证,大概逻辑如下图:

image.png

总结一下就是:

  • 第一步 获取code
  • 第二步 用code获取access_token
  • 第三步 用access_token获取所需要的资源

安装

服务安装文档

文档说的很明白,我这里是下载源码使用的,因为我用的PostgreSQL,所以需要改一下app.confadapter.go

然后后端启动:go run main.go

后端启动:

1
2
3
cd web
yarn install
yarn start

非常简单

界面配置

这里我是用最小的改动来完成对接demo的,首先需要添加一个组织,组织就是一堆资源,应用的的集合:

image.png

后面的用户,角色,权限,模型,提供商都可以先不管,需要了解的可以看文档

然后需要添加一个新的应用,主要就是下图这些

image.png

客户端ID和客户端密钥,都是后面对接时候需要的

还有一个就是证书,这个不需要改,只需要点编辑进入将公钥拿到

image.png

上面就是在界面上需要完成的事情和完成一个对接demo所需要的数据了

web 对接

下面是和 Casdoor 对接的 web 接口代码,主要有两部分,第一部分是获取 code,第二部分是通过 code 获取 access_token,而这个 access_token 已经包含了用户信息,用对应的公钥进行解密,即可得到一个用户的 json 数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import logging
from urllib.parse import urljoin

from casdoor import CasdoorSDK


@route(r'/casdoor/sso-login')
class CASDoorSSOLoginHandler(BaseHandler):

def save_user(self, user_data):
user = User.make_user(uid=user_data['id'], ext_uname=user_data['name'], username=user_data['name'], _from='casdoor')
return user

def get(self, *args, **kwargs):
code = self.get_argument('code', None)
target_uri = self.get_argument('target_uri')

subpath = config.get_config("webif.redirect_subpath", '')
trident_base = urljoin(self.origin_host, subpath.lstrip('/'))

endpoint = config.get_config('casdoor_auth.endpoint')
client_id = config.get_config('casdoor_auth.client_id')
client_secret = config.get_config('casdoor_auth.secret')
org_name = config.get_config('casdoor_auth.org_name')
certificate = config.get_config('casdoor_auth.cert')

sdk = CasdoorSDK(
endpoint,
client_id,
client_secret,
certificate,
org_name,
)

if code:
access_token = sdk.get_oauth_token(code)
user_data = sdk.parse_jwt_token(access_token)
user = self.save_user(user_data)
self.session['proxy_user_id'] = str(user.id)
redirect_url = urljoin(trident_base, target_uri)
else:
origin_url = '{}/api/v1/casdoor/sso-login?target_uri={}'.format(trident_base, target_uri)
redirect_url = sdk.get_auth_link(origin_url, state='casdoor')
logging.info('redirect to %s', redirect_url)
return self.redirect(redirect_url)

配置也比较简单:

image.png

这里需要特别注意的是,endpoint 配的是 Casdoor 访问的首页地址,也就是这里是前端地址,而不是后端地址。前端地址是:http://localhost:7001/,后端地址是:http://localhost:8000/,这里很有迷惑性,让我折腾了半天。

能否武统

不管今晚怎样,反正白天先吃了一记闷棍——亏损2% 😨

目前看到各路消息,似乎一副马上开战的样子

但是我觉得吧,打应该打不起来,那么就有两个分支:

  1. 佩洛西不敢去台湾,那双方都当无事发生,不过这也说明美国影响力在下降,但是最开始美国没说要访问台湾,只是台湾媒体渲染起来的,那最终输家就是台湾。
  2. 佩洛西前往台湾,我国战机伴飞,飞跃台湾本土,这是另一个突破,输家也在台湾。

总之,我们都是赢了。

那么明天能不能阳包阴,来个大涨!

Switch铁人三项

Switch铁人三项就是:健身环大冒险,有氧拳击,舞力全开,这三个我都有,聊聊体验吧。

接触Switch就是从健身环大冒险开始的,而这一切又都是从疫情开始的,在疫情之前从来没有想过还会在家里健身,潜意识里都是健身房,而疫情改变了这一切,改变了人的认知。

健身环大冒险因为疫情爆火,而我也在2020年12月开始Switch铁人三项之旅。

首先是健身环大冒险,做的挺不错,也挺科学的,有氧运动+无氧运动结合,搭配闯关剧情,很容易让人坚持下来,也有趣味性。刚开始的时候因为在家办公,所以我经常玩,后来去办公室上班后,每天下班之后就感觉累成狗,基本上工作日晚上就没玩过几次了,基本上都是周末玩一会。

买了健身环大冒险一个多月后,出了舞力全开这款游戏,当时就是为了尝试体验一下,后来发现这个游戏打开的机会屈指可数,主要还是我没有节奏感,不会跳舞,玩这个游戏就是群魔乱舞,基本上毫无体验感和成就感可言。所以一般玩家可以跳过这个游戏,除非喜欢跳舞。

有氧拳击是最近买的一款游戏,主要特点就是专于有氧运动,一天的运动量在40分钟左右,消耗能量300大卡左右,平均心率在130左右,每次练完一身汗,非常舒服。这款游戏从强度来说没有健身环累,主要是健身环有更多的无氧运动,比如有时候持续深蹲,就非常累,而有氧拳击全程有氧运动,心率很均匀,所以不容易疲劳。

跑步也是一项持续的有氧运动,但是跑步这件事情对于大体重的选手来说非常容易损伤膝盖,所以有氧拳击就是一项非常好的替代品。

后面的健身计划:

第一步,继续有氧拳击,争取每天都能玩一次,加强心肺功能,提高基础代谢水平,然后让体重降下来。

第二步,逐渐开始健身环大冒险,适应一下更多的无氧运动,肌肉训练。

第三步,开始逐步恢复囚徒健身,增大肌肉训练。

至于囚徒健身,之前练过一段时间,后来有所懈怠,后面会再写篇文章介绍一下。

写hexo博客的绝佳工具——hexo-client

最近逛Github,发现了一款写hexo博客的绝佳工具——hexo-client,非常好用,在这安利一下。

以前写hexo博客都是用IDE加载整个项目文件夹,在其中编辑,然后利用hexo命令在终端进行操作。有几个地方不是很方便:

  1. 上传图片的时候的操作很繁琐,首先需要把图片挪到对应文件夹下,然后在文章里面用markdown语法插入对应的图片地址。
  2. 没有markdown的编辑器,有时候一些语法还得现查
  3. 没法按照分类和标签来展示文章
  4. IDE编辑完成后,还得在终端去执行发布命令

现在这款hexo-client工具完美解决了上面的问题,它提供了一个很好用的markdown编辑器,如下图:

WX202207301024142x.png

然后上传图片的时候也很方便,点击编辑器里面的图片按钮,上传即可,工具会自动帮你上传到指定的图片文件夹下,并且会重命名,比如上面这张图片的地址是这样的:

![WX202207301024142x.png](/images/2022/07/30/66ef87ce-a338-4597-9de5-2f3b73084553.png)

很方便有木有,另外首页界面的文章也可以按照分类,标签展示,一目了然:

WX202207301026562x.png

最后最牛的一点,工具可以一键发布,太爽了有木有:

WX202207301028362x.png

太好用了,又激起我写博客的热情了,感谢 gaoyoubo

用cookiecutter来创建一个自己的代码模板

最近工作上要新建一个项目,又要搬运一些重复代码,这件事情想想都让人有点sick,于是想到搞一个代码模板多好,以后新建项目直接把相关信息一填,直接生成出新的项目,所以想到了cookiecutter。

cookiecutter以前很早就用过了,比如cookiecutter-tornadocookiecutter-flask

用这些模板生成出来的项目你就会感觉到什么叫做最佳范式,想必大家都读过最佳实践之类的书或者文章吧,这种最佳实践的确会让人感觉很舒服。

Github上其实有很多模板项目,比如上面的tornado,flask,最新的fastapi也有,但是这些都不太满足我的需求,因为我们公司有很多自己定制化的代码,而且我们是前后端分离的,项目也不需要那些前端模板项目。

因此是时候搞一个自己的模板项目了,其实搞一个模板项目很简单,只是个体力劳动。

cookiecutter其实就是利用模板渲染,把需要替换的字符串写成jinja2的形式:{{ project }},然后cookiecutter执行的时候就是把你在cookiecutter.json预定义的变量在代码里替换一下。非常简单是不是?

首先随便在Github上找一个现成的模板项目:full-stack-fastapi-postgresql

WX202207291432422x.png

把这个里面除了{{cookiecutter.project_slug}}目录,都拷贝到你自己的项目里

然后开始修改里面的cookiecutter.json,这里面的变量需要的可以保留,不需要的删掉即可,也可以新加上自己需要的。

然后就是从一个已有的项目把代码复制到{{cookiecutter.project_slug}}目录里,复制完成之后就开始最累人的部分,将在cookiecutter.json定义的变量替换到代码对应地方,比如这样:

1
2
3
4
5
6
7
8
9
import hashlib
import uuid

import peewee
from fastapi_permissions import Allow
from playhouse.postgres_ext import BinaryJSONField

from {{ cookiecutter.project_slug }}.common.enums import Role
from {{ cookiecutter.project_slug }}.models import BaseModel

所有需要变量替换的地方都写成{{ variable }}的形式,替换完成之后,利用模板生成一个项目,看是否有问题,直到修改的没有问题,大功告成。

投资往往是知易行难

投资说起来简单,但是做起来确实不容易。最大的一个障碍就是如何做到知行合一。

在股市里,知易行难的原因是人们通常低估了赚钱和亏钱时候的心理波动。上涨赚钱的时候高歌猛进,觉得未来一定是涨涨涨,也更加容易发现所谓的利多,所以也容易高位加仓,一把亏光;下跌亏钱的时候,觉得未来前景黯淡,周围也全是利空,所以也容易低位割肉,割完就涨。

而且纵然是你有投资策略,也可能架不住这种心理波动,感觉就像是有个杠杆极度的放大了这个波动,所以很容易出现情绪性的梭哈买入和恐慌割肉。

从另一个角度来看,股市是反人性的,在大家情绪热烈的时候,就需要开始减仓了,在大家情绪低迷的时候,就需要开始加仓了。如果天生可以理性的做到这一点,那么活该你赚钱。但是大部分人都是普通人,都做不到,你可能现在想这么简单,怎么可能做不到,但是事实是,在你真正实盘的时候,在盈亏都是实际的钱的时候,面临这些情况的时候可能真的是做不到的。

所以需要投资策略,还需要对投资策略的信仰。

此外,浸淫在股市这个欲望场里,诱惑很多,欲望也很多,所以另一个很重要的是自己的定力,如何能看到诱惑还能坚持自己的策略,也是一个很值得修炼的品质。

房子基础装修好了

基础装修:水电木瓦油,也就是房屋水电,吊顶,墙地砖,腻子刷漆

下来是等成品,柜子安装

期待完工的那一天

WechatIMG6169.jpegWechatIMG6170.jpeg

2022年5月

时间流逝之快,超出我的想象。之前好像还能稍微抓住它,现在只能任其飘散。越是这样,我心里会越慌,然后越来越麻木,任其流逝,最终回过头来只能懊悔。我不太喜欢这样快节奏的生活,太空虚,整个人似乎只是为了工作,为了钱而活着,其他方面没有可以想象的空间。

我想去乡村生活,有一片自己的土地,可以种想种的东西,蔬菜瓜果,花草树木,养一些小动物,每天起来读书写字,远离焦虑,然后这样慢下来生活。

不扯这些了,说说这个月实际发生的事情

装修

首先这个月开始收房装修了,五一假期的时候就把主材订了,差不多20W吧,然后收房补的面积差价,交的大修基金下来就有快3W了。收房之后等了一个周开始办装修手续,办装修手续又办了一个周,这物业实在是太差劲了,好不容易到5月29日才开工。

开工先搞的事情就是铲素灰,铲保温墙,砸墙,我们改了户型设计,将两个卫生间合并成一个卫生间了,所以砸了不少墙,砸墙就花了1600。这第一步铲砸和运垃圾一共花了4700。

5月整个在装修上做的事情就只有这些了,效率实在是太低了。截至今天,6月1日完成了新风和空调的安装,6月3日开始砌墙,这个砌墙其实就是改卫生间那块的设计,墙砸了还要按设计重新砌一下,估计就到6月4日完工,下来就是水电改造工程。

装修太花钱了,让人感觉就是一个千疮百孔的容器,里面的水(水就好比你的资金)止不住的流,但是你往进加水的速度又太慢了。而且本来以为装修仅仅是花钱的事情,没想到里面要操心的事情太多了,比如地暖会不会漏水,天花板上的下水管道会不会漏水,空调新风打的孔会不会有问题,这些都是之前没有遇到过的问题,心里容易慌。搞的现在花钱都是小问题了,只要顺利完成装修才是我现在心里着重希望的事情。

投资

股市方面,本月收益率5%,主要还是两次创业板上车的盈利,其他打野标的也就是赚了亏,亏了赚,对收益率没有啥贡献,主要还是创业板。红利其实也有所表现,只不过当时上的仓位比较少,盈利不多,收益率的话也有2%。现在交易已经省心省力了,我的交易系统已经逐渐稳定成型了。

在市场里持续赚钱,就得去重复那些让自己能赚钱的事儿,避免那些让自己亏钱的事儿和运气主导的事儿。所以得总结,做对的事儿继续做,犯过的错不再犯,业绩就会逐渐提高。我觉得现在就已经走在这条正确的路上了。

现在有一个问题,就是按照交易系统操作,其实会漏掉一部分收益,因为现在的原则就是有收益就止盈,像5月30日到6月2日的这波创业板行情,我是5月30日上车的,5月31日上涨2%,这天的收益全部满仓吃上,然后6月1日挂止盈单清仓,但是6月1日又上涨了快1%,6月2日上涨了2.5%,相当于我少吃了至少3%的收益。

现在似乎确实没有什么解决办法,因为你并不知道一波趋势来了之后,会持续多久。像从2021年12月到现在的创业板走势,大多时候一波趋势也就两三天,我的系统就能完美处理这种情况。再从另外一个角度来看,我现在定的每个月收益率目标是5%,那只要达到我的目标,哪怕少挣点,其实没什么问题,毕竟你不可能把市场的收益吃干榨净。我觉得只要能够实现稳定盈利,慢慢积累就够了,人要是贪婪起来就没完没了了。

另外我除了创业板还会做红利,还有其他标的啊,在创业板转弱的时候,还能继续在红利上取得收益,综合来看其实比创业板的收益差不了多少。所以这个问题其实并不是问题,之所以这个会成为问题,还是人性贪婪。

将来的生活

计划6-7月要娃,现在时间一步步接近,但是总是觉得忧心忡忡,比较担心将来有娃之后的生活怎么应对,谁来带娃?

目前来说最佳方案就是我在家办公,然后请一个保姆仅工作日白天来做饭看娃,这样应该是最好的方案。但是找到一个在家办公的工作确实不易,也不知道我们公司能不能给我搞个特殊。此乃上上策。

如果不能搞特殊,那要么就是我去找个远程的工作,难度比较大。此乃中策。

要么就是我不工作了,我自己看娃,省一个保姆的钱,然后自己靠股市生存,这是下下策。因为没有工作的话,自己的交易心态就会有比较大的影响,具体有多大,没经历过,也不知道。

这仅仅是谁来带娃的问题,其他小问题我想肯定只会多不会少。现在压力真的很大,工作压力:各种被优化,被毕业,工作上还各种卷,上班一天已经筋疲力尽了。经济压力:房租房贷装修,三座大山,我现在房贷+房租一个月1W+。生活压力:很多琐碎的事情你也要去应付,因为没有人帮你做。

这样的情境下,既没心情,没时间消费,也没能力消费。一天天的发消费券,有个P用,能不能直接发钱,让我先把房贷还一下。

围城

花了大概一个多星期,读完了围城这本书。

其实在我看来,围城只不过是人的天生逃避罢了,遇到问题就会觉得之前的境况能好一点,其实只要有想逃避的念头,哪里都是围城。

书中有一段话我觉得很有意思:

天下只有两种人。比如一串葡萄到手,一种人挑最好的先吃,另一种人把最好的留到最后吃。照例第一种人应该乐观,因为他每吃一颗都是吃剩的葡萄里最好的;第二种人应该悲观,因为他每吃一颗都是吃剩的葡萄里最坏的。不过事实却适得其反,缘故是第二种人还有希望,第一种人只有回忆。

以前我用kindle尝试读过一次,但是没读下去,主要是一开始的内容有点乏味。不过这次反倒觉得越读越有意思,我想了想原因,可能是我觉得里面任意一个人物的生活都比我的生活有意思,我有点浸入的感觉了。。就好像进入了别人的世界,忘记了自己现在枯燥的生活。

另外我感觉我有点像方鸿渐,啥事糊里糊涂没有主见,生活呢也是一塌糊涂,混沌度日,然后自尊心还强。。

有人说,每个人都能从《围城》中找到自己的影子,也许他就是我在本书中找到的影子。