Sentry 新版插件基类讲解

根据 sentry/plugins/bases/__init__.py 的说明,新版插件分类四类,其中常见的:

  • NotificationPlugin 比如sentry_mail、sentry_webhook插件

NotificationPlugin的基类定义参见 sentry/plugins/bases/notify.py

Posted in 默认分类 | Tagged , | Leave a comment

Sentry整理杂记

本讨论均基于Sentry 7.7版本

插件机制

自带插件 src/sentry/plugins/ 每插件一个目录

自带插件loader:src/sentry/conf/server.py 里的INSTALLED_APPS tuple

外装插件loader:utils/runner.py 里的 install_plugins()函数,对iter_entry_points()遍历并将其加入INSTALLED_APPS tuple中

外装插件注册:插件的setup.py里执行注册entry_points的过程。例https://github.com/getsentry/sentry-groveio/blob/master/setup.py

参考setuptools:pkg_resources

列出本Python安装的Sentry插件
#!/usr/bin/env python
import pkg_resources
for ep in pkg_resources.iter_entry_points('sentry.apps'):
    print str(ep)
for ep in pkg_resources.iter_entry_points('sentry.plugins'):
    print str(ep)

 

sentry-jira插件

插件基本配置

每Project分别配置,需要输入jira的instance URL、用户名和密码。以上内容base64存在数据库sentry_projectoptions表里 where `key` like ‘jira%’

配置用户名密码之后,选择Sentry project关联到哪个JIRA Project,保存设置,并Enable Plugin即可。

 

Due Date问题

在Sentry Event页面右边点击Create JIRA Issue进入创建页面。但下面Due Date总提示Operation value must be a string 。

 点击展开故障详情

也就是说,如果不改sentry-jira插件,就无解。所以我提交了pull request https://github.com/thurloat/sentry-jira/pull/71

5日下午突然发现,目前线上Sentry 6.4.4的duedate显示为django的SelectDateWidget,而不像我自己的测试装Sentry 7.7一样直接用文本框。经过仔细对比,发现ops-sentry01上的sentry-jira插件是修改过的版本,forms.py文件class JIRAIssueForm新增了一段

site-packages/sentry_jira/forms.py片段
144
145
146
147
148
149
150
151
duedate = forms.DateField(
    label="duedate",
    #widget=adminwidgets.AdminDateWidget(),
    widget=SelectDateWidget(),
    initial=datetime.datetime.now(),
    #widget=DateWidget(usel10n=True)
    required=True
)

但这段代码没在公司的git库里保存,我就做了tree diff另外保存了。

SSO集成

厂家SSO讨论:https://github.com/getsentry/sentry/issues/1372

新版Sentry有auth backend基类https://github.com/getsentry/sentry/tree/master/src/sentry/auth/

SENTRY_FEATURES['organizations:sso']改为True可以开启Auth页面,设置sso。

目前,我参考sentry-sso-google写出来的sentry-sso-sankuai放在公司内网git库。

SENTRY_SINGLE_ORGANIZATION=True会导致/auth/login/ 跳转到 /auth/login/org_slug/ ,从而无法登录非SSO的用户(如系统自带的名为sentry的超级用户)。所以我在staging环境里关闭了这个参数。

Organizations

当SENTRY_SINGLE_ORGANIZATION=False时,utils/runner.py加载配置之后会将SENTRY_FEATURES[‘organizations:create’]强制改为False,从而禁用了右上角新创建Organization的“加号”链接。如果在此状态下删除了最后一个Organization,则其中的Team会变成游离状态,只能改掉参数重启服务重新创建org了,而且重建之后某种情况下会导致游离状态的Team丢失。切记不要删除Organization!!

根据getsentry原厂GH-1372号issue,每个Organization只能开启一个SSO AuthProvider。

目前看来,咱们的使用方法对多个Organization并没有需求。

 

新来的SSO用户默认属于所有Team的问题

经阅读代码文件web/frontend/accounts.py 发现在SENTRY_SINGLE_ORGNAZATION=True时会默认设置新注册用户的has_global_access值为True,然后如果有authprovider的话,再用authprovider.default_global_access更新该值;查看auth/helper.py 也发现会在登录和关联身份时,用 authprovider.default_global_access 给用户的has_global_access赋值。

2015年10月23日,将sentry_organizationmember表中 user_id in (35, 66, 67, 69, 71, 72, 73, 74, 77, 78, 81, 83, 84, 85, 86, 88, 89, 91, 92, 95, 96, 97, 98, 99, 100, 102, 103, 105, 107, 108, 109, 110, 112, 113, 114, 115, 117, 118, 119, 120, 122, 124, 125, 126, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 164, 166, 167, 168, 172, 173, 174, 175, 176, 177, 178, 180, 182, 183, 184, 185, 186, 187, 188, 189, 191, 192, 193, 194, 195, 196, 198, 199, 200, 203, 204, 205, 206, 207) 的111位用户的 has_global_access 值改为0,并将sentry_authprovider中id=2,provider=Sankuai的default_global_access字段改为0。

至于如何在SSO插件代码中设置,以便Sentry激活该SSO authprovider时自动将 sentry_authprovider 中 default_global_access字段设置为0,尚须进一步研究。将 orgnazation和authprovider关联的代码在web/frontend/organization_auth_settings.py 但其中并没有涉及default_global_access的内容,也许这个值并不是由authprovider影响的?

Team Admin无权批准人员加入的问题

这一代 Sentry 的权限系统设计的很烂,把 一个人属于哪个Team、一个人有权管理哪个Team混在一起了,无法表达“一个人是TeamA的member,同时是TeamB的admin”的意思。所以我一直倾向于授予较低的权限,但导致了事故。但10月29日由杨明芽发现 Team Admin 无法邀请、批准人员加入本Team,所以写了个后台任务定时批准requests。

Posted in 默认分类 | Tagged , | 1 Comment

SSO跳回sentry失败的解决方法

从某月开始,公司SSO回传信息改用POST方式,放弃了之前的通过querystring传递的做法。具体到Sentry这里,因为 /auth/sso 页面受CSRF保护,拒绝接受POST回来的不含CSRF token的数据,从而无法登录。

解决这个问题,可能有三种方法吧:

  1. 让SSO回传csrf token:前提是得先把csrf token发给SSO,且SSO方愿意配合修改。考虑到现在跳转到SSO去是直接302的,CSRF如果放在querystring上,其实和他们回传时把token放在querystring上风险相当了,所以这做法不行
  2. Sentry/auth/sso不验证CSRF:/auth/sso 对应AuthProviderLoginView,该View继承自BaseView,而BaseView的dispatch()方法是@method_decorator(csrf_protect)过的,因为还有其它很多View都这么继承,想改基类还怕有别的风险。想在配置文件里monkey patch掉AuthProviderLoginView,但是import sentry.auth.helper的时候就失败了
  3. Sentry整体不验证CSRF:试了试,csrf view middleware可以去掉,但是,csrf_protect decorator依然生效。关于此处,django文档说的不太精确。该middleware除了负责种cookie,还负责验证,但实际上验证工作并不是以middleware的身份来做的,而是以decorator的身份来做的。单单禁止middleware的加载只是去掉了种cookie的行为而已。网上也有其他人遭遇过这个问题 http://www.douban.com/group/topic/11555679/

最后,参照3里帖子的做法,做了一个django middleware插在csrf view middleware前面,如当前请求URI为/auth/sso/ 就取消CSRF保护。

Posted in 默认分类 | Tagged , | Leave a comment

记一次Sentry的性能调优过程

大概从6月底开始,我们的Sentry(错误日志收集、聚合和报警系统 http://getsentry.com )遭遇了性能问题,每分钟只能处理200个事件了,经常有20多万待处理的任务积压在events队列里,延迟超过一小时,我不得不丢弃这些任务,清空队列,以促进时效。重启一下celery worker会有瞬间的改善,但很快就又不行了,似乎worker的性能会衰减。为此,我给celery worker增加了–time-limit参数,使worker不会在执行不下去的时候无限等待,而是超时出错退出,迎接新的任务,情况略有改善。

因为Sentry的结构是前端web-消息队列-后端celery这样的结构,给调试也造成了一些困难。

我先开启了celery worker的DEBUG级别日志,从中搜索包含succeed的行,然后把其中UUID给过滤掉,只保留任务名字和耗时,整理后得到任务和耗时的对应关系,发现save_event这个任务的耗时很有意思,经常是6秒的整倍数。

然后去这个任务里添加时间打点代码,发现在它调用的EventManager.save()函数里,带事务执行的三次数据库插入(调用_save_aggregate、创建EventMapping、保存Event)的耗时,不是零点几秒,就是六秒的整倍数,而完全没有2、3秒、7、8秒这种情况。于是检查的方向就被引导到了数据库方面。折腾了DBA同事几天,删了几个索引和外键约束,也没什么改善。

想到之前为了Sentry的下一个版本v8准备过一套PostgreSQL数据库用于测试,我就拿过来,另外建了一套Sentry 7的部署,在上面运行,没想到性能极其丝滑……事情似乎越来越明了,就是数据库的问题。不过当我停止使用用于测试的128G内存真机PostgreSQL,而自己申请了一个和当前MySQL一样配置的16G机器的时候,发现低配置时pgsql的性能好像还不如MySQL……不过就在我从真实环境往测试环境export/import数据的时候,发现了Sentry在执行import时,如果测试环境共用生产系统的Redis,就会速度极慢,几乎卡死,用strace上去看一下发现慢在Redis操作上,开销在数据库上的时间极少;而如果用专用的一套Redis就速度极快。

于是我的注意力又转向了Redis。尝试FLUSHDB,发现性能立刻恢复了每分钟4000条、峰值8000条的处理能力,但redis的内存碎片率上涨了不少。看了看SLOWLOG,发现有一条几乎占满了整个SLOWLOG排行榜的命令

ZRANGE b:p 0 -1

手工试了试,这条命令得好几秒种才能返回,一次就能返回数据好几万条。搜了一下源代码,在sentry/buffer/redis.py文件发现了b:p这个key名字。原来,这是把高速更新的计数器的多次更新合并起来,减轻数据库压力的一个组件。但这个组件本身因为把sorted set用到了极限,所以性能不佳。考虑到不能再让碎片率上涨,我只好偶尔手工删除这个key,放弃一些计数器更新操作:放弃更新计数器总比放弃整个任务看起来要好些吧。

去软件原厂求助(https://github.com/getsentry/sentry/issues/3870),作者说我该上供啦,不要自己架服务啦,还是买原厂服务吧……

现在,我更改了worker的配置,把这个任务单独拉出来,用六台worker伺候着,终于基本解决了问题。

 

顺便说一下我在Redis 2.8.7上遇到的极限值:

sorted set的长度在100万左右就会性能崩溃。如果有依赖这个特性的,请大家注意有可能会引起整体系统的性能雪崩。

Posted in 默认分类 | Tagged , | 3 Comments

etcd的discovery URL是怎么回事

今天玩 CoreOS,得知他们提供了一个 etcd discovery service https://discovery.etcd.io/new?size=初始数量

当足够数量的etcd按照这个URL相互发现之后,就能成功组成集群。

问题是:

这个数量是怎么被各个etcd知道的呢?搜了一些文章,也谈到了自建 discovery service的方法,但URL似乎都是/v2/keys/random_ID/_config/size 这样的,而不像 discovery.etcd.io 这样是 /random_ID 。不过后来试了试访问 https://discovery.etcd.io/random_ID/_config/size 发现能得到那个值,就明白了,这个网址其实有rewrite,把 /random_ID 给proxy_pass到后面etcd的/v2/keys/random_ID 了,让这个URL看起来更友好,但却更迷惑了。

参考资料:https://github.com/coreos/etcd/blob/master/discovery/discovery.go#L214-L217

Posted in 默认分类 | Tagged | Leave a comment

免签过境韩国、坐船去日本手记

我总有一种不愿意走回头路的情结,虽然给自己找了许多麻烦,依然乐此不疲。
这一次,经@tigerWu 提醒,我打算从韩国坐船去日本,以期获得一个日本的“上陆许可”。虽说日语里上陆就是入境的意思,但在汉语看来,有明确的从下到上的方向指示,也就是说得从海里上来才算。

去韩国驻华大使馆网站看看,中国公民持日本旅游非团体签证可以免签证过境韩国并给予30天入境期,查验标准是下一段“机票”。但我想坐船啊!那会不会是翻译有问题呢?要知道韩国大使馆上的汉语文件基本上行文都狗屁不通。再去看看韩国出入境管理局的网站,其英文版写了“flight ticket”。
这么看来是没戏了?我不甘心,就给“国民申闻鼓”写信,等不及回答又给出入境管理局的immigration contact center打电话,第一次问仁川机场进、釜山海港出,被拒了。不过比较有意思的是这个呼叫中心并不是直接做出行政解释,而是客人报告了自己的行程计划之后,她们代为向入境口岸当局咨询。考虑到中国对外国人的72小时免签证政策限定外国人不能离开口岸城市,我就尝试咨询釜山机场进、釜山口岸出,获得了允许,遂立刻买票,但为时已晚,只买到了没有头等舱的一架飞机的商务舱。(插播八卦:东航卖大韩航空的票比大韩自己卖的便宜)与此同时,我之前向韩国信访局“国民申闻鼓”递交的信访“是否可以空进海出,没指定具体口岸咨询”也收到了回答,我还到处求爷爷告奶奶找人翻译,后来发现其实有汉语翻译版的回答,就是:不限定转乘空路或海路。我把这一页打印出来了。

2月4日,大韩KE830,办理登机牌遇到了麻烦,地服代理请示大韩航空工作人员,我出示联程船票和信访记录之后拿到登机牌;中国边检出境,按规定应该查验联程客票,但都没理我就直接把中国出境章盖在了日本签证旁边。
到釜山机场,iPad丢了,于是落了单,在后面跟机组一起,机组走专用通道,我走外国乘客通道,是一位大妈入境官。入境官翻了半天,我主动提醒我是visa free transit资格,然后出示票。该女入境指着班次号问:这是flight吗?我说是ship,她拿不准,就问韩国人通道那个男入境官,然后我就被提到那个通道受审去了。因为JR九州beetle航运公司的船票确认页只写“釜山发”没说去哪儿,男入境官不能确认这是正确的票,我又重申了我从Busan international ferry
Terminal走,要去日本的Fukuoka,但他还是将信将疑,又问我是不是crew,我说不是之后,被拉出来去条凳上坐着(传说中的边检小黑屋?),入境现场指挥官,一位大妈过来问我,入境期间准备去哪儿玩,我说JSA(也就是板门店)她说板门店很远啊你知道吗,我说我做坐韩国高铁KTX,我有KR PASS,一种外国人专用的票。然后各位入境官扔下我不理,带着护照走了!!!这时船票在他们手里,信访函在我手里。过了一会儿,指挥官和男入境官把我带到入境通道,先盖章,然后手写了截止日期,又写了个不知道是TS还是TIS的字在入境章下面,这时还扣着,又说了我几句应该详细说明啊什么什么的,然后才用摄像头拍照,又说了我几句,最后主动和我握手,这事入境官的表情是很友好的。

2月8日,我提了行李到釜山火车站,经案内所工作人员指点,到站后去坐去往ferry的shuttle bus,但是没找到,只好打了个出租车去。一打吓一跳,原来google maps标的那个terminal现在已经不用了,新的在站后的左前方(东北),出租车到南边路口掉头从我到港口。巧的是,好像韩国人找零算法和美国人一样,出租车费2800、咖啡4800,我给整几千加三百,他们很难理解我是要一个500硬币找零。值得注意的是,港口的指示牌写boarding为“登机、搭乘”,写“便名”为“flight”,甚至有个指示牌写“快速船 登机口 flyer gate”我一直以为flight是飞机航班,而船班叫voyage呢!!

日本入境,边检极其顺利,其中问了我要去哪儿,我说福冈、仙台、东京,问我是飞过去吗,我说我有JR PASS,坐火车去,就给我贴了从海里上博多港陆地的“上陆许可”一枚,其实和上次大阪关西机场的只差下边的地名不一样。海关,又跟上次一样搜我的箱子,我长得有那么像走私犯吗?而且那个女关员还钓鱼执法,问我是不是住本地,我说不是啊我是中国人,然后就问那海关卡上那个地址是什么,我说那是hotel;然后又问我去哪些地方玩;最后甚至指着签证问这是什么,我说那是签证啊!!然后就过关了。他们是在考察什么?

结论:由于韩国出入境管理局各口岸部门执行政策不一致,目前这么走并不是一个很靠谱的方案,而且边检入境问话有时会有陷阱,比如问我是不是crew、问境内怎么交通之类的,稍有不慎就有可能被认为是有问题的。韩国边检把我扔在冷板凳好几分钟,谁知道是去打电话核实船票还是打电话请示政策去了……
《中国合伙人》教导我们:真实、自信、具体、合理,是签证的基本思想基本哲学,我的行程是真实的,就不怕核查、我的行程是符合信访回信解释的,也应该能进去。
不过我觉得,我得以自己的真实经历,去督促船运公司把票写清楚,不要只写起点站。

2月8日,写于博多

Posted in 默认分类 | Tagged , , , , | Leave a comment

Sentry整理杂记

本讨论均基于Sentry 7.7版本

插件机制

自带插件

src/sentry/plugins/ 每插件一个目录
自带插件loader:src/sentry/conf/server.py 里的 INSTALLED_APPS tuple
外装插件loader:utils/runner.py 里的 install_plugins()函数,对iter_entry_points()遍历并将其加入 INSTALLED_APPS tuple 中
外装插件注册:插件的 setup.py 里执行注册 entry_points 的过程:

例:https://github.com/getsentry/sentry-groveio/blob/master/setup.py
参考资料:https://pythonhosted.org/setuptools/pkg_resources.html

#!/usr/bin/env python
import pkg_resources

for ep in pkg_resources.iter_entry_points('sentry.apps'):
    print str(ep)

for ep in pkg_resources.iter_entry_points('sentry.plugins'):
    print str(ep)

sentry-jira 插件

插件基本配置

每 Project 分别配置,需要输入 jira 的 instance URL、用户名和密码。以上内容 base64 存在数据库 sentry_projectoptions 表里 where `key` like ‘jira%’
配置用户名密码之后,选择 Sentry project 关联到哪个 JIRA Project,保存设置,并 Enable Plugin 即可。

Due Date 问题

在 Sentry Event 页面右边点击 Create JIRA Issue 进入创建页面。但下面 Due Date 总提示 Operation value must be a string 。
经 tcpdump,插件发送格式为

"duedate": {"id": "2015-07-31"}

根据 /rest/api/2/issue/createmeta?projectKeys=<我们的KEY>&expand=projects.issuetypes.fields 的说明,duedate 的 schema 为

"duedate": {
    "hasDefaultValue": false,
    "name": "Due Date",
    "operations": [
        "set"
    ],
    "required": true,
    "schema": {
        "system": "duedate",
        "type": "date"
    }
}

根据网上流传的消息(https://answers.atlassian.com/questions/319018/jira-eclipse-mylyn-transition-fails-when-date-is-required-french-format#comment-319464),Jira期望的格式应该是

"duedate":"2014-07-23"

发送格式和期望格式的不同导致了无法创建 Issue。不过 sentry-jira 插件创建的内容是 id,而上述 bugreport 中是 name,也不太一样。
倒推检查插件源代码,发现从 Form 提交到 jira 客户端对象的数据就已经是u’duedate’: {‘id’: u’2015-07-31′} 了
sentry_jira:forms.py里

elif (schema.get("type") != "string"
  or schema.get("item") != "string"
  or schema.get("custom") == CUSTOM_FIELD_TYPES.get("select")):
    v = {"id": v}

这一段缺乏对 date 类型的 Jira 字段的特殊处理,导致前面多加了 id。
也就是说,如果不改 sentry-jira 插件,就无解。所以我提交了pull request https://github.com/thurloat/sentry-jira/pull/71
5日下午突然发现,目前线上 Sentry 6.4.4 的 duedate 显示为 django 的 SelectDateWidget,而不像我自己的测试装 Sentry 7.7 一样直接用文本框。经过仔细对比,发现服务器上的 sentry-jira 插件是修改过的版本,forms.py 文件class JIRAIssueForm 新增了一段

duedate = forms.DateField(
    label="duedate",
    #widget=adminwidgets.AdminDateWidget(),
    widget=SelectDateWidget(),
    initial=datetime.datetime.now(),
    #widget=DateWidget(usel10n=True)
    required=True
)

但这段代码没在公司的git库里保存。

SSO集成

厂家SSO讨论:https://github.com/getsentry/sentry/issues/1372
新版Sentry有 auth backend 基类https://github.com/getsentry/sentry/tree/master/src/sentry/auth/
SENTRY_FEATURES[‘organizations:sso’]改为 True 可以开启 Auth 页面,设置sso。
然后我就参考 sentry-sso-google 写了一个插件。

SENTRY_SINGLE_ORGANIZATION=True 会导致 /auth/login/ 跳转到 /auth/login/org_slug/ ,从而无法登录 非 SSO 的用户(如系统自带的名为 sentry 的超级用户)

Organizations

当 SENTRY_SINGLE_ORGANIZATION=False 时,utils/runner.py加载配置之后会将 SENTRY_FEATURES[‘organizations:create’] 强制改为 False,从而禁用了右上角新 创建 Organization 的“加号”链接。如果在此状态下删除了最后一个 Organization,则其中的  Team会变成游离状态,只能改掉参数重启服务重新创建了,而且重建之后某种情况下会导致游离状态的 Team 丢失。切记不要删除 Organization!!
根据 getsentry 原厂GH-1372号 issue,每个 Organization 只能开启一个 SSO AuthProvider

Posted in 默认分类 | Tagged , | Leave a comment

某种情况下PATH不生效的问题

今天遇到的情况,我同事在 virtualenv 里执行 gunicorn 运行 Django app,出来的 Django 错误信息却是 /usr/local/ 路径下面那套 Python 里的:

2015-09-18 15:32:47 [11538] [ERROR] Error handling request
Traceback (most recent call last):
File “/usr/local/lib/python2.7/dist-packages/gunicorn/workers/sync.py“, line 131, in handle_request
respiter = self.wsgi(environ, resp.start_response)
File “/usr/local/lib/python2.7/dist-packages/django/core/handlers/wsgi.py“, line 236, in __call__
self.load_middleware()
File “/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py“, line 57, in load_middleware
raise exceptions.ImproperlyConfigured(‘Middleware module “%s” does not define a “%s” class’ % (mw_module, mw_classname))
ImproperlyConfigured: Middleware module “django.contrib.auth.middleware” does not define a “SessionAuthenticationMiddleware” class

尝试 which gunicorn 返回结果是 virtualenv 里的那个 gunicorn;尝试 strace gunicorn 执行,却能正常执行。

我试了试,把 /usr/local/bin/gunicorn 的x权限取消,再让同事执行,就返回 Permission denied 说明执行的不是 Virtualenv 里的 gunicorn。改试 type gunicorn 返回结果:

gunicorn is hashed (/usr/local/bin/gunicorn)

目前尚不清楚是什么原因造成了该命令被 bash 给 hash 了。source virtualenv的 activate 命令的时候 hash 表会被清空的。

Posted in 默认分类 | Tagged , , | Leave a comment

Python import语句一处文档和行为不一致的问题

话说,我团运维组曾一度极为昌盛,可惜后来公司里有人搞政治,好好的一个组被拆成了好几个,后来竟分属不同大部门,工作关系也开始形同陌路。考虑到丰富经验,换换方向,今年7月,我转岗到了运维开发组,开始真正把Python作为职业。

无奈,基础差,还得学,8月18日,这么吉利的一天,我第n次在看Python的文档,看着看着,注意到一些标准库的名字是带点的,比如os.path,再比如logging.config等。然后就开始help各个模块,发现os.path的名字居然不叫path而是posixpath!!再看,logging是个package,但os居然是个module!!

于是,问题就来了:

Python语言里对 import A.B.C 这种形式是这么规定的:

when using syntax like import item.subitem.subsubitem, each item except for the last must be a package; the last item can be a module or a package but can’t be a class or function or variable defined in the previous item.

于是那就奇怪了,这个os既然不是package,怎么能 import os.path 呢?

本着技术事问咕果的原则,搜到这么一篇,其中提到,python在初始化时会加载一堆模块,其中就包括os,而os模块判断系统类型之后,选定了posixpath模块来配合工作,让它在本命名空间里叫path,还又把它以os.path的名字加入了sys.modules之中;待我执行import os.path时,import语句发现sys.modules里已经有了,于是直接做个symbol过来就了事。

本来事情到这里已经结束了。但是水木上有朋友在参与讨论的时候,为了说明上述其实是语言引擎的行为,而不是os的特殊性导致的,例子如下:

a.py文件:

import sys

import json as shit

sys.modules[‘a.shit’] = shit

然后从另一处import a.shit 居然也成功!

如果从a.py里去掉第三行,虽然import a.shit时会报告NameError: name ‘a’ is not defined,但sys.modules里已经出现了a,这说明a模块已经被import进来了!

 

如果说os.path含着金钥匙是因为它爸叫os,那a.shit又算老几,凭什么和os.path享受一样的待遇?

经过我精心构造的实验,不用a.py文件,而用一个空目录a代替,在python里import a.shit时,用strace偷偷的录下它的可耻行径,发现python居然先尝试找名为a的package(打开a目录找__init__.py)未果之后又尝试找各种形式文件名的名为a的module。

最后的结论是:虽然os.path它爸叫os,但其实即使它爸不叫os,也照样能成功import进来,根源在于import命令并不是像文档所说的,对 import A.B.C这种形式,坚持了A.B必须为package的原则,而是宽进(语言行为)严出(文档叙述),言行不一。

这样不好。

Posted in 默认分类 | Tagged , , | Leave a comment

我团年会2014

我是在2013年底开始接触这事的。当时钟sir找到我们组同事说想搞个直播,给来不了总部的同事们看;因为我和CDN服务商联系比较多,就把这活儿揽过来了。这期间经历了经费问题、场地和供应商考察、采购谈判、几轮试播(还发现了服务商的一次故障),直至最终正式播出。播放器设在一个限制了权限的wiki页里,由销售支持部在每个分支机构指定一名同事负责给大家播放。

2014年2月20日,我从IT部领了网线、NAT路由器、AP,从我们组拿了交换机,交付年会组待运。21日上午,我从五道口第一次用了快的打车,支付了一元钱到北京大学,以舞台旁边的机房为中心,向化妆室、百年讲堂前厅、观众席最后排分别拉了一条长线。下午,八台取票机运输到了前厅。22日上午,在化妆室安装了AP,在前厅安装了交换机,连接了取票机,在舞台机房安装了NAT路由器并把其放在音箱上用于覆盖观众区提供WiFi;直播服务商搭手把观众区网线贴在地上,并连接了视频信号开始上传。因为网线钳子的问题,取票机一直未能成功上网,一直到12点20分才修复,随后就开门放人进场,真的是好险。后来NAT掉地上了,导致观众区无WiFi,工作人员都没法上网了,于是把化妆室AP挪过来。

照例,签名照相,但今年的拍立得似乎有些感光不足。年会节目期间因为时常要开电脑检查直播情况,其实也没好好看。后来收到钟sir的通报感谢邮件,他老人家以为忘记写我了,又单独发邮件把直播这几个人夸了一通,哈哈哈。28日拷贝了原始录像,可以再回味一下。

我最喜欢的是西游那个小品,和《最初的梦想》歌曲连唱。

 

这一年,我团拥有了超过一半的市场占有率,八分之一的中国电影票房销售额,并开始扩展业务到酒店、外卖等。今天在微博看到“O2O行业今年将迎来决战之年,3大团购网都开展 年会KlCK OFF大会。美团的口号是"安心、成长平天下",点评的口号是"抢美团、争上市",糯米的囗号是"瓦解美团,立争第二",去哪儿口号是"全线抗美,誓死奋战",目前都在全力备战,面对众多对手,美团显得更加从容平静⋯⋯”,觉得真是有意思。团购已在身后,对手们还在抗美,我们已心怀天下了。

希望公司能保持目前的成绩,及时调整架构和业务形态,使之适应进一步商业斗争。我的工作虽和前线的业务形态关系不大,但仍要自省努力,为前线业务提供稳定的技术后盾。

动摇时,要不忘初心。否定追求,就是否定自己。谨以此告诫自己。

Posted in 默认分类 | Tagged | Leave a comment