2022,在一顿手忙脚乱中结束了,是的,毫无疑问的我也是杨过了,出乎意料的放开,出乎意料的发烧,同时也有其他的出乎意料。好在,一切都有惊无险,就这样匆匆忙忙的2022过去了。
疫情的放开,可以说是结束了一个时代,健康码没有了,扫码也没有了,行程码也没有了,去过三年的一切一切,都在短短几天到几周之内完全清零。回想过去的三年,就像是一场梦,梦醒了,无论心情如何,生活依旧要继续。
毫无疑问,之前几年的计划,没什么实现的,所以,今年的计划,形式感大于实际效果吧,但是还是走走过场:
2018年是非常忙碌的一年,从博客更新上就可以看出来,一年一篇都没写,哈哈。
项目组的工作内容从以前的azure cloud service更新到了kubernetes环境,docker, kubernetes, istio一路走过来,收获还是非常多的。新一代的微服务框架,的确在构建企业服务的能力方面,具有非常多的自由度,当然,更加有感开源社区的繁荣和google这个公司在开源界的影响力。
新的一年,无论是惯例还是仪式感,都要写点啥,那就从回顾一下之前的计划开始好了:
在记账的时候,每一条交易记录了什么?
或者换个角度来说,“帐”单里面的条目究竟定义和记录了什么。
我习惯会用下面的公式来给出一种解释:
$$ (d_1,d_2 … d_n ) \iff m$$
其中$d_x$代表数据维度,$m$代表一个数值。
所以:
每一条“账目”,就是在一定的数据纬度上分割的数值。
这样的公式和解释似乎比较抽象,所以可以用一个例子说明一下。首先定义几个记账中常用的数据纬度,例如,时间、地点、商家等,而数值也就是消费的金额。于是可以得到下面这样的一种定义
$$(时间, 地点, 商家) \iff 消费金额 $$
好了,在这个定义的下,账单的结果大概率会变成下面这样:
基于这个简单的例子,大家对上面记账条目的公式应该了直观的理解。联系到实际中记账的各种软件中,会发现很多都是基于这样的模型去定义记账的内容。
用我个人习惯使用MoneyWiz 2举例,下图中的是MoneyWzi 2中的交易布局
界面,在这个界面的设置基本上也展示了MoneyWzi 2中对记账条目的定义。
基于前面的数据模型定义,进一步来说,对于很多软件中的的财报
、月度总结
之类的界面,就是在某一个或者多个数据维度上对最终数值进行的聚合(aggregation)操作。
依旧用上面的例子,在2017年6月1日的日度消费总结,就是针对所有的数据在时间
维度等于2017年6月1日的条目,对数值(消费金额)列进行加
操作的聚合,也就是会变成160。
虽然这只是个最简单的例子,对于很多记账软件来讲,更加复杂的报告,基本上是在两个方面做文章:
加
操作WHY
谈了这么多的定义,下面来说说,为什么我们需要改进记账流程。
很多时候大家会把记账和省钱关联在一起,但实际上,更多人记账的目的,只是在一定程度上了解自己的消费内容和结构,这就和记录体重、每天的步数一样。
其实问过很多身边的朋友,都说使用过记账软件,短的几天几周,长的半年,最后都都没有坚持下来。大多数人没办法坚持下来,很多时候在抱怨记账中的一些痛点:
其中经常提到的一些话题:
其实在这些问题中,会发现,大家抱怨的主要在于记账本身的麻烦,这个麻烦引起了一系列的问题,最终变成放弃。
根据上面对记账数据模型的分析,仔细分一下其中的这个麻烦,显然并不是记录数字本身麻烦,也就是不在于公式右面的的数值
,基本上主要麻烦在数据维度
这个问题上。
所以,交易记录的及时性和完整性,存在着一种矛盾。如何缓解这个矛盾呢?
HOW
在GTD的核心步骤分为如下几部:
在GTD的流程中,有一个很重要的思路,就是将收集与整理/组织分开。因为定义、分析、分解一个任务内容,很难一次性完成。所以,首先采用快速的方式去清空你的大脑里的所有任务事项(收集),然后再静下心来,一条一条的分析、整理、分配任务内容(整理和组织)。利用快速简单的采集流程完成任务收集(解决及时性),利用整理组织丰富任务内容(解决完整性)。
这里可以发现,这与记账中的痛点具有很高的相似性,而大家记账之所以纠结于记账过程的复杂和麻烦,是因为期望在一次行为中完成记账条目的收集和整理两个过程。所以,利用GTD的思路,将收集和整理的两个步骤完全分离开,改进记账的工作流程,可以用一种新的思路来解决手机记账的痛点。
好了,我们的目标是一个完整详细的账目记录,完整详细这个词体现在数据定义上,其实就是丰富的数据维度这个层面,于是将记账分为收集和整理两个步骤,就是
进一步来分析,减少收集阶段的数据维度,如何做到最少?要回答这问题,有一个非常重要的因素,那就是收集和整理的行为周期是什么?
时间
这个维度。这个前提是因为一个人可以利用短期回忆快速的回顾一天的内容。举个例子,如果我有一条记录(2017年6月1日 10:30) -> 20
,虽然数据维度降低到只有一个,但是因为是当天发生的事情,所以可以很快的回忆起在这个时间段发生的这笔消费究竟是什么。于是,在采集阶段只要记录时间和金额就可以完成。同时,利用手机的特点,时间这个维度是自动采集的,也就是说我们完全可以变成只输入一个金额这么简单。那么,整理就变成了每天晚上的一个仪式性行为,每天晚上用5-10分钟,回忆当天的所有消费记录,并将完整的数据维度补充全面添加到记账软件或者记账本中。当然,从实践的结果来看,个人依旧是推荐以天为单位,这样可以最大程度减少收集阶段的维度,同时也降低整理时间的疲劳度。
WHAT
个人的实践,采用了天为单位,初衷还是最大程度减少手机阶段的数据维度。
对于采集阶段,利用了现有的效率类软件,考虑到收集过程最好连应用都不用打开就可以做到,所以采用通知中心的快速输入会更好。支持通知中心的输入,且可以自定义流程的,自然就想到了workflow
。下面利用workflow
做了这个通知中心的收集程序,收集输入金额到reminder中,同时里面可以配置成支持多币种和地点自动采集。但是在按天收集这个频率下,这些信息并不是很重要,所以下面的工作流做成可以配置的,可以在出国旅行或者其他场景下开启,默认是关闭状态
对于整理阶段,由于我的workflow
是加到特定的reminder中,同时omnifocus
会自动采集对应reminder中的数据。所以,每晚可以打开omnifocus
,在收件箱中看到所有的当天交易记录。然后,在每晚的例行或者仪式中,清空收件箱
中的交易内容,整理到MoneyWiz 2
中。
最终实现的效果,类似如下的演示
或许你觉得说了这么多,最终无非就是做了一个简单的workflow
,是的,从结果上看的确如此。
不过,个人认为,效率类的改进,并不只是依赖于一个革命性的创新,很多时候更重要的是从观念和细节出发,一些看似很小改进很多时候已经可以改变生活的体验。
很多时候,大家往往希望利用一个app来彻底解决问题,但是现实是效率的提高以及体验的提升,更多的应该在每个步骤中发挥已有工具的优势来为我们服务。
试问,下载到手机里的效率应用,有多少还在堆灰,有多少只打开过几次呢?对一个效率软件的最大的敬意,是将其用到你的每一个生活片段中。
最后,希望全家人身体健健康,新的一年,新的努力开始~~
京都(きょうと)城南宮(じょうなんぐう)
很多去京都的人,大多数都会选择伏见稻荷大社,金阁寺,清水寺,袛园等等比较著名的游玩景点。不过,这次本着小众游的原则,选择了京都的城南宫。
都の守護と国の安泰を願って、平安遷都の際に京都の南に創建されてから1200年。城南宮は、引越・工事・家相の心配を除く「方除(ほうよけ)の大社」と仰がれています。
城南宫为守护国都,祈求安泰之用,于1200年平安迁都的时候建造于京都的南侧。城南宫称为【消除灾难的名神社】,以消除人们对搬家、建筑作业、风水的忧虑为人们所敬仰。
当然,这次我并不是为了城南宫本身而来,而是因为城南宫旁边的「源氏物語花の庭(げんじものがたりはなのにわ)」
每年的4月底5月初,正值紫藤花盛开的季节,这个时候来这里,可以看到美丽的紫藤花景观。
当然,既然是庭院,赏花只是其中一部分,更多的还是整体的庭院布景。依旧感慨日本庭院的精致程度的。
和服,绿树,青草,日本文化独有的庭院景观。
同时,每种植物都有详细的注明,话说浅葱真的是葱么?~ ^_^
在庭院里闲游闲游完毕,想着既然来到了京都,就来刷刷景点吧。不过,咱们还是本着特立独行的态度,这次刷的是京都刚刚开幕的京都铁道博物馆。
京都(きょうと)鉄道博物館(てつどうはくぶつかん)
买票倒是很方便,有自动卖票机,直接给钱拿票就可以。除了门票,因为是刚刚开馆,竟然还送了开馆纪念券,哈哈,为啥有种解锁游戏特定任务的感觉。
既然是铁道博物馆,最重要的角色当然就是火车了。进馆的正面就是这个车头,想必是很重要的型号之一。
话说铁道博物馆的内部还真是相当的庞大,有三层,各种时期的火车车头和火车设施的展示,一应俱全。如果大家有时间,还是比较推荐的。
这个是旧时期日本火车车站的小铺缩影,话说我毫不犹豫的想起了昭和杂货店物语2
这个游戏。
来到博物馆的顶层,是有一个开放平台的,在这里正好可以鸟瞰博物馆周围的真正的铁道景观。同时,不时经过的各种列车,都可以在博物馆里找到相应的模型,真正的学以致用。可见博物馆的选址是经过认真考虑的。
说实话,今天本来只是逛逛神户的outlet买点乱七八糟的,没想到,在outlet的里面竟然遇到了传说的日本必吃冰激凌Cremia
。没听说过的话,可以随便去微博搜一下,推荐的帖子一堆一堆的。官方地址 https://www.nissei-com.co.jp/cremia/
难怪一个小小的冰激凌需要500日元,入口即化的口感,香浓满嘴的味道,不愧为必吃美食之一。大家逛街的时候遇到,一定不能放过!!
姫路城(ひめじじょう)
作為日本最具象徵意義,保留度最為完整的城堡,姬路城既是日本政府指定的國寶及國家特別史跡,也是日本首批世界文化遗产之一。姬路城是日本100名城之一,與熊本城、松本城合稱為日本三大名城;由於其保存度较完好(城內的天守為日本的12座現存天守之一),也被稱為「日本第一名城」。有很多時代劇和電影也在這裡進行拍攝,或以姬路城作為江戶城的象徵。
终于明白,日本也有那种日本人人满为患的景点,姬路城就是个明显的情况。周围的外国旅游者并不多,但是,一片一片的日本大爷大妈,哈哈。所以,登上姬路城游览这条就放弃了。因为要等一个小时排队,对于一个已经去过大阪天守阁的人来说,并没有那么多吸引力再上一次姬路城。倒是姬路城周围的场地非常心旷神怡。
姫路城西御屋敷跡庭園好古園(こうこえん)
如果说,姬路城本身并没有让人有太大的惊喜的话,好古园却是着实让人喜爱。可以说这里体现了日本庭院的精致之美。湖水,小桥,绿树,木屋,安心于其中,非常的舒服。
想想古代的武士住的地方,舒服起来也真是享受
日本的庭院技能真是让人惊叹,如果说中国的庭院是真正的大片景色。日本的就是在小地方做出的精致。下面这个荷花不是在一个荷花池里,而是在一个像花坛大小的人造区域中种植的。
姫路市立美術館(ひめじびしりつじゅつかん)
逛完了庭院竟然下起来大雨,正好我们下一站是姬路美术馆,即可躲雨又能熏陶一下艺术气息。
这次美术馆的展览,主要是以蜡笔为元素的展览
可惜展览馆内是不让拍照的,大家有机会可以自行游玩吧
奈良(なら)
女王大人说一定要去奈良玩鹿,好吧,于是行程中就多了奈良。
除了奈良站没走多远,就看到这个奈良鹿的大人偶。这家伙肯定是累了,一屁股靠在了垃圾桶上,当时就把我们笑翻了。
这个逗逼的正面
想必去过京都周围的,都会去奈良玩吧。相比上次去的时候没有人,这次真是人山人海。直接变成的后果就是,鹿都被喂饱了,根本不吃给的东西。哎,其实还是怀念上次被鹿满世界追着跑的感觉。
所以,最后买的鹿饼都为了这群鱼了,比鹿疯狂多了,还好它们不能上岸 >_<
感觉这次旅行对庭院和植物情有独钟,所以,既然来了春日大社,就顺便来了旁边的万叶植物园。说实话,刚刚去了姬路的好古园,所以,这里的景观明显比好古园的精致度差了很多。所以就当刷景点了,不给大家分享太多照片了。
完了一天,回家,坐着JR回家,车窗中的日落
逛了一天,两人都快饿疯了,吃吃吃,鸡食堂,这名字,不懂日语随便进都行,哈哈
岡山城(おかやまじょう)
为何这次要沿着濑户内海游玩呢,当然,最主要的原因还是因为这张票。刚好最近关西地区的旅游局准备了这样的一份旅行票套餐,其中可以自由乘坐岡山、倉敷之间的JR,而且包含了倉敷、岡山所有景点的门票,11000日元,三天的行程。完全符合我们这次旅行的期待。于是我们的濑户内海北岸的主要三天游就围绕这份套餐开始啦
所以,第一站就是「岡山」
出了冈山站之后,套餐里面直接包含了冈山站到刚山城的巴士,就是这辆电车。竟然是有轨电车,我竟然是头一次坐有轨电车。
下了电车,基本上步行就可以到所有的景观。当然,在去冈山城路上,我不得不提到这位买东西的小哥!!头一次在日本见到这种类型的叫卖小哥,真的是太会做生意了,看到我们拍了照片,就各种上来和我们搭讪。最后还是盛情难却的买了他的团子(据说是京都的团子。。结果我跑来冈山买,呵呵呵了~)
毕竟去过大阪天守阁,也去过姬路城,比起前两位的宏伟,冈山城可以说更加迷你。基本上大家都是一个格局,一个风格。
里面的各种装饰很有意思,让我各种想起火影忍者的画面
逛完了冈山城,河对面就是下一站,冈山后乐园
岡山後楽園(おかやまこうらくえん)
冈山后乐园感觉像是一个公园式的庭院,面积比普通的日本庭院大了很多,而且,有非常多的宽阔的地域。
当然,大中有小,小中有大,各种小湖流水,景色清幽
如果说冈山什么出名,那一定不能不提到大家都听说过的,桃太郎!
感觉桃太郎对于冈山,就像是奈良的鹿,熊本的くまモン是相同的存在。
连井盖都是桃太郎,我也真是 -_-||
冈山逛得差不多了,下一站是仓敷。回去冈山站的公交,竟然是做了一个特别版
传说的「たま(tama)電車」,真是幸运~~
倉敷(くらしき)
一路坐着JR的自由席,顺顺利利的来到了仓敷
而我们所住的宾馆,就是车站门口,对的,正门口,走路10秒钟的这个大楼
仓敷站门口的广场
逛了一天的仓敷,实在是累了,于是直接回宾馆休息啦
说实话,这个アパホテル的实际情况着实一般。
反正是睡个觉,太累了就不挑了。
没想到刚刚来到仓敷,就赶上个雨天,下的这个大。然后,我们发现了仓敷站后面,竟然是outlet。我勒个去,这购物的设定也太明显了。。。
但是,毕竟是下雨,于是我们就顺(wu)其(ke)自(nai)然(he)着去逛逛了,然后。。。
这广告牌,亮瞎双眼。。。。我回去点眼药了。。。
下午看雨小点了,于是我们开始闲逛美观地区,顺着恵比寿通り乱走
然后糊里糊涂的逛到了阿智神社,这个就是传说的「阿智の藤」,一棵500多年的古树。
这就是阿智神社,其实是一个不太大的庭院,在美观地区的一座小山上,一路上很清幽
顺着阿智神社的山路下来,就是美观地区的小巷,为啥有来到丽江的感觉
周围的各种小店都很精致,其中发现了MT的专卖店,小宝库一个,女王大人已经失去理智了。。
这里到提到一段有意思的经历,我们逛到一家布置很别致的陶制品馆,馆里只有以为白发苍苍的老太太,她静静的坐在店的中间,脸庞非常有气质。见到我们进来就很热情的攀谈起来,很惊讶我们是中国人,竟然来仓敷这里游玩(好吧,这里非常少外国人来玩么。。。)然后提到了【大原】。其实,我只知道仓敷的美观地区的重要景点之一,就是大原美术馆。我本来以为大原只是个区域名,原来是建馆人的名字。老太太带着各种崇敬之情给我们介绍大原先生。然后还说道她从小喜欢艺术,曾经家里很穷,一直有想去大原美术馆参观的愿望。大原先生为仓敷地区做出了非常大的贡献等等等。可见大原先生对仓敷人的影响有多深,以及大原美术馆对仓敷地区的重要性有多大。这种和本地人聊天的感觉,比听导游的介绍,感触深很多。
逛完了仓敷的小巷,已经很晚了,本来计划的美观地区的景点都没来得及刷,明天要忙了 >_<
倉敷美観地区(くらしきびかんちく)
昨天欠的债,今天总要还的。开始各种刷。
不过,刷景点还没开始,竟然遇到了仓敷的「ヒョウ猫の森」,好吧,女主人走不动了。
里面的猫咪都很可爱,不过,都是高冷型,只能各种热脸贴冷屁股。不过想必诸位铲屎官早已经磨练出来了 - -|
从猫猫的世界出来,就来到了传说的大原美术馆
大原美术馆分为好几个分管,分别存着不同类型的藏品,大家不要只去主馆哦。不过,馆内都是不让拍照的,所以图片基本上只留在记忆中了。
出来不远就是日本乡土玩具馆,感慨日本人的收藏耐心。这里真是的收藏了日本各个时期的各种玩具,一应俱全!!
然后随便刷了一下仓敷民俗馆,就赶紧下午的行程啦
児島(こじま)
儿岛的第一站就是濑户内海大桥.濑户大桥是日本一座位于本州(冈山县仓敷市)到四国(香川县坂出市)之间,跨越濑户内海的桥梁,属于本州四国连络桥路网的三条路线之一。全桥由多座吊桥、斜张桥与梁桥连结,构成壮观的桥梁群。
实际上,来到山下可以乘坐观光的游船。可惜全程的导游,只有会日文的船长爷爷。本来听老人家的日语就是半斤八两,还赶上个带口音的,基本上就看景吧,哈哈哈。
从濑户大桥周围乘着公交车,一路飙到这个Jean Street。虽然是儿岛的景点之一,不过是不是我们来的日子不对,人好少,虽然我喜欢。为啥这个是儿岛的景点之一呢,主要是因为儿岛这个地方的牛仔布料出名,于是,专门有这么一条街,买各种原创的牛仔布料制品。
说实话,感觉儿岛真的像是来到了日本大农村,哈哈。和大阪东京之类的发达大城市比,有明显的差别。濑户大桥很壮观,不过,其他地方就是比较小的游玩景点了。当然,本着小众游的原则,来看看是种不同的感受。
终于刷完计划中的景点了,坐上JR一路睡回神户啦
便随着旅行的结束,竟然是直接飞西雅图出差,我也真是醉了~~~
所以,小众游的主要内容便随着儿岛落幕了。总体来讲,濑户内海北岸的几个城市各有各的特点,至于是否感兴趣,就看大家的喜好了。其实,濑户内海中各种岛也很好玩,本来最开始的计划是这样。但是从大阪到高松实在是路途麻烦。而且,预定酒店的时候,高松各种找不到合适的酒店。所以,最终促成了这次北岸的游玩。濑户内海的列岛,属于濑户艺术圈之旅,下次有机会一定去玩玩。
The WIF(Windows Identity Foundation)
provides a Claims-Based Identity Model. And in ASP.NET
, we can already build a Claims-Aware ASP.NET Web Application. Especially, when using with different kind of authentication middleware, WIF
provides the same abstract layer to access the identity information across the whole asp.net pipeline context.
In this article we will talk about some detail about asp.net authentication middleware based on the CookieAuthenticationMiddleware
. At the end, let’s discuss more about persist claim in cookie across request.
Simple speaking, Owin pipeline is a link of middleware, and the request will dive into this link to the end(or short-circuited before end), and the response will pop up back though the pipeline middleware. When coming through the middleware, this gives opportunity to middleware to process and even short-circuit the whole process line.
Request /=====\ /=====\ /=====\=====> | | =====> | | =====> | | | MW1 | | MW2 | | MW3 |<===== | | <===== | | <===== | | <== Response \=====/ \=====/ \=====/
This diagram is a very simple and straightforward explanation about the Owin Middleware.
And authentication middleware if one kind of middleware that will give their effect to the request and response process.
1 | using System.Threading.Tasks; |
Here is the definition the OwinMiddleware
, there is two key pieces in code:
Invoke
abstract function: this is the how this middleware could perform on the owin conext. The usually pattern would be “Before action” + “next.Invoke()” + “after action”.AuthenticationMiddleware
is the base class for all authentication middleware. And it implement the basic pattern for the Invoke
function for a concrete authentication middleware. Here is the flow:
And more detail code is as below:
1 | public override async Task Invoke(IOwinContext context) |
So we can see that, the concrete authentication middleware will create an authentication handler and use that handler for further processing. The main logic for a authentication middleware is mainly about how the handler is implemented.
So let’s see the peice of code for CreateHandler
in CookieAuthenticationMiddleware
:
1 | protected override AuthenticationHandler<CookieAuthenticationOptions> CreateHandler() |
So the main logic of authentication middleware will be focus on authentication handler. Let see a deeper graph when we expand the AuthenticationHandler
level:
The blue part will be the main part of how AuthenticationHandler
works. And the red part will be how a specific authentication handler, here is CookieAuthenticationHandler
implemented.
We do not have the plan to dive into every detail of how CookieAuthenticationHandler implemented. So we only focus to original issue, how claim identity does the persistence underlying.
In fact, originally, i thought the claim identity which we can access via User.Identity
will be persist in cookie. So if I update the claims in User
context, everything will be persist ( i mean update ) into the cookie later. So I can access the updated value in later request.
However, the thing is not as I wish.
Here is the expanded version for CookieAuthenticationHandler
:
The TeardownAsync
will perform the persistence logic from the identity, but not like we wish. From the diagram, we found that findally, the CookieAuthenticationHandler
use the ApplyResponseGrantAsync
to save information. And what is Grant
?
We can refer to more detail from the implementation:
1 | protected override async Task ApplyResponseGrantAsync() |
From the code, two things need to pay attention to:
grant
into cookiegrant
Ok, let’s find the final piece about this grant
1 | public AuthenticationResponseGrant LookupSignIn(string authenticationType) |
So we finally found where the data comes from - Authentication.AuthenticationResponseGrant.
Grant
is the concept of the information that we retreive after we do the authentication. If the cookie handler save this grant into the cookie, we need to find where is grant come from.
1 | public void SignIn(AuthenticationProperties properties, params ClaimsIdentity[] identities) |
So in fact, this grant
information is set when we call the SignIn
function of the authenticaton manager. And if we do not call SignIn
, the AuthenticationResponseGrant will be null. In that case, the cookie will not be updated.
And the User.Identity
will NOT set to this authentication grant if SignIn
is not called. That is why even update the User.Identity
but those updated information will not take effect for any further request in asp.net.
This is also explain every detail about this stackoverflow question Persisting claims across requests
After dive into such more detail from code level, let’s discuss about the original topic.
I found a solution here from the stackoverflow How to update a claim in ASP.NET Identity?, this solution will set the AuthenticationResponseGrant
explicitly when try to update the claim items, this will trigger the sign in logic when Teardown
is called.
In fact, this is one of the solution that indeed take effect. But it has a potential issue, that is, in fact, such kind of code will trigger the sign in logic underlying, and the sign in logic will update the cookie expiration time. This behavior works as you sigin in again when you explicitly set the AuthenticationResponseGrant
. Of course, if the cookie expiration time is not a problem for your business logic, espeically when you use the SlidingExpiration, everyting should be fine.
But for my business which use the cookie authentication expiration as the web application login expiration. This will be a problem, and i should not use this way to update claim information in cookie.
So based on the implementation of Katana( i do not read the latest code, so correct me if i miss something), the identity saved in cookie is not designed for dynamically update. Those claim identity in the cookie is represented as a unique identity information that binding to a specific login and it should be immutable. And, updating the claim data in cookie with AuthenticationResponseGrant
means to a totally new login instead of updating the current login context. So all the claim data that is persisted in the cookie should not be updated within a specific login session. So that is why we can found that most of the authenticaton middleware save the claim identity information belong to the login itself to the cookie.
Yes, you may say that, how about those that do not belong to so-called “login information”, where should I save it? In fact, within asp.net project template the microsoft already provide us the way when you select “Individual Use Account”.
I do not want to dive more code here, the magic is ApplicationUserManager
. This manager is using UserStore
binding to a entityframework DbContext
object. We can use the user manager’s AddClaimAsync
|GetClaimAsync
|RemoveClaimAsync
to manipulate the claim data we want to save. The user manager will sync those saved claim everytime when the next query is comming, like what we wish. And yes, i think you already guess the conclusion, user manager is using the database to save the claim identity information underlying.
And one more option, if you just want to save some data between request, and those information is not a sensitive data, such as access code, you can use the Request.Cookie
directly, and you can see those data from chrome debug panel under Resource
tab directly. More simple, right?
Ok, claim identity in asp.net is so a big topic so i would talk more aspect on other article, let’s end here.
Any question, please just reply and discuss with me :-)
References:
Originally, I think this may be the bug of a 3rd party tag plugin for hexo
.
But after some investigation, this is the bug of hexo itself.
Before I start to explain about the detail of this bug.
I indeed speed several hours to dive into the code of Nunjucks
.
Nunjucks
A rich and powerful templating language for JavaScript.
Nunjucks
is an interesting template framework for JavaScript. It has many great features and one of them used by hexo
is the custom tag.
In Nunjucks
, you can define the custom tag
to integrate it into whole lexical analytics system. So that it will be processed as a Nunjucks
native supported tag.
In order to implement a custom tag
, you need to implement two function:
parse
is used to get the travel through lexically-marked token. So that you will analyze each part of the arguments you want for your specific custom tag
.
For example, if you define a tag like below:
1 | {% mytag "param1" "param2" "param3" %} |
You need to define the parse
function like:
1 | function(parser, nodes, lexer) |
You use the parser.nextToken()
to get each part of your arguments.
For example (here is the pseudo code to explain, more info refer to lexer.js of Nunjucks:
Usually, we travel through each of the lexical node, save them as specific node in nodes
parameter back to Nunjucks
framework, and found the TOKEN_BLOCK_END
to stop our process for any custom tag
Run
is used to process the information you just analyze from the parse
function. Simply speaking, it is used to render the parameters to its expected result for the case of hexo
Here is the pull request for fix the issue, i would like to talk based on it.
We can see that, current implementation of hexo
:
parse
functionargs
, but use the string +=
to connection all things together, which result in a string. Cannot make sure it is a expected code or the writer think he could use +=
for array elements.run
Hexo
simply split all the parameter with space, this is the root cause for non space supported issue. And it pass the arguments into registered function. so we cannot use the space anymore even wrapped in a string literal.One more detail about my change:
If you want to use the Nunjucks
to pass the result of parse
correctly to run
, you need to follow the internal structure of Nunjucks
. Here my fix need to pass a array to run
, instead of give the value of a nodes.Literal
a simple javascript array object, we need to create a nodes.Array
object, and add each of part of the tag arguments as nodes.Literal
into it. so that Nunjucks
can understand what we really want to pass.
So here i give a fix and hope hexo will merge it ASAP.
In case anyone see the same issue, so I write it down here.
References:
随着ios9添加的新的Network Extension
的特性,surge(App Store)出现了,利用surge所提供给我们的功能,我们将给出一个近乎完美的全局代理方案。
Surge是一个ios网络包抓取分析工具,它是基于ios9中Network Extension
和VPN
的新特性而来。
Surge是一个基于规则(Rule)
的可配置型工具,我们可以利用Surge在ios网络层中添加代理,这样可以将ios设备的很多网络数据进行截获,从而实现对网络数据的分析和采集。
如果大家在windows上经常发开http程序的话,想必著名的fiddler
大家一定不会陌生,在利用代理进行网络数据包分析方面,Surge可以说与fiddler
的做法是很相似的。
如果说surge的初衷,还是一个网络包抓取和分析工具的话,它的基于规则
的数据截获和转发能力,给我们提供了非常好的网络代理功能。
如果Surge可以长久的存活下去的话,想必一定是将来一个非常有效的ios设备网络代理工具。
如果想了解surge的具体细节,可以参考官方的主页介绍 surge.run。
Surge并不是一个打开即可以使用的工具,它是一个高可配置的工具。
打开Surge之后,我们只有一个默认的配置文件。
我们可以看到,默认的配置文件是所有的网络都是直接进行访问(all to Direct)。
点击Edit
之后,我们可以进行更多的设置
对于已有的文件,我们可以更改名称,添加更多的代理,添加规则
这个是代理设置界面,在这里,可以添加三种类型的代理,http
|https
|socks5
。
我们发现,这里并没有我们所需要的shadowsocks
。是的,shadowsocks
在界面似乎不可以直接添加,只能通过直接编辑配置文件,添加custom类型的代理。我们随后会说到。
在规则配置界面中,可以添加三种很多种不同的预定义类型的规则
192.168.1.1/24
CN(中国)
,US(美国)
等等我们添加了规则之后,可以选择当数据包满足规则的时候如何处理:
我们知道,很多时候,大家对于手工配置上文提到的这些东西,是很闲麻烦的。
所以,网络下载配置文件也是surge非常人性化的feature,我们可以通过URL直接下载surge的配置文件,让他人的东西劳动成果很快的为我们所用。
这里分享一个很不错的配置文件,源自surge.pm
网站。
在编辑已有配置
中,选择Download configuration from URL
,然后添加
我们上文提到的配置文件中,已经为我们添加了shadowsocks的代理,不过,添加的代理只是例子,不能使用,我们需要利用上面提到的更改配置文件的办法,更改这个代理到我们自己的shadowsocks服务器的配置。
这样就可以直接使用这个配置文件进行shadowsocks的网络代理了。
我们上面提到了很多的surge的配置,当然,最原始的初衷,还是为了使用surge进行网络监控。我们可以在Analytics
选卡中看到
在请求界面中,我们可以看到具体的每个请求的header和一些相关的参数。据说作者之后会加入请求的body的显示。
在Rule
界面中,我们也可以看到规则的相应情况,而且,最人性化的,我们在这里可以动态添加规则项
经过上面的介绍,相比大家已经对surge的功能有了比较详细的了解,打开配置文件中,大部分的配置行都和我们之前介绍的界面相关,语法很容易理解。也希望大家多多分享自己的配置文件,服务于大家。
这样做可以获得什么优势:
在开始本文之前,强烈建议读一下我的另一篇文章从DNS到github pages自定义域名 – 漫谈域名那些事,可以帮助你熟悉过程中的很多原理。
实现github和gitcafe部署
1 | deploy: |
这样就可以使用hexo delpy
直接部署github和gitcafe了。
这里要说一句,github使用master分支作为静态页展示,gitcafe使用gitcafe-pages分支作为静态页展示。这点hexo的deploy里面已经很好的处理了,我们不用特别指定。
NDSPod的域名解析服务中,提供根据线路进行不同的路由的功能是免费的,这也是为什么选择DNSPod的原因
添加了域名后,DNSPod会自动导入当前的域名下的各种记录
在这里我们可设置针对不同线路的CNAME记录
我是用的是godaddy申请的域名,godaddy中NS记录不是在Zone File
的配置下,而是点击域名旁边的下拉列表更改的
将其设置成DNSPod的服务器地址
经过上面设置后,还需要等待全球DNS服务器的递归更新。
然后可以测试从不同地区的主机效果:
这里是从家里的机器:
MacBook-Pro:~ winterTTr$ dig winterttr.me
; <<>> DiG 9.8.3-P1 <<>> winterttr.me
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7737
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 10;; QUESTION SECTION:
;winterttr.me.INA;; ANSWER SECTION:
winterttr.me.600INCNAMEwinterttr.gitcafe.io.
winterttr.gitcafe.io.600INA103.56.54.5;; AUTHORITY SECTION:
gitcafe.io.600INNSf1g1ns1.dnspod.net.
gitcafe.io.600INNSf1g1ns2.dnspod.net.;; ADDITIONAL SECTION:
f1g1ns1.dnspod.net.171513INA180.153.9.189
f1g1ns1.dnspod.net.171513INA182.140.167.166
f1g1ns1.dnspod.net.171513INA111.30.132.180
f1g1ns1.dnspod.net.171513INA113.108.80.138
f1g1ns1.dnspod.net.171513INA125.39.208.193
f1g1ns2.dnspod.net.55977INA101.226.30.224
f1g1ns2.dnspod.net.55977INA112.90.82.194
f1g1ns2.dnspod.net.55977INA115.236.137.40
f1g1ns2.dnspod.net.55977INA115.236.151.191
f1g1ns2.dnspod.net.55977INA182.140.167.188;; Query time: 1649 msec
;; SERVER: 192.168.0.1#53(192.168.0.1)
;; WHEN: Sat Oct 24 17:45:10 2015
;; MSG SIZE rcvd: 294
然后登陆一台国外的主机:
~$ dig winterttr.me
; <<>> DiG 9.9.5-3ubuntu0.4-Ubuntu <<>> winterttr.me
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37875
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1280
;; QUESTION SECTION:
;winterttr.me.INA;; ANSWER SECTION:
winterttr.me.600INCNAMEwinterttr.github.io.
winterttr.github.io.854INCNAMEgithub.map.fastly.net.
github.map.fastly.net.30INA103.245.222.133;; Query time: 173 msec
;; SERVER: 100.72.124.100#53(100.72.124.100)
;; WHEN: Sat Oct 24 09:46:29 UTC 2015
;; MSG SIZE rcvd: 125
完成!~
在python中,类型
属于对象
,变量
是没有类型的,这正是python的语言特性,也是吸引着很多pythoner的一点。所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。所以,希望大家在看到一个python变量的时候,把变量
和真正的内存对象
分开。
类型是属于对象的,而不是变量。
这样,很多问题就容易思考了。
例如:
1 | nfoo = 1 #一个指向int数据类型的nfoo(再次提醒,nfoo没有类型) |
对应于上一个概念,就必须引出另了另一概念,这就是可更改(mutable)对象
与不可更改(immutable)对象
。
对于python比较熟悉的人们都应该了解这个事实,在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。那么,这些所谓的可改变和不可改变影响着什么呢?
1 | nfoo = 1 |
代码第2行中,内存中原始的1对象因为不能改变,于是被“抛弃”,另nfoo指向一个新的int对象,其值为2
代码第5行中,更改list中第一个元素的值,因为list是可改变的,所以,第一个元素变更为2。其实应该说,lstFoo指向一个包含一个对象的数组
。赋值所发生的事情,是有一个新int对象被指定给lstFoo所指向的数组对象的第一个元素,但是对于lstFoo本身来说,所指向的数组对象并没有变化,只是数组对象的内容发生变化了。这个看似void*的变量所指向的对象仍旧是刚刚的那个有一个int对象的list。
如下图所示:
对于变量(与对象相对的概念),其实,python函数参数传递可以理解为就是变量传值操作,用C++的方式理解,就是对void*赋值。如果这个变量的值不变,我们看似就是引用,如果这个变量的值改变,我们看着像是在赋值。有点晕是吧,我们仍旧据个例子。
1 | def ChangeInt( a ): |
这时发生了什么,有一个int对象2,和指向它的变量nfoo,当传递给ChangeInt的时候,按照传值的方式,复制了变量nfoo的值,这样,a就是nfoo指向同一个Int对象了,函数中a=10的时候,发生什么?(还记得我上面讲到的那些概念么),int是不能更改的对象,于是,做了一个新的int对象,另a指向它(但是此时,被变量nfoo指向的对象,没有发生变化),于是在外面的感觉就是函数没有改变nfoo的值,看起来像C++中的传值方式。
1 | def ChangeList( a ): |
当传递给ChangeList的时候,变量仍旧按照“传值”的方式,复制了变量lstFoo 的值,于是a和lstFoo 指向同一个对象,但是,list是可以改变的对象,对a[0]的操作,就是对lstFoo指向的对象的内容的操作,于是,这时的a[0] = 10,就是更改了lstFoo 指向的对象的第一个元素,所以,再次输出lstFoo 时,显示[10],内容被改变了,看起来,像C++中的按引用传递。
恩,现在是不是对python中的变量和对象的概念有了更深入的理解了呢?
通过我上面的解释,我想大家也可以自己搞定其他类型对象的传递问题了吧。
原文写于2008年,发表在CSDN,发现文章反馈比较多,所以适当更新后重新发表在个人博客。
域名就是一段文字,更具体地说,是一段人类容易识别的文字,它的作用很简单,就是用来给一个ip起一个人们能够记得住的名字。
域名是一种资源,很多时候甚至是地位财富和身份的象征,所以越来越多的geek们都趋之若鹜的申请自己的域名,我也不例外。
我们通常知道的域名,例如winterttr.me,其实是完整域名的缩写。 真正的全称为winterttr.me.,请注意最后的.
,这个就是根域。 它的现实体现为全球13台固定ip的根域服务器。 从a.root-servers.net
到m.root.servers.net
。
当我们在进行dns查询的时候,如果一个全新的域名从来没有进行查询,那么,最终会向这13台根域服务器进行请求。 不过,现在的浏览器已经基本上默认不再添加这个”多余”的点了,已然成为一种默认习惯。
当然,事实上,并不是真正的13台,而是13组,每一台在全球都有很多的镜像节点,所以你不用担心其中一台挂了会引起全球混乱~
常用的顶级域名分为几种:
1 国家顶级域名,例如.cn
,.jp
2 机构顶级域名,例如.com
,.edu
3 还有其他分类
这个就是我们常常能够申请到的域名,在顶级域名的左侧加上的一个自定义的文字段。据个例子: winterTTr.me。所以,我们通常所说的域名,往往指的是这个二级域名。
相对于上文所提到的“我们通常所说的域名”(二级域名)的基础上,又加入了子域名的概念,就是在一个域名的前面,加上新的字段,代表这个域名下的某个特定的主机或者协议。最常用的就是WWW
协议,所以,我的子域名www.winterttr.me
就是winterttr.me
的WWW
子域名。
咱们谈完了域名,那么就不得不说到DNS(Domain Name System),DNS所承担的主要任务,就是所谓的域名解析。这些由DNS系统中的DNS服务器负责。那么,DNS服务器解析一个域名得到了什么? – IP地址
所以,域名解析的过程,说白了就是把一个人类记得住的域名变成ip网络中机器认识的ip地址。
那么,DNS服务器上都存了些啥?最主要的就是能够完成域名解析的一些记录
A记录在DNS中的意义就是,域名到ip地址的转换。
所以,当我们在DNS服务器中添加一个A记录时,是告诉服务器,将某个特定的域名映射到一个ip地址。这个算是最简单直白的转换规则了。
CNAME的意义,简单说就是别名,即将一个域名射到另一个域名(区别于A记录的ip)。所以,CNAME通常有两种用法:
winterTTr.me
(顶级域名为me
)。如果我希望,当我访问这个域名的时候,实际上是访问我的winterTTr.github.io
(顶级域名为io
)的主页时,虽然他们在不同的顶级域名,但是我可以用CNAME记录映射。www.winterTTr.me
(一个WWW
子域名)的时候,仍旧访问winterTTr.me
这个域名所指向的内容时,可以将www.winterTTr.me
利用CNAME记录映射到winterTTr.me
。简单来说,就是声明谁来负责解析我这个域名,指定了负责解析我这个域名的服务器的地址。这条记录赋予我们一个特殊的能力,就是,我可以让自己指定的一个DNS解析服务器,而不一定是域名提供商自带的域名解析服务器。简单来说,就是在godaddy买的域名,默认是使用godaddy的域名服务器来进行域名解析的,但是如果我想让别的server解析(例如NDSPod),而不受godaddy服务器的限制呢?那就是更改这个NS记录的内容。一般来讲,是两条记录,一条主服务器,一条副服务器。
这个就是我的域名在DNSPod
中的设置。
可以看到,NS记录为dnspod的服务器域名,dnspod
提供了一种非常方便的服务。就是可以根据不同的线路类型,进行不同的解析。图中可以看到,国内的使用gitcafe,国外的使用github,同时,baidu抓取国内线路gitcafe的内容,躲避了github封闭baidu spider的问题。
这不是废话么,买到了域名!
当然,除了这一串字符串之外,是什么让我们真正拥有了这些字符串使用权呢?
话说花了钱,买了域名,为啥这个域名就是你的呢?
这是因为,godaddy将这个域名中的各种记录的配置权力分配了给你,于是,你可以定义域名的ip(A记录),或者将这个域名指向另一个别名(CNAME记录)。
默认的域名,是在godaddy自带的域名解析服务器中进行的。godaddy是提供更改NS记录的权利的,所以,我们可以将这个域名解析的能力交给godaddy之外的人,这就是我如何做到使用DNSPod
来进行域名解析的。
那么,经过上面一堆讲解,最终还是回到最实际的问题,github中的自定义域名。
github的域名是支持A记录的,这个意思就是,github的服务器域名是个固定ip。所以,当我们需要将申请的域名给予一个自己的github.io的地址的时候,我们可以在DNS服务器的配置中添加一条A记录,指向github的服务器地址。
现在github的服务器地址为:
如果你做了上面的操作,那意思就是,我希望把我的主域名winterTTr.me
完全指向github.io的主页。
不过,如果你的只是想将一个子域名,例如www.winterTTr.me
,而不是主域名winterTTr.me
分配给你的github主页,那么,A记录会完全绑定你的主域名,所以这个场景A记录不适合。你需要在DNS服务器中添加一条CNAME记录,将子域名指向winterTTr.github.io
,这样,当用户访问WWW
子域名的时候,会跳转到github.io的主页上。
在项目下建立一个CNAME文件,在其中写上给你的主页分配的域名地址。
这个操作的作用在哪里?
github.io
的网站时,我们会发现,域名自动变成了我们制定的自定义域名。这是因为CNAME中指出了自定义域名是什么,所以,当我们访问github.io
的时候,会触发http 301
。azureuser@ubuntu-jpe:~$ curl -I winterttr.github.io
HTTP/1.1 301 Moved Permanently
Server: GitHub.com
Content-Type: text/html
Location: http://winterttr.me/
X-GitHub-Request-Id: 2BF9481E:370C:8930CF:562B48F6
Content-Length: 178
Accept-Ranges: bytes
Date: Sat, 24 Oct 2015 09:02:36 GMT
Via: 1.1 varnish
Age: 54
Connection: keep-alive
X-Served-By: cache-nrt6130-NRT
X-Cache: HIT
X-Cache-Hits: 1
X-Timer: S1445677356.846925,VS0,VE0
Vary: Accept-Encoding
我们可以看到,github的服务器知道,我们需要访问的io网站已经有了别的域名,并且返回301让浏览器跳转到自定义域名。
xxx.github.io
的内容。这基本上就是从一个从申请域名到到github.io配置后的完整故事
References:
Helm
has been released a long time, but I am always get used to ido
.But based on some article recently, and I am really like new things, so I decided to use the this helm
package.
So, let’s start.
Helm Github already tell many things about the install, here I want to memo down what I do.
I always use the subtree to manage the package by myself, so
> git subtree add –prefix .emacs.d/plugins-subtree/helm-suite/helm –squash https://github.com/emacs-helm/helm.git master
> git subtree add –prefix .emacs.d/plugins-subtree/helm-suite/emacs-async –squash https://github.com/jwiegley/emacs-async.git master
go the helm folder:
> make
Before the config step, you can simply use the following command to have a test:
> ./emacs-helm.sh
This would be a simple test environment, and it’s very handy.
Add the package to load path and active the helm
1 | ;; -*- coding: utf-8 -*- |
Enable global M-x
1 | (global-set-key (kbd "M-x") 'helm-M-x) |
The default behavior of Tab
key is not handy enough, so change it:
1 | (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action) ; rebind tab to run persistent action |
Still, grep under windows, here is my solution, please not that, your grep
under windows may not support --include
well, so remove %e
will fix it.
1 | (if (executable-find "perl") |
C-c C-i
C-t
. Running C-t
again returns the Helm window back to horizontal and so onC-SPC
; this is useful when you need to perform an action on many candidates of your choice. M-a
to select all.Read this article helm-intro
References:
And also:
因为这次出行首先要帮老婆在神户办些事情,所以,是从神户出发的。
在神户机场的时候专门和机场的服务人员确认了一下,神户机场不是国际机场。所以,是没有国际直达飞机的,也就是只能进行国内航班的飞行。于是乎,体验了一下日本国内航空。
话说,小小的神户机场,竟然还有空中展台:
这次去日本订购的是日本ANA国内航线。订购日本国内机票的时候,才知道,原来日本的国内机票,订购的时候连护照号都不需要,只需要生日和护照姓名,然后,最后会发给你一个confirmation code。到时候,凭着护照和confirmation code就可以登记了,方便是方便,但是还是着实为这么简单的流程心虚了一下(我木有被骗吧,哈哈)。
虽然后来我找了半天,终于又找到了这个confirmation code的页面,对于不善于日语的同学,还是推荐把这页打出来,因为想再从ANA的网站上查到这个地方,着实不容易 >_<
于是乎,直接在前台拿出confirmation code的打印纸和护照,就可以顺利登机了。
飞机抵达冲绳那霸机场,非常浓重的军事气息,感觉这个机场就是军用机场分了条跑道给民用吧,哈哈
说实话,去之前完全没有留意到冲绳的温度,下了飞机就感觉很“暖和“,实际上过不了一会儿就开始浑身出汗,作为一个在神户被冻感冒的小盆友,我表示瞬间被蒸好了。
从那霸机场去那霸市内,基本上就是依仗这个ゆいレール(Yui rail)
。对于国人来讲,还是很容易的认识站名的,我就住在美栄橋(みえばし miebashi)
。
ゆいレール(Yui rail)
的票和关西地区的地铁使用的方式不太一样,不是插入闸机的,而是在闸机那里扫描二维码
上了车后站名还是很清楚的,大家记好发音就行,到了会有广播通知。
传说的首里城
就在终点站,不过这次没有安排,对于在京都看了各种城的人来说,没有太大的兴趣跑去冲绳再看看日本的城池。
冲绳那霸
作为一个基本上最靠近赤道的日本城市,阳光是非常充沛的,加上日本的绿化,所以城市本身还是美丽阳光的。
冲绳那霸
还真是热啊,热啊,好热啊(重要的事情要说三遍)
冲绳那霸
街道随拍
终于到了酒店了HOTEL ESTINATION
,当初定这个酒店是因为便宜,不过,基本情况还好。所有日本的酒店都是小小小,所以这个屋内大小就不纠结了。只是,我没有找到烧水壶= =|。这个酒店重要的在于距离美栄橋(みえばし miebashi)
地铁站还是很近的,走路7分钟,路上还有个邮政局,方便同学们各种寄明信片。
这里提醒一下同样三年签证的同学们:
三年前签证回来的时候,需要酒店证明,别最后一天急忙走忘记了,在酒店前台交完钱,可以直接找酒店服务人员,要求开证明。他们估计都见多了,所以会直接拿出证明,写清时间后给你。好好收好,回国激活护照哦
到了宾馆开空调一个小时后,终于凉快了一些,看着时间还是挺早的,查了一下google地图,发现酒店距离国际通
不远,所以,果然乱入了一段国际通
乱逛的计划。其实重要是考虑,既然来了,就别再屋里呆着了,哈哈
日本是个到处都会卖萌的国家。。
暖暮ラーメン(だんぼらーめん)
感觉是又一个被国人攻陷的地点,很早的时间就已经一堆人在外面排队。
你们别担心,我只路过看看。。。
到达了传说的国際通り
说实话,国際通り
给我的感觉一般,道路两面的游客很多,但是整体比较乱。两面有各种各样的商店,从免税店到土产,我是对这种类型的市场型大街不怎么感冒的,不过可能很多人喜欢逛这种”琳琅满目“的大街吧,所以,我就随便逛逛了。
冲绳这个地方,最著名的一种东西,就是。。。盐。。。
是的,你没听错,于是乎,就有了一个专门买盐出名的商店塩屋(「しおや」じゃなくて、「ますや」だよ)
。
能把盐搞得这么有门道,我也是佩服,可以浏览一下官网
里面的盐真的是琳琅满目,着实佩服一下
很快就逛完了整个大街,其实还是很短的,据说1.6公里,不过考虑到我没什么兴趣,就不跟大家更多介绍了,返程回宾馆了。
于是乎,回来的路上,我真的。。。
明天,上大餐~~水族馆伺候
首先,你要知道,冲绳是个岛,岛上也是分很多个小城市的。
我们到达的叫那霸
,在冲绳岛的最南面。我们要去的美ら海水族館
在名护市
,在冲绳岛的最北面,所以,我们要穿越整个海岛才可以到达 >_<。
这个传说google地图中指出的Limousine Bus
,就是下面站牌所说的やんばる急行バス
。
我给大家放大一下时刻表信息吧,游玩的时候一定要注意下时刻表。
错过了就要等下一班,非常影响行程的安排,到站后也要注意一下回来的时刻表,按照时刻表提前10分钟到站等着就好。我会说我提前到了一个小时,傻傻站了一个小时么。。。
至于买票的话,是不用提前到什么售票处去买的,上车之后,会从前面的一个机器上拿一个整理券(せいりけん)
然后随着车的行驶,对应的整理券
上的价格会自动变化,下车的时候拿着整理券
和钱给司机就好。
一路上的海非常美丽,没有被破坏的自然之美。
天蓝蓝,水蓝蓝
两个多小时,终于到了
我们要去的美ら海水族館
,其实是这个冲绳纪念公园
里面的一个景点而已。
整个冲绳纪念公园
是无料的,就是免费的。着实让我惊讶一下,要是在中国,呵呵呵
后面会介绍的海豚表演等等,都是免费的户外活动,每隔一段时间都会有一次,大家可以自己安排好时间,只有美ら海水族館
进入是需要收费的
整个冲绳纪念公园
非常的大,我倒是感觉有时间可以入住周围的宾馆慢慢游览一番,考虑到我的行程匆忙,就不深度游了。这里是入口的广场。
其实,海豚表演全世界的海洋馆基本上都有,但是,以真实的大海为背景的,却是第一次见到,这也是冲绳纪念公园
的最大亮点吧
这里附上一段精彩的海豚跳跃~~
旁边还有海豚池和海象池,大家可以自己近距离观察这些可爱的动物
沿着几个池子的路线走下去,很快就能到我们的最终目标了
水族馆的入口是一个高台上
从上面纵览整个海洋的景色,异常美丽
水族馆的门票是1850日元,其实水族馆的门票是可以多次进入的,所以如果真的不小心出来了还想再进去,可是可以的哈。
水族馆其实本身的展览都是鱼,不过,毕竟不是什么专家,也就是只能看个热闹了.
这个还比较萌~
介绍在这里。。虽然,我也不是很关心,哈哈哈
水族馆里面是有一些影片表演的,据说是水族馆的人们自己收集制作的,所以既然来了,为什么不看看呢,记得好时间,总体来讲,人不算多,提前10分钟到就能有座位。
整个水族馆最精彩的,当然就是拥有多项吉尼斯世界纪录的鲸鲨
.
究竟有多大,近距离比较一下
贴个视频,大家看看最下面拍照的人,就知道这东西有多大了。
旁边就是专门的鲨鱼池,里面的东西,看着都不是善主。。这东西竟然和鲨鱼放一个池子里。。可见。。。。
大家不要忽略,水族馆是还有更高一层的,在上面可以直接看到水族馆的水池里面的鱼,换一个视角,另一种感觉。看!!游过去了!!!忽然闪过各种电影画面。。。快撤
对了,忘记了,进入水族馆的时候,可以去摸海星,真的很好玩,带着小盆友去的就一定不要忘记了!!毕玩项目,可惜我当时光顾着玩,没照相,哈哈,所以此处省略XXX字。
其他的各种看不懂的鱼就不给大家贴了。。。反正我也不认识它们,它们也不认识我。
回那霸~~~ 还是那句话,大家看好巴士的时间
回到那霸
后,想想看也不是很晚。
既然来了,就都逛逛,发现DFS环球免税店
果然不是我等人士应该逛的,一水的奢侈品店,话说我只是以为过来能吃点东西 >_<
还好距离不远的地方就有The Daiso Sanenaha Maine Place
,终于可以吃顿饭了,饿屎了
回宾馆,今天结束。
HOTEL ESTINATION
是要求10点退房的,而且,由于第二天的飞机票是晚上的,所以早晨起来就直接退房,去机场了。
在机场可以找到行李寄存处,收费的,大箱子一天好像480日元,其实还可以,于是存了箱子就上路了。
如果一天乘坐3次以上的ゆいレール
,那就可以买一日券了,非常的划算。
目标之一,奥特莱斯,在机场直接乘坐公交车直达哦。
就是下面这辆车,公交车95号
这里就是传说的奥特莱斯,话说,这辆公交车上都是国人,一路上坐着冲绳的破公交车加上周围一堆国人聊天,俨然已经回到祖国啦 _
买买买相信大家不感兴趣了
随后又去了AEON百货
,说实话,这里才是屌丝的天堂,哈哈哈,就是各种乱七八糟的百货市场。
不过对于吃货来讲,终于吃到了觊觎很久的mister donuts
了。
伴随着冲程的落日,旅行也就结束啦
提着一箱战利品回国喽~~
–
]]>npm
from emacs eshell.npm
can be found from bash in iTerm2
but cannot be found from emacs eshell.PATH
with what you can check from bash
.I fact the wiki already mentioned those issues here.
And also, you can find from the stackoverflow with the same question.
Here I want to tell a good package for emacs from stackoverflow:
exec-path-from-shell
With simple setup, you path could be right:
1 | (require 'exec-path-from-shell) |
Enjoy emacs.
git submodule
to organise the packages. I do not use any package management for my emacs currently. But recently, i lose the evil
package git repository access. That makes me to really the potential issue about git-submodule
. Meanwhile, I found the git-subtree
, so i decide to move all my packages from git-submodule
to git-subtree
.First, let’s talk about some differences between submodule
and subtree
.
When you clone you main repository, you have all you files in your main repository and the submodule record file .gitmoduless
.
You need to use git submodule init
to initialize the submodle configuration and git submodule update
will check out the exact version of the sub repository from their remote git service. Of course, you can do all of these in one command, git clone --recursive (repository)
.
In fact, .gitmoduless
only store the information of remote repositories, such as
1 | [submodule ".emacs.d/plugins/ace-jump-mode"] |
And git stores SHA1 of commit for all the sub repositories directly in Git’s object database. You can check the content with
git ls-tree master:[path-to-directory-containing-submodule]
For example:
~ $ git ls-tree master .emacs.d/plugins/ace-jump-mode
160000 commit 8351e2df4fbbeb2a4003f2fb39f46d33803f3dac.emacs.d/plugins/ace-jump-mode
So here is one of the potential issue for git-submodule
, when you lose the access of other submodule remote git server or your remote server delete the commits with that SHA1 you want to reference, you cannot give your environment setup correctly again if you clone your main repository in new environment.
This is exact issue i met now.
When you clone your repository, you will get all the code no matter you main repository and sub repositoies. In fact, all the sub repository code will be commmitted to your main repository as normal files. So, this would be like a local snapshot of the sub repository. You clone your repository and you got all you should have for your whole environment.
Even though you cannot access some of the sub repository git service, you do not need to care about that because you already have the workable snapshot in your repository. The only case you need to access those remote service is when you need to pull their latest change and merge to your local snapshots, or you want to push your local change back to sub repositories remote git server.
This gives us several benifits:
And what git-subtree
do is to help you check out this snapshots and merge upstream changes and etc.
So personally, I would consider git-subtree
as a way to manage your sub repositories.
It’s a pity that we do not have a git submodule rm
, so remove a submodule will be manual steps.
For a submodule, there are many places that record the relevant information, we need to clean them as expected to remove submodule gracefully:
.gitmodules
.git/config
git submodule init
, git read the information from .gitmodules
and save it here to track local submodule. You can use git submoudle deinit [submodule-path]
to remove that module from this file. One thing need to pay attention to, if you use git module deinit
, it will also remove submodule from working tree..git/modules/[submodule path]
submodule file in working tree
rm -rf [submodule path]
So the steps are:
clean .gitmodules
Manually edit this file to remove relevant git submodule section
> git add .gitmodules
clean .git/config
manually delete submodule section in
.git/config
clean files in index/staged
git rm –cached [submodule path]
clean .git/modules/[submodule path]
> rm -rf .git/modules/[submodule path]
commit your change
> git commit -m “remove submodule xxxx”
clean local if necessary
> rm -rf [submodule path]
Here is my example:
~ $ ff .gitmodules
#<buffer .gitmodule>
~ $ git add .gitmodules
~ $ ff .git/config#<buffer config>
~ $ git rm –cached .emacs.d/plugins/ace-jump-mode
rm '.emacs.d/plugins/ace-jump-mode'
~ $ git status
On branch masterYour branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits)Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: .emacs.d/plugins/ace-jump-mode modified: .gitmodules
~ $ git commit -m “remove submodule ace-jump-mode”
[master 24a9e7d] remove submodule ace-jump-mode Committer: winterTTr <winterTTr@gmail.com>
The common process to get remote repository into subtree:
add remote branch to local track
> git remote add ace-jump-mode https://github.com/winterTTr/ace-jump-mode.git
get the latest version of the code into working tree
> git subtree add –prefix .emacs.d/plugins-subtree/ace-jump-mode –squash ace-jump-mode master
git fetch ace-jump-mode masterFrom https://github.com/winterTTr/ace-jump-mode * branch master -> FETCH_HEAD
Added dir '.emacs.d/plugins-subtree/ace-jump-mode'
> git subtree pull –prefix .emacs.d/plugins-subtree/ace-jump-mode –squash ace-jump-mode
References:
Markdown Tips:
<
in your markdown block, you need to escape it as <
1 | { |
But when in local debug, the message would be more detail:
1 | { |
Theoretically, you should not show those detail information to your final user, no matter for detail code security, or user experience.
But when debugging our cloud service after deploying, we may need to figure out what happened in backend.
In fact, ASP.NET web api has a separate configuration for how the error detail is shown in different environments.
In your HttpConfiguration
, there is a property called IncludeErrorDetailPolicy
. Let’s check its possible value.
1 | // Summary: |
Its comments has already show the detail, so for my case:
1 | public class Startup |
Done~~
Eventhub
in Azure Service Bus
, there is many things you need understand, I will talk something specific about about concurrent reader on consumer group.As Event Hubs Overview said:
The publish/subscribe mechanism of Event Hubs is enabled through consumer groups. A consumer group is a view (state, position, or offset) of an entire Event Hub. Consumer groups enable multiple consuming applications to each have a separate view of the event stream, and to read the stream independently at its own pace and with its own offsets. In a stream processing architecture, each downstream application equates to a consumer group. If you want to write event data to long-term storage, then that storage writer application is a consumer group. Complex event processing is performed by another, separate consumer group. You can only access partitions through a consumer group. There is always a default consumer group in an Event Hub, and you can create up to 20 consumer groups for a Standard tier Event Hub.
In order to consume events from an Event Hub, a consumer must connect to a partition. As mentioned previously, you always access partitions through a consumer group. As part of the partitioned consumer model, only a single reader should be active on a partition at any one time within a consumer group. It is common practice when connecting directly to partitions to use a leasing mechanism in order to coordinate reader connections to specific partitions. This way, it is possible for every partition in a consumer group to have only one active reader. Managing the position in the sequence for a reader is an important task that is achieved through checkpointing. This functionality is simplified by using the EventProcessorHost class for .NET clients. EventProcessorHost is an intelligent consumer agent and is described in the following section.
So based on the event hub documentation, we should only have ONE active reader( receiver ) on a partition within the same consumer group.
OK, you know, sometimes should does not means “must”. So let’s see another documentation from Azure Stream Analytics
.
Azure Stream Analytics Preview limitations and known issues:
Each Stream Analytics job input should be configured to have its own event-hub consumer group. When a job contains self-join or multiple outputs, some input may be read by more than one reader, which causes the total number of readers in a single consumer group to exceed the event hub’s limit of 5 readers per consumer group. In this case, the query will need to be broken down into multiple queries and intermediate results routed through additional event hubs. Note that there is also a limit of 20 consumer groups per event hub. For details, see Event Hubs developer guide.
OK, here we see that, in fact, the limitation of total number of readers in a single consumer group on a specific partition is 5.
Let’s do some code test:
[TestMethod]public void Concurrent_Readers_On_1Partition_1ConsumerGroup() { var connectionString = CloudConfigurationManager.GetSetting("ServiceBus.Eventhub.ConnectionString"); const string eventhubPath = "eventhub"; var nsm = NamespaceManager.CreateFromConnectionString(connectionString); var description = nsm.CreateEventHubIfNotExists(eventhubPath); var builder = new ServiceBusConnectionStringBuilder(connectionString) { TransportType = TransportType.Amqp }; var factory = MessagingFactory.CreateFromConnectionString(builder.ToString()); var client = factory.CreateEventHubClient(eventhubPath); var partition = description.PartitionIds[0]; var group = client.GetDefaultConsumerGroup(); try { var receiverList = new List < EventHubReceiver > { group.CreateReceiver(partition), group.CreateReceiver(partition), group.CreateReceiver(partition), group.CreateReceiver(partition), group.CreateReceiver(partition), group.CreateReceiver(partition), // we create more than 5 first and comment this line to pass the test }; var taskFactory = new TaskFactory(); var task = ( from r in receiverList select taskFactory.StartNew( () = > { Task.Delay(TimeSpan.FromSeconds(1)); var msg = r.Receive(); var body = Encoding.UTF8.GetString(msg.GetBytes()); Trace.TraceInformation( String.Format( "Receiver{0}: {1} at offset {2}", receiverList.IndexOf(r), body, msg.Offset)); })).ToList(); Task.WaitAll(task.ToArray()); } catch (Exception e) { Trace.TraceInformation(e.Message); }}```First we create more than 5 reader one a specific partition:![](https://winterttrgithubio.blob.core.windows.net/images/2015-09-05-Concurrent-Reader-on-a-specific-EventHub-Partition-within-one-consumer-group/exception-more-than-5-reader.jpg)We will received the above exception.After comments the 6th reader, and send some data to the event hub, I try to rerun the test again. Then we got the following result:> vstest.executionengine.x86.exe Information: 0 : Receiver1: { a : 16} at offset 0vstest.executionengine.x86.exe Information: 0 : Receiver0: { a : 16} at offset 0vstest.executionengine.x86.exe Information: 0 : Receiver2: { a : 16} at offset 0vstest.executionengine.x86.exe Information: 0 : Receiver3: { a : 16} at offset 0vstest.executionengine.x86.exe Information: 0 : Receiver4: { a : 16} at offset 0So we can see that, the result is obvious, we can see the five reader can work at the same time without competing.Hope they can give you some more idea when you try to read the data from even hub directly by yourself without using `EventProcessorHost`---
]]>Microsoft Fake
.Here let me talk about Microsoft Fake , and how to unit test your application which depends on many system or other references.
Microsoft Fakes is a test framework help you isolate the code you are testing by replacing other parts of the application.
Microsoft Fake is highly integrated with Visual Studio and you can easily start to use it with very several clicks.
Fakes come in two flavours:
A shim
modifies the compiled code of your application at run time so that instead of making a specified method call, it runs the shim code that your test provides. Shims can be used to replace calls to assemblies that you cannot modify, such .NET assemblies.
A stub
replaces a class with a small substitute that implements the same interface. To use stubs, you have to design your application so that each component depends only on interfaces, and not on other components.
A good diagram to distinguish those two:
Comparison | Shim | Stub |
---|---|---|
Performance | Slow ( rewrite code ) | As it is ( just interface implementation ) |
Static method, seal type | ✔ | ✗ |
Internal Type with InternalsVisibleToAttribute | ✔ | ✔ |
Private methods | ✔ if all the types on the method signature are visible | ✗ |
Interfaces and abstract methods | ✗ | ✔ |
Here let we say that we want to test a function in a DemoClassLibrary
.
We have a class that implements something depends on System
library.
1 | using System; |
So this UtcNowTick
is depends on the DateTime
, and right now we want to test this function.
This is a typical scenario for us to use shim
to test because we cannot control the behaviour of the system
library.
First, we add the fake to the library to we want to fake.
After this operation, we can find that we have a fake assembly created:
So right now, we have the fake assembly for system now, and we can control the behaviour of system namespace api as below:
1 | using System; |
As the code show, the fake framework will add an extra code for your faked namespace (such as system
in our demo), and all the extra code will under Fakes
namespace.
We can replace function calls under Fakes
namespace to our test implementation expected. Every type will have a Shim+Typename
under Fakes
for us to replace.
** There is some kinds of different scenarios which we need to test using Shim, here I list some:**
Let’s say that we have a 3rd party library like this:
1 | namespace DemoDependence |
And we have our application that use this library:
1 | using System; |
we start our test.
1 | [ ] |
1 | [ ] |
1 | [ ] |
Stum is typically using to implement the interface, let say we have a interface:
1 | public interface IDependenceInterface |
And our Foo has one more function:
1 | public class Foo |
The stub would be like this:
1 | [ ] |
When you use fake the System
library, you may see the compilation warning, that is because some type cannot be fakes, you can change the fake configuration file to fixit.
The warning is like below:
Warning 20 Some fakes could not be generated. For complete details, set Diagnostic attribute of the Fakes element in this file to ‘true’ and rebuild the project.
Just shim or stub the things you really need, here is one of my example in real project:
1 | <Fakes xmlns="http://schemas.microsoft.com/fakes/2011/" Diagnostic="true"> |
References:
And about markdown:
I have to say that the git hub page custom domain manual
is really unclear. And thanks for that someone could give more detail.
About how to setup godaddy, here is good reference article.
Done~~
Let’s blog~~!
1 | hexo new draft "your article name" |
After this, the draft file will be created under source/_drafts
folder without Date
tag added.
1 | hexo publish "your article name" |
After this, the draft will will be move to source/_posts
and the internal Date
flag will be updated to the publish timestamp.
1 | hexo generate |
Generate all the static blog file and start the local server.
You can simply do this via alias:
1 | hexo g && hexo s |
1 | hexo generate |
Of course you need to config the git in site _config.yml
file. And the hexo will publish the result to github page.
<!--more-->
It takes 2 days for all the configuration and I have to say that this should be quick enough for a self build static blog.
Enable most of the feature for Next theme, which is really a good theme.
For all the configuration and environment, please refer to the hexo-content branch of the my github.
It is a long time, I do not write something. In fact, there is always something that feels not very satisfied when writing down something new on web. Because I need to spend too much time on formatting things. Fonts, title, format, bullet and etc, I cannot focus on writing.
Markdown is a really good choice now.
Meanwhile, hexo is really a good framework to start from scratch again.
Comparing with Jekyll and github page from raw, I think the hexo is better and more easy.
Here, I want to thanks to the Next theme for hex, it is really a well prepared theme and support many things which may need U to spend a lot of times to config. Thanks again.
Let’s start again.