微光的Notes

梦想是要有的,万一实现了呢


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

  • 搜索

打造自己专属的英语词典软件

发表于 2020-01-28 | 分类于 随笔 | 评论数:

推荐词典软件:欧路(iOS),深蓝(android)

选择哪些词典

新手 柯林斯双解-韦氏高阶-朗文当代5-剑桥高阶
中级 朗文当代5-韦氏高阶-剑桥高阶
中高 牛津简明-ODE-AHD-韦氏新世界-朗文当代
高级 AHD-韦氏大学-ODE-兰登韦氏-SOED

词典分类:

1.英汉词典

  • 《新英汉词典》

2.Learner’s Dictionary学习型英语词典

  • 牛津高阶词典Oxford Advanced Learner’s Dictionary(简称OALD)
    • esl类词典的祖宗,根正苗红,收录词量大
  • 朗文当代英语词典第五版LONGMAN Dictionary of Contemporary English(简称LDOCE)
    • 优点:收词量最大,图片最为丰富,短语、用法完备,例句也多,号称用这一本足矣
  • 柯林斯高阶Collins COBUILD Advanced Dictionary&柯林斯双解
    • 优点:双语词典最棒,适合词汇量低于3K的学习者,汉语翻译最好(不仅给出释义,还配合语境),释义用完整的句子
    • 解释篇幅占比较大,收词量相对小,释义不清时候发生
  • 剑桥高阶英语学习词典Cambridge Advanced Learner’s Dictionary(简称CALD)
    • 优点:词汇3K以上首选,例句丰富,图片解释,更适合入门,所选用的例句统统都是整句,是从语料库中整理出来的日常常用例句,语境和例证最为清晰
    • 缺点:搭配用法和近义词讲的不够
      • 麦克米伦高阶英语词典Macmillan English Dictionary for Advanced Learners
    • 优点:按使用频率标出的7500核心词汇十分有用
      • 韦伯斯特高阶词典Merriam-Webster’s Advanced Learner’s English Dictionary
    • 偏重美语,音标以美语音标为主,支持短语的查询
      3.英文母语词典
      • 牛津简明Oxford Concise,释义最简洁和简单,相对于母语词典来说,收录短语、搭配很多,兼顾了esl辞典例证多的优点
      • ODE,新牛津英语词典,释义也很简洁,例句很丰富
      • 美国传统字典,AHD——这个是我用过里面排版看起来比较舒服和比较顺眼的,在海边兄弟要鉴定下
      • 韦伯斯特新世界大学词典 Webster New World,名气大,收词多,释义比韦氏平易一些
      • 韦氏大学词典,non-esl词典里难度较高的,主要释义用词文,词汇量大
      • 兰登韦氏词典Random House Webster’s Unabridged Dictionary,收词量大,是介于大学词典和大词典(OED、韦氏大词典)之间的规模,实用性较强
      • World Book Dictionary,释义相对不难,例句非常多,摘抄作家或报刊,难度低于韦氏不少,高阶过渡到母语词典的不错选择

搭配词典和同义词

1.牛津搭配词典第二版,太出名了我们就不说了
2.collins柯林斯英语同义词字典,Collins Thesaurus,darkdickens
3.Collins柯林斯英语用法词典,Collins COBUILD English Usage,darkdickens
4.BBI collocation dictionary,BBI英语搭配词典
5.WordNet 3.0
6.USE THE RIGHT WORD

查词典没搞定

1.查网络流行语or俚语:http://www.urbandictionary.com/
2.查专业词汇:http://en.termwiki.com/
3.大杀器:用Google或是Bing查“单词/词组+definition”

下载地址:
1、奶爸1.6G包 http://pan.baidu.com/share/link?shareid=434963&uk=2902556240
2、bluedict的作者yhssdl的分享,目前已超过8G,有大量的专业词库 http://pan.baidu.com/share/link?shareid=409113&uk=370055267#dir (此链接已失效)
3、麦克米伦高阶学习词典 http://pan.baidu.com/s/1o6v6a8q
4、本文对奶爸1.6G包的补充 “我的补充包” http://pan.baidu.com/share/link?shareid=416737&uk=3809515419
5、AHD4 Huge双解版 http://pan.baidu.com/share/link?shareid=433856&uk=3809515419
6、LDOCE5 Asicsfree版-增大发音图标bluedict版(欧陆词典不适用) http://pan.baidu.com/share/link?shareid=93214400&uk=3809515419
7、AHD4 Asicsfree双解版http://pan.baidu.com/share/link?shareid=3485920596&uk=3809515419

PS:尽量避免使用双解词典和词典,因为这会影响我们适应全英文环境

参考:
奶爸词典帖子:http://site.douban.com/195274/widget/notes/12076215/note/265205135/?start=0#comments
奶爸1.6G Mdict词库的补充及在Bluedict中使用的心得:http://www.douban.com/note/278501822/

2020年计划

发表于 2020-01-15 | 分类于 随笔 | 评论数:

新的一年开始了,详细的计划就不列了,写下今年的几个目标,因为一年的变数太多,详细的计划往往作废。提醒自己要养成长期思维,long term。

1.让身体更强壮,心灵更健康,身体是革命的本钱。

运动方面,每周3次健身,总计150次,适当增加有氧运动,体重保持在70-75kg,注意体脂。健身确实可以给我持续的快乐。饮食方面多吃水果蔬菜、优质蛋白质和粗粮。11点前睡,每天保证8小时左右睡眠,早上不用闹钟为准,但也不要多睡。

平均每天冥想10分钟左右,后期可慢慢加长。给自己适当的压力,但不要太大,做些自己喜欢的事情。

今年准备开始做基因检测和癌症筛查,早预防。重疾意外险也考虑配置。

2.让家人能更开心一点,挚友也是。

其实这个世界上对自己重要的人没几个,家人对于我来说是最重要的。在自己力所能及的前提下,不求回报的帮助、陪伴他们,让他们能按照自己的想法更开心一点。

19年最突然的是家人生病,这也是一个警钟,告诉我家人相处的宝贵,至亲之人真的在随着时间流失慢慢离我而去,不胜唏嘘。

有空尽量回家多看看父母、兄弟姐妹,不能回去也多通电话。今年整个大家庭出游一次。多带父母出去旅行。

对于老婆要多鼓励、多沟通、多体贴。

3.育儿

对与阅读习惯、写作习惯、良好的学习习惯的培养不能松懈。把语数英等基础知识给做扎实了。在孩子教育上深度参与,能有些输出更好,像憨爸、刘烁等。

今年准备学习认知科学的知识,顺便应用到孩子身上,实践、输出、复盘。

4.财富投资

建立投资知识框架、打基础,进一步学习经济学(主题阅读),搞清楚其中的概念、体系等等,有输出。

养成记账习惯

开源节流,存钱

5.技术的持续精进

Java的经典著作,计算机基础,架构,有输出。

6.学习成长

保证平均每天1小时以上的阅读时间,尽量大块时间。

认知科学做主题阅读,有输出。

写作能力的持续锻炼。

日记也要坚持写,19年这块坚持的不错,20年继续的前提下,争取写周记,定期复盘。

7.独立产品

开发的应用目标月收入xxxx,卖货尝试走起。

8.让自己更开心

人生在世,往往都是只争朝夕。但在忙碌的同时也得让自己开心点。但是快乐无止境,就像一本书《娱乐至死》所说。还是努力让自己达到一种平和的心境,减少痛苦。看经典电影动画、阅读(历史书、小说)、音乐、旅行

有空弹弹吉他,这个确实能给自己很大的愉悦感。

学习方面主要集中在3大主题,技术、经济学、认知科学,不贪多了,不然也学不完。

根据目标,再细化每个季度、每个月、每个星期的任务。反正就是挑重要的做。完成比完美重要,持续迭代。

Naval:人生公式 I

发表于 2019-12-17 | 分类于 读书笔记 | 评论数:

原文:https://nav.al/life-formulas-i

Happiness = Health + Wealth + Good Relationships
快乐 = 健康 + 财富 + 良好的人际关系

Health = Exercise + Diet + Sleep
健康 = 锻炼 + 饮食 + 睡眠

Exercise = High Intensity Resistance Training + Sports + Rest
锻炼 = 高强度抗阻训练 + 竞技运动+ 休息

Diet = Natural Foods + Intermittent Fasting + Plants
饮食 = 天然食品 + 间歇性禁食 + 蔬菜

Sleep = No alarms + 8-9 hours + Circadian Rhythms
睡眠 = 没有闹钟 + 8-9小时睡眠 + 昼夜节律

Wealth = Income + Wealth (Return on Investment)
财富 = 收入 + 财富
投资回报

Income = Accountability + Leverage + Specific Knowledge
收入 = 勇于承担责任 + 杠杆 + 特种知识

Accountability = Personal Branding + Personal Platform + Taking Risk?
勇于承担责任 = 个人品牌 + 个人(公开表达看法或信仰的)机会【这块拿不准】 + 承担风险

Leverage = Capital + People + Intellectual Property
杠杆 = 资本 + 人力 + 知识产权

Specific Knowledge = Knowing how to do something that society cannot yet easily train other people to do
特种知识 = 知道怎么去做那些不容易通过培训就可以做到的事

RoI = Buy-and-Hold + Valuation + Margin of Safety
投资回报率 = 买并拿住 + 估值 + 安全边际

如何科学的休息?

发表于 2019-12-15 | 分类于 随笔 | 评论数:

我认为,学会工作的前提是学会休息,现在年龄渐长,精力宝贵,另外现在是信息爆炸的时代,也会导致精力涣散,所以要善于保护自己的精力。

很多人认为刷刷抖音、微博、逛淘宝、打游戏等是休息,其实真正的休息不消耗意志力与注意力。

  • 意志力 = 主动控制自己注意力、情绪和欲望的能力
  • 自控力 = 意志力 = 自制力
  • 注意力 = 主动控制关注焦点的能力

休息的目的

  • 休息是为了从意志力和注意力的消耗中恢复
  • 休息还有助于大脑对信息的处理,加深学习和练习效能
  • 休息模式下开启默认模式网络可以增加创造力

我按照工作中的一天,来说说具体的休息方法

上班时间休息方法

  • 尽早开始休息,早上虽然不累,但是也要开始,频繁进行的短休息比集中一次性要好,工作1小时,休息10分钟。
  • 散步算休息,5分钟左右即可
  • 接触大自然有助于休息,即便没有办法真的接触到,看自然风光的图片6分钟以上也是有益的,可以考虑切换桌面
  • 短休息可以用于社交,找个人聊聊天比独自一人休息的效果好。
  • 冥想是休息,任何时候感到精神状态不佳都可以进行冥想,甚至一分钟的正念呼吸都是有效的
  • 正念行走是休息
  • 运动,跳绳5分钟,不过在公司不太好执行

午睡

科学家经过量化分析得出“小憩20分钟,能带来3个小时的清醒时间。”虽然打盹不能进入 REM ,可以恢复精力。但超过25分钟进入睡眠惯性醒来后昏沉的时间非常长。最佳的精准打盹流程应该是:

  • 先喝杯咖啡,其中咖啡因的含量应该是200毫克;
  • 手机设置25分钟定时;
  • 开始睡;
  • 25分钟一到马上起来

上班的时候我们都像把油门踩到底的跑车,等下班后,怎么让车停下来休息休息,也是需要技巧的。我现在的技巧是:

  • 运动,我有健身的习惯,去健身房撸铁一小时感觉大脑放松很多
  • 适量的饮酒
  • 看不太需要思考的纸质书
  • 听音乐,尽量舒缓些的
  • 洗热水澡,有条件的可以去澡堂子,泡澡、桑拿、按摩一条龙。
  • 按摩,有时候下班很累,头昏脑胀,让师傅按一按头,会舒服很多

睡眠

睡眠周期中的“快速眼动期( Rapid Eye Movement )”得名于这个阶段眼球一直在快速运动。

快速眼动期 REM 身体开始修复工作,而大脑却非常活跃,是因为它在处理白天收集的信息。《巅峰表现》中作者表示 REM 期间分泌的合成代谢荷尔蒙,才能让此后的合成代谢工作开始,白天学的技能和知识,此时大脑在晚上消化、重组一番,才算真正“长”在了我们身上,变成长期记忆。在这个阶段,大脑还会回忆白天发生的各个事件,特别是引起感情波动的那些事件,形成更深入的理解和认识。

最佳的睡眠时间在7~9个小时,因人而异,以不用闹钟起床为宜。

冥想

现代认知科学发现冥想的确有助于锻炼注意力、减轻压力等,同时还能让你迅速进入休息状态,在工作和休息之间切换自如。最简单的冥想一共分6步:

  1. 找个不受打扰的时间,非常舒服地坐在一个安静的地方;
  2. 设个闹钟,这样你就不用管时间了;
  3. 用鼻子深呼吸;
  4. 使用正常频率呼吸,把意识专注于体会自己的呼吸,体会你的肚子随着呼吸起起伏伏。
  5. 如果在此过程中你脑子里冒出别的想法,不用刻意回避,承认这个想法,然后把它放走。意识始终只想呼吸。
  6. 时间一到就可以“收功”。从每天1分钟开始,逐渐增加时间。

这个对我比较有效果,推荐大家尝试。

其他休息方法

  • 跟朋友聚会,聊聊天、喝点酒。必须是朋友,是为了友情,而不是为了什么“人脉”。
  • 休假,好的休假就像充电,可以让人在后面很长一段时间有充沛精力。休假的时候,要完全抛开工作。

休息的坏习惯

  • 消耗意志力 :做决策要消耗意志力,所以网购不算休息。
  • 消耗注意力:阅读会消耗注意力,所以浏览新闻不算休息。
  • 玩手机,看书,看电影,这都不叫休息
  • 打游戏、半夜刷手机

2019年度书单

发表于 2019-12-04 | 分类于 随笔 | 评论数:

分类

历史
经济
教育

《小岛经济学》
简介+读后感+摘抄,不宜过多,能大概说清楚本书主要内容
《反脆弱》
《送你一颗子弹》
《随机漫步的傻瓜》
《HBO的内容战略》
《优秀的绵羊》
《人地城》
《你凭什么做好互联网》
《java并发编程实践》
《Spring实战》
《深入理解Java虚拟机》
《生而贫穷》
《讲给大家的中国历史 01 中国是怎么出现的》
《大学之路》
《优秀的绵羊》
《第一本经济学》
先列出来,简介和读书笔记,后续慢慢补充……

参考:https://mp.weixin.qq.com/s/NnLM5vgf8sT4H6KpRRggRQ

RESTful API规范

发表于 2019-12-02 | 分类于 架构 | 评论数:

URI

URI规范

  • 不要用大写
  • 单词间使用下划线’_’
  • 不使用动词,资源要使用名词复数形式,如:users、rooms、tickets
  • 层级大于等于三层,则使用’?’带参数
    1
    sers/1/address/2/citys (bad) /citys?users=1&address=2; (good)

避免层级过深的URI

过深的导航容易导致url膨胀,不易维护,如 GET /zoos/1/areas/3/animals/4,尽量使用查询参数代替路径中的实体导航,如GET /animals?zoo=1&area=3;

Request

Method

  • GET:查询资源
  • POST:创建资源
  • PUT:全量更新资源(提供改变后的完整资源)
  • PATCH:局部更新资源(仅提供改变的属性)
  • DELETE:删除资源

下面一些例子

  • GET /zoos:列出所有动物园
  • POST /zoos:新建一个动物园
  • GET /zoos/ID:获取某个指定动物园的信息
  • PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
  • PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
  • DELETE /zoos/ID:删除某个动物园
  • GET /zoos/ID/animals:列出某个指定动物园的所有动物
  • DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

安全性与幂等性

  • 安全性:任意多次对同一资源操作,都不会导致资源的状态变化
  • 幂等性:任意次对同一资源操作,对资源的改变是一样的
Method 安全性 幂等性
GET 是 是
POST 否 否
PUT 否 是
PATCH 否 是
DELETE 否 是

参数

Method

GET

非id的参数使用’?’方式传输
/users/1?state=closed

POST、PATCH、PUT、DELETE

非id的参数使用body传输,并且应该encode

过滤

?type=1&state=closed

排序

分页

?size=10&page=1

  • size:返回记录数量
  • page:第几页

版本控制

三种方案:

  • 在uri中加入版本: /v1/room/1
  • Accept Header:Accept: v1
  • 自定义 Header:X-ProjectName-Media-Type: projectname.v1 (我们使用此方案)

状态码

成功

Code Method Describe
200 ALL 请求成功并返回实体资源
201 POST 创建资源成功

客户端错误

Code Method Describe
400 ALL 参数错误
401 ALL 用户验证失败(用户名、密码错误等)
404 ALL 资源不存在

服务器错误

Code Method Describe
500 ALL 服务器未知错误

5C699083-6F00-4CCD-ABFD-1FDF4775F610

Response

不要包装:response的body直接就是数据,不要做多余的包装。
错误示例:

1
2
3
4
{
"success":true,
"data":{"id":1,"name":"xiaotuan"},
}

正确示例:

1
{"id":1,"name":"xiaotuan"}

json格式的约定:

  • 时间用长整形(毫秒数),客户端自己按需解析(moment.js)
  • 不传null字段

分页response

1
2
3
4
{
"paging":{"limit":10,"offset":0,"total":729},
"data":[{},{},{}...]
}

错误处理

  1. 不要发生了错误但给2xx响应,客户端可能会缓存成功的http请求;
  2. 正确设置http状态码,不要自定义;
  3. Response body 提供 1) 错误的代码(日志/问题追查);2) 错误的描述文本(展示给用户)。
1
2
3
4
5
{
"code" : 1234,
"message" : "Something bad happened :-(",
"description" : "More details about the error here"
}

对PUT,POST,PATCH的输入的校验也应该返回相应的错误信息,例如:

1
2
3
4
5
6
{
"status": 400,
"developerMessage": "Verbose, plain language description of the problem. Provide developers suggestions about how to solve their problems here",
"userMessage": "This is a message that can be passed along to end-users, if needed.",
"errorCode": "444444"
}

参考

http://imweb.io/topic/5707561f06f2400432c139a5
https://blog.restcase.com/rest-api-error-codes-101/

Naval:Work As Hard As You Can(尽自己最大努力工作)

发表于 2019-11-18 | 分类于 读书笔记 | 评论数:

原文:https://nav.al/work-hard

Work as hard as you can. Even though what you work on and who you work with are more important.

尽自己最大努力工作。即使你的工作内容,以及和谁一起工作更重要。

Work as hard as you can

尽自己最大努力去工作

Let’s talk about hard work. There’s a battle that happens on Twitter a lot. Should you work hard or should you not? David Heinemeier Hansson says, “It’s like you’re slave-driving people.” Keith Rabois says, “No, all the great founders worked their fingers to the bone.”

我们来说说努力工作。推特上有很多这个话题的讨论。是应该努力工作还是不应该?David Heinemeier Hansson说:“就像您是奴隶主。”Keith Rabois说:“不,所有伟大的创始人都是辛勤工作的。”

They’re talking past each other.

他们在彼此背后说话。

First of all, they’re talking about two different things. David is talking about employees and a lifestyle business. If you’re doing that, your number one priority is not getting wealthy. You have a job, a family and also your life.

首先,他们是在讨论两个不同的事情。David讨论的是关于雇员和生活方式型企业(属于小企业的一种特定的类型,其设立的目标一般是维持特定的收入水平,而不是更高的收入,而更多的动机则是创立者个人的兴趣、爱好和技能)。如果你在这种企业中,你的首要任务不是让自己变的更富有。你有一份工作、一个家庭,另外还有你的生活。

Keith is talking about the Olympics of startups. He’s talking about the person going for the gold medal and trying to build a multi-billion dollar public company. That person has to get everything right. They have to have great judgment. They have to pick the right thing to work on. They have to recruit the right team. They have to work crazy hard. They’re engaged in a competitive sprint.

Keith正在谈论创业的奥林匹克竞赛【表示竞争很激烈】。他说的是那些想夺取金牌,并试图建立一个数十亿美金的上市公司的人。这些人必须把每件事都做对。他们必须有非凡的判断力。他们必须选择正确的工作。他们必须打造优秀的团队。他们必须疯狂的工作。他们正在进行一场竞争性的冲刺。

If getting wealthy is your goal, you’re going to have to work as hard as you can. But hard work is no substitute for who you work with and what you work on. Those are the most important things.

如果你的目标是变得富有,那么你必须尽自己最大的努力工作。但是,努力工作并不能取代你同谁一起工作以及你从事工作的重要性。这两件事更重要。

What you work on and who you work with are more important

你从事什么工作,和谁一起工作是最最重要的事

Marc Andreessen came up with the concept of the “product-market fit.” I would expand that to “product-market-founder fit,” taking into account how well a founder is personally suited to the business. The combination of the three should be your overwhelming goal.

Marc Andreessen提出了“product-market fit.(可以理解为产品符合市场需求)【注1】”的概念。我将其扩展到“产品-市场-创始人匹配”。考虑到什么是创始人个人适合企业的能力。这三者(对的人、对的事、努力工作)的结合应该是您的压倒性目标。

You can save a lot of time by picking the right area to work in. Picking the right people to work with is the next most important piece. Third comes how hard you work. They are like three legs of a stool. If you shortchange any one of them, the whole stool is going to fall. You can’t easily pick one over the other.

你能找到适合自己的工作,那将会节省你很多时间。找到合适的人一起工作是下一件最重要的事。第三是你工作的努力程度。它们就像凳子的三条腿。如果你少找其中任何一个,整个凳子都会倒掉。你不能轻易地选择其中一个。

When you’re building a business, or a career, first figure out: “What should I be doing? Where is a market emerging? What’s a product I can build that I’m excited to work on, where I have specific knowledge?”

当你去创业,或是攻读学位,首先要弄清楚:“我应该做什么?哪里是新兴市场? 什么样的产品是我可以创造出来,并且我有特定的知识,能让我兴奋地投入到工作中去? ”

No matter how high your bar is, raise it

不论你的标准有多高,提高它

Second, surround yourself with the best people possible. If there’s someone greater out there to work with, go work with them. When people ask for advice about choosing the right startup to join, I say, “Pick the one that’s going to have the best alumni network for you in the future.” Look at the PayPal mafia—they worked with a bunch of geniuses, so they all got rich. Pick the people with the highest intelligence, energy and integrity that you can find.

第二点,尽可能让自己身边有最好的人一起工作。如果有更优秀的人可以一起工作,赶紧去吧。当有人向我咨询应该怎么选择合适的创业公司加入时,我会告诉他,“选择那些可以让你在未来有更好校友网络的人。”你看PayPal黑帮,他们和一堆天才打交道,所以他们都变的很富有。尽自己最大努力去寻找那些最聪明、最有活力、最正直的人。

And no matter how high your bar is, raise it.

不论你的标准有多高,提高它

Finally, once you’ve picked the right thing to work on and the right people, work as hard as you can.

最后,当你选择了正确事和好的合作伙伴,那就尽你所能努力工作吧。

Nobody really works 80 hours a week

没有人真的一周工作80个小时

This is where the mythology gets a little crazy. People who say they work 80-hour weeks, or even 120-hour weeks, often are just status signaling. It’s showing off. Nobody really works 80 to 120 hours a week at high output, with mental clarity. Your brain breaks down. You won’t have good ideas.

这就是神话变得有点疯狂的地方。那些声称自己每周工作80小时,甚至120小时的人,往往只是给自己打的标签而已。这是在炫耀。没有人真的可以在每周工作80到120小时的情况下,还能保持高产出和头脑清晰。你的大脑崩溃了。你不会有好想法产生。

The way people tend to work most effectively, especially in knowledge work, is to sprint as hard as they can while they feel inspired to work, and then rest. They take long breaks.

对于知识工作者来说,最有效率的工作方式是,当他们有激情工作的时候,就尽可能去冲刺,然后好好休息。他们会休息很长时间。

It’s more like a lion hunting and less like a marathoner running. You sprint and then you rest. You reassess and then you try again. You end up building a marathon of sprints.

这更像一只在狩猎狮子,而不是马拉松运动员。你先冲刺,然后休息。你重新评估,然后再试一次。你最终建立了一个马拉松式的短跑。

Inspiration is perishable

灵感稍纵即逝

Inspiration is perishable. When you have inspiration, act on it right then and there.

灵感稍纵即逝。当你有灵感的时候,立马行动起来。

If I’m inspired to write a blog post or publish a tweetstorm, I should do it right away. Otherwise, it’s not going to get out there. I won’t come back to it. Inspiration is a beautiful and powerful thing. When you have it, seize it.

当我有灵感想要写一篇博客或发表一篇推特风暴,我应该立马去做。否则,它不会发出来。我不会再回来发送他们。灵感是一种美丽而强大的东西。当你拥有它时,抓住它。

Impatience with actions, patience with results

对行动缺乏耐心,对结果充满耐心

People talk about impatience. When do you know to be impatient? When do you know to be patient? My glib tweet on this was: “Impatience with actions, patience with results.” I think that’s a good philosophy for life.

人们谈论不耐烦。什么时候应该缺乏耐心?什么时候应该拥有耐心?我在推特上油嘴滑舌的写道:“对行动缺乏耐心,对结果充满耐心。”我认为这是一个很好的人生哲学。

Anything you have to do, get it done. Why wait? You’re not getting any younger.

无论你要做什么,行动起来。为什么要等?你已不再年轻。

You don’t want to spend your life waiting in line. You don’t want to spend it traveling back and forth. You don’t want to spend it doing things that aren’t part of your mission.

你不想一辈子都排队等候吧。你不会想把时间花在来回旅行上。你不想把时间花在不属于你使命的事情上。

When you do these things, do them as quickly as you can and with your full attention so you do them well. Then be patient with the results because you’re dealing with complex systems and a lot of people.

当你要做这些事情的时候,尽可能快的去做,全神贯注的去做,这样你才能做好他们。然后耐心的等待结果,因为你面对的是复杂的系统和很多人。

It takes a long time for markets to adopt products. It takes time for people to get comfortable working with each other. It takes time for great products to emerge as you polish away.

需要很长时间市场才能接受你的产品。人们需要时间来适应彼此的工作。伟大的产品需要时间的磨砺。

Impatience with actions, patience with results.

对行动缺乏耐心,对结果充满耐心

If I discover a problem in one of my businesses, I won’t sleep until the resolution is at least in motion. If I’m on the board of a company, I’ll call the CEO. If I’m running the company, I’ll call my reports. If I’m responsible, I’ll get on it, right then and there, and solve it.

如果我在自己的生意中遇到了问题,至少在解决方案开始实施之前,我不会睡觉。 如果我是一家公司的董事会成员,我会打电话给首席执行官。 如果由我来管理公司,我会给我的报告打电话【这句话不知道怎么翻译妥当】。 如果是我的责任,我会马上着手解决这个问题。

If I don’t solve a problem the moment it happens—or if I don’t move towards solving it—I have no peace. I have no rest. I have no happiness until the problem is solved. So I solve it as quickly as possible. I literally won’t sleep until it’s solved—maybe that’s just a personal characteristic. But it’s worked out well in business.

如果问题发生的那一刻我没有解决它,或者我没有让事情朝着正确的方向推进,我会坐立不安。我不会休息。除非事情解决,否则我不会快乐。所以我尽可能快地解决它。在问题解决之前,我是不会睡觉的ーー也许这只是我个人的特点。 但是这在商业上运作良好。

【注1】product/market fit,简称PMF,意思是产品符合市场需求(如此精髓的概念直译过来为何如此苍白-_-!!!),这是大名鼎鼎的Marc Andreesen提出来的概念,他认为:世上的创业公司只有两种,一种是找到PMF的,一种是没找到的。详细解释可以参考该链接

Git分支策略-我的实践

发表于 2019-10-18 | 分类于 利器 | 评论数:

具体策略还是要根据项目的大小和复杂度来定。遵循的原则是先简单,然后根据项目情况不断的精细化,不要上来就搞一个特别复杂的规则。

这样做的好处:

  • 简单,团队更容易接受、执行,更易操作
  • 简单也更不容易出错

如果是个全新的项目,我觉着保留一个主干master,和线上代码一致,仅用来发布新版本。不用非得维护一个develop分支,这样反而麻烦。在项目上线之前,规则都相对简单。但是等项目上线后,问题就慢慢复杂了。因为你分支策略还得考虑怎么保证master和线上一致。我们分多个场景来讲:

场景1 我们要添加一个新功能

场景1.1 当前没有并行的功能

1.开发的时候先从master建一个功能分支,命名:Feature-xxx,后续在此基础上做开发就行了,视情况决定是否建个人分支(命名:Feature-xxx-light)

2.功能开发完成,准备提测,如果是按照瀑布模型开发,可以直接把Feature分支的代码部署到测试环境(QA),发现bug直接在此分支修改就行,然后部署到测试环境;如果是敏捷开发,那最好分出来一个测试分支,因为该功能分支还得继续开发,可能会包含未完成的功能,这时候如果拿功能分支部署测试环境,会遇到问题。如果开辟了一个测试分支,这样就把开发跟测试隔离了,测试环境的bug什么都可以在测试分支搞定。

3.(可选) 开发测试完成提交到pre-production

4.(可选) 进一步测试没有问题提交到release分支

5.后续就是上线,则合并到master分支,并在master分支上做发布

6.(可选) 如果需要延迟发布则新建production分支(比如,等待iOS审核,需遵守upstream first)

场景1.2 有多个功能并行

可能有个极端情况就是两个团队需要开发同一个功能,我觉着这种情况在前期任务划分的时候就应该尽量避免掉,实在无法避免,可以让一个团队负责开发,并暴露接口供另一个团队调用

其实流程跟场景1.1差不多,就是从哪里新开分支,怎么合并会遇到些问题?

还是从master创建分支。合并的时候根据项目情况选择是合并到pre-production或release,反正最终要合并到master分支并做发布。

场景2 发现了线上bug

最重要的是记得记得保存当前的代码,别一着急把当前的代码丢了,那真会欲哭无泪。
1.如果本地还在开发,记得先使用git stash暂存,以免切换到master后代码丢失

2.从master创建本地bug分支,命名:bugfix-xxx

3.修复bug

4.(可选)修改测试完成后依次同步到pre-produciton和release分支

5.各个环境的测试通过,合并到master,并在master上做发布

6.切换到自己的开发分支,使用git stash恢复暂存,继续工作

但这些也不够全面,想到哪写到哪,后续持续迭代吧。

徐十胖公众号所得

发表于 2019-09-01 | 分类于 读书笔记 | 评论数:

最近看了他的文章,感觉有所收获,就准备把他公众号有用的文章翻来看看,我把一些收获总结一下。

写作对我虽然难,更要多写,复盘,大力出奇迹

执行力是基础,大力出奇迹,干干干。多次在不同文章提到执行力的重要性。为啥这样说呢,他的观点是在你很弱的时候,你的思考能力和执行能力必然都是很弱的。思考能力很弱,很多事情你是根本想不清楚的,因为一个是逻辑不过关,一个是因为见的不够多,做的不够多!就像一个经典的笑话:乞丐说,等我发财了,我就买个大房子,天天坐在门口要饭吃。

专注,没钱的时候,做事一定要专注,把小事做好,然后做深,做大。多做试验,找到适合自己的方法,然后拼命跑马圈地。

团队,想做事赚钱一定要有团队

知道自己短处和长处,并总是想法突破自己,文章中提到“确实没有办法耐着性子研究技术”,后来转到产品岗位,发现自己特别喜欢,但是转到产品岗,也是吃了不少苦头,打击最大的一次是把美团几万人使用的系统给搞挂了,因此差点辞职。但是通过自己不断的学习和突破,坚持了下来,后边绩效也很好。

除了主业,还得多去尝试和试验,不断接触新人、新知识,提升认知

什么是牛逼?能够克服得了难的东西,持续的做下去,不断的思考,做深,做精。哪怕最后没有结果,与困难战斗的过程中越战越强,本身就是很爽的事情。【这个估计也是作者的目标】
什么事都是有代价的,比我漂泊在外,就能换取自己的一份自由,想干嘛就干嘛

Reversion History
2019.9.1 第一次记录

我为什么写作

发表于 2019-08-29 | 分类于 随笔 | 评论数:

其实我对写作有很大的心里恐惧。还记刚上初中时,因为作文写不好彻夜难眠,也哭了无数次。有一次考试作文竟然只憋出来了6行,得了6分,至今难忘。但是我认为写作还是很有用处,所以也就硬着头皮写吧,以下说说我对写作的理解。

写作大概能分为两种,一种是为自己而写,主要想通过写作来更好的学习、思考、记录、复盘等;一种是为他人而写,通过写作扩大影响力、赚钱等等。我的目标是“为自己而写”。

为了深度思考,把自己学的东西。通过写作,将自己的思考转换为文字后,很多实践经验和总结往往才能够真正的系统化和结构化,同时完成整个思维的抽象和升华过程。写作有时候像教人,能把一件事写明白了,证明自己掌握的也挺好的了。

让自己慢下来,平时看的太多,太杂,除了通过写作来梳理,为了更好的吸收,其实写作是真正能够让人平静下来的事情,写作是在安静的时候,一个人,自己和自己的心灵的对话,让自己听到自己内心的声音。

也有一些交流的需要,我现在有个习惯,就是如果觉着一个人思想还不错,我就会去找他的博客、公众号等,把他写的文章看一遍,就能大概了解这个人了,减少了沟通的成本。

你们有没有觉着回忆是不准确的,我们经常修改和美化自己的记忆,以验证自己当下的判断(就像“禀赋效应”那样)。所以记录下来,回头再看,是对自己成长过程最好的交代。另外,记录当时状态对于人生也是一份宝贵的财富。

参考

  • 为什么你应该(从现在开始就)-刘未鹏
  • 当我谈博客时我在谈些什么-Fatesinger
  • 为何写作-zero

Revision history

2019.8.29 开写

读《股票作手回忆录》

发表于 2019-08-24 | 分类于 财富投资 | 评论数:

自己认为比较有收获的点

  • 坚持自己独立思考,必须对自己和自己的判断充满信心,不断跟市场学习,虽然这要付出金钱的代价,但是没有捷径,因为一个人从自己的错误中总结经验,需要经历漫长的过程。找到自己的优势、缺点,记录每次亏损或失误的原因,不断复盘来不断改进修炼,让某些操作成为潜意识
  • 赚大钱不能靠股价的短期波动,而要靠大趋势,看对趋势,买进仓位后就持有不动,直到你觉得牛市将尽,抛出获利
  • 用筹码去测试市场,市场永远是对的,识别最小阻力位是个艺术
  • 止损,出错的时候必须认错,然后卖掉;止盈,牛市时广大股民一开始都能赚钱,可后来都因为流连于牛市不肯收手而亏损
  • 不要受情绪的影响,亲情、感激之情、愧疚

一些摘抄

市场信号出现之前,不要操作。
股价上涨时,不要问它为啥涨,持续买进自然会推动股价上涨。

只有真金白银才能证明自己观点的正确性
试错是股市永恒的主题

判断对错并不重要,重要的在于正确时获取多大利润,错误时亏损了多少
市场对我有利,就在自己财力范围内尽可能大规模交易

防止成为“股呆”,图标分析不是万能的,别成为查理芒格说的“拿着锤子,看谁都像钉子”的人,扩大专注面。
投资一定要理论结合实际,既要研究理论,也要善于在实际操作中变通

搞清楚自己的交易对手是谁

没有一个人能抓住所有起伏,不理会大波动,总是抢进抢出,这是投资大忌
不要尝试去抓住最初或最后的八分之一点利润

择时很重要啊
最重要的能力是能保证对买进卖出时机判断的正确率和在错误时及时止损的能力

牛市整体上扬才有机会挣大钱,走势要依赖整个经济环境
股票投机中,解读行情很重要,在正确的时间进场、坚持自己的立场也同样重要。更重要的是必须研究和评估经济形势,这样才能准确预测市场可能会往哪个方向发展

研究人性让你受益匪浅
人性的两大弱点~希望与恐惧
最致命的敌人来自自己的内心
最危险的敌人就是聪明的朋友的热切规劝和人格魅力
赌博的心态要不得

股票投机成功原则的基础是:人类会在将来犯过去犯过的相同的错误
股票投机永远不会消失,因为人性不希望它消失

操盘技巧
K线是个好的宣传工具
炒作的目的通常是激活市场,让自己能随时以某个价位抛出大宗股票
要让一只股票一路上涨,第一步是让它先涨起来
没人愿意做空这种股权不够分散的股票,因为你会完全受制于持股的内线集团

知道不该做什么和知道什么该做同样重要

程序员健康生活指南总目录

发表于 2019-04-22 | 评论数:

收录了《程序员健康生活指南》的中文翻译,给为码友可以关注下一。

  • 程序员健康生活指南(一) - 前言
  • 程序员健康生活指南(二) - 做出改变
  • 程序员健康生活指南(三) - 努力健康起来吧
  • 程序员健康生活指南(四)-永别了,椅子?
  • 程序员健康生活指南(五)-灵活的饮食方案
  • 程序员健康生活指南(六) - 预防头痛和眼部疲劳
  • 程序员健康生活指南(七) - 预防背部疼痛
  • 程序员健康生活指南(八) - 预防手腕疼痛
  • 程序员健康生活指南(九) - 让锻炼贴近生活
  • 程序员健康生活指南(十) - 到户外来思考
  • 程序员健康生活指南(十一) - 重构健康
  • 程序员健康生活指南(十二) - 组起队来
  • 程序员健康生活指南(十三) - 继续行动吧,健康的程序员

Naval:如何不靠运气致富

发表于 2019-03-04 | 分类于 财富投资 | 评论数:

How to Get Rich (without getting lucky)
如何变的富有,不需要任何的运气。

Seek wealth, not money or status. Wealth is having assets that earn while you sleep. Money is how we transfer time and wealth. Status is your place in the social hierarchy.
寻求财富,而不是金钱或身份。财富是指拥有产生睡后收入的资产。金钱是我们怎么调动时间和财富。身份是你在社会体系中的位置。

Understand that ethical wealth creation is possible. If you secretly despise wealth, it will elude you.
理解道德正确的创造财富是可能的。如果你秘密的鄙视财富,它会避开你。

Ignore people playing status games. They gain status by attacking people playing wealth creation games.
忽略那些做地位博弈的人。他们获得地位的方式是通过攻击做创造财富博弈的人。

You’re not going to get rich renting out your time. You must own equity - a piece of a business - to gain your financial freedom.
你不可能通过租赁你的时间变得富有,你需要拥有资产,一个生意的一部分,来获取你的财务自由。

You will get rich by giving society what it wants but does not yet know how to get. At scale.
你可以通过给于这个社会想要但还不知道如何得到的东西去变得富有。规模化的。

Pick an industry where you can play long term games with long term people.
选择一个行业,在这个行业里你可以和长期的人去玩长期的游戏。

The Internet has massively broadened the possible space of careers. Most people haven’t figured this out yet.
互联网极大的扩展了职业生涯的可能性,大部分人还没有意识到这件事。

Play iterated games. All the returns in life, whether in wealth, relationships, or knowledge, come from compound interest.
做重复博弈。一生中所有的回报,财富,关系,或者知识,都是来源于复利。

Pick business partners with high intelligence, energy, and, above all, integrity.
挑选商业合伙人的条件是,非常聪明,富有能量,最重要的是,正直。

Don’t partner with cynics and pessimists. Their beliefs are self-fulfilling.
不要和犬儒主义者和悲观主义者合伙。他们的信仰是自证(预言)。

Learn to sell. Learn to build. If you can do both, you will be unstoppable.
学习去销售事物,学习去建造事物。如若这两者都可以做,那么你将是不可阻挡的。

Arm yourself with specific knowledge, accountability, and leverage.
通过特种知识,负责,杠杆,来武装你自己。

Specific knowledge is knowledge that you cannot be trained for. If society can train you, it can train someone else, and replace you.
特种知识是无法通过培训获得的知识。如果社会可以培训你,那么也可以培训其他人,他们可以取代你。

Specific knowledge is found by pursuing your genuine curiosity and passion rather than whatever is hot right now.
特种知识是通过追寻你单纯的好奇与热情获得的,而不是去寻找当下什么地方最热。

Building specific knowledge will feel like play to you but will look like work to others.
建造特种知识的过程看上去是对他人的工作,实际上是对自己的索取。

When specific knowledge is taught, it’s through apprenticeships, not schools.
当特种知识被教授的时候,是通过学徒制的,而不是学校制的教育。

Specific knowledge is often highly technical or creative. It cannot be outsourced or automated.
特种知识通常有很高的技术性和创造性。它不能被外包或者自动化。

Embrace accountability, and take business risks under your own name. Society will reward you with responsibility, equity, and leverage.
拥抱负责,把商业风险放在自己的名下。社会将会奖赏你于责任,股份,杠杆。

The most accountable people have singular, public, and risky brands: Oprah, Trump, Kanye, Elon.
那些最负责的人拥有独一无二的,公开的,有风险的品牌。奥普拉,川普,坎耶,马斯克。

“Give me a lever long enough, and a place to stand, and I will move the earth.” - Archimedes
给我一个足够长的杠杆,和一个支点,我能够移动地球。- 阿基米德

Fortunes require leverage. Business leverage comes from capital, people, and products with no marginal cost of replication (code and media).
财富需要杠杆。商业杠杆来自于资本,人民,和那些没有边际复制成本的产品(媒体作品和程序)

Capital means money. To raise money, apply your specific knowledge, with accountability, and show resulting good judgment.
资本意味着金钱。想要募集金钱,应用你的特种知识,负责,并且展示发生的好的判断。

Labor means people working for you. It’s the oldest and most fought-over form of leverage. Labor leverage will impress your parents, but don’t waste your life chasing it.
人力意味人那些为你工作的人。这是最古老和斗争最激烈的杠杆形式。人力杠杆会给你的父母留下深刻印象,但是不要浪费一生去追逐它。

Capital and labor are permissioned leverage. Everyone is chasing capital, but someone has to give it to you. Everyone is trying to lead, but someone has to follow you.
资本和人力是需要许可的杠杠。每个人都在追逐资本,但是必须有人把它给你。每个人都想领头,但是必须有人跟随你。

Code and media are permissionless leverage. They’re the leverage behind the newly rich. You can create software and media that works for you while you sleep.
程序和媒体作品是无需许可的杠杠。它们是新富们背后的杠杆。你可创造出程序和媒体作品,(它们)在你睡觉时还为你工作。

An army of robots is freely available - it’s just packed in data centers for heat and space efficiency. Use it.
一只机器人军队可以被自由使用 - 只是为了散热和空间效率被打包放置在数据中心。使用它。

If you can’t code, write books and blogs, record videos and podcasts.
如果你不会编程,那就写书和写博客,录制视频和播客。

Leverage is a force multiplier for your judgement.
杠杆是你的判断的力量倍增器。

Judgement requires experience, but can be built faster by learning foundational skills.
判断需要经验,但是判断能力的构建可以通过学习基础技能的加速。

There is no skill called “business.” Avoid business magazines and business classes.
没有一种技能叫做商业。避免商业杂志和商业课程。

Study microeconomics, game theory, psychology, persuasion, ethics, mathematics, and computers.
学习微观经济学,博弈论,心理学,说服力,伦理学,数学,和计算机。

Reading is faster than listening. Doing is faster than watching.
阅读比听更快,做比看更快。

You should be too busy to “do coffee,” while still keeping an uncluttered calendar.
你应该保持日程的干净,但是应该足够忙碌而没时间去做很多会面。

Set and enforce an aspirational personal hourly rate. If fixing a problem will save less than your hourly rate, ignore it. If outsourcing a task will cost less than your hourly rate, outsource it.
给自己设置并执行一个有抱负的时薪。如果修复一个问题节省费用比时薪少,忽略它。如果外包任务比你的时薪便宜,外包它。

Work as hard as you can. Even though who you work with and what you work on are more important than how hard you work.
尽最大可能的努力工作。尽管和谁一起工作,在哪件事上工作比努力工作重要许多。

Become the best in the world at what you do. Keep redefining what you do until this is true.
在你所做的事情上成为世界上最好的。不停的重新定义你的工作直到成真。

There are no get rich quick schemes. That’s just someone else getting rich off you.
不会有快速致富的体系。只是有一些想从你身上发财的人。

Apply specific knowledge, with leverage, and eventually you will get what you deserve.
加杠杠应用特种知识,最终你会得到你想要的。

When you’re finally wealthy, you’ll realize that it wasn’t what you were seeking in the first place. But that’s for another day.
当你最终获取了财富,你会意识到这并不是你最开始想寻找的东西。但是这是日后再说的事情。

Summary: Productize Yourself.
最后,Naval提供了一句话的总结 —— “总结:把你自己产品化”。

原文:

  • https://twitter.com/naval/status/1002103360646823936

徐远《人地城》读书笔记

发表于 2019-02-21 | 分类于 读书笔记 | 评论数:

序言

占用了那么多耕地,也没听谁说我们的粮食不够吃,储粮的仓库都是满的。【问号脸???】

他父亲的一句话:“人都走了,种地都机械化了,还是住城里吧”【趋势不可阻挡】

第一篇:城市的起源

新石器是“容器”

吃不完的食物可以储存,对于古代人类而言,是个巨大的进步。储存成为可能后,人类的食物供应会更加充足和稳定,进一步的发展成为可能。

食物的供给能力,决定了人口能够达到的密度。只有人口密度达到一定的水平,分工才会出现,高度的聚居才成为可能。

充足的食物让人类慢慢定居下来,村庄出现并变大。

然后慢慢演化为城市。

城市出现的另一个基本前提,是运输技术的进步。动物的驯化,对于运输无疑是有帮助的。

有容乃大

“金融”无非是今天与明天的交换。把今天的财富“储存”起来,换取明天更大(也可能更少)的财富。

乡村经济的一个基本特征,是“自给自足”,或者说是“封闭”。

筑城以卫君,造郭以居民

在城市兴起的过程中,王权是最初的主导因素,商业因素反而是其次。【在国外是不是也是这样?】

城市最重要的功能,是方便了人们的交流。

现在流行的马拉松赛事,来源于一场马拉松战役,42.193公里

文明的最早起源,是在离地中海东岸不远处的美索不达米亚平原,也就是如今的伊拉克、叙利亚一带

历史上的城市化

南宋临安的人口更是达到250万。

明清时期,北京的人口也都在70万-80万的水平。

迁徙自由不仅为城市发展创造了空间,也极大促进了经济的繁荣。

今天的中国,有5亿左右的城市户籍人口。在未来5-10年,或者更长时间内,会有另外5亿甚至更多的人到城市定居。【趋势啊!!!不知道未来聚居方式会怎样?】

何为城?何为乡?

人类进步的源动力:交流和合作。【人类还是往总体最优的方式发展,哪种方式更利于发展就会留下来,像城市化、城市集群化必然是发展方向】

农业的基础是光合作用,需要在大面积的土地上铺开,而且考虑到交通的因素,农村和耕地不能太远,要交叉分布,因此传统乡村的人口密度不能太大。

第二篇 城市印象

东京印象

日本地形多山,耕地很少,资源贫瘠,只要精耕细作,才能生存。做事马虎的懒汉,在这里很难生存的。久而久之,就形成了精益求精的工匠文化。【原来工匠文化是这样来的,环境决定文化,比如四川独特的享乐文化】

大东京地区有3600万人口,超过日本总人口的1/4。尽管日本人口在减少,这里的人口却依然在增加。据说东京为了疏散人口而修了很多地铁和卫星城,结果人口不但没有疏散,反而更聚集到这里。【中国大概率也会走上这条路,之前都是受政策的影响,鼓励小城镇的发展,现在发现大都市、大都市圈才是正途】

人类从丛林走向村庄再走向都市,无一不是以蒸腾的欲望作为驱动力。

深圳印象

每到一个地方,都会写一点对当地的印象,有点到此一游的意思。【好想法,可以模仿】

改革开放迄今的成就,说白了就是两句话。
一句话是农村改革打了更多的粮食,解决了吃饭问题;
另一句是农业发展释放的劳动力进城,和城市人口一起推动了工业化。

真正意义上的城市化,是人口在城市里聚集、居住、工作、生活、思想,形成内生的时长需求。真正意义上的城市化,或许才刚刚开始。

歧视外地,几乎每个大城市都会,但是北京会好一些。为啥呢?因为是天子脚下,能人异士向来往这里聚集,已经好几百年了。京城的子民们,多少习惯了这一点了。

台北印象

人类的大部分精力,其实没有用来生产或娱乐,而是消耗在猜忌、争斗之中,这才是这个物种最大的悲哀。【哎!!!】

第三篇 城乡二元

征地冲突的根源:农村土地产权太弱

农地与市场无关,也就无法充分分享经济增长、收入增加的好处了,这是这些年来农村相对落后的根源。

产权的缺失,是农村落后的根源。

历史的节点:1998年《土地管理法》修订

农村集体土地是不能直接参与非农建设的

1998年的这一次修法,最重要的后果之一,就是政府垄断了城市化的供地。农村集体土地要参与城市化建设,首先要征为城市国有土地。

是“农村集体所有”,还是“农村集体代耕”?

有了一个分割的城乡“二元”,就有了一个套利的机会。这通道最好是唯一的、垄断的,这样就可以大赚其钱了。“征地”就是这样一个通道。

我们的城乡二元体制,对农村的歧视是全方位的,不仅歧视农村的居民,还歧视农村的土地。

进一步的经济发展,要求释放更多的人的更大的积极性和创造力,包括农民的积极性和创造力。

崽卖爷田不心疼–“代耕”制度下的土地城市化

1998年以来经济的告诉增长,跟两件事情密切相关:一是1998年房改,二是2001年加入WTO

我国耕地面积大约20.3亿亩,18亿亩耕地红线。

2004-2012年我国共征收土地大约5200万亩。2004年我国城镇建设用地总量也就5000万亩左右。城市直接翻倍。

从1998-2012年,我国地级市以上城市建成区面积增加125%,也就是翻了一倍还要多。相比较之下,地级以上城市户籍人口只增加了45%,两者的增速查了将近三倍。加上非户籍人口,人口也才增加了71%。【错配严重,为啥,未来人口增速会不会上来?】

北京二环、三环、四环、五环、六环的长度分别为33公里、48公里、65公里、99公里、192公里,环内面积分别为60平方公里、150平方公里、300平方公里、750平方公里、2500平方公里。2000年以三环为北京城的边界,现在(2015)以五环作为城市边界。15年间面积的扩张,不是一倍,而是5倍了。而北京的人口的增加,也就一倍多,不到两倍。

深圳全境面积1900多平方公里,已经开发了900多平方公里,开发强度接近50%,剩下的基本都是生态保护区。也就是说深圳的建设用地已经使用殆尽了【深圳房价还得涨啊】

征地是挣钱的买卖,花一个农用地的成本价把地征了,转手用市场价卖给开发商或者企业,中间差价就是地方政府的了。全国平均综合地价3069元每平方米,折合每亩204万元。而农用地的补偿成本仅为不到4万元一亩,有的甚至更低,中间相差50倍以上。也因此各地政府都喜欢建新城,成本足够低。建筑成本一平米也就几百元,少的四五百,多的上千元,加上配套也就几千元。

谁动了18亿亩唐僧肉?

全国农村居民住用地高达2.5亿亩。农村每7亩耕地对应1亩的农村居住用地。城镇用地仅仅为5100万亩。倘若农村的人均占地和城市一样,就可以凭空节约出2亿亩土地。【我认为国家早晚要动这块的脑筋】

世界范围内,农场大多是家庭经营,农业工人数量不多,大多只是作为临时性帮手。农业生产非标准化的东西太多,监督比较困难。经营农业,农场主自己很多时候可能要身体力行,充当主要的劳动力。这就导致大部分不会从事这个工作。
算一笔账,简单说,一亩地一年纯利润800元,1000亩地一年纯利润80万元。那么,投资回报率多少呢?买1000亩地需要3000多万元,外加各种机械、化肥、种子、人工以及日常运营的开销,总要有5000万的投入。80万/5000万,净资产回报率1.6%。【太低了,比放到银行还低】

第二次世界大战以来世界粮食价格一直有一个下降的趋势,意味着农业的价值也是慢慢下降的。到现在大概下降了差不多一半。究其原因,就是农业技术越进步,粮食变得越来越不紧张。【技术进步带来耕地的价值不断下降】

城市里的地产,那是城市的聚集效应带来的高额地租。

第四篇 不堪重负的土地

还有其他用途的土地,在有需要、条件许可、经济上合理的情况下,是可以转化为耕地的。

假设需要的时候,81亿亩的林地、耕地可以有3%转为耕地(2.4亿亩),1.2亿其他农用地节约出1/4(3000万亩),农村宅基地节约出1.5亿亩,总数就是4.2亿亩,相当于增加了20%多的耕地。看这个数,或许会缓解部分人对于粮食安全的过度担忧。

第六章 “剩余”的人

【大事情:农民进城】,在就业、养老、子女教育、医疗等各方面减少歧视。

城市是节约使用资源的手段。人群的聚集,极大地节约了交流和交换的通信、交通、运输、仓储等各方面的成本,促进了交换和劳动分工的发展,推动了科技进步,从而促进了经济增长,这是200多年经济学的精髓。

城市面临的问题正指出了城市的瓶颈,提供了城市进一步发展建设的动力方向

解决“城市承载力”问题后,这些城市吸引了更多的人和更多的资源,并成为世界政治、经济、文化生活的中心,为人们提供着极大的便利,享受高额的地租;建设这样的城市,才是我们应该努力的方向。【中国政府一直担心大城市病,抑制超级城市的诞生】

60%是快速城市化的一个槛,过了这个数字,城市化的进城会放慢【中国18年达到这个临界点】

产品经理应具备的能力(读张小龙有感)

发表于 2018-10-16 | 分类于 产品设计 | 评论数:

最近在读有关张小龙的材料,觉着最真实、最有价值的还数龙哥在还未封神之前饭否的言论啦。花了好几天,断断续续的读完,泪流满面啊,龙哥牛逼,研究产品都搞到哲学、人性层面了,但感觉自己能吸收的东西了了。那就先写下自己当前的想法,另记下认为比较经典的话,以后可以常来翻一翻,常看常新吧。

产品人是站在上帝身边的人

产品经理看到这句,心里肯定会乐开了花,但权利越大,责任越大啊。龙哥为啥这样说呢,一是来奉承产品经理,二是产品经理确实是洞悉人性,满足用户欲望,设计规则,并让产品和人群按照规则演化的主,想想是不是有点像上帝。

所以我后来发现,人不是培养出来的,是给他们土壤,然后,有才能的人自己会成长起来的。甚至不需要我分享什么。后来我就不培养人了,只观察人了。

优秀的人,有了土壤,自己就会长起来,其他职业也是。

如果工作不是被好奇心驱动,不如回家卖红薯。

做产品,最要紧是保持十年如一日的新鲜感。

好奇心和发现是最值得珍惜的品德。

好奇心很重要,这是人最原始的东西,也最能持久。

以后招产品经理,直接给他一个小电器,比如mp3播放器,让他写个产品使用手册,就知道行不行了。

要提防那些Blog写得好的产品经理,因为在Blog上花的时间越多,在产品上花的时间就越少。原来还以为有例外,现在看起来无一例外。

咳咳,Fenng老师,你怎么看?

做产品,就是跟用户谈一场恋爱,否则只是一场商业交易而已。产品人是多么的博爱啊。

面试产品经理,所有技能合格后,要问,你喜欢摇滚吗。回答否的,就算了。

我在生活上不是一个有洁癖的人,但对待产品却分毫必究,介大概就是传说中的精神分裂吧。

很多人看到MJ的海报说pm心中有爱。不是爱,是愤怒。

产品不应该仅仅是功能(需求)的堆砌,应该还要有“温度”,可能包含了产品经理的价值观、爱、愤怒或者其他,那样才有灵魂。

老乔是Intel的葛洛夫的唯有偏执狂才能生存的证明人。 偏执需要权力的辅佐才能进行到底。

我还是想活的久一点,当个普通人也未尝不可吧。就像龙哥说的“ think less,live more(想得少,活得更久)”。

要知道用户的心理,并且知道用一个什么样的规则去引导他。为什么这么说呢?规则是很简单的,只有简单的规则才可以演化出一个非常复杂的事件出来。所以我经常很不认同说,在很多做产品里面,一开始就做一个复杂的规则出来,最后没有任何演化的空间。……像Twitter之类的产品都非常简单,……,但是这样的东西是最有生命力的。

我们要做的工作是在群体里面做一个加速器、催化剂,是做这一类的工作,而不是把这一块钉死了之后,用户进来以后,只能怎么样,一步一步的走。

设计出简单规则的能力

让用户保持饥饿,让他们保持愚蠢。…….对我们的用户来说,我们是要想办法让他们知道他们的饥渴在哪里。

我们要满足他们的贪嗔痴。贪是贪婪,嗔是嫉妒,痴是执着。

那我们要洞察这一点,是因为我们的产品对用户产生黏性,就是让用户对你的产品产生贪,产生嗔,产生痴。

我们给大家各种钻,钻体现了什么心理?其实都体现了人性的这几个弱点,各种黄钻、绿钻,他会贪,他要升级;他会嗔,他会跟人比较,说你的钻比我的等级高,所以我也要升上去;他会痴,觉得我一定要把所有的钻给收齐。……就像苹果的手机,它同样的会对用户产生这样的吸引力……这是人性本身的一个共同的弱点

所以当我们在做一个产品的时候,我们在研究人性,而不是说在研究一个产品的逻辑。

所有的产品都需要有人性的研究。……..你可以对每一个好的产品都提出来问题,那么它背后肯定都是从人性的角度来说的,而不是从别的理由来说的。

深刻的理解人性

我们做产品要找到用户心理诉求的本质。……用户上微博干什么?用户上微博的原因是为了炫耀,是因为害怕孤独,不是利群而离群,是用它有追感。……实际上微博是一个构筑另一个自我的地方,构筑一个自我。…….一个内心强大的人是不需要写微博的。

功能是做需求,定位是做一种心理诉求,也就是说定位是更底层的一些心理供给。

让他获得安慰感、排除孤独感,让他获得成就感,让他在里面更加的自信,让他在里面像一个敌人一样的,要做到这些诉求。

善于抓住用户心理诉求的本质,挖掘到底层的需求

我们引用老乔的一句话,产品是技术和艺术的结合,或者产品是技术和人文的结合…..但是你怎么样在你的产品里面去包含这种人文或者艺术的成分?这是挺不容易的。

对于一个产品经理来说,需要提高自身修养的一个方向,在技术之外的人文方面有一些自己的认识

其实产品是可以表达产品经理自己情感的地方,如果大家用得好的话。……所以你看如果你把你个人的情感包含到产品里面,是一件很爽的事情。

除了技术之外也需要提升自己的人文修养

所以有的时候,我们的产品经理经常是在做坏事,不是做好事,因为他拼命的引入新的功能进来,后面反正又不用自己写代码就不管,然后就把开发的累死了。……因为我们要冒出一个想法来,太容易了;但是我们要知道这个东西是一个正确的想法,就太难了。

控制需求的能力

其实研究客户心理就是研究自我,很多时候我们是瞄准自我的需求来做产品的。……当我们研究不到用户需求时,我们就会说只要让我们自己用得爽…..怎么样让用户用得爽呢?……一个比较简单的方法,把自己当作一个傻瓜来用产品,傻瓜心态。……他没有这么多的背景,他们用这个东西只是第一眼的感觉或者用一次,一、两分钟的体验就决定了。

把自己当傻瓜用产品,傻瓜心态,一秒变傻瓜的能力

使他(用户)感受趋势才是最重要的,因为用户只能够对过去的事情产生认知,未来的东西才是趋势,你怎么知道下一阶段会流行什么样的潮流,那才是最重要的。我们怎么去了解这个趋势是什么?有很多方法,很多人去分析数据【分析数据也有弊端,举了修理飞机的例子】

感知趋势是来自于我们的各种渠道,包括生活中的各种渠道,或者微博上的各种渠道。我自己的个人喜好我会看一些论坛或者微博这样的东西,去看这些离我很远的用户,他们在什么样的氛围、什么样的场景里面去用我们的产品。

坚持一个习惯,以前提过1000、100、10这样的习惯,就是说每周要去看1000个帖子,不管是微博的,还是哪里的。要看100天博客,要做10个CE这样子的。

之前就有一句话说web已经死掉了,大概从移动互联网的发展趋势来说,确实是有这个趋势。…..因为PC的不增长和手机的快速增长,这个对比实在是太强烈了。……而手机端的话,浏览器可能不是一个主要的入口,可能APP才是。

而且APP的趋势,不是要做一个大而全的APP,而是说做成尽可能小的APP。为什么不是大而全的呢?因为用户很懒,我要看天气,我就点天气的APP。我要看股票,就点股票的APP。我不会跑到腾讯所有服务的APP里面去,然后钻到里面去找天气、找股票。

感知趋势的能力

用户要什么我们就给什么,这个在很高层的用户里面甚至也会出现这个问题。比如说你的上级可能会给你说,你看用户给你提出这个需求了,你为什么不做?……为什么不做?因为我们觉得不能用户要什么就给他什么,要变个花样给他,用户要的不一定是对的。…..所以这种需求挺多的,但是我想表达的是如果我们针对需求一个人去满足,你可能获取了这部分用户,但是得罪了另外一部分用户。

把控需求的能力,不要人云亦云,要有自己的思考

但产品也是,一个产品里面很简单的一个产品可能也包含了上百个功能在里面,这些功能你可以像写代码一样的,你可以按一个线性的方式把它串起来,但是也可以做成一个很有架构在里面的东西。…….我们心中一定要有一个产品的架构在这里,而不是说我们这个产品就是一大堆功能的集合,只是一个无序的集合那样就很糟糕了。

产品架构的能力,产品不仅仅是功能的堆砌

一个产品技能的心态,这也是我感触比较深的,就是跟人讨论问题的时候会争论起来。对产品性能来说我觉得它是抱着一种求知的态度来讨论,而不是争论谁赢了谁输了这样一个观点。如果是这种求知的话,当别人说服了你,辩赢了你,那你很高兴,因为你接触到了新的知识。应该是说很鼓励这种辩驳,但不是为了自尊心而战。

抱着求知的心态看待和讨论产品

作为产品经理来说,一方面是自身要保持饥渴,保持一个觉得自己很无知的状态。

最后说的就是——我所说的都是错的。

总结一下

  1. 拥有好奇心,洞悉人性,善于抓住用户心理诉求的本质,挖掘到底层的需求

  2. 基于用户体验的产品思路,打造有“温度”、有“骨架”、有“灵魂”(PM的爱恨情仇)产品的能力

  3. 感知趋势的能力

  4. 设计出简单规则的能力

  5. 变傻瓜用户的能力

怎么具备以上能力呢,除了技术之外也需要提升自己的人文修养。多看书,多做产品。

产品不应该是冰冷的工具,乔老爷子如是说:“产品是技术和艺术的结合,或者产品是技术和人文的结合”。

Revision history

2019.10.26 读张小龙2012产品公开课总结
2019.10.16 读张小龙饭否后的总结

张小龙饭否摘抄

发表于 2018-10-16 | 分类于 产品设计 | 评论数:

网络&群体

你的价值在于一个集体对你的认证,这是很多人具备的意识。然后这个意识再被sns网站利用,又增强了这个意识。

物种多样性:一个物种并没有进化为全能生物,相反,只加强它独具的一个功能才能让它生存。

动物会向中心聚集,处在边缘的动物会有被天敌吃掉的危险。成千上万人在一个时间段看和谈论一部最新的电影,也是同样的心理:不看的人会地处文化的边缘,隐性地影响交配权。

将来每个人需要两个微博,一个是社交型微博,一个是自我表达型微博。

社交网络,并不是好友越多越好。

sns上,一下子给我100个好友,怎能给我一个一个去发现和找到好友的乐趣呢。

社会是所大学,选修表演专业者众。

陌生人组成的社区,虽然冰冷,但是去掉了熟人社区的温情和虚伪,多了规则。

评论的意义:一个人如果自顾自写东西会写到离谱,评论相当于自动控制系统的负反馈,让人从偏激中收缩,趋于稳定。最终一个社区的个体互相影响,形成一个稳定的中庸的气场。

人分熟人,半熟的人,陌生人。沟通方式有广播,群展示,群聊,单聊。排列组合是满足不到人们的需求的,因为基因的选择策略不是这么机械。重要的是,让人不自觉地掉进圈套,而不是让他建立圈子。

人性

用手指同步自己的话到各个微博,原来是基于一种自我肯定和自我赞赏的快感,在paste的时候有机会欣赏到作品的复制和传播,这种快感源自基因的“复制和传播是第一目的”。任何自动同步工具都是扼杀这种快感的,因此会被基因排斥,这种同步产品注定失败。

往往能够获胜的,不是真善美,而是贪嗔痴。因为后者更给力。

你说的每一句话都是炫耀。因为,当你想说一句话的时候,你的意识里其实有1亿个念头在动,它们像精子一样争先恐后,最终,最炫的那个念头胜利了,成为了那句话,成为了冠军,获得了从你口里说出来的权力。

方向盘是手的延伸,油门刹车是脚的延伸。汽车并不单是个运输工具, 而更像人工肢体。开车和坐车是不同的。对驾驶者来说,汽车是有生命的。

手机是人的最重要的人工器官。

手机是人的躯体的延伸,手机发博才是躯体对世界的回应,越来越感觉如此。

人们等待一个平板电脑的发布,因为那是他们的第三支手第三只眼,所以,重量和分辨率是头等重要的。

这么多年了,我还在做通讯工具,这让我相信一个宿命,每一个不善沟通的孩子都有强大的帮助别人沟通的内在力量。

做自己的人又分两种,一种不断打破自己,一种沉溺于自己。

人会集中注意力做一件事情,因为这样可以避免胡思乱想。这是一种基于生存本能的自我保护动作。

人只是基因操作的一台机器,绝大部分的生存和选择策略,已经被写在基因的程序库里。人只是不自觉地听从这些基因制定的策略的指挥,却以为是自己的头脑做的选择。少数人可以挣脱并产生自己的选择策略

基本人性需求,不应该压抑。谢安听说打破苻坚的军团之后,强行忍住了得瑟的冲动,结果把自己木屐的齿都折断了。谢安是宰相,折木屐。普通人的话,恐怕太压抑会折JJ的说。

好战是人的天性,所以任何一个团队或者产品都要树立一个敌人来提振精神,哪怕没有敌人都要找一个假想敌。

人就是环境。人就像变色龙,进入到环境就会去适应,最终成为那个环境的一部分。比如在微博上,环境就是听众,发表的言论自然就是适合粉丝听的言论。环境就是个天然的过滤器。

产品

还是你们用户爽,哪里爽到哪里,苦的是做互联网的,要整天分析你们的阴暗心理好让你们更爽,还不能明说。

一个产品,要加多少的功能,才能成为一个垃圾产品啊!

产品往往是做着做着,就主旋律了,因为跟着用户走,用户爱听主旋律的歌。

虽然人人都说简单是美,但没有几个人真正喜欢简单的。看看这个越来越复杂的世界就知道了。

产品就象一个生物,有它自然的进化之道。最重要的,是制定好产品的内在基因的“竞争策略”,让竞争策略在进化中再自行演化为具体的表现形态。如何搞?没想清楚。

异议就象基因的突变,有突变才能滋生出更强的新一代基因。

基因并不会思考,它们只是漫无目的的胡乱产生变异,有些碰巧更有利于生存,就存活下来了。

如果说产品做的一切都在满足用户的虚荣,热闹,逃避,贪恋等,那么,大众同样都有受虐的心理,为什么不做一款产品去虐待他们?

平均每天收到一张iTunes Store的帐单。我要在apple上疯狂消费,来赎回以前在windows上狂用盗版的罪。【张小龙在app store体验了很多应用】

twitter的创始人起先无所事事,就去钓鱼,看到鱼儿成群结队的拥挤,生怕掉队,于是得到启发,便开发了twitter,方便人们成群结队。

网络尚未普及的时候,产品必须依靠功能多来取胜,并且产品是自成一体的。网络普及后,才催生出一种新的产品形态:产品极简,反而有利于在网络这个大生命体中自我繁衍,并且产品是面向连接的。

好的网络产品,必然是无法预料其会不会成功的。成功的产品,刚好是“碰巧”成功了。但这个碰巧并不是创造者的运气,而是,这个产品刚好能在网络生命体中生存和繁殖。就像我们无法知道一个小孩为什么成长如此迅速一样,我们无法知道一个网络产品为什么会成功,即使做的人,也应该感谢主。

如果说一个细菌能够自然粘上另一个细菌是细菌的“意识”,一个极简的产品元素的能够连通另一个用户的接口,就是一种连接“意识”。那么这个接口就是有“意识”的。接口是主人,人是接口要使用的对象。

一个被人预见必然会成功的产品,必然会是个失败的产品。成功的产品是不能预见的。至少,是不能被人所预见的。机器来预见还有可能。

因为我们无从知道复杂生命体的运作规律。

缘起,性空。

互联网产品应该是由用户推动,而不是产品经理来推动。产品经理的作 用只是找到四两拨千斤的地方稍微用点力。

流通正在取代内容本身,内容变得更小更方便传播。mp3取代唱片,微博取代书本,短信取代信件。我怀疑有一天,微小的内容也没有了,大家直接通过网络交换荷尔蒙。

和“敌人比我们自己更了解我们”相对应的是,用户比我们自己更了解我们的产品。产品是有人用才成为有灵魂的产品,图纸和代码堆出来的只是躯壳。

你问我们和竞争对手比有什么区别,我说我们没有他们那些臃肿艳俗的部分。

FaWave这类工具,汇集了多个产品的数据和功能,看似提高效率,但人毕竟不只是效率工具,汇集类工具脱离了原产品的气息后,就丢失了对产品的情感认同。

心有千千结,每种结都是一个产品。关系千万重,每种关系都是一个产品。未来世界,应该是每个人都会做一个十个用户的产品的时代。

网站的发展趋势是,内容有标题的都不行了,没有标题的才兴起,如twitter,facebook。我现在好奇的是,quora的是有标题,还是没标题?

哥做的不是产品,哥做的是发挥潜力的自由。

IM是没有前景的,因为破坏了“让人更轻松互连”的定律。IM可能需要尽快将双向关系转换到单向关注体系中。

用户的忍耐力太强了,最后就不辩美丑了

美女们是否爱用,是产品能否流行起来的风向标。特别是天上人间的。

一晚上没睡着,想明白了一件事:单对单聊天是通讯工具,多人聊天是社区。

手机开发,对体验的要求更高了,一寸短,一寸险。

以后招产品经理,直接给他一个小电器,比如mp3播放器,让他写个产品使用手册,就知道行不行了。

程序员当然不知道产品的走向,但产品人员就知道吗?也不知道。不同的是,程序员会盲目创造出100个尝试后,总有一个胜出并生存下来。生存的这个就是走向。

facebook为什么在国外这么火?因为他们的生活是在是太单调了。

产品总是会包含功能之外的价值观的。因此不能指望天朝设计的产品能被世界认同。

哥喜欢的不是产品,是战争。

互联网产品,好友是如何来的?必须是用户自己手把手拉来的。任何的批量好友导入,都是收效不大的。

原来产品的气质,无非是说做产品要带些文艺。

互联网的最终目的,是让关系学见鬼去。

产品人是站在上帝身边的人

think less,live more

信息越透明,整合的作用就越小。不在单点突破,而希望依靠整合来成事,是老套路了。所以N合一的东西一般很垃圾。网络的使命,包含了取缔整合。整合是系统来自然演绎的,不应该是人干的事。

做产品,最要紧是保持十年如一日的新鲜感。

怎么能跟战略部的人谈产品呢,他们面对的是评论家,我们面对用户。

周国平说,即使产品是个悲剧,我们也要做得有滋有味。即使用户都是傻逼,我们也要帮他们装得有声有色。

深夜,突然悟到Jobs说的“Stay hungry , stay foolish”,原来本意是对用户说的,“让你们保持饥渴,让你们保持愚蠢”。

要提防那些Blog写得好的产品经理,因为在Blog上花的时间越多,在产品上花的时间就越少。原来还以为有例外,现在看起来无一例外。

告诉大家一个秘密,苹果为什么崇尚白色?崇尚一个按钮的设计?因为他们的首席设计师乔纳森之前搞工业设计是浴室用品领域。想想马桶吧。

互联网的产品,哪怕任何一个非常小的产品,其实都想一场战争,一场瓜分资源的战争。用户就是待瓜分的资源。

做产品,就是跟用户谈一场恋爱,否则只是一场商业交易而已。产品人是多么的博爱啊。

面试产品经理,所有技能合格后,要问,你喜欢摇滚吗。回答否的,就算了。

我在生活上不是一个有洁癖的人,但对待产品却分毫必究,介大概就是传说中的精神分裂吧。

很多人看到MJ的海报说pm心中有爱。不是爱,是愤怒。

从前的理论是,市场营销是炮弹,打炮才能带动销量。现在的情况是, 产品本身是炮弹,只要爆炸力强就能炸晕一片用户。

Instapaper本来就是小众,走收费路线可以理解。whatsapp那才叫牛逼,大众软件依然收费。【小众软件可以收费】

如果社会进步到没有商人了,生产的人直接赚钱,该是种进步吧。appstore所以好。

用户体验

原研哉:设计是为了让人在高速的现代生活节奏中更轻松,而非更紧张。

原研哉:古人需要复杂的物品装饰图案和式样来吓唬敌人和统治民众, 现代人不再需要装饰有这类作用,因此流行简约设计。我感觉我生活在古代,因为我也不能完全接受简约。

检验你是否真的接受简约的方法是,把家里装修成现代简约风,如果你住上几个月没疯,就通过了。

我们遵循一个很古老的设计原则:UI人员禁止使用同类产品,但PM必须博采众长,PM将需求转述给UI去设计。这样避免UI进入思维定势

多任务,在windows上是alt tab,在iphone上是双击马桶按钮,在macO S上是四个指头往下一撸。原始的就是人性的。

需要说明书的产品不是好产品。需要弹tip告知用户如何使用的功能不是好功能。

每天都有很多产品发布或升级,介绍都是罗列功能指标。用户又不是按功能来付费的,你列那么多新功能去完成kpi没有问题,去糊弄用户就不对了。

我不喜欢的UI例子:弹tip;半透明遮盖;页面上来一块固定toolbar;令人不解的数字;etc

创新

问:深泽直人,为什么无印良品要创新呢?答:战后日本刚开始也都是模仿,因为模仿就可以在市场立足了。但是后来用户逐渐不买模仿的帐了,自然,企业必须要创新才能生存了。所以,创新是用户逼的。

关于创新:1,创新是无稽之谈。创新不是决定取舍的理由。创新一般都是空想。创新是靠不住的,死得很快的。2,要的,是把握用户心理。其结果,才被观察为创新。3,创新是件碰运气的事,你只是蝴蝶,剩下的交给云。ppt over。

指望中国人比西方更创新,是不太靠谱的。创新是基于用户环境的,比如我们都还没有普及智能手机,自然不会催生出一个智能手机普及环境下的创新应用。

大部分的所谓创新,都是把问题搞复杂化而已。

思维

程序设计最近20年的发展,是引入了更“自然”的思维方法:万事万物有其“基本类型”,高级类型是从基本类型派生出来的,并呈多态性,高级的个体是高级类型的实例,个体之间是通过消息来通讯的。这就是C++的全貌,虽然很多C++程序员对此理解不深。我想说的是,这也是PM分析问题的法宝。

思维习惯和思维方法,其实是可以训练出来的。那么,为什么大学没有做产品这门课呢?

词语只是金属表面散射出来的一点点光,虽然能汇集成为耀眼变换的光,却还只是变换的光。

《黑天鹅》里总结得太好了:人有天生的归纳能力来将复杂事物模型化。但这也导致人的认知误区,没有见过黑天鹅就认为天鹅都是白的。

《黑天鹅》让我知道我是如何被我的经验和直觉所愚弄了。

成功是个概率事件,这些传记可以当故事看,但不能当推理看。

勤奋只是一种长年形成的癖好。

程序语言和自然语言一样,都是对想法的整理和表达。因此我现在面试程序员,不让他写程序做题了,改为聊天。

无欲则刚,一欲则强,多欲则乱

Think Different!我经常用这句话来提醒自己,我没有乔老爷的才,一定不能因为要think different而走火入魔。

好奇心和发现是最值得珍惜的品德。

其他一些思考

这两年,我博览了群书和群山,路过了死亡之谷和罪恶之源,现在终于可以坐下来喘口粗气了。

少用电脑多看书,多看电影少上网

你看到一本说出你心里话的经验之谈的书,然后会很痛苦,因为这类书是于人无益的:能明白的,不看此书也自然会明白。不明白的,看了此书也不会有改变。

所以我后来发现,人不是培养出来的,是给他们土壤,然后,有才能的人自己会成长起来的。甚至不需要我分享什么。后来我就不培养人了,只观察人了。

一个热衷于打勾的国度。源自对盖印的崇拜。盖印又源自宝塔葱白。

古时候,男人出去打猎,是没有计划的,遇到什么打什么,也可能空手而回。今天的男人每天规定必须要打几只狼回来,就焦虑了。

原来光有好的想法没用,还要有渠道才能传播出去。问题是,渠道反过来又会选择性传播和放大不该传播的。

想法如同基因,有生存价值的想法最终会取得传播的胜利。这是《自私的基因》里关于文化基因的观点。

整天将KPI挂在嘴上,以KPI为目标来工作的leader,都是不合格的,庸俗的,没有想法的,令人痛苦的,无法言语的。

转:“人们往往出于责任做慈善,但事实是,做慈善有益于自己的身心, 有助于自己了解生活。我从难民身上学到了最多东西,比如如何当母亲、做一个坚强的人并生存下去,所以我只是希望能回报一些东西。”– 安吉丽娜朱莉

我们总说每个人都是独一无二的,但同时又要求每个人都有一样的能力,因为不能提供给每个人独一无二的职业。

人多了做事情就痛苦。哥的理想是,以后一个人弄一个很大面积的办公室,养花种草写程序,一个人做,爱怎么搞怎么搞。

马太效应揭示出,不光是人会往人多的地方走,就连金钱都是这样。

太初,人太自由了,于是,要有约束,便有了节日。大大小小的节日。

如果工作不是被好奇心驱动,不如回家卖红薯。

在一个平庸的人群里,需要有人独断专行

转:人生的成长过程之中,要是不彻底的绝望一次,就不知道自己身上什么东西,决不可放弃,也就不知道什么是真正的快乐。——吉本芭娜娜《厨房》

如果不是有乔老爷这只黑天鹅,我差点就相信三十岁以上的人都已经属于被时代脉搏淘汰的人了。

愚人节的真是含义是,让人自省一下自己其实是个愚人。这一天,每个人都应该戴个驴面具上街的。

人一成功,就喜欢将归纳法当作推导出真理的方法。

人要成功很难,比成功更难的是,知道自己的成功是偶然的。

归纳法来推导结论是错误的。所以很多时候,比拼的是谁更能独断专行。不过独断专行也是依赖在历史经验的基础上归纳出来的思路。

陪练要找高手。

群体互联时代,靠个人或者中心影响力来做宣传,显得不合失宜。

没有压力和压力过重,都会导致不可承受的轻。

王阳明是个了不起的人。看他的著作,不如在实践中尝试无畏。

为了证明人是环境的反应器,在自然环境中才会产生更多的想法,我做了个实验,用语音记事本记录了今天在路上的十分钟产生的想法,结果发现,路上的思维很活跃。

与其说乔布斯的强大在于对市场,商业,管理的洞察,不如说是对人性的洞察。

我相信将来会有一种随时随地的微博形态来取代现在的微博。因为人在环境中实时产生的反应才是丰富而有实感的,而坐在电脑前产生的反应,是对电脑里的虚拟思维做二次反应。

如果不能做一些特别的事情,那做它有什么意义呢。

上帝让众生平庸,是不想看到他们都变成精神病

一个理想的人生应该这么过,四十岁前把所有的欲望满足一遍,四十岁后过没有欲望的生活

幸福感不一定和财富成正比,却和无知度成正比。

丹·艾瑞里在《怪诞行为学》中,用大量的历史案例与实践经验证明:在很多时候,不是消费者的购买意愿影响市场价格,而是市场价格影响了消费者的预期,进而影响购买。

在这本书中,高汀断言,我们已经进入了一个消费者几乎已经拥有了所必需的一切、很难有东西能激起其消费欲望的后消费时代,传统的面向大众的产品定位和老的营销法则即将衰亡,在这个需求饱和的时代,一种产品想要卖得出去——遑论获取成功——必须是值得注意的、例外的、全新的、有趣的

从前的理论是,市场营销是炮弹,打炮才能带动销量。现在的情况是, 产品本身是炮弹,只要爆炸力强就能炸晕一片用户。

要么你让市场营销人员去前线大炮,费钱费精力。要么你直接让用户打炮,一劳永逸。

老乔是Intel的葛洛夫的唯有偏执狂才能生存的证明人。

偏执需要权力的辅佐才能进行到底。

中国的教育是扼杀好奇心的完美历程。

写文章,做网站,写代码,要求的能力都是一样的,就是条理。

比特币的前世今生

发表于 2018-06-25 | 分类于 区块链 | 评论数:

比特币诞生的背景?

2008年,金融危机,当时政府和银行管理经济的能力遭到各方质疑,信用降入谷底。美国政府向华尔街和底特律汽车公司注入大笔资金,美联储推出“量化宽松”政策,本质上就是大量印美钞刺激经济,金价上涨。相当于政府拿民众的钱来补贴这些资本家。

为什么要创建加密电子货币?

其实一直有一帮自由主义者密码极客希望创建一种完全去中心化的电子现金系统。可以脱离政府自由流通和发展。

因为之前的信用卡或者货币都存在诸多问题。比如信用卡需要用户的真实身份,对于用户的隐私保护不够。货币就存在超发的情况,政府通过这种方式从民众手里掠夺财富。

加密电子货币的坎坷之路

1983年大卫·乔姆(David Chaum)最早提出把加密技术运用于现金上的想法。写了论文《不可被追踪的交易》,使用盲签(blind signature)的技术来实现匿名的网络支付系统。1989年大卫·乔姆创建了数字现金公司;在之前系统基础上创建了电子现金(Ecash)系统,这个系统还有一个别名为网络现金(cyberbucks)系统。得到了微软和visa等巨头的支持。但是因为理念太超前,未得到大范围的支持,另外,该系统支持企业对企业转账也是其失败的原因。

1998年,戴伟(Wei Dai)阐述了一种匿名的、分布式的电子现金系统:b-money。B.money强调点对点的交易和不可更改的交易记录。每个交易者都保持对交易追踪的权力。但是B.money系统中,大卫并没有解决账本同步的问题。

1998年,尼克·萨博(Nick Szabo)发明了比特黄金(BitGold),提出了工作量证明机制。

哈尔·芬尼(Hal Finney)发明了哈希现金(HashCash)则把该机制进一步完善为一种”可重复
利用的工作量证明(RPOW)“。“RPOW”的特色在于可重复使用,且虽然控管权集中,但发行者没有通胀压力。

2008年10月31日,名不见经传的中本聪在metzdowd.com的密码学邮件组列表中发表了比特币白皮书《Bitcoin: A Peer-to-Peer Electronic Cash System》(《比特币:一种点对点的现金支付系统》)。

2009年1月3日,比特币网络诞生,中本聪本人发布了开源的第一版比特币客户端。

2009年1月3日,比特币网络的第一个区块诞生了。

2009年1月9日,中本聪本人发布了开源的0.1版比特币客户端。

2009年1月12日,第一笔比特币交易,中本聪发送了 10 比特币给开发者、密码学活动份子,Hal Finney(哈尔·芬尼)。

2009年10月5日,有记录的最早比特币汇率: 1 美元 = 1309.03 比特币。

参考:
https://en.wikipedia.org/wiki/Ecash
https://daimajia.com/2018/02/10/blockchain-share-in-freesfund
https://zhuanlan.zhihu.com/p/32754317

JVM之Java堆

发表于 2018-05-13 | 分类于 Java | 评论数:

JVM之Java堆

先看图,搞清楚堆在运行区的位置

图片来自网络

介绍
Java堆是JVM所管理内存中最大的一块,存放几乎所有对象实例(例外:栈上分配等)。这块是线程共享的区域。

垃圾回收器管理的主要区域,现代的收集器基本都采用分代收集算法,所以可划分为新生代与老年代,新生代可进一步划分为Eden区、From Survivor区(S0)、To Survivor区(S1)。

可以处于物理上不连续内存空间中,所以标记-清理算法才会起作用。

这个区域一些参数的解释

新生代与老年代的比例可以通过参数-XX:NewRatio来设置。比如,设置-XX:NewRatio=3,表示新生代:老年代=1:3,也就是说老年代占3/4。

Eden区、From Survivor区(S0)、To Survivor区(S1)之间的初始比例是8:1:1,通过-XX:SurvivorRatio参数来设置。比如:-XX:SurvivorRatio=8,表示两个Survivor:Eden = 2:8 ,每个Survivor占 1/10

其他一些参数

  • -Xms:设置堆内存最小值
  • -Xmx:设置堆内存最大值
  • -Xmn堆中新生代初始及最大大小(NewSize和MaxNewSize为其细化)
  • -XX:NewSize设置新生代最小空间大小
  • -XX:MaxNewSize设置新生代最大空间大小
  • -XX:PermSize设置永久代最小空间大小
  • -XX:MaxPermSize设置永久代最大空间大小
  • -Xss设置每个线程的堆栈大小

是不是参数太多,不容易记,这里有个记忆的小技巧

Xmx(memory maximum), Xms(memory startup), Xmn(memory nursery/new), Xss(stack size)

对于参数的格式可以这样理解

  • -: 标准VM选项,VM规范的选项。
  • -X: 非标准VM选项,不保证所有VM支持。
  • -XX: 高级选项,高级特性,但属于不稳定的选项。

会触发的异常
OutOfMemoryError,堆中没有内存完成实例分配,并堆也无法再扩展时会触发。

JVM之程序计数器

发表于 2018-04-29 | 分类于 Java | 评论数:

JVM运行时数据区的图,从这个图能清楚程序计数器的位置

线程私有的,生命周期与线程相同

什么是程序计数器?

通俗的讲就是告诉当前线程该执行哪条字节码了。

周志明定义

可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器的工作就是通过改变程序计数器的值来选择下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都要依赖这个计数器来完成。

程序计数器存在哪?

虚拟机区别于硬件,组成原理里学的程序计数器是用CS和IP寄存器来存,来表示指令地址。
而Java把程序计数器存在内存里。

程序计数器保存内容?

两种情况:
一、存的是Java字节码的地址,实现上可能有两种形式:
1.是相对该方法字节码开始处的偏移量,叫做bytecode index,简称bci;
2.是该Java字节码指令在内存里的地址,叫做bytecode pointer,简称bcp。

二、为空(Undefined)
如果调用的是Native方法

程序计数器有啥作用?

现实中程序往往是多线程协作完成任务的。JVM的多线程是通过CPU时间片轮转来实现的,某个线程在执行的过程中可能会因为时间片耗尽而挂起。当它再次获取时间片时,需要从挂起的地方继续执行。在JVM中,通过程序计数器来记录程序的字节码执行位置。程序计数器具有线程隔离性,每个线程拥有自己的程序计数器

异常

此区域一般不会发生OutOfMemoryError,如果发生此类异常,排查的时候先别着急找这块了。

拓展

是跟操作系统中的程序计数器有点类似。这个有时间再补充吧。

参考

  • https://www.sunjs.com/article/detail/6700cb3679744dc190adfdd9202d4055.html
  • https://www.cnblogs.com/noKing/p/8157608.html
  • 《深入理解Java虚拟机》
  • https://www.zhihu.com/question/40598119/answer/87381512

区块链和生产关系

发表于 2018-03-11 | 分类于 区块链 | 评论数:

之所以说区块链是生产关系,就是因为区块链技术可以很完美地为不同参与者分配利益。各方人对这种利益的分配规则的认可,就是一种共识。

《数字黄金》读书笔记

发表于 2018-02-04 | 分类于 区块链 | 评论数:

比特币的优势

  • 匿名
  • 绝对个人所有
  • 跨境支付,速度快,手续费低
  • 抗通货膨胀

相比于现有货币,具备稀缺,分割性更强,耐用性更强,流通性更强

比特币的价格

全世界的黄金的价值应该是7万亿美元。假如比特币在全世界一半的地区流行,每个比特币的价格应该在50万美元

全球货币种类:170种(来自于维基百科),大概只有5%比较有价值,8种左右,美元、欧元、人民币、日元、英镑

比特币的价值

比特币的价值在于存储价值,不一定在交易上有多强悍的功能,类似黄金,比特币它自身有支付功能,但重点不在支付。它的重点是资产,是资产带支付,而不是支付带资产,反过来。什么意思呢?就是这个东西本身像黄金,也是一个商品嘛。但黄金有没有免费的支付,黄金从这边搬到那边要把它搬过去的 ,黄金没有脚不会走过去。比特币就不同,比特币是资产啊,但你叫它走过去,它可以自己走过去的。因为移动数字很便宜,没有成本嘛。所以它是资产带支付。

13年的时候百度的云保安服务就开始接收比特币支付,这个还挺让人惊奇的

Oracle学习笔记——配置监听

发表于 2017-09-26 | 分类于 数据库 | 评论数:

什么是监听

如果你做过网络开发,对于监听listen就比较熟悉了,监听是什么,在网络开发中,就是监听某个端口,等待客户端的连接。同理,在Oracle中,监听的意思也和这差不多。

在Oracle数据库服务器中,通过一个叫“监听器”的组件接收来自客户端的连接请求,它是客户端和 服务器端中间的桥梁。监听器虽然在Oracle服务器端,但是它和Oracle主进程并不绑定,它是一个独立运行在服务器端的后台进程,独立于数据库运行。它负责对客户端传入的连接请求进行监听,并且对服务器端的连接负荷进行调整。当客户端准备建立一个到服务器端的连接时,监听器接收客户端的连接请求, 然后再将这个客户端连接请求交给服务器进行处理,一旦客户端和服务器建立连接,客户端和服务器以后就直接进行通信,而不再需要监听器的参与,这就好比师傅 领进门,修行靠个人。就如下图所示:

  1. 客户端向服务器端发出连接请求,监听器监听到客户端的连接请求;
  2. 监听器把客户端的连接请求交给数据库服务器进行处理;
  3. 经过监听器建立连接以后,客户端与服务器端就可以直接进行通信,而不再需要监听器的参与了。
    以上就是监听器干的活,也就是说,你对数据库服务器的第一次,肯定会给监听器,如果监听器没有配好,你就甭想连数据库了。那么,怎么才能配好监听器呢?主要涉及以下三个文件:

listener.ora、sqlnet.ora和tnsnames.ora文件

listener.ora文件

打开listener.ora文件看看,贴上一部分有代表性的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#这是一个名为LISTENER1的监听器
#监听的协议是TCP协议
#监听的主机IP是127.0.0.1
#监听的端口是1521端口
LISTENER1 =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
)

#记录了监听器LISTENER1服务的全局数据库名、数据库路径和数据库实例名
SID_LIST_LISTENER1 =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = ORCL)
(ORACLE_HOME = C:\Oracle11g\product\11.2.0\dbhome_1)
(SID_NAME = ORCL)
)
)

sqlnet.ora文件

打开sqlnet.ora文件看看,内容如下:

1
2
SQLNET.AUTHENTICATION_SERVICES= (NTS)
NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)

这个sqlnet.ora文件实际上就是一个配置文件,主要是和接下来说的tnsnames.ora配合使用。

tnsnames.ora文件

tnsnames.ora文件存放于客户端机器上,和sqlnet.ora文件配合使用。可以简单的看看tnsnames.ora文件中的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ORCL是个别名
#这条信息记录了我们使用TCP协议,去连接IP地址为127.0.0.1,端口号为1521的数据库主机上服务名为orcl的数据库
ORCL =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl)
)
)
以后,我们还会配置更多的数据库连接信息,比如我要连接192.168.10.2主机上的数据库,我们只需要在tnsnames.ora中继续追加新的连接信息即可,比如:

STDB =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.10.2)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = stdb)
)
)

服务器端配置

直接编辑listener.ora,就像下面这样,配置一个新的监听器:

1
2
3
4
5
6
7
8
9
10
11
12
13
LISTENER =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
)

SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = ORCL)
(ORACLE_HOME = C:\Oracle11g\product\11.2.0\dbhome_1)
(SID_NAME = ORCL)
)
)

你想在哪个数据库上配置一个新的监听,就修改对应的内容即可。

常用命令

启动监听器

1
2
3
4
lsnrctl start <监听器的名字>

// 启动名为LISTENER的监听器
lsnrctl start LISTENER

当我们不指定监听器的名字时,就默认启动LISTENER监听器;

关闭监听器

1
2
3
4
lsnrctl stop <监听器的名字>

// 关闭名为LISTENER的监听器
lsnrctl stop LISTENER

当我们不指定监听器的名字时,就默认关闭LISTENER监听器;

查看监听器的状态

1
2
3
4
lsnrctl status <监听器的名字>

// 查看名为LISTENER的监听器的运行状态
lsnrctl status LISTENER

当我们不指定监听器的名字时,就默认查看LISTENER监听器的状态。

测试连通性
可以使用工具TNSPING

参考

  • Oracle学习笔记——配置监听

Oracle学习笔记——Linux设置Oracle环境变量

发表于 2017-09-25 | 分类于 数据库 | 评论数:

有三种设置方法,具体如下:

方法一:直接运行export命令定义变量,该变量只在当前的shell(BASH)或其子shell(BASH)下是有效的,shell关闭了,变量也就失效了,再打开新shell时就没有这个变量,需要使用的话还需要重新定义。

以设置oracle环境变量为例

1
2
3
4
export ORACLE_BASE=/data/app/oracle; 
export ORACLE_HOME=$ORACLE_BASE/product/12.1.0/dbhome_1;
export ORACLE_SID=orcl;
export PATH=$ORACLE_HOME/bin:$PATH;

方法二:在用户目录下的.bash_profile文件中增加变量,改变量仅会对当前用户有效,并且是“永久的”。

1
2
3
4
ORACLE_BASE=/data/app/oracle;                          export ORACLE_BASE
ORACLE_HOME=$ORACLE_BASE/product/12.1.0/dbhome_1; export ORACLE_HOME
ORACLE_SID=orcl; export ORACLE_SID
PATH=$ORACLE_HOME/bin:$PATH; export PATH

方法三: 在/etc/profile文件中添加变量,该变量将会对Linux下所有用户有效,并且是“永久的”。

1
2
3
4
ORACLE_BASE=/data/app/oracle;                        export ORACLE_BASE
ORACLE_HOME=$ORACLE_BASE/product/12.1.0/dbhome_1; export ORACLE_HOME
ORACLE_SID=orcl; export ORACLE_SID
PATH=$ORACLE_HOME/bin:$PATH; export PATH

Linux环境变量总结

发表于 2017-09-25 | 分类于 理解计算器机 | 评论数:

Linux是一个多用户多任务的操作系统,可以==在Linux中为不同的用户设置不同的运行环境==,具体做法是设置不同用户的环境变量。

Linux环境变量分类

一、按照生命周期来分,Linux环境变量可以分为两类:
1、永久的:需要用户修改相关的配置文件,变量永久生效。
2、临时的:用户利用export命令,在当前终端下声明环境变量,关闭Shell终端失效。

二、按照作用域来分,Linux环境变量可以分为:
1、系统环境变量:系统环境变量对该系统中所有用户都有效。
2、用户环境变量:顾名思义,这种类型的环境变量只对特定的用户有效。

Linux设置环境变量的方法

一、在/etc/profile文件中添加变量 对所有用户生效(永久的)
用vim在文件/etc/profile文件中增加变量,该变量将会对Linux下所有用户有效,并且是“永久的”。
例如:编辑/etc/profile文件,添加CLASSPATH变量

1
2
vim /etc/profile    
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib

注:修改文件后要想马上生效还要运行source /etc/profile不然只能在下次重进此用户时生效。

二、在用户目录下的.bash_profile文件中增加变量 【对单一用户生效(永久的)】
用vim ~/.bash_profile文件中增加变量,改变量仅会对当前用户有效,并且是“永久的”。

1
2
vim ~/.bash.profile
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib

注:修改文件后要想马上生效还要运行$ source ~/.bash_profile不然只能在下次重进此用户时生效。

三、直接运行export命令定义变量 【只对当前shell(BASH)有效(临时的)】
在shell的命令行下直接使用export 变量名=变量值
定义变量,该变量只在当前的shell(BASH)或其子shell(BASH)下是有效的,shell关闭了,变量也就失效了,再打开新shell时就没有这个变量,需要使用的话还需要重新定义。

Linux环境变量使用

一、Linux中常见的环境变量有:

PATH:指定命令的搜索路径

1
2
3
4
5
PATH声明用法:
PATH=$PAHT:<PATH 1>:<PATH 2>:<PATH 3>:--------:< PATH n >
export PATH
你可以自己加上指定的路径,中间用冒号隔开。环境变量更改后,在用户下次登陆时生效。
可以利用echo $PATH查看当前当前系统PATH路径。

HOME:指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)。
HISTSIZE:指保存历史命令记录的条数。
LOGNAME:指当前用户的登录名。
HOSTNAME:指主机的名称,许多应用程序如果要用到主机名的话,通常是从这个环境变量中来取得的
SHELL:指当前用户用的是哪种Shell。
LANG/LANGUGE:和语言相关的环境变量,使用多种语言的用户可以修改此环境变量。
MAIL:指当前用户的邮件存放目录。

注意:上述变量的名字并不固定,如HOSTNAME在某些Linux系统中可能设置成HOST

二、Linux也提供了修改和查看环境变量的命令,下面通过几个实例来说明:

echo 显示某个环境变量值 echo $PATH
export 设置一个新的环境变量 export HELLO=”hello” (可以无引号)
env 显示所有环境变量
set 显示本地定义的shell变量
unset 清除环境变量 unset HELLO
readonly 设置只读环境变量 readonly HELLO

Oracle学习笔记——在centos6.5上安装oracle11g

发表于 2017-09-21 | 评论数:

操作系统:CentOS6.5(64位)
数据库:Oracle 11g Release2 (64-bit)

第一步:配置yum以下载oracle的依赖

1
2
3
4
# cd /etc/yum.repos.d
# wget https://public-yum.oracle.com/public-yum-ol6.repo
If case you received Certificate error then, use following command
# wget https://public-yum.oracle.com/public-yum-ol6.repo –no-check-certificate

第二步:导入GPG Keys

1
2
3
# wget https://public-yum.oracle.com/RPM-GPG-KEY-oracle-ol6 -O /etc/pki/rpm-gpg/RPM-GPG-KEY-oracle
If case you received Certificate error, then use following command
# wget https://public-yum.oracle.com/RPM-GPG-KEY-oracle-ol6 -O /etc/pki/rpm-gpg/RPM-GPG-KEY-oracle –no-check-certificate

第三步:安装oracle的依赖

1
# yum install oracle-rdbms-server-11gR2-preinstall

第四步:设置服务器hsotname

# vi /etc/hosts

# vi /etc/sysconfig/network

第五步:在服务器创建oracle账户

在终端里执行:

1
2
3
4
5
6
7
# passwd oracle
Change password for user oracle.
New password:
BAD PASSWORD:it is based on a dictinory word
BAD PASSWORD:is too simple
Retype new password:
passwd:all authentication tokens updated sucessfully.

第六步:配置文件“90-nproc.conf”

# vi /etc/security/limits.d/90-nproc.conf

第七步:配置selinux参数为“permissive”

# vi /etc/selinux/config

重启服务器,然后使用『oracle』账户登录。使用其他账户登录的使用su - oracle切换到oracle账户下。

第八步:在 “.bash_profile” 中添加oracle的相关配置

[oracle@oracle~]# vi .bash_profile

1
2
3
4
ORACLE_BASE=/home/oracle/app/oracle;                 export ORACLE_BASE
ORACLE_HOME=$ORACLE_BASE/product/11.2.0/dbhome_1; export ORACLE_HOME
ORACLE_SID=orcl; export ORACLE_SID
PATH=$ORACLE_HOME/bin:$PATH; export PATH

其中ORACLE_BASE、ORACLE_HOME一定要和oracle的安装目录一致,不一致需要后续做修改。

[oracle@oracle~]# source .bash_profile #修改的文件立即生效

第九步:在服务器的虚拟控制台调用图形界面安装

转到存放oracle压缩文件的目录,执行unzip命令来解压缩文件

1
2
[oracle@oracle~]# unzip /media/linux.x64_11gR2_database_1of2.zip 
[oracle@oracle~]# unzip /media/linux.x64_11gR2_database_2of2.zip

文件会解压到一个database的目录。

执行./runInstaller就可以调出图形化界面了。(==注意:这个需要在服务器上的终端来执行,SSH连接到服务器运行该命令是不行的,需要通过xhost单独设置。==)

在执行上边的命令后可能会遇到乱码,执行以下命令再重新运行./runInstaller

[oracle@oracle~]# export LANG=en_US.utf-8

可能会遇到网络不通的情况,应该是hostname没有设置正确。需要在oracle账户下执行:

1
2
3
[oracle@oracle~]# hostname oracle.xdf.cn
[oracle@oracle~]# hostname
oracle.xdf.cn #有这个信息出来就表示设置好了

输入接受一些安全问题的邮件地址(什么也不输入)

选择安装数据库软件并创建数据,也可以选择第二项仅安装数据库软件

选择服务器版本

单实例数据库

高级模式安装

添加支持的语言(可以不添加,只支持英文也行)


选择要安装的版本

安装数据库目录

Oracle Base: /hywl/oracle/app
Sofeware Location: /hywl/oracle/app/product/11.1.1/db_1
这个根据自己服务器的账户名不同有所差别

数据库用途

数据库名及服务名(可以修改为自己想要)

开启自动内存管理

设置默认编码为UTF8(如果未添加中文支持,无这步操作)

启用Oracle企业管理控制台OEM

选择数据库文件目录

自动备份设置:不启用

设置数据库用户名和密码

授权的组

检查Oracle一些安装要求,包都已经安装,(直接忽略,因为yum安装过依赖了)

开始安装

安装即将完成


解锁用户SCOTT, SCOTT的默认口令为tiger(这步也可以不设置)

弹出“执行配置脚本”对对话框

安装完成了,系统提示需要用root账户执行两个脚本(orainstRoot.sh,root.sh)系统才算完全安装完成

1
2
3
[root@oracle~]$ /hywl/oracle/app/oraInventory/orainstRoot.sh

[root@oracle~]$ /hywl/oracle/app/product/11.2.0/dbhome_1/root.sh

==很重要的一点,一定要记得在防火墙上开启1521端口,不然客户端是连接不上的,提示no listner。==

1
2
3
$ su - root
# /sbin/iptables -I INPUT -p tcp --dport 1251 -j ACCEPT
# /etc/rc.d/init.d/iptables save

参考

  • INSTALLATION OF ORACLE 11g Release 2 on CentOS 6.5
  • CentOS 6.5_x64安装Oracle 11g R2

IDEA在tomcat中的热部署

发表于 2017-09-18 | 分类于 利器 | 评论数:

idea在tomcat中的热部署

如果每次jsp页面或后台代码有修改,都得重启,确实比较麻烦,现在找到一种利用jrebel这个插件来实现热不熟。

在idea的插件库中找到jrebel,如下图

因为这个插件是需要收费,这里有办法可以免费获取激活码:

方法1:JRebel有一个免费获得激活码的方法,登录https://my.jrebel.com这个网站(FQ),然后用Twitter或者Facebook账号登录这个网站,就能获得免费的激活码。

在IDEA里面Help->JRebel->Activate,复制粘贴激活码就行了。

方法2:
1.激活时填写的 http://idea.jrebel.ml/ilanyu 及 lanyu19950316@gmail.com,授权服务器地址格式为:http://idea.jrebel.ml/{用户名},邮箱随意填写,idea.jrebel.ml 可能随时会被封,可使用反向代理工具代理 http://idea.lanyus.com/,即可稳定使用授权服务器。

2.该激活方法可离线使用6个月,可根据需要点击 Renew offline seat 即可更新离线过期时间。

MySQL存储引擎

发表于 2017-07-21 | 分类于 数据库 | 评论数:

对于初学者来说我们通常不关注存储引擎,但是 MySQL 提供了多个存储引擎,包括处理事物安全表的引擎和处理非食物安全表的引擎。在 MySQL 中,不需要在整个服务器中使用同一种存储引擎,针对具体的要求,可以对每一个表使用不同的存储引擎。

存储引擎简介

MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,你能够获得额外的速度或者功能,从而改善你的应用的整体功能。 存储引擎说白了就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。

例如,如果你在研究大量的临时数据,你也许需要使用内存存储引擎。内存存储引擎能够在内存中存储所有的表格数据。又或者,你也许需要一个支持事务处理的数据库(以确保事务处理不成功时数据的回退能力)。

InnoDB

InnoDB是一个健壮的事务型存储引擎,这种存储引擎已经被很多互联网公司使用,为用户操作非常大的数据存储提供了一个强大的解决方案。我的电脑上安装的 MySQL 5.6.13 版,InnoDB就是作为默认的存储引擎。InnoDB还引入了行级锁定和外键约束,在以下场合下,使用InnoDB是最理想的选择:

  • 更新密集的表。InnoDB存储引擎特别适合处理多重并发的更新请求。
  • 事务。InnoDB存储引擎是支持事务的标准MySQL存储引擎。
  • 自动灾难恢复。与其它存储引擎不同,InnoDB表能够自动从灾难中恢复。
  • 外键约束。MySQL支持外键的存储引擎只有InnoDB。
  • 支持自动增加列AUTO_INCREMENT属性。
  • 从5.7开始innodb存储引擎成为默认的存储引擎。

一般来说,如果需要事务支持,并且有较高的并发读取频率,InnoDB是不错的选择。

MyISAM

MyISAM表是独立于操作系统的,这说明可以轻松地将其从Windows服务器移植到Linux服务器;每当我们建立一个MyISAM引擎的表时,就会在本地磁盘上建立三个文件,文件名就是表名。例如,我建立了一个MyISAM引擎的tb_Demo表,那么就会生成以下三个文件:

  • tb_demo.frm,存储表定义。
  • tb_demo.MYD,存储数据。
  • tb_demo.MYI,存储索引。

MyISAM表无法处理事务,这就意味着有事务处理需求的表,不能使用MyISAM存储引擎。MyISAM存储引擎特别适合在以下几种情况下使用:

  1. 选择密集型的表。MyISAM存储引擎在筛选大量数据时非常迅速,这是它最突出的优点。
  2. 插入密集型的表。MyISAM的并发插入特性允许同时选择和插入数据。例如:MyISAM存储引擎很适合管理邮件或Web服务器日志数据。

MRG_MYISAM

MRG_MyISAM存储引擎是一组MyISAM表的组合,老版本叫 MERGE 其实是一回事儿,这些MyISAM表结构必须完全相同,尽管其使用不如其它引擎突出,但是在某些情况下非常有用。说白了,Merge表就是几个相同MyISAM表的聚合器;Merge表中并没有数据,对Merge类型的表可以进行查询、更新、删除操作,这些操作实际上是对内部的MyISAM表进行操作。

Merge存储引擎的使用场景。对于服务器日志这种信息,一般常用的存储策略是将数据分成很多表,每个名称与特定的时间端相关。例如:可以用12个相同的表来存储服务器日志数据,每个表用对应各个月份的名字来命名。当有必要基于所有12个日志表的数据来生成报表,这意味着需要编写并更新多表查询,以反映这些表中的信息。与其编写这些可能出现错误的查询,不如将这些表合并起来使用一条查询,之后再删除Merge表,而不影响原来的数据,删除Merge表只是删除Merge表的定义,对内部的表没有任何影响。

  • ENGINE=MERGE,指明使用MERGE引擎,其实是跟MRG_MyISAM一回事儿,也是对的,在MySQL 5.7已经看不到MERGE了。
  • UNION=(t1, t2),指明了MERGE表中挂接了些哪表,可以通过alter table的方式修改UNION的值,以实现增删MERGE表子表的功能。比如:
1
alter table tb_merge engine=merge union(tb_log1) insert_method=last;
  • INSERT_METHOD=LAST,INSERT_METHOD指明插入方式,取值可以是:0 不允许插入;FIRST 插入到UNION中的第一个表; LAST 插入到UNION中的最后一个表。
  • MERGE表及构成MERGE数据表结构的各成员数据表必须具有完全一样的结构。每一个成员数据表的数据列必须按照同样的顺序定义同样的名字和类型,索引也必须按照同样的顺序和同样的方式定义。

MEMORY

使用MySQL Memory存储引擎的出发点是速度。为得到最快的响应时间,采用的逻辑存储介质是系统内存。虽然在内存中存储表数据确实会提供很高的性能,但当mysqld守护进程崩溃时,所有的Memory数据都会丢失。获得速度的同时也带来了一些缺陷。它要求存储在Memory数据表里的数据使用的是长度不变的格式,这意味着不能使用BLOB和TEXT这样的长度可变的数据类型,VARCHAR是一种长度可变的类型,但因为它在MySQL内部当做长度固定不变的CHAR类型,所以可以使用。

一般在以下几种情况下使用Memory存储引擎:

  • 目标数据较小,而且被非常频繁地访问。在内存中存放数据,所以会造成内存的使用,可以通过参数max_heap_table_size控制Memory表的大小,设置此参数,就可以限制Memory表的最大大小。
  • 如果数据是临时的,而且要求必须立即可用,那么就可以存放在内存表中。
  • 存储在Memory表中的数据如果突然丢失,不会对应用服务产生实质的负面影响。
  • Memory同时支持散列索引和B树索引。B树索引的优于散列索引的是,可以使用部分查询和通配查询,也可以使用<、>和>=等操作符方便数据挖掘。散列索引进行“相等比较”非常快,但是对“范围比较”的速度就慢多了,因此散列索引值适合使用在=和<>的操作符中,不适合在<或>操作符中,也同样不适合用在order by子句中。

CSV

CSV 存储引擎是基于 CSV 格式文件存储数据。

  • CSV 存储引擎因为自身文件格式的原因,所有列必须强制指定 NOT NULL 。
  • CSV 引擎也不支持索引,不支持分区。
  • CSV 存储引擎也会包含一个存储表结构的 .frm 文件,还会创建一个 .csv 存储数据的文件,还会创建一个同名的元信息文件,该文件的扩展名为 .CSM ,用来保存表的状态及表中保存的数据量。
  • 每个数据行占用一个文本行。

因为 csv 文件本身就可以被Office等软件直接编辑,保不齐就有不按规则出牌的情况,如果出现csv 文件中的内容损坏了的情况,也可以使用 CHECK TABLE 或者 REPAIR TABLE 命令检查和修复

ARCHIVE

Archive是归档的意思,在归档之后很多的高级功能就不再支持了,仅仅支持最基本的插入和查询两种功能。在MySQL 5.5版以前,Archive是不支持索引,但是在MySQL 5.5以后的版本中就开始支持索引了。Archive拥有很好的压缩机制,它使用zlib压缩库,在记录被请求时会实时压缩,所以它经常被用来当做仓库使用。

BLACKHOLE

黑洞存储引擎,所有插入的数据并不会保存,BLACKHOLE 引擎表永远保持为空,写入的任何数据都会消失,

PERFORMANCE_SCHEMA

主要用于收集数据库服务器性能参数。MySQL用户是不能创建存储引擎为PERFORMANCE_SCHEMA的表,一般用于记录binlog做复制的中继。在这里有官方的一些介绍MySQL Performance Schema

FEDERATED

主要用于访问其它远程MySQL服务器一个代理,它通过创建一个到远程MySQL服务器的客户端连接,并将查询传输到远程服务器执行,而后完成数据存取;在MariaDB的上实现是FederatedX

其他

这里列举一些其它数据库提供的存储引擎,OQGraph、SphinxSE、TokuDB、Cassandra、CONNECT、SQUENCE。提供的名字仅供参考。

常用引擎对比

不同存储引起都有各自的特点,为适应不同的需求,需要选择不同的存储引擎,所以首先考虑这些存储引擎各自的功能和兼容。

特性 InnoDB MyISAM MEMORY ARCHIVE
存储限制(Storage limits) 64TB No YES No
支持事物(Transactions) Yes No No No
锁机制(Locking granularity) 行锁 表锁 表锁 行锁
B树索引(B-tree indexes) Yes Yes Yes No
T树索引(T-tree indexes) No No No No
哈希索引(Hash indexes) Yes No Yes No
全文索引(Full-text indexes) Yes Yes No No
集群索引(Clustered indexes) Yes No No No
数据缓存(Data caches) Yes No N/A No
索引缓存(Index caches) Yes Yes N/A No
数据可压缩(Compressed data) Yes Yes No Yes
加密传输(Encrypted data[1]) Yes Yes Yes Yes
集群数据库支持(Cluster databases support) No No No No
复制支持(Replication support[2]) Yes No No Yes
外键支持(Foreign key support) Yes No No No
存储空间消耗(Storage Cost) 高 低 N/A 非常低
内存消耗(Memory Cost) 高 低 N/A 低
数据字典更新(Update statistics for data dictionary) Yes Yes Yes Yes
备份/时间点恢复(backup/point-in-time recovery[3]) Yes Yes Yes Yes
多版本并发控制(Multi-Version Concurrency Control/MVCC) Yes No No No
批量数据写入效率(Bulk insert speed) 慢 快 快 非常快
地理信息数据类型(Geospatial datatype support) Yes Yes No Yes
地理信息索引(Geospatial indexing support[4]) Yes Yes No Yes
  1. 在服务器中实现(通过加密功能)。在其他表空间加密数据在MySQL 5.7或更高版本兼容。
  2. 在服务中实现的,而不是在存储引擎中实现的。
  3. 在服务中实现的,而不是在存储引擎中实现的。
  4. 地理位置索引,InnoDB支持可mysql5.7.5或更高版本兼容

查看存储引擎

使用“SHOW VARIABLES LIKE ‘%storage_engine%’;” 命令在mysql系统变量搜索磨人设置的存储引擎,输入语句如下:

1
2
3
4
5
6
7
8
9
10
11
mysql> SHOW VARIABLES LIKE '%storage_engine%';
+----------------------------------+---------+
| Variable_name | Value |
|----------------------------------+---------|
| default_storage_engine | InnoDB |
| default_tmp_storage_engine | InnoDB |
| disabled_storage_engines | |
| internal_tmp_disk_storage_engine | InnoDB |
+----------------------------------+---------+
4 rows in set
Time: 0.005s

使用“SHOW ENGINES;”命令显示安装以后可用的所有的支持的存储引擎和默认引擎,后面带上 \G 可以列表输出结果,你可以尝试一下如“SHOW ENGINES\G;”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> SHOW ENGINES;
+--------------------+---------+--------------------------------------+-------------+--------+-----------+
| Engine | Support | Comment | Transactions| XA | Savepoints|
|--------------------+---------+--------------------------------------+-------------+--------+-----------|
| InnoDB | DEFAULT | Supports transactions, | YES | YES | YES |
| | | row-level locking, and foreign keys | | | |
| MRG_MYISAM | YES | Collection of identical MyISAM tables| NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful | NO | NO | NO |
| | | for temporary tables | | | |
| BLACKHOLE | YES | /dev/null storage engine (anything | NO | NO | NO |
| | | you write to it disappears) | | | |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| FEDERATED | NO | Federated MySQL storage engine | <null> | <null> | <null> |
+--------------------+---------+--------------------------------------+-------------+--------+-----------+

由上面命令输出,可见当前系统的默认数据表类型是InnoDB。当然,我们可以通过修改数据库配置文件中的选项,设定默认表类型。

设置存储引擎

对上面数据库存储引擎有所了解之后,你可以在my.cnf 配置文件中设置你需要的存储引擎,这个参数放在 [mysqld] 这个字段下面的 default_storage_engine 参数值,例如下面配置的片段

1
2
[mysqld]
default_storage_engine=CSV

在创建表的时候,对表设置存储引擎,例如:

1
2
3
4
5
6
CREATE TABLE `user` (
`id` int(100) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL DEFAULT '' COMMENT '姓名',
`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机',
PRIMARY KEY (`id`)
)ENGINE=InnoDB;

在创建用户表 user 的时候,SQL语句最后 ENGINE=InnoDB 就是设置这张表存储引擎为 InnoDB。

如何选择合适的存储引擎

提供几个选择标准,然后按照标准,选择对应的存储引擎即可,也可以根据常用引擎对比来选择你使用的存储引擎。使用哪种引擎需要根据需求灵活选择,一个数据库中多个表可以使用不同的引擎以满足各种性能和实际需求。使用合适的存储引擎,将会提高整个数据库的性能。

  1. 是否需要支持事务;
  2. 是否需要使用热备;
  3. 崩溃恢复,能否接受崩溃;
  4. 是否需要外键支持;
  5. 存储的限制;
  6. 对索引和缓存的支持;

数据库事务详解

发表于 2017-07-21 | 分类于 数据库 | 评论数:

事务

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在计算机术语中,事务通常就是指数据库事务。

概念

一个数据库事务通常包含对数据库进行读或写的一个操作序列。它的存在包含有以下两个目的:

1、为数据库操作提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
2、当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

当一个事务被提交给了DBMS(数据库管理系统),则DBMS需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态(要么全执行,要么全都不执行);同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。

但在现实情况下,失败的风险很高。在一个数据库事务的执行过程中,有可能会遇上事务操作失败、数据库系统/操作系统失败,甚至是存储介质失败等情况。这便需要DBMS对一个执行失败的事务执行恢复操作,将其数据库状态恢复到一致状态(数据的一致性得到保证的状态)。为了实现将数据库状态恢复到一致状态的功能,DBMS通常需要维护事务日志以追踪事务中所有影响数据库数据的操作。

特性

并非任意的对数据库的操作序列都是数据库事务。事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。

原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。

举例

用一个常用的“A账户向B账号汇钱”的例子来说明如何通过数据库事务保证数据的准确性和完整性。熟悉关系型数据库事务的都知道从帐号A到帐号B需要6个操作:

1、从A账号中把余额读出来(500)。
2、对A账号做减法操作(500-100)。
3、把结果写回A账号中(400)。
4、从B账号中把余额读出来(500)。
5、对B账号做加法操作(500+100)。
6、把结果写回B账号中(600)。

原子性:

保证1-6所有过程要么都执行,要么都不执行。一旦在执行某一步骤的过程中发生问题,就需要执行回滚操作。 假如执行到第五步的时候,B账户突然不可用(比如被注销),那么之前的所有操作都应该回滚到执行事务之前的状态。

为了实现原子性,需要通过日志:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电/系统崩溃/其它的软硬件错误而无法继续,则通过回溯日志,将已经执行成功的操作撤销,从而达到“全部操作失败”的目的。最常见的场景是,数据库系统崩溃后重启,此时数据库处于不一致的状态,必须先执行一个crash recovery的过程:读取日志进行REDO(重演将所有已经执行成功但尚未写入到磁盘的操作,保证持久性),再对所有到崩溃时尚未成功提交的事务进行UNDO(撤销所有执行了一部分但尚未提交的操作,保证原子性)。crash recovery结束后,数据库恢复到一致性状态,可以继续被使用。

一致性

在转账之前,A和B的账户中共有500+500=1000元钱。在转账之后,A和B的账户中共有400+600=1000元。也就是说,数据的状态在执行该事务操作之后从一个状态改变到了另外一个状态。同时一致性还能保证账户余额不会变成负数等。

隔离性

在A向B转账的整个过程中,只要事务还没有提交(commit),查询A账户和B账户的时候,两个账户里面的钱的数量都不会有变化。
如果在A给B转账的同时,有另外一个事务执行了C给B转账的操作,那么当两个事务都结束的时候,B账户里面的钱应该是A转给B的钱加上C转给B的钱再加上自己原有的钱。

原子性并不能完全保证一致性。在多个事务并行进行的情况下,即使保证了每一个事务的原子性,仍然可能导致数据不一致的结果。例如,事务1需要将100元转入帐号A:先读取帐号A的值,然后在这个值上加上100。但是,在这两个操作之间,另一个事务2修改了帐号A的值,为它增加了100元。那么最后的结果应该是A增加了200元。但事实上, 事务1最终完成后,帐号A只增加了100元,因为事务2的修改结果被事务1覆盖掉了。

为了保证并发情况下的一致性,引入了隔离性,即保证每一个事务能够看到的数据总是一致的,就好象其它并发事务并不存在一样。用术语来说,就是多个事务并发执行后的状态,和它们串行执行后的状态是等价的。怎样实现隔离性,已经有很多人回答过了,原则上无非是两种类型的锁:1)悲观锁 2)乐观锁

持久性

一旦转账成功(事务提交),两个账户的里面的钱就会真的发生变化(会把数据写入数据库做持久化保存)!

原子性与隔离性

一致性与原子性是密切相关的,原子性的破坏可能导致数据库的不一致,数据的一致性问题并不都和原子性有关。
比如刚刚的例子,在第五步的时候,对B账户做加法时只加了50元。那么该过程可以符合原子性,但是数据的一致性就出现了问题。

因此,事务的原子性与一致性缺一不可。

参考

  • 彻底理解数据库事务

java HashMap工作原理和实现

发表于 2017-07-20 | 评论数:

1. HashMap的数据结构

JDK1.8之前数据结构是数组和链表来实现对数据的存储,但这两者基本上是两个极端。JDK1.8当链表长度大于阈值(默认为8),将链表转化为红黑树,减少搜索时间。

1.1 数组

数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;

1.2 链表

链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。

1.3 哈希表

那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表。哈希表(Hash table)既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。

哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法—— 拉链法,我们可以理解为“链表的数组” ,如图:

从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

  HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。

  首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

1
2
3
4
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry[] table;

2. HashMap的存取实现

既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现:

1
2
3
4
5
6
7
8
9
// 存储时:
int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值
int index = hash % Entry[].length;
Entry[index] = value;

// 取值时:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index];

2.1 put

疑问:如果两个key通过hash%Entry[].length得到的index相同,会不会有覆盖的危险?
  这里HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方, 第一个键值对A进来,通过计算其key的hash得到的index=0,记做:Entry[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,Entry[0] = B,如果又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。所以疑问不用担心。也就是说数组中存储的是最后插入的元素。到这里为止,HashMap的大致实现,我们应该已经清楚了。

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
public V put(K key, V value) {
if (key == null)
return putForNullKey(value); //null总是放在数组的第一个链表中
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
//遍历链表
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果key在链表中已存在,则替换为新value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}


void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //参数e, 是Entry.next
//如果size超过threshold,则扩充table大小。再散列
if (size++ >= threshold)
resize(2 * table.length);
}

当然HashMap里面也包含一些优化方面的实现,这里也说一下。比如:Entry[]的长度一定后,随着map里面数据的越来越长,这样同一个index的链就会很长,会不会影响性能?HashMap里面设置一个因子,随着map的size越来越大,Entry[]会以一定的规则加长长度。

2.2 get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
//先定位到数组元素,再遍历该元素处的链表
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}

2.3 null key的存取

null key总是存放在Entry[]数组的第一个元素。

保存

1
2
3
4
5
6
7
8
9
10
11
12
13
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}

获取数据

1
2
3
4
5
6
7
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}

2.4 确定数组indexhashcode tablelength取模

HashMap存取时,都需要计算当前key应该对应Entry[]数组哪个元素,即计算数组下标;算法如下:

1
2
3
4
5
6
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}

按位取并,作用上相当于取模mod或者取余%。
这意味着数组下标相同,并不表示hashCode相同。

2.5 table初始大小

1
2
3
4
5
6
7
8
9
10
11
public HashMap(int initialCapacity, float loadFactor) {
.....
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}

注意table初始大小并不是构造函数中的initialCapacity!!

而是 >= initialCapacity的2的n次幂!!!!

3. 解决hash冲突的办法

  1. 开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
  2. 再哈希法
  3. 链地址法
  4. 建立一个公共溢出区

Java中hashmap的解决办法就是采用的链地址法。

4. 再散列rehash过程

当哈希表的容量超过默认容量时,必须调整table的大小。当容量已经达到最大可能值时,那么该方法就将容量调整到Integer.MAX_VALUE返回,这时,需要创建一张新表,将原表的映射到新表中。

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
/**
* Rehashes the contents of this map into a new array with a
* larger capacity. This method is called automatically when the
* number of keys in this map reaches its threshold.
*
* If current capacity is MAXIMUM_CAPACITY, this method does not
* resize the map, but sets threshold to Integer.MAX_VALUE.
* This has the effect of preventing future calls.
*
* @param newCapacity the new capacity, MUST be a power of two;
* must be greater than current capacity unless current
* capacity is MAXIMUM_CAPACITY (in which case value
* is irrelevant).
*/
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
//重新计算index
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}

参考

  • HashMap实现原理分析
  • http://www.cnblogs.com/chenssy/p/3521565.html
  • http://www.importnew.com/10620.html
  • http://yikun.github.io/2015/04/01/Java-HashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/

Servlet的线程安全问题

发表于 2017-07-09 | 评论数:

为了有效利用JVM允许多个线程访问同一个实例的特性,来提高服务器性能。在非分布式系统中,Servlet容器只会维护一个Servlet的实例。

如果 Web 应用中的 Servlet 被标注为分布式的,容器应该为每一个分布式应用程序的 JVM 维护一个 Servlet 实例池。

Servlet容器通过维护一个线程池来处理多个请求,线程池中维护的是一组工作者线程(Worker Thread)。Servlet容器通过一个调度线程(Dispatcher Thread)来调度线程池中的线程。

当客户端的servlet请求到来时,调度线程会从线程池中选出一个工作者线程并将请求传递给该线程,该线程就会执行对应servlet实例的service方法。同样,当客户端发起另一个servlet请求时,调度线程会从线程池中选出另一个线程去执行servlet实例的service方法。Servlet容器并不关心这些线程访问的是同一个servlet还是不同的servlet,当多个线程访问同一个servlet时,该servlet实例的service方法将在多个线性中并发执行。

所以,==Servlet对象是单实例多线程,Servlet不是线程安全的==

为什么不安全?

先看两个定义:
实例变量:实例变量在类中定义。类的每一个实例都拥有自己的实例变量,如果多个线程同时访问该实例的方法,而该方法又使用到实例变量,那么这些线程同时访问的是同一个实例变量,会共享该实例变量。

局部变量:局部变量在方法中定义。每当一个线程访问局部变量所在的方法时,在线程的堆栈中就会创建这个局部变量,线程执行完这个方法时,该局部变量就被销毁。所有多个线程同时访问该方法时,每个线程都有自己的局部变量,不会共享。

看如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyServlet extends HttpServlet{
private static final long serialVersionUID = 1L;

private String userName1 = null;//实例变量

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
userName1 = req.getParameter("userName1");

String userName2 = req.getParameter("userName2");//局部变量

//TODO 其他处理
}
}

userName1则是共享变量,多个线程会同时访问该变量,是线程不安全的。

userName2是局部变量,不管多少个线程同时访问,都是线程安全的。

解决Servlet的线程安全问题

如果不涉及到全局共享变量,就直接使用局部变量

如果使用到全局共享的场景,可以使用加锁的方式.对全局变量的读写操作置于synchronized同步块中,这样不同线程排队依次执行该代码块,从而避免线程不安全情况发生。还可以使用线程安全的数据类型。比如hashtable,blockQueue等

参考

  • Servlet 3.1规范学习笔记(一)——Servlet生命周期和线程安全性问题
  • Servlet的线程安全问题
  • java servlet拾遗(3)-servlet 线程安全问题

Servlet开发实战

发表于 2017-07-09 | 分类于 Java | 评论数:

Web框架是开发者在使用某种语言编写Web应用服务端时关于架构的最佳实践。很多Web框架是从实际的Web项目抽取出来的,仅和Web的请求和响应处理有关,形成一个基础,在开发别的应用项目的时候则可以从这个剥离出来的基础做起,让开发者更关注更具体的业务问题,而不是Web的请求和响应的控制。

框架很多,但套路基本类似,帮你隐藏很多关于 HTTP 协议细节内容,专注功能开发。

但对一个初学者来说,过早的接触框架往往是事倍功半!同样一个问题,换一种框架你可能需要从头开始研究。

下面是针对初学 Java 开发 Web 过程一些个人见解和思路,高手可略过。

然后将下面 XML 内容替换 Tomcat 下的 conf/server.xml 文件:

1
2
3
4
5
6
7
8
9
10
11
<?xml version='1.0' encoding='utf-8'?>
Server"8005"shutdown"SHUTDOWN"
Service"Catalina"
Connector"8080"protocol"HTTP/1.1"connectionTimeout"20000"redirectPort"8443"URIEncoding"UTF-8"/>
Engine"Catalina"defaultHost"localhost"
"localhost"
ContextdocBase"D:\WORKDIR\ServletDemo\webapp"reloadable"true"/>
</
</Engine
</Service
</Server

再次启动 Tomcat 后在浏览器打开 http://localhost:8080/hello 便可看到 Hello World 的输出信息。

了解 Servlet 和 Filter

好了,我已经把环境搭起来了,接下来该干嘛呢?

前面的步骤为的是搭建一个测试的环境,然后让你了解一个最基本的 Java Web 项目的结构。

一个最基本的 Java Web 项目所需的 jar 包只需要一个 servlet-api.jar ,这个 jar 包中的类大部分都是接口,还有一些工具类,共有 2 个包,分别是 javax.servlet 和 javax.servlet.http。我把这个jar包放到了 webapp 目录外的一个独立 packages 文件夹里,这是因为所有的 Servlet 容器都带有这个包,你无需再放到Web项目里,我们放到这里只不过是编译的需要,运行是不需要的。如果你硬是把 servlet-api.jar 放到 webapp/WEB-INF/lib 目录下,那么 Tomcat 启动时还会报一个警告信息。

Java Web 项目还需要一个非常重要的配置文件 web.xml ,在这个项目中已经被我最小化了,只保留有用的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

<servlet>
<servlet-name>hello_world</servlet-name>
<servlet-class>demo.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>hello_world</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

</web-app>

每个 servlet 都必须在 web.xml 中定义并进行 URL 映射配置,早期 Java 开发 Web 在没有框架满天飞的时候,这个文件会定义了大量的 servlet,或者有人为了省事干脆来一个 /servlet/* 来通过类名直接调用 Servlet。

Servlet 规范里还有另外一个非常重要而且非常有用的接口那就是 Filter 过滤器。

下面是一个最简单的 Filter 类以及相应的定义方法:

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
package demo;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class HelloFilter implements Filter {

@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("Filter 初始化");
}

@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
System.out.println("拦截 URI="+request.getRequestURI());
chain.doFilter(req, res);
}

@Override
public void destroy() {
System.out.println("Filter 结束");
}
}

在 web.xml 中的配置必须放在 Servlet 的前面:

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

<filter>
<filter-name>helloFilter</filter-name>
<filter-class>demo.HelloFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>helloFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
<servlet-name>hello_world</servlet-name>
<servlet-class>demo.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>hello_world</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

</web-app>

访问 http://localhost:8080/hello 时看看 Tomcat 控制台有何输出信息。

Servlet 和 HTTP 的对应关系

Servlet 是 J2EE 最重要的一部分,有了 Servlet 你就是 J2EE 了,J2EE 的其他方面的内容择需采用。而 Servlet 规范你需要掌握的就是 servlet 和 filter 这两项技术。绝大多数框架不是基于 servlet 就是基于 filter,如果它要在 Servlet 容器上运行,就永远也脱离不开这个模型。

为什么 Servlet 规范会有两个包,javax.servlet 和 javax.servlet.http ,早先设计该规范的人认为 Servlet 是一种服务模型,不一定是依赖某种网络协议之上,因此就抽象出了一个 javax.servlet ,同时在提供一个基于 HTTP 协议上的接口扩展。但是从实际运行这么多年来看,似乎没有发现有在其他协议上实现的 Servlet 技术。

javax.servlet 和 javax.servlet.http 这两个包总共加起来也不过是三十四个接口和类。你需要通过 J2EE 的 JavaDoc 文档 熟知每个类和接口的具体意思。特别是下面几个接口必须熟知每个方法的意思和用途:

  • HttpServlet
  • ServetConfig
  • ServletContext
  • Filter
  • FilterConfig
  • FilterChain
  • RequestDispatcher
  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • 一些 Listenser 类

再次强调 HttpServletRequest 和 HttpServletResponse 这两个接口更应该是烂熟于心。

如果你从字面上无法理解某个方法的意思,你可以在前面那个项目的基础上做实验看看其输出,再不行你可以到讨论区提问,这样的提问非常明确,很多人都可以帮到你。

为什么我这么强调 HttpServletRequest 和 HttpServletResponse 这两个接口,因为 Web 开发是离不开 HTTP 协议的,而 Servlet 规范其实就是对 HTTP 协议做面向对象的封装,HTTP协议中的请求和响应就是对应了 HttpServletRequest 和 HttpServletResponse 这两个接口。

你可以通过 HttpServletRequest 来获取所有请求相关的信息,包括 URI、Cookie、Header、请求参数等等,别无它路。因此当你使用某个框架时,你想获取HTTP请求的相关信息,只要拿到 HttpServletRequest 实例即可。

而 HttpServletResponse接口是用来生产 HTTP 回应,包含 Cookie、Header 以及回应的内容等等。

再谈谈 Session

HTTP 协议里是没有关于 Session 会话的定义,Session 是各种编程语言根据 HTTP 协议的无状态这种特点而产生的。其实现无非就是服务器端的一个哈希表,哈希表的Key就是传递给浏览器的名为 jsessionid 的 Cookie 值。

当需要将某个值保存到 session 时,容器会执行如下几步:

a. 获取 jsessionid 值,没有的话就生成一个,也就是 request.getSession() 这个方法
b. 拿到的 HttpSession 对象实例就相当于一个哈希表,你可以往哈希表里存放数据(setAttribute)
c. 你也可以通过 getAttribute 来获取某个值

而这个名为 jsessionid 的 Cookie 在浏览器关闭时会自动删除。把 Cookie 的 MaxAge 值设为 -1 就能达到浏览器关闭自动删除的效果。

关于 JSP

任何一个 JSP 页面在执行的时候都会编译成一个 Servlet 类文件,如果是 Tomcat 的话,这些生成的 java 文件会放置在 {TOMCAT}/work 目录下对应项目的子目录中,例如 Tomcat 生成的类文件如下:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.*;

public final class test_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {

private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();

private static java.util.List<String> _jspx_dependants;

private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.tomcat.InstanceManager _jsp_instancemanager;

public java.util.List<String> getDependants() {
return _jspx_dependants;
}

public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}

public void _jspDestroy() {
}

public void _jspService(final HttpServletRequest request, final HttpServletResponse response)
throws java.io.IOException, ServletException {

final PageContext pageContext;
HttpSession session = null;
final ServletContext application;
final ServletConfig config;
JspWriter out = null;
final Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;


try {
response.setContentType("text/html;charset=utf-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;

out.write("\r\n");
out.write("<html>\r\n");
out.write(" <title>Test</title>\r\n");
out.write(" <style>\r\n");
out.write(" </style> \r\n");
out.write(" <body>\r\n");
out.write("<h1>Test Demo (oschina)</h1>\r\n");
out.write("<table cellspacing=\"1\" cellpadding=\"5\">\r\n");

Enumeration Names=request.getHeaderNames();
while(Names.hasMoreElements())
{String name=(String)Names.nextElement();
String value=request.getHeader(name);

out.write("\r\n");
out.write(" <tr>\r\n");
out.write(" <td>");
out.print(name);
out.write("</td>\r\n");
out.write(" <td>");
out.print(value);
out.write("</td>\r\n");
out.write(" \r\n");
out.write(" </tr>\r\n");
out.write(" ");

}

out.write("\r\n");
out.write("</table>\r\n");
out.write(" </body>\r\n");
out.write("</html>");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}

在 servlet 中有一个包 javax.servlet.jsp 是跟 JSP 相关的一些接口规范定义。JSP 比 Servlet 方便的地方在于可直接修改立即生效,不像 Servlet 修改后必须重启容器才能生效。

因此 JSP 适合用来做视图,而 Servlet 则适合做控制层。

总结

罗哩罗嗦一大堆,归纳一下就是下面几点:

熟知 Servlet 规范之前,请不要学习任何框架
使用最简单的工具,不要任何向导和可视化
熟知 HTTP 协议
等你真的掌握了 Servlet 规范再去看框架,便会觉得一些都小菜。总之一点:不要被框架牵着鼻子走,框架是你的工具,它应该听你的!

参考

  • 初学 Java Web 开发,请远离各种框架,从 Servlet 开发

Servlet生命周期和工作原理

发表于 2017-07-09 | 评论数:

名词解释:

  • web.xml = 部署描述符(Deployment Descriptor )
  • 容器 = Servlet Container/Engine

生命周期

主要有四个过程:init->service->doGet/doPost->destory

第一步:加载和实例化

  1. Servlet容器启动时自动装载某些Servlet,实现它只需要在web.XML文件中的<Servlet></Servlet>之间添加如下代码:
    <loadon-startup>1</loadon-startup>
  2. 在Servlet容器启动后,客户首次向Servlet发送请求
  3. Servlet类文件被更新后,重新装载Servlet

Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期内,init()方法只被调用一次。

第二步:初始化

实例化后会立马进行初始化,并传递实现ServletConfig接口的对象。也就是执行init方法。在init()方法中,Servlet可以部署描述符中读取配置参数,或者执行任何其他一次性活动。在Servlet的整个生命周期类,init()方法只被调用一次。

第三步:请求处理

初始化后,Servlet就可以接受请求了。
基本方式是执行Servlet接口中的service方法。并把请求和响应对象作为参数传递。当并行的请求到来时,多个service()方法能够同时运行在独立的线程中。
service()方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet()、doPost()、doPut(),doDelete() 等方法。
当然,API也提供了HttpServlet抽象类,其中有doGet、doPost等特殊方法。
注意:任意的容器按照规范必须实现上述几种方法,所以你的代码写在这几个方法中都可以。

第四步:终止服务

容器会在合适的时候销毁某个Servlet对象,这个策略取决于容器的开发者/商。
在容器关闭的时候Servlet对象一定会被销毁。
当1或2发生时,也就是Servlet对象被销毁时,destroy方法会被调用

PS:Servlet的生命(周期)是由容器管理的,换句话说,Servlet程序员不能用代码控制其生命。

工作原理

首先简单解释一下Servlet接收和响应客户请求的过程,首先客户发送一个请求,Servlet是调用service()方法对请求进行响应的,通过源代码可见,service()方法中对请求的方式进行了匹配,选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没有doGet,doPost等等这些方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。

每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可。

Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest,ServletResponse强转为HttpRequest和HttpResponse。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void service(ServletRequest req,ServletResponse res) 
throws ServletException,IOException
{
HttpRequest request;
HttpResponse response;

try
{
req = (HttpRequest)request;
res = (HttpResponse)response;
}catch(ClassCastException e)
{
throw new ServletException("non-HTTP request response");
}
service(request,response);
}

代码的最后调用了HTTPServlet自己的service(request,response)方法,然后根据请求去调用对应的doXXX方法,因为HttpServlet中的doXXX方法都是返回错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void doGet(HttpServletRequest res,HttpServletResponse resp)
throws ServletException,IOException
{
String protocol = req.getProtocol();
String msg = IStrings.getString("http.method_get_not_supported");
if(protocol.equals("1.1"))
{
resp.sendError(HttpServletResponse.SC.METHOD.NOT.ALLOWED,msg);
}
esle
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST,msg);
}
}

所以需要我们在自定义的Servlet中override这些方法!

Servlet响应请求阶段

对于用户到达Servlet的请求,Servlet容器会创建特定于这个请求的ServletRequest对象和ServletResponse对象,然后调用Servlet的service方法。service方法从ServletRequest对象获得客户请求信息,处理该请求,并通过ServletResponse对象向客户返回响应信息。

对于Tomcat来说,它会将传递过来的参数放在一个Hashtable中,该Hashtable的定义是:

1
private Hashtable<String String[]> paramHashStringArray = new Hashtable<String String[]>();

这是一个String–>String[]的键值映射。

HashMap线程不安全的,Hashtable线程安全。

Servlet终止阶段

当WEB应用被终止,或Servlet容器终止运行,或Servlet容器重新装载Servlet新实例时,Servlet容器会先调用Servlet的destroy()方法,在destroy()方法中可以释放掉Servlet所占用的资源。

Servlet何时被创建

  1. 默认情况下,当WEB客户第一次请求访问某个Servlet的时候,WEB容器将创建这个Servlet的实例。

  2. 当web.xml文件中如果元素中指定了子元素时,Servlet容器在启动web服务器时,将按照顺序创建并初始化Servlet对象。

注意:在web.xml文件中,某些Servlet只有元素,没有元素,这样我们无法通过url的方式访问这些Servlet,这种Servlet通常会在元素中配置一个子元素,让容器在启动的时候自动加载这些Servlet并调用init()方法,完成一些全局性的初始化工作。

Web应用何时被启动

  1. 当Servlet容器启动的时候,所有的Web应用都会被启动
  2. 控制器启动web应用

JSP运行原理

当Web服务器上的JSP页面第一次被请求执行时,JSP引擎先将JSP页面文件转译成一个Java文件,即Servlet,Java Servlet是基于服务器端编程的API,用Java Servlet编写的Java程序称为servlet,servlet通过HTML与客户交互。服务器将前面转译成的Java文件编译成字节码文件,再执行这个字节码文件来响应客户的请求。当这个JSP页面再次被请求时,只要该JSP文件没有被改动,JSP引擎就直接调用已装载的Servlet。

JSP工作原理
所有JSP页面,在执行的时候都会被服务器端的JSP引擎转换为Servelet(.java),然后又由JSP引擎调用Java编译器,将Servelet(.java)编译为Class文件(.class),并由Java虚拟机(JVM)解释执行。下面验证这一点:
有一个JSP页面Test.jsp,在浏览器地址栏中输入http://localhost:8080/Test.jsp ,将会出现执行结果。同时在%CATALINA_HOME%/work/Catalina/localhost下多出两个文件:_Test_jsp.java和_Test_jsp.class,他们分别就是Servelet和Class文件

JSP和servlet的运行原理探讨

<%page language=”java”%>在服务器端执行.
客户端用户填写 HTML 表单,发送请求。将请求发送给 服务器端servlet(tomcat是servlet的容器)。 servlet 将该 HTTP 请求转换成一个 MQSeries 消息,并将其放入一个队列。 后端应用程序处理该消息,然后通过消息队列发回一个应答。 servlet 从队列中检索消息,并将其存放在一个 Java Bean 中。 然后 servlet 调用编译过的 Java Server Page(JSP) 并动态生成结果 HTML 页面。 JSP 从 Java Bean 检索出该页面需要的数据,将其合并到 HTML,然后将结果页面发送给客户端。

JSP中的html代码和javascrīpt代码不会在服务器端执行,servlet 调用编译过的 Java Server Page(JSP) ,也就是运行由JSP编译成的class文件(运行的代码包括actionBean,formBean,一般的bean 和内嵌在JSP的Java代码).

一个Web运行程序(网站)可以有多个servlet,一般认为一个action就是一个servlet.

所谓Servlet是指运行在服务器端的Java小程序。用于响应客户端的请求。在默认情况下,Servlet采用一种无状态的请求-响应处理方式。Servlet代码的主要作用是为了增强Java服务器端的功能,它运行在服务器端,用于接收并且处理浏览器客户端发出的请求,该请求是通过配置文件web.xml的相关配置进行转发。也就是说Servlet是一个标准的Java类,它符合Java类的一般规则。和一般的Java类不同之处只是在于Servlet可以处理Http请求。

  1. servlet是持久的。servlet只需Web服务器加载一次,后续又用到这个servlet,就不需要再加载。(所谓加载是指servlet加载进JVM运行)
  2. servlet是与平台无关的。
  3. servlet是可扩展的。

Servlet与JSP的比较

  • 有许多相似之处,都可以生成动态网页。
  • JSP的优点是擅长于网页制作,生成动态页面比较直观,缺点是不容易跟踪与排错。
  • Servlet是纯Java语言,擅长于处理流程和业务逻辑,缺点是生成动态网页不直观。

参考:

  • Servlet的历史与规范
  • Servlet 3.1规范学习笔记(一)——Servlet生命周期和线程安全性问题
  • Servlet生命周期与工作原理

Servlet容器详解

发表于 2017-07-09 | 评论数:

什么是 Servlet 容器?

容器就是你的程序运行时需要的环境。
Servlet Container(Servlet 容器) 是 Web 服务器或者应用服务器的一部分,用于提供基于请求/响应发送模式的网络服务,解码基于 MIME 的请求,并且格式化基于 MIME 的响应。Servlet 容器同时也包含和管理他们的生命周期里Servlet。

Servlet容器可以嵌入到宿主的 Web 服务器中,或者通过 Web 服务器的本地扩展 API 单独作为附加组件安装。Servelt 容器也可能内嵌或安装到启用 Web 功能的应用服务器中。

所有的 Servlet 容器必须支持 HTTP 协议用于请求和响应,但额外的基于 请求/响应 的协议,如 HTTPS (HTTP over SSL)的支持是可选的。对于 HTTP 规范需要版本,容器必须支持 HTTP/1.0 和 HTTP/1.1。因为容器或许支持 RFC2616 (HTTP/1.1)描述的缓存机制,缓存机制可能在将客户端请求交给 Servlet 处理之前修改它们,也可能在将 Servlet 生成的响应发送给客户端之前修改它们,或者可能根据 RFC2616 规范直接对请求作出响应而不交给 Servlet 进行处理。

Servlet 容器应该使 Servlet 执行在一个安全限制的环境中。在 Java 平台标准版(J2SE, v.1.3 或更高) 或者 Java平台企业版(Java EE, v.1.3 或更高) 的环境下,这些限制应该被放置在 Java 平台定义的安全许可架构中。比如,高端的应用服务器为了保证容器的其他组件不受到负面影响可能会限制 Thread 对象的创建。

Java SE 7 是构建 Servlet 容器最低的 Java平 台版本。

常见的Servlet容器

Tomcat,apache,Jetty是Servlet的运行环境,即一个Servlet容器,做过java web开发的应该都知道。

Servlet的执行流程

  1. Servlet容器的作用是负责处理客户请求,当客户请求来到时,Servlet容器获取请求,然后调用某个Servlet,并把Servlet的执行结果返回给客户
  2. Servlet容器的工作过程是:当客户请求某个资源时,Servlet容器使用ServletRequest对象把客户的请求信息封装起来,然后调用java Servlet API中定义的Servlet的一些生命周期方法,完成Servlet的执行,接着把Servlet执行的要返回给客户的结果封装到 ServletResponse对象中,最后Servlet容器把客户的请求发送给客户,完成为客户的一次服务过程。每一个Servlet的类都执行 init()、service()、destory()三个函数的自动调用,在启动时调用一次init()函数用以进行参数的初始化,在服务期间每当接收到对该Servlet的请求时都会调用Service()函数执行该Servlet的服务操作,当容器销毁时调用一次destory()函数。

Servlet的概述

发表于 2017-07-08 | 评论数:

什么是Servlet?

servlet 是基于 Java 的 Web 组件,由容器(Servlet容器)进行管理,来生成动态内容。像其他基于 Java 的组件技术一样,servlet 也是基于平台无关的 Java 类格式,被编译为平台无关的字节码,可以被基于 Java 技术的 Web 服务器动态加载并运行。容器(Container),有时候也叫做 servlet 引擎,是 Web 服务器为支持 servlet 功能扩展的部分。客户端通过 servlet 容器实现的 request/response paradigm(请求/应答模式) 与 Servlet 进行交互。

Servlet与其他技术的对比

从功能上看,servlet 位于Common Gateway Interface(公共网关接口,简称 CGI)程序和私有的服务器扩展如 Netscape Server API(NSAPI)或 Apache Modules 这两者之间。
相对于其他服务器扩展机制 Servlet 有如下优势:

  • 它们通常比 CGI 脚本更快,因为采用不同的处理模型。
  • 它们采用标准的 API 从而支持更多的Web 服务器。
  • 它们拥有 Java 编程语言的所有优势,包括容易开发和平台无关。
  • 它们可以访问 Java 平台提供的大量的 API。

Servlet的历史

Servlet的由来

  • 背景
    上世纪90年代,随着Internet和浏览器的飞速发展,基于浏览器的B/S模式随之火爆发展起来。
    最初,用户使用浏览器向WEB服务器发送的请求都是请求静态的资源,比如html、css等。
    但是可以想象:根据用户请求的不同动态的处理并返回资源是理所当然必须的要求。
  • CGI (现在几乎不用了)
    必须要满足上述需求,所以CGI(Common Gateway Interface)出现了。CGI程序使用C、Shell Script或Perl编写,CGI是为特定操作系统编写的(如UNIX或Windows),不可移植,CGI程序对每个请求产生新的进程去处理。步骤如下:

    1. WEB服务器接收一个用户请求;
    2. WEB服务器将请求转交给CGI程序处理;
    3. CGI程序将处理结果返回给WEB服务器;
    4. WEB服务器把结果送回用户;
  • Java
    与此同时,Java语言也在迅速发展。必然的,Java要支持上述需求。
    Java有两种方案来实现动态需求,它们都属于JavaEE技术的一部分。

  1. applet (中文意思:小程序)
    这是纯客户端(浏览器)方案(基本被flash、ajax和HTMLL5替代),applet就是浏览器中的Java插件,浏览器通过它就能够解释执行WEB服务器发过来的Java代码,从而实现动态。但是,显然这种方案不好,既需要浏览器必须安装插件,又受限于浏览器,所以Java代码不能太多和太复杂。
    比如,如果安装了JRE,虽然IE浏览器会自动启用Java插件,但是你可以轻易禁止。再比如Chrome还需要你手动去安装插件才行,普通用户连Java是什么都不知道他怎么会去装呢?
    IE如下图:
  2. Servlet
    既然浏览器不方便执行Java代码,那自然还是服务端来执行了,所以Servlet出现了,Servlet就是server端的applet的意思。

Servlet的工作原理概述

其实Servlet的工作原理基本类似上面的CGI,不过Servlet比CGI更好。

  1. WEB服务器接收一个用户请求;

  2. WEB服务器将请求转交给WEB服务器关联的Servlet容器;

  3. Servlet容器找到对应的Servlet并执行这个Servlet;

  4. Servlet容器将处理结果返回给WEB服务器;

  5. WEB服务器把结果送回用户;

Servlet的发展

  1. Servlet诞生后,SUN公司很快发现了Servlet编程非常繁琐,这是因为:

    • Servlet代码中有大量冗余代码,每个Servlet都有一模一样的或基本近似的代码,比如out输出你可能就得写成百遍;
    • 开发Servlet必须精通网页前端和美工,你得非常不直观的在Servlet中写前端代码,这使得实现各种页面效果和风格非常困难。
  2. 所以,SUN借鉴了Microsoft的ASP,正式提出JSP(Servlet1.1),已期望能代替Servlet。但是很快,SUN发现JSP也有问题:

    • 前端开发人员需要看JSP中大量的令他困惑的后端代码;
    • 同样,Servlet开发人员也得在复杂的前端代码中找到其能写Servlet代码的地方;
  3. 所以,Servlet1.2出现了,这个版本的Servlet倡导了MVC思想:

    • JSP(V):将后端代码封装在标签中,使用大量的标签,JSP只用来写前端代码而不要有后台代码;
    • Servlet(C):Servlet完成Controller的功能再加上部分代码逻辑;
    • Model(M):Servlet将数据发送给Model,Model包括部分代码逻辑,最主要的Model也代表着被组织好的用于返回的数据。最终,Model数据会被显示在JSP上(V)。
      基本上到这里Servlet的大方向已经固定了,随之,成熟的发展至今 - 2016年5月26日…

java之NIO编程

发表于 2017-07-04 | 评论数:

是什么?

NIO(Non-blocking I/O,在Java领域,也称为New I/O,从 Java 1.4 开始),是一种==同步非阻塞的I/O模型==,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。

主要包括三个核心组件:
NIO的工具包提出了基于Selector(选择器)、Buffer(缓冲区)、Channel(通道)的新模式;Selector(选择器)、可选择的Channel(通道)和SelectionKey(选择键)配合起来使用,可以实现并发的非阻塞型I/O能力。

NI/O 是一种同步非阻塞的 I/O 模型。同步是指线程不断轮询 I/O 事件是否就绪,非阻塞是指线程在等待 I/O 的时候,可以同时做其他任务。同步的核心就是 Selector,Selector 代替了线程本身轮询 I/O 事件,避免了阻塞同时减少了不必要的线程消耗;非阻塞的核心就是通道和缓冲区,当 I/O 事件就绪时,可以通过写道缓冲区,保证 I/O 的成功,而无需线程阻塞式地等待。

优缺点?

相较之前的io,效率更高,提升了并发量

I/O相比于BI/O,采用一种基于通道和缓存区的I/O方式 ,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆的 DirectByteBuffer 对象作为这块内存的引用进行操作,避免了在 Java 堆和 Native 堆中来回复制数据。

Java NIO 和 IO 的主要区别

下表总结了 Java NIO 和 IO 之间的主要差别,我会更详细地描述表中每部分的差异。

1
2
3
4
IO                NIO
面向流 面向缓冲
阻塞IO 非阻塞IO
无 选择器

面向流I/O的系统,一次处理一个字节的数据。一个输入流会产生一个字节的数据,而一个输出流同样一次消费一个字节的数据。对于流式数据,很容易创建过滤器。可以相对简单地把几个过滤器连接在一起,每个过滤器完成自己的工作,也是按字节进行过滤,精细的处理机制。另一方面,面向流I/O的通信往往比较缓慢。

面向块I/O的系统,以块为单位处理数据。每个操作步骤会生成或消费一个块的数据。以块为单位处理数据,其处理速度远快于以字节流为单位的方式。但是,与面向流I/O的通信相比,面向块I/O的通信缺乏优雅和简洁。

什么时候应该使用java.io?什么时候又该使用java.nio呢?

1、 可扩展性。这可能会促使你选择不同的软件包。Java.net需要每个Socket通信都有一个线程。编码将大为简化。java.nio更富有效率,但相对难以编码。

2、 在处理成千上万的连接时,你可能需要更好的扩展性;但是如果连接数较低时,你可能更注重块I/O的高吞吐率。

3、当使用SSL (Secure Sockets Layer,安全套接字层) 工作时,选择java.nio则实现难度很大。

内部的原理?

假设某银行只有10个职员。该银行的业务流程分为以下4个步骤:

1) 顾客填申请表(5分钟);

2) 职员审核(1分钟);

3) 职员叫保安去金库取钱(3分钟);

4) 职员打印票据,并将钱和票据返回给顾客(1分钟)。

  我们看看银行不同的工作方式对其工作效率到底有何影响。

1 BIO方式
  每来一个顾客,马上由一位职员来接待处理,并且这个职员需要负责以上4个完整流程。当超过10个顾客时,剩余的顾客需要排队等候。

  我们算算这个银行一个小时到底能处理多少顾客?一个职员处理一个顾客需要10分钟(5+1+3+1)时间,一个小时(60分钟)能处理6个顾客,一共10个职员,那就是只能处理60个顾客。

  可以看到银行职员的工作状态并不饱和,比如在第1步,其实是处于等待中。

  这种工作其实就是BIO,每次来一个请求(顾客),就分配到线程池中由一个线程(职员)处理,如果超出了线程池的最大上限(10个),就扔到队列等待 。

2 NIO方式
  如何提高银行的吞吐量呢?

  思路:分而治之,将任务拆分开来,由专门的人负责专门的任务。

  具体来讲,银行专门指派一名职员A,A的工作就是每当有顾客到银行,他就递上表格让顾客填写,每当有顾客填好表后,A就将其随机指派给剩余的9名职员完成后续步骤。

  我们计算下这种工作方式下银行一个小时到底能处理多少顾客?

  假设顾客非常多,职员A的工作处于饱和中,他不断的将填好表的顾客带到柜台处理,柜台一个职员5分钟能处理完一个顾客,一个小时9名职员能处理:9*(60/5)=108。

  可见工作方式的转变能带来效率的极大提升。

这种工作方式其实就NIO的思路。下图是非常经典的NIO说明图,mainReactor线程负责监听server socket,accept新连接,并将建立的socket分派给subReactor;subReactor可以是一个线程,也可以是线程池(一般可以设置为CPU核数),负责多路分离已连接的socket,读写网络数据,这里的读写网络数据可类比顾客填表这一耗时动作,对具体的业务处理功能,其扔给worker线程池完成。

  可以看到典型NIO有三类线程,分别是mainReactor线程、subReactor线程、work线程。不同的线程干专业的事情,最终每个线程都没空着,系统的吞吐量自然就上去了。

具体的用法是什么?

应用场景?

netty

参考资料

  • Java NIO与Java IO的分析比较
  • 一个故事讲清楚NIO
    https://yq.aliyun.com/articles/2371
    http://www.yangyong.me/java-nio%E5%85%A5%E9%97%A8%E4%B8%8E%E8%AF%A6%E8%A7%A3/
    http://www.hollischuang.com/archives/184
    http://wiki.jikexueyuan.com/project/java-nio/nio-io.html

网络协议进阶2——TCP/UDP协议详解

发表于 2017-07-02 | 分类于 理解计算器机 , 网络协议 | 评论数:

分开叙述tcp、ip、http/https等协议

这个协议在java中的体现?

分为应用层、传输层、网络层和实体层

一、TCP/IP协议

TCP/IP协议(传输控制协议)由网络层的IP协议和传输层的TCP协议组成。IP层负责网络主机的定位,数据传输的路由,由IP地址可以唯一的确定Internet上的一台主机。TCP层负责面向应用的可靠的或非可靠的数据传输机制,这是网络编程的主要对象。

二、TCP与UDP

TCP是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。

  UDP是一种面向无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。

TCP与UDP区别:

TCP特点:

  1、TCP是面向连接的协议,通过三次握手建立连接,通讯完成时要拆除连接,由于TCP是面向连接协议,所以只能用于点对点的通讯。而且建立连接也需要消耗时间和开销。

  2、TCP传输数据无大小限制,进行大数据传输。

  3、TCP是一个可靠的协议,它能保证接收方能够完整正确地接收到发送方发送的全部数据。

UDP特点:

  1、UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。

  2、UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。

  3、UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。

TCP与UDP应用:

  1、TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。

  2,UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些

TCP通信需要服务器端侦听listen、接收客户端连接请求accept,等待客户端connect建立连接后才能进行数据包的收发(recv/send)工作。

而UDP则服务器和客户端的概念不明显,服务器端即接收端需要绑定端口,等待客户端的数据的到来。后续便可以进行数据的收发(recvfrom/sendto)工作。

参考

  • HTTP TCP UDP Socket 关系的几个经典图
  • Chapter 7 – ISP Services

网络协议进阶1——HTTP协议入门之HTTP协议的历史演变和设计思路

发表于 2017-07-02 | 分类于 理解计算器机 , 网络协议 | 评论数:

HTTP 协议是互联网的基础协议,也是网页开发的必备知识,最新版本 HTTP/2 更是让它成为技术热点。

本文介绍 HTTP协议的历史演变和设计思路。

一、HTTP/0.9

HTTP 是基于 TCP/IP 协议的应用层协议。它不涉及数据包(packet)传输,主要规定了客户端和服务器之间的通信格式,默认使用80端口。

最早版本是1991年发布的0.9版。该版本极其简单,只有一个命令GET。

1
GET /index.html

上面命令表示,TCP 连接(connection)建立后,客户端向服务器请求(request)网页index.html。

协议规定,服务器只能回应HTML格式的字符串,不能回应别的格式。

1
2
3
<html>
<body>Hello World</body>
</html>

服务器发送完毕,就关闭TCP连接。

二、HTTP/1.0

2.1 简介

1996年5月,HTTP/1.0 版本发布,内容大大增加。

首先,任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。这为互联网的大发展奠定了基础。

其次,除了GET命令,还引入了POST命令和HEAD命令,丰富了浏览器与服务器的互动手段。
再次,HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。

其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。

2.2 请求格式

下面是一个1.0版的HTTP请求的例子。

1
2
3
GET / HTTP/1.0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*

可以看到,这个格式与0.9版有很大变化。
第一行是请求命令,必须在尾部添加协议版本(HTTP/1.0)。后面就是多行头信息,描述客户端的情况。

2.3 回应格式

服务器的回应如下。

1
2
3
4
5
6
7
8
9
10
HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
<body>Hello World</body>
</html>

回应的格式是”头信息 + 一个空行(\r\n) + 数据”。其中,第一行是”协议版本 + 状态码(status code) + 状态描述”。

2.4 Content-Type 字段

关于字符的编码,1.0版规定,头信息必须是 ASCII 码,后面的数据可以是任何格式。因此,服务器回应的时候,必须告诉客户端,数据是什么格式,这就是Content-Type字段的作用。
下面是一些常见的Content-Type字段的值。

1
2
3
4
5
6
7
8
9
10
11
12
* text/plain
* text/html
* text/css
* image/jpeg
* image/png
* image/svg+xml
* audio/mp4
* video/mp4
* application/javascript
* application/pdf
* application/zip
* application/atom+xml

这些数据类型总称为MIME type,每个值包括一级类型和二级类型,之间用斜杠分隔。
除了预定义的类型,厂商也可以自定义类型。

1
application/vnd.debian.binary-package

上面的类型表明,发送的是Debian系统的二进制数据包。
MIME type还可以在尾部使用分号,添加参数。

1
Content-Type: text/html; charset=utf-8

上面的类型表明,发送的是网页,而且编码是UTF-8。
客户端请求的时候,可以使用Accept字段声明自己可以接受哪些数据格式。

1
Accept: */*

上面代码中,客户端声明自己可以接受任何格式的数据。
MIME type不仅用在HTTP协议,还可以用在其他地方,比如HTML网页。

1
2
3
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- 等同于 -->
<meta charset="utf-8" />

2.5 Content-Encoding 字段

由于发送的数据可以是任何格式,因此可以把数据压缩后再发送。Content-Encoding字段说明数据的压缩方法。

1
2
3
Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate

客户端在请求时,用Accept-Encoding字段说明自己可以接受哪些压缩方法。

1
Accept-Encoding: gzip, deflate

2.6 缺点

HTTP/1.0 版的主要缺点是,每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。
TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。所以,HTTP 1.0版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。
为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。

1
Connection: keep-alive

这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。

1
Connection: keep-alive

一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。

三、HTTP/1.1

1997年1月,HTTP/1.1 版本发布,只比 1.0 版本晚了半年。它进一步完善了 HTTP 协议,一直用到了20年后的今天,直到现在还是最流行的版本。

3.1 持久连接

1.1 版的最大变化,就是引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。
客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。

1
Connection: close

目前,对于同一个域名,大多数浏览器允许同时建立6个持久连接。

3.2 管道机制

1.1 版还引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。

举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发出B请求。管道机制则是允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。

3.3 Content-Length 字段

一个TCP连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的。这就是Content-length字段的作用,声明本次回应的数据长度。

1
Content-Length: 3495

上面代码告诉浏览器,本次回应的长度是3495个字节,后面的字节就属于下一个回应了。
在1.0版中,Content-Length字段不是必需的,因为浏览器发现服务器关闭了TCP连接,就表明收到的数据包已经全了。

3.4 分块传输编码

使用Content-Length字段的前提条件是,服务器发送回应之前,必须知道回应的数据长度。

对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。更好的处理方法是,产生一块数据,就发送一块,采用”流模式”(stream)取代”缓存模式”(buffer)。

因此,1.1版规定可以不使用Content-Length字段,而使用”分块传输编码”(chunked transfer encoding)。只要请求或回应的头信息有Transfer-Encoding字段,就表明回应将由数量未定的数据块组成。

1
Transfer-Encoding: chunked

每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度。最后是一个大小为0的块,就表示本次回应的数据发送完了。下面是一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the data in the first chunk

1C
and this is the second one

3
con

8
sequence

0

3.5 其他功能

1.1版还新增了许多动词方法:PUT、PATCH、HEAD、 OPTIONS、DELETE。

另外,客户端请求的头信息新增了Host字段,用来指定服务器的域名。

1
Host: www.example.com

有了Host字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。

3.6 缺点

虽然1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为”队头堵塞“(Head-of-line blocking)。

为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等等。如果HTTP协议设计得更好一些,这些额外的工作是可以避免的。

四、SPDY 协议

2009年,谷歌公开了自行研发的 SPDY 协议,主要解决 HTTP/1.1 效率不高的问题。
这个协议在Chrome浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。

五、HTTP/2

2015年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。

5.1 二进制协议

HTTP/1.1 版的头信息肯定是文本(ASCII编码),数据体可以是文本,也可以是二进制。HTTP/2 则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为”帧”(frame):头信息帧和数据帧。

二进制协议的一个好处是,可以定义额外的帧。HTTP/2 定义了近十种帧,为将来的高级应用打好了基础。如果使用文本实现这种功能,解析数据将会变得非常麻烦,二进制解析则方便得多。

5.2 多工

HTTP/2 复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了”队头堵塞”。

举例来说,在一个TCP连接里面,服务器同时收到了A请求和B请求,于是先回应A请求,结果发现处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。

这样双向的、实时的通信,就叫做多工(Multiplexing)。

5.3 数据流

因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。

HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID一律为奇数,服务器发出的,ID为偶数。

数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。1.1版取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2 可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。

客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。

5.4 头信息压缩

HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如Cookie和User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。

HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

5.5 服务器推送

HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。

常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析HTML源码,发现有静态资源,再发出静态资源请求。其实,服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。

参考

  • HTTP 协议入门
  • http协议和tcp协议的区别是什么

网络协议入门(二)

发表于 2017-07-02 | 分类于 理解计算器机 , 网络协议 | 评论数:

七、一个小结

先对前面的内容,做一个小结。

我们已经知道,网络通信就是交换数据包。电脑A向电脑B发送一个数据包,后者收到了,回复一个数据包,从而实现两台电脑之间的通信。数据包的结构,基本上是下面这样:

发送这个包,需要知道两个地址:

  • 对方的MAC地址
  • 对方的IP地址

有了这两个地址,数据包才能准确送到接收者手中。但是,前面说过,MAC地址有局限性,如果两台电脑不在同一个子网络,就无法知道对方的MAC地址,必须通过网关(gateway)转发。

上图中,1号电脑要向4号电脑发送一个数据包。它先判断4号电脑是否在同一个子网络,结果发现不是(后文介绍判断方法),于是就把这个数据包发到网关A。网关A通过路由协议,发现4号电脑位于子网络B,又把数据包发给网关B,网关B再转发到4号电脑。

1号电脑把数据包发到网关A,必须知道网关A的MAC地址。所以,数据包的目标地址,实际上分成两种情况:

发送数据包之前,电脑必须判断对方是否在同一个子网络,然后选择相应的MAC地址。接下来,我们就来看,实际使用中,这个过程是怎么完成的。

八、用户的上网设置

8.1 静态IP地址

你买了一台新电脑,插上网线,开机,这时电脑能够上网吗?

通常你必须做一些设置。有时,管理员(或者ISP)会告诉你下面四个参数,你把它们填入操作系统,计算机就能连上网了:

  • 本机的IP地址
  • 子网掩码
  • 网关的IP地址
  • DNS的IP地址

下图是Windows系统的设置窗口。

这四个参数缺一不可,后文会解释为什么需要知道它们才能上网。由于它们是给定的,计算机每次开机,都会分到同样的IP地址,所以这种情况被称作”静态IP地址上网”。

但是,这样的设置很专业,普通用户望而生畏,而且如果一台电脑的IP地址保持不变,其他电脑就不能使用这个地址,不够灵活。出于这两个原因,大多数用户使用”动态IP地址上网”。

8.2 动态IP地址

所谓”动态IP地址”,指计算机开机后,会自动分配到一个IP地址,不用人为设定。它使用的协议叫做DHCP协议。

这个协议规定,每一个子网络中,有一台计算机负责管理本网络的所有IP地址,它叫做”DHCP服务器”。新的计算机加入网络,必须向”DHCP服务器”发送一个”DHCP请求”数据包,申请IP地址和相关的网络参数。

前面说过,如果两台计算机在同一个子网络,必须知道对方的MAC地址和IP地址,才能发送数据包。但是,新加入的计算机不知道这两个地址,怎么发送数据包呢?

DHCP协议做了一些巧妙的规定。

8.3 DHCP协议

首先,它是一种应用层协议,建立在UDP协议之上,所以整个数据包是这样的:

(1)最前面的”以太网标头”,设置发出方(本机)的MAC地址和接收方(DHCP服务器)的MAC地址。前者就是本机网卡的MAC地址,后者这时不知道,就填入一个广播地址:FF-FF-FF-FF-FF-FF。

(2)后面的”IP标头”,设置发出方的IP地址和接收方的IP地址。这时,对于这两者,本机都不知道。于是,发出方的IP地址就设为0.0.0.0,接收方的IP地址设为255.255.255.255。

 (3)最后的”UDP标头”,设置发出方的端口和接收方的端口。这一部分是DHCP协议规定好的,发出方是68端口,接收方是67端口。
 
 这个数据包构造完成后,就可以发出了。以太网是广播发送,同一个子网络的每台计算机都收到了这个包。因为接收方的MAC地址是FF-FF-FF-FF-FF-FF,看不出是发给谁的,所以每台收到这个包的计算机,还必须分析这个包的IP地址,才能确定是不是发给自己的。当看到发出方IP地址是0.0.0.0,接收方是255.255.255.255,于是DHCP服务器知道”这个包是发给我的”,而其他计算机就可以丢弃这个包。
 
接下来,DHCP服务器读出这个包的数据内容,分配好IP地址,发送回去一个”DHCP响应”数据包。这个响应包的结构也是类似的,以太网标头的MAC地址是双方的网卡地址,IP标头的IP地址是DHCP服务器的IP地址(发出方)和255.255.255.255(接收方),UDP标头的端口是67(发出方)和68(接收方),分配给请求端的IP地址和本网络的具体参数则包含在Data部分。

新加入的计算机收到这个响应包,于是就知道了自己的IP地址、子网掩码、网关地址、DNS服务器等等参数。

8.4 上网设置:小结

这个部分,需要记住的就是一点:不管是”静态IP地址”还是”动态IP地址”,电脑上网的首要步骤,是确定四个参数。这四个值很重要,值得重复一遍:

  • 本机的IP地址
  • 子网掩码
  • 网关的IP地址
  • DNS的IP地址

有了这几个数值,电脑就可以上网”冲浪”了。接下来,我们来看一个实例,当用户访问网页的时候,互联网协议是怎么运作的。

九、一个实例:访问网页

9.1 本机参数

我们假定,经过上一节的步骤,用户设置好了自己的网络参数:

  • 本机的IP地址:192.168.1.100
  • 子网掩码:255.255.255.0
  • 网关的IP地址:192.168.1.1
  • DNS的IP地址:8.8.8.8

然后他打开浏览器,想要访问Google,在地址栏输入了网址:www.google.com。

这意味着,浏览器要向Google发送一个网页请求的数据包。

9.2 DNS协议

我们知道,发送数据包,必须要知道对方的IP地址。但是,现在,我们只知道网址www.google.com,不知道它的IP地址。

DNS协议可以帮助我们,将这个网址转换成IP地址。已知DNS服务器为8.8.8.8,于是我们向这个地址发送一个DNS数据包(53端口)。

然后,DNS服务器做出响应,告诉我们Google的IP地址是172.194.72.105。于是,我们知道了对方的IP地址。

9.3 子网掩码

接下来,我们要判断,这个IP地址是不是在同一个子网络,这就要用到子网掩码。

已知子网掩码是255.255.255.0,本机用它对自己的IP地址192.168.1.100,做一个二进制的AND运算(两个数位都为1,结果为1,否则为0),计算结果为192.168.1.0;然后对Google的IP地址172.194.72.105也做一个AND运算,计算结果为172.194.72.0。这两个结果不相等,所以结论是,Google与本机不在同一个子网络。

因此,我们要向Google发送数据包,必须通过网关192.168.1.1转发,也就是说,接收方的MAC地址将是网关的MAC地址。

9.4 应用层协议

浏览网页用的是HTTP协议,它的整个数据包构造是这样的:

HTTP部分的内容,类似于下面这样:

```
  GET / HTTP/1.1
  Host: www.google.com
  Connection: keep-alive
  User-Agent: Mozilla/5.0 (Windows NT 6.1) ……
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
  Accept-Encoding: gzip,deflate,sdch
  Accept-Language: zh-CN,zh;q=0.8
  Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
  Cookie: … …

``` 
 
我们假定这个部分的长度为4960字节,它会被嵌在TCP数据包之中。

9.5 TCP协议

TCP数据包需要设置端口,接收方(Google)的HTTP端口默认是80,发送方(本机)的端口是一个随机生成的1024-65535之间的整数,假定为51775。

TCP数据包的标头长度为20字节,加上嵌入HTTP的数据包,总长度变为4980字节。

9.6 IP协议

然后,TCP数据包再嵌入IP数据包。IP数据包需要设置双方的IP地址,这是已知的,发送方是192.168.1.100(本机),接收方是172.194.72.105(Google)。

IP数据包的标头长度为20字节,加上嵌入的TCP数据包,总长度变为5000字节。

9.7 以太网协议

最后,IP数据包嵌入以太网数据包。以太网数据包需要设置双方的MAC地址,发送方为本机的网卡MAC地址,接收方为网关192.168.1.1的MAC地址(通过ARP协议得到)。

以太网数据包的数据部分,最大长度为1500字节,而现在的IP数据包长度为5000字节。因此,IP数据包必须分割成四个包。因为每个包都有自己的IP标头(20字节),所以四个包的IP数据包的长度分别为1500、1500、1500、560。

9.8 服务器端响应

经过多个网关的转发,Google的服务器172.194.72.105,收到了这四个以太网数据包。

根据IP标头的序号,Google将四个包拼起来,取出完整的TCP数据包,然后读出里面的”HTTP请求”,接着做出”HTTP响应”,再用TCP协议发回来。

本机收到HTTP响应以后,就可以将网页显示出来,完成一次网络通信。

这个例子就到此为止,虽然经过了简化,但它大致上反映了互联网协议的整个通信过程。

参考

互联网协议入门(二)

网络协议入门(一)

发表于 2017-07-02 | 分类于 理解计算器机 , 网络协议 | 评论数:

网络协议入门(一)

我们每天使用互联网,你是否想过,它是如何实现的?
全世界几十亿台电脑,连接在一起,两两通信。上海的某一块网卡送出信号,洛杉矶的另一块网卡居然就收到了,两者实际上根本不知道对方的物理位置,你不觉得这是很神奇的事情吗?

互联网的核心是一系列协议,总称为”互联网协议”(Internet Protocol Suite)。它们对电脑如何连接和组网,做出了详尽的规定。理解了这些协议,就理解了互联网的原理。

下面就是我的学习笔记。因为这些协议实在太复杂、太庞大,我想整理一个简洁的框架,帮助自己从总体上把握它们。为了保证简单易懂,我做了大量的简化,有些地方并不全面和精确,但是应该能够说清楚互联网的原理。

一、概述

1.1 五层模型

互联网的实现,分成好几层。每一层都有自己的功能,就像建筑物一样,每一层都靠下一层支持。

用户接触到的,只是最上面的一层,根本没有感觉到下面的层。要理解互联网,必须从最下层开始,自下而上理解每一层的功能。

如何分层有不同的模型,有的模型分七层,有的分四层。我觉得,把互联网分成五层,比较容易解释。

如上图所示,最底下的一层叫做”实体层”(Physical Layer),最上面的一层叫做”应用层”(Application Layer),中间的三层(自下而上)分别是”链接层”(Link Layer)、”网络层”(Network Layer)和”传输层”(Transport Layer)。越下面的层,越靠近硬件;越上面的层,越靠近用户。

它们叫什么名字,其实并不重要。只需要知道,互联网分成若干层就可以了。

1.2 层与协议

每一层都是为了完成一种功能。为了实现这些功能,就需要大家都遵守共同的规则。
大家都遵守的规则,就叫做”协议”(protocol)。

互联网的每一层,都定义了很多协议。这些协议的总称,就叫做”互联网协议”(Internet Protocol Suite)。它们是互联网的核心,下面介绍每一层的功能,主要就是介绍每一层的主要协议。

二、实体层

我们从最底下的一层开始。

电脑要组网,第一件事要干什么?当然是先把电脑连起来,可以用光缆、电缆、双绞线、无线电波等方式。

这就叫做”实体层”,它就是把电脑连接起来的物理手段。它主要规定了网络的一些电气特性,作用是负责传送0和1的电信号。

三、链接层

3.1 定义

单纯的0和1没有任何意义,必须规定解读方式:多少个电信号算一组?每个信号位有何意义?

这就是”链接层”的功能,它在”实体层”的上方,确定了0和1的分组方式。

3.2 以太网协议

早期的时候,每家公司都有自己的电信号分组方式。逐渐地,一种叫做”以太网”(Ethernet)的协议,占据了主导地位。

以太网规定,一组电信号构成一个数据包,叫做”帧”(Frame)。每一帧分成两个部分:标头(Head)和数据(Data)。

“标头”包含数据包的一些说明项,比如发送者、接受者、数据类型等等;”数据”则是数据包的具体内容。

“标头”的长度,固定为18字节。”数据”的长度,最短为46字节,最长为1500字节。因此,整个”帧”最短为64字节,最长为1518字节。如果数据很长,就必须分割成多个帧进行发送。

3.3 MAC地址

上面提到,以太网数据包的”标头”,包含了发送者和接受者的信息。那么,发送者和接受者是如何标识呢?

以太网规定,连入网络的所有设备,都必须具有”网卡”接口。数据包必须是从一块网卡,传送到另一块网卡。网卡的地址,就是数据包的发送地址和接收地址,这叫做MAC地址。

每块网卡出厂的时候,都有一个全世界独一无二的MAC地址,长度是48个二进制位,通常用12个十六进制数表示。

前6个十六进制数是厂商编号,后6个是该厂商的网卡流水号。有了MAC地址,就可以定位网卡和数据包的路径了。

3.4 广播

定义地址只是第一步,后面还有更多的步骤。

首先,一块网卡怎么会知道另一块网卡的MAC地址?

回答是有一种ARP协议,可以解决这个问题。这个留到后面介绍,这里只需要知道,以太网数据包必须知道接收方的MAC地址,然后才能发送。

其次,就算有了MAC地址,系统怎样才能把数据包准确送到接收方?

回答是以太网采用了一种很”原始”的方式,它不是把数据包准确送到接收方,而是向本网络内所有计算机发送,让每台计算机自己判断,是否为接收方。

上图中,1号计算机向2号计算机发送一个数据包,同一个子网络的3号、4号、5号计算机都会收到这个包。它们读取这个包的”标头”,找到接收方的MAC地址,然后与自身的MAC地址相比较,如果两者相同,就接受这个包,做进一步处理,否则就丢弃这个包。这种发送方式就叫做”广播”(broadcasting)。

有了数据包的定义、网卡的MAC地址、广播的发送方式,”链接层”就可以在多台计算机之间传送数据了。

四、网络层

4.1 网络层的由来

以太网协议,依靠MAC地址发送数据。理论上,单单依靠MAC地址,上海的网卡就可以找到洛杉矶的网卡了,技术上是可以实现的。

但是,这样做有一个重大的缺点。以太网采用广播方式发送数据包,所有成员人手一”包”,不仅效率低,而且局限在发送者所在的子网络。也就是说,如果两台计算机不在同一个子网络,广播是传不过去的。这种设计是合理的,否则互联网上每一台计算机都会收到所有包,那会引起灾难。

互联网是无数子网络共同组成的一个巨型网络,很像想象上海和洛杉矶的电脑会在同一个子网络,这几乎是不可能的。

因此,必须找到一种方法,能够区分哪些MAC地址属于同一个子网络,哪些不是。如果是同一个子网络,就采用广播方式发送,否则就采用”路由”方式发送。(”路由”的意思,就是指如何向不同的子网络分发数据包,这是一个很大的主题,本文不涉及。)遗憾的是,MAC地址本身无法做到这一点。它只与厂商有关,与所处网络无关。

这就导致了”网络层”的诞生。它的作用是引进一套新的地址,使得我们能够区分不同的计算机是否属于同一个子网络。这套地址就叫做”网络地址”,简称”网址”。

于是,”网络层”出现以后,每台计算机有了两种地址,一种是MAC地址,另一种是网络地址。两种地址之间没有任何联系,MAC地址是绑定在网卡上的,网络地址则是管理员分配的,它们只是随机组合在一起。

网络地址帮助我们确定计算机所在的子网络,MAC地址则将数据包送到该子网络中的目标网卡。因此,从逻辑上可以推断,必定是先处理网络地址,然后再处理MAC地址。

4.2 IP协议

规定网络地址的协议,叫做IP协议。它所定义的地址,就被称为IP地址。

目前,广泛采用的是IP协议第四版,简称IPv4。这个版本规定,网络地址由32个二进制位组成。

习惯上,我们用分成四段的十进制数表示IP地址,从0.0.0.0一直到255.255.255.255。

互联网上的每一台计算机,都会分配到一个IP地址。这个地址分成两个部分,前一部分代表网络,后一部分代表主机。比如,IP地址172.16.254.1,这是一个32位的地址,假定它的网络部分是前24位(172.16.254),那么主机部分就是后8位(最后的那个1)。处于同一个子网络的电脑,它们IP地址的网络部分必定是相同的,也就是说172.16.254.2应该与172.16.254.1处在同一个子网络。

但是,问题在于单单从IP地址,我们无法判断网络部分。还是以172.16.254.1为例,它的网络部分,到底是前24位,还是前16位,甚至前28位,从IP地址上是看不出来的。

那么,怎样才能从IP地址,判断两台计算机是否属于同一个子网络呢?这就要用到另一个参数”子网掩码”(subnet mask)。

所谓”子网掩码”,就是表示子网络特征的一个参数。它在形式上等同于IP地址,也是一个32位二进制数字,它的网络部分全部为1,主机部分全部为0。比如,IP地址172.16.254.1,如果已知网络部分是前24位,主机部分是后8位,那么子网络掩码就是11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。

知道”子网掩码”,我们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,否则为0),然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否则就不是。

比如,已知IP地址172.16.254.1和172.16.254.233的子网掩码都是255.255.255.0,请问它们是否在同一个子网络?两者与子网掩码分别进行AND运算,结果都是172.16.254.0,因此它们在同一个子网络。

总结一下,IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个是确定哪些地址在同一个子网络。

4.3 IP数据包

根据IP协议发送的数据,就叫做IP数据包。不难想象,其中必定包括IP地址信息。

但是前面说过,以太网数据包只包含MAC地址,并没有IP地址的栏位。那么是否需要修改数据定义,再添加一个栏位呢?

回答是不需要,我们可以把IP数据包直接放进以太网数据包的”数据”部分,因此完全不用修改以太网的规格。这就是互联网分层结构的好处:上层的变动完全不涉及下层的结构。

具体来说,IP数据包也分为”标头”和”数据”两个部分。

“标头”部分主要包括版本、长度、IP地址等信息,”数据”部分则是IP数据包的具体内容。它放进以太网数据包后,以太网数据包就变成了下面这样。

IP数据包的”标头”部分的长度为20到60字节,整个数据包的总长度最大为65,535字节。因此,理论上,一个IP数据包的”数据”部分,最长为65,515字节。前面说过,以太网数据包的”数据”部分,最长只有1500字节。因此,如果IP数据包超过了1500字节,它就需要分割成几个以太网数据包,分开发送了。

4.4 ARP协议

关于”网络层”,还有最后一点需要说明。

因为IP数据包是放在以太网数据包里发送的,所以我们必须同时知道两个地址,一个是对方的MAC地址,另一个是对方的IP地址。通常情况下,对方的IP地址是已知的(后文会解释),但是我们不知道它的MAC地址。

所以,我们需要一种机制,能够从IP地址得到MAC地址。

这里又可以分成两种情况。第一种情况,如果两台主机不在同一个子网络,那么事实上没有办法得到对方的MAC地址,只能把数据包传送到两个子网络连接处的”网关”(gateway),让网关去处理。

第二种情况,如果两台主机在同一个子网络,那么我们可以用ARP协议,得到对方的MAC地址。ARP协议也是发出一个数据包(包含在以太网数据包中),其中包含它所要查询主机的IP地址,在对方的MAC地址这一栏,填的是FF:FF:FF:FF:FF:FF,表示这是一个”广播”地址。它所在子网络的每一台主机,都会收到这个数据包,从中取出IP地址,与自身的IP地址进行比较。如果两者相同,都做出回复,向对方报告自己的MAC地址,否则就丢弃这个包。

总之,有了ARP协议之后,我们就可以得到同一个子网络内的主机MAC地址,可以把数据包发送到任意一台主机之上了。

五、传输层

5.1 传输层的由来

有了MAC地址和IP地址,我们已经可以在互联网上任意两台主机上建立通信。

接下来的问题是,同一台主机上有许多程序都需要用到网络,比如,你一边浏览网页,一边与朋友在线聊天。当一个数据包从互联网上发来的时候,你怎么知道,它是表示网页的内容,还是表示在线聊天的内容?

也就是说,我们还需要一个参数,表示这个数据包到底供哪个程序(进程)使用。这个参数就叫做”端口”(port),它其实是每一个使用网卡的程序的编号。每个数据包都发到主机的特定端口,所以不同的程序就能取到自己所需要的数据。

“端口”是0到65535之间的一个整数,正好16个二进制位。0到1023的端口被系统占用,用户只能选用大于1023的端口。不管是浏览网页还是在线聊天,应用程序会随机选用一个端口,然后与服务器的相应端口联系。

“传输层”的功能,就是建立”端口到端口”的通信。相比之下,”网络层”的功能是建立”主机到主机”的通信。只要确定主机和端口,我们就能实现程序之间的交流。因此,Unix系统就把主机+端口,叫做”套接字”(socket)。有了它,就可以进行网络应用程序开发了。

5.2 UDP协议

现在,我们必须在数据包中加入端口信息,这就需要新的协议。最简单的实现叫做UDP协议,它的格式几乎就是在数据前面,加上端口号。

UDP数据包,也是由”标头”和”数据”两部分组成。

“标头”部分主要定义了发出端口和接收端口,”数据”部分就是具体的内容。然后,把整个UDP数据包放入IP数据包的”数据”部分,而前面说过,IP数据包又是放在以太网数据包之中的,所以整个以太网数据包现在变成了下面这样:

UDP数据包非常简单,”标头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包。

5.3 TCP协议

UDP协议的优点是比较简单,容易实现,但是缺点是可靠性较差,一旦数据包发出,无法知道对方是否收到。

为了解决这个问题,提高网络可靠性,TCP协议就诞生了。这个协议非常复杂,但可以近似认为,它就是有确认机制的UDP协议,每发出一个数据包都要求确认。如果有一个数据包遗失,就收不到确认,发出方就知道有必要重发这个数据包了。

因此,TCP协议能够确保数据不会遗失。它的缺点是过程复杂、实现困难、消耗较多的资源。

TCP数据包和UDP数据包一样,都是内嵌在IP数据包的”数据”部分。TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割。

六、应用层

应用程序收到”传输层”的数据,接下来就要进行解读。由于互联网是开放架构,数据来源五花八门,必须事先规定好格式,否则根本无法解读。

“应用层”的作用,就是规定应用程序的数据格式。

举例来说,TCP协议可以为各种各样的程序传递数据,比如Email、WWW、FTP等等。那么,必须有不同协议规定电子邮件、网页、FTP数据的格式,这些应用程序协议就构成了”应用层”。

这是最高的一层,直接面对用户。它的数据就放在TCP数据包的”数据”部分。因此,现在的以太网的数据包就变成下面这样。

至此,整个互联网的五层结构,自下而上全部讲完了。这是从系统的角度,解释互联网是如何构成的。

参考

互联网协议入门(一)

java反射基础知识

发表于 2017-07-01 | 评论数:

什么是反射?

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制.

白话
简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。

程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

Java反射框架主要提供以下功能:

1.在运行时判断任意一个对象所属的类;
2.在运行时构造任意一个类的对象;
3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
4.在运行时调用任意一个对象的方法

==重点:是运行时而不是编译时==

有什么用处?

==反射最重要的用途就是开发各种通用框架。具有强大的解耦性==

JUnit的@Test和Spring的@Controller和@RequestMapping是很好的例子。

当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。

很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

举一个例子,在运用Struts 2框架的开发中我们一般会在struts.xml里去配置Action,比如:


/shop/shop-index.jsp
login.jsp

配置文件与Action建立了一种映射关系,当View层发出请求时,请求会被StrutsPrepareAndExecuteFilter拦截,然后StrutsPrepareAndExecuteFilter会去动态地创建Action实例。
——比如我们请求login.action,那么StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。
对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。

怎么使用?

JAVA的反射你一定要知道四个类:

Class,Constructor,Field,Method;
Class 代表类的对象
Constructor 代表类的构造器对象
Field 代表了类的成员变量
Method 代表了类的方法对象

获得Class对象

方法有三种
(1)使用Class类的forName静态方法:

1
2
public static Class<?> forName(String className)
Class c1=Class.forName("me.light.Person");

(2)直接获取某一个对象的class,比如:

1
2
3
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;
Class c2=me.light.Person.class;

(3)调用某个对象的getClass()方法,比如:

1
2
3
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();
Class c3=person.getClass();

2、判断是否为某个类的实例

一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断是否为某个类的实例,它是一个Native方法:

public native boolean isInstance(Object obj);

3、创建实例

通过反射来生成对象主要有两种方式。

(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

1
2
Class<?> c = String.class;
Object str = c.newInstance();

(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例

1
2
Object obj = constructor.newInstance("23333");
System.out.println(obj);

4、获取方法

获取某个Class对象的方法集合,主要有以下几个方法:

4.1 getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
public Method[] getDeclaredMethods() throws SecurityException

4.2 getMethods()方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
public Method[] getMethods() throws SecurityException

4.3 getMethod()方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
public Method getMethod(String name, Class<?>... parameterTypes)

通过getMethods()获取的方法可以获取到父类的方法,比如java.lang.Object下定义的各个方法。

5、获取构造器信息
获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

public T newInstance(Object ... initargs)

此方法可以根据传入的参数来调用对应的Constructor创建对象实例~

6、获取类的成员变量(字段)信息
主要是这几个方法,在此不再赘述:
getFiled: 访问公有的成员变量
getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量
getFileds和getDeclaredFields用法同上(参照Method)

7、调用方法
当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为:

1
2
3
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException

下面是一个实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class test1 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> klass = methodClass.class;
//创建methodClass的实例
Object obj = klass.newInstance();
//获取methodClass类的add方法
Method method = klass.getMethod("add",int.class,int.class);
//调用method对应的方法 => add(1,4)
Object result = method.invoke(obj,1,4);
System.out.println(result);
}
}
class methodClass {
public final int fuck = 3;
public int add(int a,int b) {
return a+b;
}
public int sub(int a,int b) {
return a+b;
}
}

关于invoke()方法的详解,后面我会专门写一篇文章来深入解析invoke的过程。

一些注意事项

由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。
另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

参考

  • 深入解析Java反射(1) - 基础
  • 开发笔记之你弄不懂的JAVA反射机制
  • Java 基础与提高干货系列—Java 反射机制 | 掘金技术征文

Mac下shadowsocks全自动地代理翻墙

发表于 2017-05-08 | 分类于 利器 | 评论数:

Shadowsocks服务端

可以搭建自己服务,也可以买现成的服务。我就是在shadowsocks.com买的服务。

测速 speed test

手机上下载 【BestTrace】 app 测试连接翻墙服务节点的路由跳数,并绘制地图:

  1. 路由跳数
  2. 地图:是否有绕路

MAC和windows

官方网站 下载:https://www.ipip.net/download.html

Windows / Mac OS X 客户端
android / iOS 客户端

Shadowsocks客户端配置

shadowsocks on Mac OS X

下载

github release 下载地址:

  • Mac 新版客户端:https://github.com/shadowsocks/ShadowsocksX-NG/releases/
  • Mac 旧版客户端:https://github.com/shadowsocks/shadowsocks-iOS/releases/

MD5 hash

打开 终端 应用,使用 md5 命令校验下载文件的 MD5 哈希值:

1
2
$ md5 Downloads/ShadowsocksX-2.6.3.dmg
MD5 (Downloads/ShadowsocksX-2.6.3.dmg) = c3406e8d4a5009efaa74d3a37b53fed8

ShadowsocksX安装配置

  1. 运行 ShadowsocksX
  2. 配置 代理服务器
  3. 连接 代理服务器
  4. 开启 系统代理
  5. 测试 科学上网
  6. 从 GFWList 更新 PAC 文件

【运行】ShadowsocksX
运行 ShadowsocksX 后,会在 menubar 显示 「纸飞机」 图标
默认图标为 灰色 表示 「系统代理」 未启动

【编辑】代理服务器

点击 「纸飞机」 选择 「服务器」 菜单,然后点击 【打开服务器设定】 选项:

弹出 「服务器设定」 窗口:

【连接】代理服务器

选中 「服务器」 菜单创建的代理服务器配置,出现对号 「√」 表示与代理服务器建立连接
成功与代理服务器建立连接后,便会创建 「SOCKS5 代理」

【开启】系统代理

点击「主菜单」 第二行 【打开 shadowsocks】 启用 「系统代理」:

  1. 第一行:系统代理 运行 状态,显示 「关闭」 状态
  2. 第二行:系统代理 运行 开关,默认 系统代理 没有启动( 图标为 灰色 )

系统代理 启动后 menubar 的 「纸飞机」 图标也变为 黑色:

「系统代理」 与 「SOCKS5 代理」 区别:
系统代理

  • 浏览器的访问请求全部由 shadowsocks 创建的 系统代理 处理
  • 浏览器默认不需要任何设置,也无需安装 代理插件 (Firefox 除外)
  • 如果浏览器安装了代理插件,需要 禁用 代理插件 或把插件设置为 使用系统代理

SOCKS5 代理

  • 若不 【启用系统代理】 shadowsocks 成功连接代理服务器后,仅创建了 「SOCKS5 代理」
  • 浏览器需要安装 代理插件 或设置浏览器的代理配置,才能科学上网

【测试】科学上网

如果 系统代理 运行成功,就可以访问 google 。如果失败,请检查 「服务器配置」 是否正确:

【更新】PAC 文件

最后在主菜单中点击 「从 GFWList 更新 PAC 文件」 更新翻墙列表:

SOCKS5 代理
默认 ShadowsocksX 创建的 「SOCKS5 代理」端口 是 1080 :

1
2
$ grep 127 ~/.ShadowsocksX/gfwlist.js
var proxy = "SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT;";

ShadowsocksX-NG 本地端口更换为 1086 :

1
2
3
4
5
6
7
8
9
10
11
12
$ cat "/Users/yourname/Library/Application Support/ShadowsocksX-NG/ss-local-config.json"a

{
"method" : "rc4-md5",
"server" : "IP",
"password" : "...",
"local_address" : "127.0.0.1",
"server_port" : ...,
"auth" : false,
"timeout" : 60,
"local_port" : 1086
}

可以自行修改 ~/.ShadowsocksX/gfwlist.js 配置文件,自定义 「SOCKS5 代理」端口

shadowsocks on IOS

Potatso

APP Store :https://itunes.apple.com/app/apple-store/id1070901416
https://potatso.com/
https://manual.potatso.com/
https://github.com/shadowsocks/Potatso

Mume VPN

基于 Potatso 开发的 Shadowsocks 协议 VPN 客户端
APP Store :https://itunes.apple.com/cn/app/mume-vpn/id1144787928
https://github.com/liruqi/Mume-iOS
http://vpn.liruqi.info/ios/
http://api.liruqi.info/
http://mume.site/

PAC代理模式

代理模式

翻墙原理就是通过 「SOCKS5 代理」 来访问被和谐的网站

shadowsocks 客户端除了创建 「SOCKS5 代理」 外,还实现了创建 「系统代理」 的功能,通过 「系统代理」 来管理浏览器的访问请求(分流)。「系统代理」 支持 2 种 代理模式:

  1. 自动代理模式 ( 即 PAC 模式 ,默认 )
  2. 全局代理模式

PAC 代理模式

PAC 维基百科:http://zh.wikipedia.org/zh/PAC

默认 shadowsocks 启用 「系统代理」 后使用的是 【PAC 代理模式】

Shadowsocks 成功 连接代理服务器 后,会创建一个 「SOCKS5 代理」

Socks 5 代理 使用的是本机 127.0.0.1:1080 端口

【PAC 代理模式】 当浏览器访问某个网站时,会去匹配 PAC 配置文件 pac.txt 里 URL 列表。如果能匹配到 PAC 文件配置的 URL 就会使用「SOCKS5 代理」访问该网站。否则不使用代理,直接访问网站。既节省 ss 流量,也会提高 国内 网站的访问速度,不然访问国内网站要绕到国外代理再绕回来。

全局代理模式

全局代理模式 所有请求全部走 「SOCKS5 代理」。访问 国内 网站时,将会先绕到 国外 的代理服务器,然后在绕回来,南辕北辙了。当访问的网站没有包含在 PAC 文件的匹配规则列表时,可以 临时 开启全局代理进行访问。

菜单

编辑 PAC 规则

从 GFWList 同步的 PAC 规则后,会在本地生成的 PAC 文件 :

Mac OS X 是在~/.ShadowsocksX/gfwlist.js里

新版本的可以在shadowsocks里直接设置

然后在弹框中输入

里边的文档格式可以参考https://adblockplus.org/en/filter-cheatsheet,两者的写法完全相同,下面是我的用户规则文件user-rule.txt的内容:

1
2
3
4
5
6
7
! Put user rules line by line in this file.
! See https://adblockplus.org/en/filter-cheatsheet

||aws.amazon.com^
||amazonwebservices.com^
||cloudfront.net^
||google.com^

找出非常隐蔽的被墙掉的地址

好了,既然学会了自定义PAC文件,那么,想必应该是配置完毕了吧?当然不是,你有没有遇到过这种情况,明明把指定网址加到PAC里面了,但是打开该网址还是非常缓慢,或者只能看到部分控件,而网页主体始终刷不出来?但是将代理模式切到全局模式,又能正常打开该网页。

这又是为何?

其实,虽然目标网址是走了代理,但是目标网址上的一些资源(可能是某些JavaScript、CSS文件),可能是储存在某些被墙掉的地方,但是又没有添加到PAC文件里,所以使用全局代理模式时能轻松打开,但是使用自动代理模式时却又显示不出来,那么,如何解决呢?

使用日志来查找

从菜单里点击显示日志...,其实就是打开系统应用控制台,系统日志中所有以 ShadowsocksX: 开头的是Shadowsocks的日志,我们再右上角输入ShadowsocksX,即可只显示Shadowsocks的日志。我们在全局模式下,刷新一次在自动代理模式下打不开的网页,然后马上切到控制台查看Shadowsocks的日志,看看是否有遗漏没有添加到PAC文件的网址,如图中的*.cloudfront.net。

我们在用户规则文件user-rule.txt添加一行

1
2
3
4
5
6
! Put user rules line by line in this file.
! See https://adblockplus.org/en/filter-cheatsheet

...

||cloudfront.net^

并执行一遍从GFWList更新PAC文件,即可。

使用浏览器来查找

当然,可以使用浏览器的开发者工具来查看目标网址所需的网络请求,然后将可以的地址加入到PAC文件即可。

ShadowSocks 的 Alfred Workflow来添加规则

如果你是 小帽子 Alfred 的用户,你可以直接使用我写的 ShadowSocks-Workflow(点击下载)。在 Alfred 中执行 ssadd ,然后粘贴你准备加入 gfwlist 的 url,url 的域名就添加进 ShadowScoks 了。

浏览器代理插件

Chrome

Chrome 代理插件:Proxy SwitchyOmega

  1. 如果你不想用全局 PAC 代理,想配合 SwitchySharp 等插件使用,可在菜单栏图标里点关闭 Shadowsocks。关闭后代理仍会运行在 127.0.0.1:1080 上,代理类型为 SOCKS v5。之所以不叫关闭 PAC,因为很多人不懂什么是 PAC。写关闭 Shadowsocks 更容易理解。

  2. 切换服务器后,因为 Chrome 保持长连接,可能需要重启浏览器才能生效。也可以重启 ShadowsocksX 来强制 Chrome 重新连接。

Shadowsocks的PAC模式与全局模式与VPN的区别

VPN,即虚拟专用网络

虚拟专用网络的功能是:在公用网络上建立专用网络,进行加密通讯。在企业网络中有广泛应用。VPN网关通过对数据包的加密和数据包目标地址的转换实现远程访问。VPN有多种分类方式,主要是按协议进行分类。VPN可通过服务器、硬件、软件等多种方式实现。

Shadowsocks,即Sock5代理

采用socks协议的代理服务器就是SOCKS服务器,是一种通用的代理服务器。Socks是个电路级的底层网关,是DavidKoblas在1990年开发的,此后就一直作为Internet RFC标准的开放标准。Socks 不要求应用程序遵循特定的操作系统平台,Socks 代理与应用层代理、 HTTP 层代理不同,Socks 代理只是简单地传递数据包,而不必关心是何种应用协议(比如FTP、HTTP和NNTP请求)。所以,Socks代理比其他应用层代理要快得多。

VPN顾名思义,虚拟专网,你接入VPN就是接入了一个专有网络,那么你访问网络都是从这个专有网络的出口出去,好比你在家,你家路由器后面的网络设备是在同一个网络,而VPN则是让你的设备进入了另一个网络。同时你的IP地址也变成了由VPN分配的一个IP地址。通常是一个私网地址。你和VPN服务器之间的通信是否加密取决于连接VPN的具体方式/协议。

Sock5代理服务器则是把你的网络数据请求通过一条连接你和代理服务器之间的通道,由服务器转发到目的地。你没有加入任何新的网络,只是http/socks数据经过代理服务器的转发送出,并从代理服务器接收回应。你与代理服务器通信过程不会被额外处理,如果你用https,那本身就是加密的。

特点

VPN的开发目的是给企业内网直接传输加密数据,最重要的就是安全性,相反VPN的流量特征变得很明显,特别是SSL VPN类型,比如Openvpn有SSL证书的加密,安全性不必多说,但是握手依然是明文,流量更加明显,导致匹配流量特征很容易,在我这里一旦链接Openvpn那就是秒封。

VPN目前就科学上网方面来讲,PPTP大部分地区已死,L2TP大部分地区已经出现干扰和断开连接情况,Openvpn一封一个准。而anyconnect大多数都是企业用的,所以墙不敢乱封,IKEv1/IKEv2需要注意证书中间人攻击问题。

所以,在VPN科学上网这方面,一些地区已经根据VPN的流量特征做出了相应的匹配策略,可以有效封杀VPN了。

Shadowsocks的开发目的就是穿透防火墙,最重要的是增加墙的匹配流量效率封杀成本和难度,也就是混淆隐秘性。

Shadowsocks是更注重流量混淆隐秘,VPN则是更注重加密安全性。如果你需要安全你可能需要 VPN 或者 Shadowsocks+TOR匿名 ,否则就抗干扰能力来说Shadowsocks更适合拿来科学上网,VPN中的Opnevpn是最安全的VPN协议之一,然而第一个被墙宣布效率检测、封杀!

没有完美的工具,VPN和Shadowsocks在某种程度上可以说是两种相反的技术,开发目的不一样,注重点也不一样,缺点相应的也不一样,所以根据当地运营商的封杀策略选择最适合自己的方式。

通过ProxyChains让Iterm终端上网

如我们之前所说的,浏览器科学上网只是一部分,身为一名工程师或非 Windows 用户,我们经常会使用到一些命令行工具,想让命令行工具科学上网肿么办?

ProxyChains 就是一个这样用途的工具,它可以让你的其它工具通过 Socks 或 HTTP 代理访问网络。

安装

首先从 Homebrew 安装 ProxyChains。

1
brew install proxychains-ng

配置

编辑配置文件 mvim /usr/local/etc/proxychains.conf
在 [ProxyList] 下面(也就是末尾)加入代理类型,代理地址和端口
例如使用 TOR 代理,注释掉原来的代理并添加

1
2
3
4
5
6
7
8
9
strict_chain
proxy_dns
remote_dns_subnet 224
tcp_read_time_out 15000
tcp_connect_time_out 8000
localnet 127.0.0.0/255.0.0.0
quiet_mode
[ProxyList]
socks5 127.0.0.1 1086

需要说明一点,在这里我配置的是 strict_chain。如果你有多重代理,类似我之前一样,比如让命令先通过 Lantern 代理出去,如果失败再走 SS 代理,可以配置成 dynamic_chain 模式。不过这超出今天的文章范畴了,我还是以 strict_chain 举例,如果你对 dynamic_chain 感兴趣,可以自行研究。

每次使用 proxychains4 命令其实有些不爽,太长容易输错。
编辑 ~/.bash_profile 文件,添加下面一行命令为 proxychains4 设置别名为 pc 或者你喜欢的任何命令。重启终端。

1
alias pc="proxychains4 -f ~/.proxychains.conf"

在执行 source ~/.bash_profile 重新加载环境变量之后,就可以尝试是否配置成功了。

1
pc curl https://twitter.com

为git,nodejs,gradle,maven等进行代理服务器的配置

http://www.snowdream.tech/2016/03/31/proxy-settings-with-shadowsocks/#IDEA

参考

  • 打造基于 ShadowSocks + ProxyChains 的全栈式科学上网工具
  • 科学上网漫游指南
  • Shadowsocks for OSX 帮助
  • Shadowsocks(Sock5代理)的PAC模式与全局模式与VPN的区别
  • Shadowsocks-GUI For Mac OS使用指南
  • 用户自定义规则参考之Adblock Plus filters explained

MySQL中text、blob字段类型的不同

发表于 2017-05-08 | 评论数:

在MySQL中有两个字段类型容易让人感觉混淆,那就是TEXT与BLOB,特别是自己写博客程序的博主不知道改为自己的博客正文字段选择TEXT还是BLOB类型。

下面给出几点区别:

一、主要差别

TEXT与BLOB的主要差别就是BLOB保存二进制数据,TEXT保存字符数据。目前几乎所有博客内容里的图片都不是以二进制存储在数据库的,而是把图片上传到服务器然后正文里使用<img>标签引用,这样的博客就可以使用TEXT类型。而BLOB就可以把图片换算成二进制保存到数据库中。

二、类型区别

BLOB有4种类型:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB。它们只是可容纳值的最大长度不同
TEXT也有4种类型:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。这些类型同BLOB类型一样,有相同的最大长度和存储需求。

三、字符集

BLOB列没有字符集,并且排序和比较基于列值字节的数值值。TEXT列有一个字符集,并且根据字符集的校对规则对值进行排序和比较

四、大小写

在TEXT或BLOB列的存储或检索过程中,不存在大小写转换,都一样!

五、严格模式

运行在非严格模式时,如果你为BLOB或TEXT列分配一个超过该列类型的最大长度的值值,值被截取以保证适合。如果截掉的字符不是空格,将会产生一条警告。使用严格SQL模式,会产生错误,并且值将被拒绝而不是截取并给出警告。

六、其它

当保存或检索BLOB和TEXT列的值时不删除尾部空格。
对于BLOB和TEXT列的索引,必须指定索引前缀的长度。
BLOB和TEXT列不能有默认值。
当排序时只使用该列的前max_sort_length个字节。max_sort_length的 默认值是1024.
当你想要使超过max_sort_length的字节有意义,对含长值的BLOB或TEXT列使用GROUP BY或ORDER BY的另一种方式是将列值转换为固定长度的对象。标准方法是使用SUBSTRING函数。
BLOB或TEXT对象的最大大小由其类型确定,但在客户端和服务器之间实际可以传递的最大值由可用内存数量和通信缓存区大小确定。你可以通过更改max_allowed_packet变量的值更改消息缓存区的大小,但必须同时修改服务器和客户端程序。

读《九败一胜》

发表于 2015-02-14 | 分类于 读书笔记 | 评论数:

王兴的什么能力造就了他的成功?

  • 对行业和市场的了解很深,提出三高三低理论
  • 合作十年的核心团队
  • 快速的学习能力
  • 对创业的激情
  • 从别人的创业经验学到很多经验教训(校内借鉴facebook,美团借鉴国外模式)
  • 清华毕业,留美博士
  • 执着,不轻言放弃,是团队的主心骨,为团队抵御了很多风浪
  • 杨俊的加入增加了团队的执行力
  • 王兴的极客范,注重细节,对创业的执着吸引了杨俊的加入
  • 当断则断,面对在千橡多待一年,就可以多拿钱,他选择了时间,而不是金钱
  • 在创业圈积累了个人知名度,对项目启动很有帮助(饭否,美团)
  • 看各种项目,各种网站,了解最新信息
  • 独立思考能力强,逻辑强
  • 长期专注互联网,专注于sns,相信sns可以创造价值
  • 实干,快速执行想法
  • 身边有人,有想法就马上做
  • 战略眼光精准,有远见(来源于获取信息并整理分析的能力)
  • 坚持
  • 有亲和力
  • 有信仰,确定科技改变世界
  • 通过玩游戏,玩linux,学好了英文
  • 强烈的好奇心

美团成功的原因?

  • 不错的核心团队(契约精神),大部分来自清华
  • 战略正确
  • 健康的现金流
  • 利用技术提升效率,降低成本
  • 顺势而为(互联网的大势)
  • 重团队,重人才

王兴简历

2003年中断学业回国创业,耗时2年时间,试错尽10个项目

2005创建校内网,因资金断链

2006年买与陈一舟,拿到创业来第一桶金。

2007年

先后创建饭否(因政府原因多次被关停服务器,最终未发展起来)

海内网(被开心网的偷菜、抢车位干倒)

海内网

2010年

美团网,2014年从把业务从团购转向了更大的市场O2O(猫眼电影,酒店,外卖)

公司文化

保持危机意识(离破产只有6个月)

Light微光

Light微光

关于技术、产品、个人感悟等的记录

43 日志
11 分类
40 标签
RSS
© 2020 Light微光
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Mist v6.4.1