Author: 逸飞

Python 微型ORM Peewee 教程

Python 中最著名的 ORM 自然是 sqlalchemy 了,但是 sqlalchemy 有些年头了,体积庞大,略显笨重。Peewee 还比较年轻,历史包袱比较少,也仅仅支持 Postgres、MySQL、Sqlite 这三种互联网公司最常见的数据库,所以整体上来说是比较轻量的。

peewee 在创建模型的时候就设定了数据库链接,个人感觉这个设计似乎不是很好。。。

from peewee import *

db = SqliteDatabase(None)

class Person(Model):
    name = CharField()
    birthday = DateField()

    class Meta:
        database = db # This model uses the "people.db" database.

class Pet(Model):
    owner = ForeignKeyField(Person, backref='pets')
    name = CharField()
    animal_type = CharField()

    class Meta:
        database = db # this model uses the "people.db" database

连接和创建数据库

db.init(**args)
db.connect()
db.create_table([Person])

存储数据,跟 django 的 ORM 貌似是一样的。

from datetime import date
uncle_bob = Person(name='Bob', birthday=date(1960, 1, 15))
uncle_bob.save() # bob is now stored in the database
grandma = Person.create(name='Grandma', birthday=date(1935, 3, 1))
bob_kitty = Pet.create(owner=uncle_bob, name='Kitty', animal_type='cat')  # 带有外键的宠物

删除数据

herb_mittens.delete_instance() # he had a great life
# Returns: 1

更新数据

herb_fido.owner = uncle_bob
herb_fido.save()

读取数据

基本的语法是 Model.select().where(**coditions).get()

query = Pet.select().where(Pet.animal_type == 'cat')
for pet in query:
    print(pet.name, pet.owner.name)  # 注意这里有 N+1 问题,N 指的是获取 owner.name 

grandma = Person.get(Person.name == 'Grandma L.')

# 可以使用 join 解决 N+1 问题
query = (Pet
         .select(Pet, Person)
         .join(Person)
         .where(Pet.animal_type == 'cat'))
         .order_by(Pet.name)  # 或者 Pet.name.desc() 逆序排列

for pet in query:
    print(pet.name, pet.owner.name)

# prints:
# Kitty Bob
# Mittens Jr Herb

可以直接使用 | 来作为查询条件,这个相比 django 需要使用 Q 来说,设计地非常优雅。

d1940 = date(1940, 1, 1)
d1960 = date(1960, 1, 1)
query = (Person
         .select()
         .where((Person.birthday < d1940) | (Person.birthday > d1960)))

for person in query:
    print(person.name, person.birthday)

# prints:
# Bob 1960-01-15
# Grandma L. 1935-03-01

最后也是最牛逼的一点,可以使用 pwiz 工具从已有的数据库产生 peewee 的模型文件:

python -m pwiz -e postgresql charles_blog > blog_models.py

flask 全家桶学习笔记(未完待续)

)看到标题有的同学可能就问了,flask 是一个微框架,哪儿来的全家桶啊。其实作为一个框架来说,除非你提供的只有静态页面,那么肯定要和数据库打交道的,那么这篇文章里介绍的就是 flask + ORM + login + uwsgi 等等一系列的工具。

flask

flask-login

https://flask-login.readthedocs.io/en/latest/

flask admin

https://github.com/flask-admin/flask-admin/blob/master/examples/peewee/app.py

swagger+flasgger

swagger 是一套定义 API 的工具,可以实现 API 的文档化和可交互。flasgger 是 flask 的一个插件,可以实现在注释中使用 swagger 语法。

swagger 本身是一套工具,但是后来被社区发展成了 OpenAPI 规范。最新版本是 OpenAPI 3.0,而现在用的最多的是 swagger 2.0。我们这里

参考文献

  1. https://blog.csdn.net/u010466329/article/details/78522992
  2. https://blog.csdn.net/qq_21794823/article/details/78194164
  3. http://www.manongjc.com/article/48448.html
  4. https://juejin.im/post/5964ce816fb9a06bb21abb23
  5. https://www.cnblogs.com/whitewolf/p/4686154.html

uwsgi

Baelish: An Introspection

baelish 是一个基于配置的爬虫系统,目标是让标注员也能够通过可视化界面的来抓取数据。最近一年一直都在写这个项目。在这个过程中可以是说踩了无数的坑,杀死了不少脑细胞终于搞了一个勉强能用的 demo 版本。

总体思想上出的问题,老是想把知道的工具都用上去,试试好不好玩儿,而不是从项目需要的角度来选择。这种思想其实是自己早就知道是错的,可是真的能够自己负责一个项目的选型和架构的时候还是忍不住手痒痒啊。不过好在自己老早就知道这样是错的,至少以后再做项目不会犯这种错误啦。

这篇文章主要是总结下在其中犯得各种错误,以备查阅。

项目组织

最开始把项目分成了若干个代码仓库。baelish 负责调度和下载,jaqen 负责代理管理,bolton 负责解析和存储,inf 是基础库的代码,futile 是和爬虫业务无关的 utility,app_common 是数据库的 orm 和 Django 的后台,conf 是配置文件、idl 是 protobuf 代码。对于一个小型项目来说,分这么多库显然太复杂了。最终干掉了大多数库,只保留了 baelish、 app_common、idl、conf 和 futile 库,现在准备再干掉其他的库,只留下 baelish 和 futile。并且在打包 docker 镜像的时候全部都打包成一个镜像,这样部署也方便些。

基础组件选型

容器编排平台选型

最开始想通过 ansible 直接部署到多台机器上,然后使用 consul 服务发现的机制。但是这个过程中发现在同一个机器上如果部署同一个服务的多个副本的话不是很方便。脑袋一热,开始寻找一个真正的编排平台。

去年的十一假期研究了几天 k8s,概念是在太多了,看得我实在是头昏脑涨,所以放弃了 k8s。这时候因为已经选用了 consul,就注意到了同一家公司出的 nomad。nomad 号称是一个轻量级的调度平台,只有一个 binary,而且还能够和 consul 无缝集成。nomad 简直是一场灾难。首先他的调度是有问题的,尤其是其中一个比较有特色的功能叫做 parameterized job,顾名思义就是可以以不同的参数启动一个任务。这个任务就总是启动失败,而且还有看不到日志的情况。由于 nomad 的社区较小,在 GitHub 上只有不到一万的 star,所以除了问题以后只能看到几个悬而未决的 issue,然后就是干瞪眼。

最终选择了使用阿里云托管版的 k8s,虽然贵了点,但是对于公司来说,这点钱确实不算什么了。这时候距离我学习 k8s 的概念也有了几个月了,经过几个月的沉淀,一些难点也逐渐想明白了。使用了 k8s 之后,确实没有什么大的问题了。

这里要特别说明一下 k8s 上的服务发现实现的优点。在传统的集群中,比如说我们使用 zk 或者 consul 作为服务发现的话,一种模式是服务方主动把自己的 IP 和端口注册到注册中心,在退出的时候解注册。这样的不好是侵入性比较强,在客户端中需要自己去解析服务地址。k8s 上的服务注册在 etcd 中,然后内部服务访问的时候通过 DNS 解析的方式获取到 IP。那么这里就有个问题了,一般语言或者系统的实现中,DNS 可能有也可能没有缓存,那么当服务在集群中漂移的时候怎么能保证总能访问到正确的地址呢?k8s 的实现比较神奇,他的 clusterIP 是虚拟的,并且在服务的整个生命周期都是不变的,也就是说,DNS 和 IP 一定是固定的,服务层有没有 DNS 缓存就无所谓了。

消息队列选型

最开始的时候觉得 kafka 实在太重了,虽然很熟悉 kafka 的时候,但是考虑到自己运维的压力,所以就想找个轻量级的工具。首先尝试使用了Redis,但是因为消息都堆在内存里面,一旦消费端发生了阻塞,很快就oom了。

后来尝试了使用更加“工业级”一点的 rabbitmq,毕竟还自带了管理界面。但是折腾了一周,rabbitmq 总是会神奇的自动退出,查了下可能是 Erlang VM 的问题,并且没有更多任何日志消息,最终放弃了。而且 rabbitmq 没有一个很好的 python 客户端,有一个叫做 pika 的 python 客户端,但是基本跟玩具一样,什么也没有,完全需要自己写。

在之后,正好 redis 发布了 5.0 版本,提供了 redis stream 的功能,号称是和 kafka 一样的设计理念,所以就尝试了一下。遇到了两个问题,首先当时 redis-py 还没有跟进,所以只好使用比较低端的 python 客户端来和 redis 通信,这样导致工作量大了很多;还有一个就是 ack 的语义不明,倒是消费总是重复,最终放弃了。

因为 ack 的问题总解决不好,又想使用一些比较全家桶的方案,这时候 celery 进入了我的视野。celery 作为一个异步框架,只需要编写 worker 函数就行了,至于 broker 可以使用 rabbitmq 或者是 redis。因为 rabbitmq 之前一直跑不起来,所以选择了 redis。用了大概一个月的时间还是比较满意的。celery 虽然可以支持 redis,但是他是使用了 kombu 这个库,把 redis 封装成了 AMQP 协议,也就是 rabbitmq 来使用的,这就导致了想要改一些东西的话还是很复杂的。同时 redis 毕竟还是在内存里的数据库,一开始提到的 OOM 的问题还是没有彻底解决,这时候就想着在换一下了。

终于又想起了 kafka,仔细把 kafka 的文档通读了一遍,然后又看了下官方的例子,发现运行一个简单的 kafka 集群其实并没有想象的那么难。kafka 背后的公司现在叫做 confluent,他们官方提供了 kafka-docker 的镜像,最终使用 docker-compose 把 kafka 和 zk 都做了一个单节点的部署,虽然听起来可用性不高,但是到目前为止确实没有发生过任何问题,当然以后流量大了肯定要搞集群的,不过这也不过就是需要把 compose 文件改几个参数罢了。至于 kafka 的客户端,则是使用 confluent-kakfa 加 threadpoolexecutor 自己封装了一个。

RPC 选型和微服务

在前东家的时候一直用 thrift,但是 thrift 不支持 uint64,这点让我一直不是很爽。而且听说 thrift 的序列化性能和 protobuf 相比差了不少。于是乎,在研究了一段时间 thrift 和 gRPC 的优缺点之后,毅然选择了 gRPC。

但是问题来了,gRPC 虽好,暂时用不上啊。虽然设想着代理、解析、下载等等可能都需要微服务,但是最终都没有用,因为运维几个微服务的代价太高了,人手不够的时候还是单体应用好,不能切分太细了。而且其实在最开始并没有多大的流量,不如先使用快糙猛的 http 服务搞起来。另外 gRPC 的 Python 版本到目前为止还不支持多进程模式,所以更要慎重使用。

除了 gRPC 以外,还使用 protobuf 定义了几个全局透传的对象,现在也马上要被移除了。开始想着是这几个对象可能最终要被持久化存储,那么使用 protobuf 做序列化再适合不过了。对于应用的内部通信,实际上用语言本身的对象就是最好的了,protobuf 完全没必要,画蛇添足。

存储系统的设计

对于 mysql 竟然了解地不是很充分。高性能 MySQL 这本书到现在为止也才只看了 50%。当时我竟然以为事务可以让一批数据批量入库,想想真是 naive 啊。

监控系统

不懂的地方很多,但是最终弄对了,收获也很大。大概花了一个月的时间首先学习了什么是时序数据,然后系统调研了 opentsdb、influxdb、prometheus 等等时序数据库或者监控方案的优缺点,最终选择了 influxdb + grafana 的方法。这里有个坑就是对于带有各种 tag 的数据的聚合方式,各家都支持地不太好,哪怕是 influxdb 的亲儿子 telegraf 也会把数据理解错,这里只能是自己根据业务来实现了一个打点的库,自己在客户端做好聚合工作。

因为其中被 telegraf 坑了一把,所以监控这块还有一些短板,不过补上也很简单,只是工作量的问题。

业务逻辑

调度

由于在开始项目之前,刚刚看了MIT 的信息检索导论这本书,其中提到了爬虫的 frontier 组件,然后就模仿着写了一个调度的组件,可是根本就是想多了。书中提到的调度算法是面向的全网爬取,也就是说搜索引擎级别的爬取,实际上和我要解决的半定向爬取的问题不是一个问题。虽然浪费了大概一个月时间实现了这么一个东西,但是实际上并没有什么卵用,最后抛弃了。

调度中一个很重要的问题就是频控。我是知道一个叫做 token_bucket 的算法的,在这里就特别想把这个算法用上,但是事实有一次证明我错了。对于这种主动发起请求,自己能控制频率的情形,最好的方法还是 sleep 就好了。

可是毕竟sleep总让人感觉可能会很低效啊,这时候我又想起了操作系统中进程调度的各种优先级算法。如你所知,又掉进了坑里。这里的调度问题实际上和进程调度完全不是一个问题,非要用那个优先级算法实际上除了会造成好多任务没有在运行以外,并没有什么卵用。

最终采用的方式就是每个线程负责 N 个爬虫的调度,简单轮询,稳定又高效。

下载解析

这里可以说是整个项目从一开始设计基本正确的地方了。使用 pipeline 的模式,把每个步骤都抽象成一个 stage,其实和 django 的 middleware 有点像,最终完成一个网页的抓取。

这里唯一的坑就是开始想把规则加载、代理和解析都设计成一个 RPC 服务去调用,后来发现完全没有精力搞这些事情,就算了。

缓存

设计地太复杂了。考虑了缓存加载和缓存过期两种时间,搞得大家都比较迷惑。最终发现绝大多数的项目也都不需要缓存,这块直接去掉了。

代理

本来想自己使用阿里云或者 adsl 机器自己搭个集群,但是自己搭建的 IP 对于当前的场景来说不够用啊,而且自己搭建太复杂了,还是直接买得好。

uwsgi 的使用和性能优化配置

假设我们编写了如下的 flask 应用,要用 uwsgi 部署,希望性能越高越好,那么下面是一份还说得过去的配置。

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "world"

if __name__ == "__main__":
    app.run()

对应的 uwsgi 配置

[uwsgi]
wsgi-file=app.py  # 应用的主文件
callable=app  # 应用中的 flask 实例
chdir=/opt/app  # chdir 到给定目录
env= XXX=XXX  # 额外的环境变量

# 以下三者任选其一
http=0.0.0.0:5000  # 如果直接暴露 uwsgi 的话用这个
http-socekt=0.0.0.0:5001  # 如果用nginx反向代理的话,用这个
socket=:3031  # 在 3031 使用 uwsgi 协议,nginx 中使用 uwsgi_pass 更高效

chmod-socket = 664

pidfile=xxx  # pid 文件路径
venv=xxx  # 虚拟环境路径
logto = /var/log/www.log

# 并发设置
workers = 2  # 一般为 CPU 核数 * 2
threads = 2  # 线程比进程开销更小一点。如果没有使用 threads 那么 thread 直接不工作的,必须使用 enable_threads。
max-requests = 100000  # 处理过多少个请求后重启进程,目的是防止内存泄露
master = true  # 使用 max-requests 必须采用这个选项
listen = 65536  # 每个进程排队的请求数量,默认为 100 太小了。并发数 = procsses * threads * listen
buffer-size = 65536  # header 的 buffer 大小,默认是 4 k
thunder-lock = true  # 避免惊群效应
uid=www-data
gid=www-data
harakiri=30  # 所有进程在 30s 没有响应后傻屌
log-slow=3000  # 记录满于 3000 毫秒的请求
# lazy-apps  # 不使用 prefork,而是在需要时才启动进程

# 监控设置
stats = 127.0.0.1:9191  # 可以使用 uwsgi top 监控
python-autoreload=1  # 自动重载,开发时非常方便

# 静态文件
check-static = /var/static  # 尝试从该目录下加载静态文件
static-map = /static=/var/static  # 把对应目录映射
route = /static/(.*)\.png static:/var/www/images/pngs/$1/highres.png  # 使用高级路由模式
offload-threads = 10  # 让 uwsgi 启动额外的进程处理

参考

  1. https://blog.zengrong.net/post/2568.html
  2. https://stackoverflow.com/questions/34255044/why-use-uwsgi-max-requests-option/34255744
  3. https://blog.csdn.net/apple9005/article/details/76232852
  4. https://mhl.xyz/Python/uwsgi.html
  5. https://stackoverflow.com/questions/34824487/when-is-thunder-lock-beneficial

Linux 下的内核参数调优与 sysctl

最近在配置 influxdb 的 udp 端口的时候看到文档[1]里面提到:强烈建议修改 Linux 的默认网络 buffer 大小,否则会对性能有严重影响。感觉挺有意思,深入研究了一下 Linux 下 TCP 和 UDP 的一些参数配置。

influxdb 的推荐配置

首先查看这两个配置:

sysctl net.core.rmem_max  # 读取 buffer 最大值
sysctl net.core.rmem_default  # 读取 buffer 默认值

然后更新这两个配置为 25 MiB

sysctl -w net.core.rmem_max=26214400
sysctl -w net.core.rmem_default=26214400

最后写入到配置文件,以便重启之后依然生效

# /etc/sysctl.conf
net.core.rmem_max=26214400
net.core.rmem_default=26214400

这里的设置其实就是增大网络 buffer 的大小,避免收到的 udp 包过多的时候造成丢包或者阻塞的现象。

sysctl 命令

在上面的命令中,我们使用了 sysctl。sysctl 的 man page 也很简洁:sysctl – configure kernel parameters at runtime。也就是说在运行时调整内核参数。

sysctl -a 显示当前的所有参数
sysctl -w xxx=xxx 设定内核参数
sysctl xxx 显示内核参数的值
sysctl -fFILE 加载给定文件的值,默认是 /etc/sysctl.conf 也就是我们刚刚更改的文件

我们可以看下阿里云上默认的 ubuntu 机器的 /etc/sysctl.conf 文件:

tiger@iZ2ze0z0xdiqgv15k20evmZ [01:53:09 AM] [~]
-> % cat /etc/sysctl.conf
vm.swappiness = 0
net.ipv4.neigh.default.gc_stale_time=120

# see details in https://help.aliyun.com/knowledge_detail/39428.html
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce=2
net.ipv4.conf.all.arp_announce=2

# see details in https://help.aliyun.com/knowledge_detail/41334.html
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2

kernel.sysrq=1

sysctl 可以控制的配置大概分成了以下几类:

  • 内核子系统 (通常前缀为: kernel.)
  • 网络子系统 (通常前缀为: net.)
  • 虚拟内存子系统 (通常前缀为: vm.)
  • MDADM 子系统 (通常前缀为: dev.)

对应的参数除了使用 sysctl 读取以外,还可以直接通过文件系统读取:

cat /proc/sys/net/core/

甚至也可以写入来直接更新内核参数,这样证实了 Linux 下一切皆文件的设计理念。

在 docker 中如何设置 sysctl

我们知道在 docker 中,容器适合宿主机共享同一个内核的,那么显然容器是不能随便动宿主机的配置的。所以 /proc/sys 目录在容器中默认是只读挂载的。

要想在容器运行的时候更改内核参数,需要在 docker run 的时候添加参数:

  1. --cap-add=SYS_ADMIN
  2. --privileged
  3. --sysctl

以上三个参数任选其一,应该就可以了

在 kubernetes 中如何设置 sysctl

在 docker 中设置还好说,其实就是把默认的只读属性去掉就好了。在 kubernetes 的 pod 中要想设置 sysctl 就比较复杂了,因为你不知道 pod 究竟会调度到哪台机器上。在这里我没有深入研究了,毕竟 influxdb 这种有状态的服务还没有迁到 kubernetes 上,看了下官方文档[4],大概思路是:

  1. 给对应机器的 kubelet 指定参数,然后使用 taint 调度到这台机器上
  2. 对于一些安全的属性,可以直接在 pod 中使用 securityContext 设置
apiVersion: v1
kind: Pod
metadata:
  name: sysctl-example
spec:
  securityContext:
    sysctls:
    - name: kernel.shm_rmid_forced
      value: "0"
    - name: net.core.somaxconn
      value: "1024"
    - name: kernel.msgmax
      value: "65536"
  ...

参考资料

  1. https://docs.influxdata.com/influxdb/v1.7/supported_protocols/udp/
  2. https://stackoverflow.com/questions/54845095/cannot-run-sysctl-command-in-dockerfile
  3. https://www.kernel.org/doc/Documentation/sysctl/README
  4. https://kubernetes.io/zh/docs/tasks/administer-cluster/sysctl-cluster/

节选:Choose Boring Technology

It’s really worthwhile to natually write down what all of the awkward things you’d have to do are. A lot of the time when you do this, you realize that the situation isn’t really that bad. Or it may be bad, but not as bad as the task of operationalizing a new thing.

But it can go the other way, too. You can list all of the unnatual costs and conclude that adding a new thing will be worth it.

Happiness comes from shipping stuff.

IT桔子逆向实战

使用 jadx 反编译

首先,我们到豌豆荚下载最新的 APK。然后执行 jadx-gui itjuzi.apk。很遗憾发现加壳了,应该是百度的壳,我们先跳过,去找一个老版本,看看有没有没加过壳的。

很遗憾,没有找到不带壳的版本,所以我们需要进行脱壳

脱壳

阅读了[这篇文章][1],我们知道凡是脱壳都会有两个步骤,一个是找到原始的 classes.dex 文件,一个是修复这个 dex 文件。

首先,我们知道动态加载的dex必然会调用dalvik/vm/DvmDex.cpp中以下两个函数任意一个:

  1. dvmDexFileOpenFromFd 从文件描述符获取DexFile结构体
  2. dvmDexFileOpenPartial 从内存获取DexFile结构体

通过编写函数可以把这个 dex 文件 dump 出来,但是这个函数运行在哪儿呢?

未完待续。。

[1] https://bbs.pediy.com/thread-218891.htm

手机如何使用 Charles 抓 https 包

问题描述

安装 Charles 之后,使用手机自带浏览器访问 http://chls.pro/ssl ,下载到了 getssl.crt 文件,点击安装之后提示“未找到可安装的证书”

解决方法

首先下载 Chrome 浏览器,然后再访问 http://chls.pro/ssl ,安装下载到的证书就好了。

通过浏览器访问 https://www.baidu.com ,Charles 中能够抓到包的内容,说明安装成功了

参考

  1. https://segmentfault.com/a/1190000011573699
  2. https://testerhome.com/topics/9445

张一鸣:你的尺子的长度

编者按:今天例行摸鱼过程中在 Web Archive 上偶然发现了一篇“99房”的博客。99房是张一鸣在创立字节跳动之前的创业项目。根据文中的“延迟满足感”等词,我推测这篇文章应该是他写的吧。在 Google 上搜索了文中的几个句子,发现这篇文章并没有见诸媒体。文章写得挺好,分享给大家。

请注意,本文未获得任何转载授权,主要是除了 CEO 面对面的时候在几十米外眺望过以外,我也和张一鸣没有任何交集……Anyway,如果有任何问题,我当立即删除此文就是了。

原文发表于 2011 年 4 月 11 日。不到一年后,张一鸣创立了字节跳动。

原文链接(需特殊上网能力方可访问):https://web.archive.org/web/20110924075809/http://blog.99fang.com/?p=151

先说一个问题:我已经有一年多没写博客了。原因有两个。

1、发现我所谓的一点感悟和总结,其实很多经典的书有系统的分析和著名的人也有经典的总结,自己写的对人帮助少。

2、相信少说多做,延迟满足感,人生废话居多,行动太少。

但是后来发现这两点不全对。按照哲学和科学的体系,大多数事情都可以从少数公理开始推导,还是需要具体的定理和行动来指导行动。对于写博客而言,只要能做到对有相似经历和相似背景的读者有所裨益,相对容易有共鸣和帮助,就有了价值不是“废话”。

回到正题:看待事情,衡量经历,很多就像拿把尺子去估量,当然这个尺子也许不是一维的,或许也不是直的,但是我发现有一个最关键的因素是这个尺子是否足够长。尺子长短能决定测量的结论:比如你拿把短的尺子去量铁轨是否是直的,答案肯定都是直的。随着年龄的增长,经常发现当年自己在某个问题上想错了,做法错了。这种情况很多时候是因为当年因为年纪小,经历短,所以只能拿把短的尺子去衡量。

我想举几个事情:

1、选择工作:大学毕业的你对于 5 千元薪水和 7 千元薪水,你用当时的尺子感觉差距一定不小。但是可能 3-5 年后,你会发现这个差距可以作为误差,毕业选择工作的时候可以忽略不计此因素。

2、寻找伴侣:年轻人少有对未来伴侣的人品,价值观、追求等关注,肯定少于对外形、活泼,甚至少于对星座、爱好的关注。但是 30 岁的时候你再找(如果你还没找到),相信会变化。

3、关于度尽劫波兄弟在,相逢一笑泯恩仇:我想应该是既然劫波已度过,尺子自然变长了,恩仇已然不重要,是是非非成鸡毛蒜皮。

这些问题有人说是需要阅历,我同意。但是我更多取决于于你心里对尺子设置的长度,你若心中想的是人生你的尺子自然就长当然也不会那发型是否酷来寻找伴侣。

:) 回到正题的正题:创业公司拿多长的尺子去衡量人?

1、是否以会 gdb 打印 map、是否写过分词程序来招工程师,是否以会用 visio 图来招聘产品经理?

2、是否以业内有客户关系来评判商务总监候选人,亦或是在媒体有所资源来选择PR经理?

我想这取决于你如何看待创业,如果你把目光可能到下个月的进展,这些尺子自然合适,但是如果你把创业看作是做一个创造巨大价值的公司和伟大组织的长跑,我相信这些都不是重要标准。这时候你会更加看重:人品、作风、学习力、反省力、价值观。如下三把尺子是递长的:Ta 一年后在公司承担的角色和能成长的高度;假设还有下次创业是否还希望邀请;会不会成为人生道路上的可信赖的好伙伴。

还有一些更极端的例子,在“仰望星空”的时,在想像浩瀚宇宙的边界时,在险峻山峰一览众山小的时,很多人感到看待问题会开阔深刻很多,这些也可以理解成尺子长度变化的效果。

所以很多事情用长的尺子和短尺子衡量的结果不一样。如果你希望的更远,请别忘适时的选择长尺子。