Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15896 articles
Browse latest View live

一张图让你看明白亚马逊的利润来自哪里 提示:别猜电商

$
0
0

BI中文站 5月3日报道

想到亚马逊,你可能首先就会想到网络购物。亚马逊网站上出售的商品五花八门,小到书籍、手机,大到家电、汽车,应有尽有。可以说,亚马逊就是电商领域的龙头老大。

但是从统计机构Statista公布这张图表来看,亚马逊最著名的业务并不是最大的利润来源。实际上,亚马逊的绝大多数利润来自其云计算部门AWS。亚马逊上个季度的总营业利润为10亿美元,89%来自AWS服务。

然而,AWS部门上季度的营收仅占公司总营收的10%。

这并不是新鲜事儿,亚马逊多年来一直给人一种投资巨大、利润率较低的感觉。现在,这些投资终于开始获得回报了,AWS已经成为云服务市场上排名第一的服务,这就是最好的例子。从这个意义上来说,亚马逊首选发展壮大云计算部门就不难理解了。(编译/林靖东)


严杰夫: 投资世界是由心理和经济两大因素构建的

$
0
0

[ 读写人(duxieren.com)文摘] [文章来源: 严杰夫]

文/严杰夫

许多投资者都知道,个体心理会对市场波动产生影响,但像里昂·利维这样,将心理因素视为投资核心因素的投资大师,恐怕就不多见了。

对我国投资者来说,里昂·利维的名气远不如“股神”沃伦·巴菲特、“金融大鳄”乔治·索罗斯及吉姆·罗杰斯。但在华尔街,里昂则是与巴菲特齐名的传奇人物。遗憾的是,里昂在2003年去世后,并未留下太多关于他的投资理念的文字,唯有一部名为《股价为什么会上涨》的自传式作品,成为后来者管窥他投资思想的 “秘籍”。

投资世界是由心理和经济两大因素构建的 - 严杰夫 - 多余的话
 

想要了解里昂的投资理念,我们或许该先了解一下他的家庭和出身。1925年,里昂·利维出生在一个犹太人家庭。与许多美籍犹太裔一样,利维家族也是个商人家族。里昂的祖父1865年从波兰移民到美国,成为商贩。刚开始,里昂的祖父专门给特拉华州和哈德逊公司的平底船提供给养品。但在与著名的伊利铁路公司的竞争中,利维祖父的公司倒闭了。此后,他又合伙开办过纺织品批发公司,还有一家批发袜子的企业——利维家族公司。后面这家公司成为利维的家族产业。

里昂的父亲杰罗姆·利维是长子,自然也就成了家族产业的继承人。不过,杰罗姆的兴趣并不在做生意,而是研究经济学。杰罗姆研究的是如何同时实现充分就业与政府、企业的收支平衡——一个典型的政治经济学问题。然而,在研究过程中,杰罗姆学到了预测企业利润的能力,这使得他在投资上获得了不少成绩。当然,归根到底,杰罗姆一生的兴趣还是在经济学而非赚钱。用儿子里昂的话说,投资对杰罗姆来说,“更多的是一种智力体操”。但他确确实实地对里昂的职业生涯产生了潜移默化的影响。

事实上,里昂自己也并不避讳这一点。据他回忆,在他很小的时候,就已成了父亲投资学思想的“学徒”,其中最重要的是,他从父亲那里学会了如何在交易中寻找模型。而这一点在里昂投身于华尔街以后,特别是在成为奥本海默基金公司合伙人之后,便成了他最重要的一项“技能”。凭借这项技能,里昂领悟到了“如何从内部人员股票交易的月度报告中,寻觅公司正在筹谋事项的预警信号”,这也使得他能从各种公开信息中判断出投资机会和投资风险。

一个有意思的故事发生在20世纪60年代末,彼时里昂已是奥本海默基金的合伙人,凭借着“公开信息”,里昂帮助公司成功地躲过了大股东所挖的“陷阱”。20世纪60年代中期,一位名叫伯尼·科恩菲尔德的投机客创建的基金公司购买了奥本海默基金公司的股份,并成了奥本海默的最大股东。但包括里昂在内的合伙人,都十分警惕科恩菲尔德会干预公司对股票的购买选择。这种担心很快变成了现实。有一天,科恩菲尔德想给里昂介绍一位石油大亨,并试图向里昂“推销”这位大亨的油田——这位大亨想向奥本海默出售2万英亩北极油田,而事实上这些油田也由科恩菲尔德的公司控制。更蹊跷的是,这位大亨同意,如果奥本海默愿意以每英亩8美元的价格收购,那么八年之内不需要提供任何资金。

我相信,大部分人在看到大亨提出的交易条件时,一定觉得这是桩只赚不赔的生意。但里昂偏偏留了个心眼。他在会见大亨之前,就咨询了标准石油公司的首席地质学家。这位学者告诉里昂,北极油田的价值应在每英亩大约50美分。事先掌握了这一关键信息,里昂迅速理解了科恩菲尔德和他的大亨朋友的“阴谋”:他们希望借着与奥本海默的交易,将北极油田维持在每英亩8美元的虚高价格上,而这正是科恩菲尔德在购买这些油田时所支付的对价。更进一步,凭着这项交易,科恩菲尔德还能顺便抬高旗下基金公司的估值,以误导投资者!

在里昂超过50年(如从上学时投资铁路债券开始算,则有60年)的投资生涯里,借助公开信息的投资决策,可以说已成了他的“套路”。此外,里昂也十分重视宏观经济和全球格局的变化在投资领域的影响。正如他所说的“投注经济而非股票”。里昂认为,未来最大的行情很可能会诞生于那些有着巨大增长潜力的国家:如印度、中国或俄罗斯。从这一角度来看,经济分析在里昂的投资理念中占据着重要地位。

不过,假如只谈“经济学”,那只算理解了里昂投资理念的一半甚至一小半内容。因为,在里昂的投资世界里,位于核心位置的并非“经济因素”,而是“心理因素”。正是这一点,决定了里昂投资理念的独特性和原创性,而并非简单地继承和复制他父亲的思想。

里昂相信,任何事情的发生都起源于人们的各种决策,而在这背后,起着驱动作用的就是投资者个人的心理。所以,忽视投资市场的心理因素,就像忽略“房间里的大象”一样可笑。里昂坚信,“如果不去探究背后驱使人们付诸行动的心理,即便是大泡沫也不值得去探讨”。在里昂看来,正是投资者个体的心理因素汇聚到一起,才形成了股价的上涨或下跌,甚至催生了股价的持续疯涨,以及随后而来的“泡沫破灭”和金融海啸。

《股价为什么会上涨》的第11章,小标题是“股票为什么会下跌”。从字面上看,这一章的内容似乎与全书主题相悖,但其实股票无论上涨还是下跌,其背后都是市场参与者的心态波动在作祟。因此,这实际上是浓缩了里昂投资理念中最核心一环的章节。

就在这一章节里,里昂大量分析了人们的投资行为背后体现出的投资心理:为何典型的投资者不卖亏损的股票?为何投资者常常会低估所持有的投资组合的下行风险?这些市场上司空见惯的投资行为,背后都有着深刻的心理逻辑。比如,之所以投资者不愿意卖出亏损的股票,是因为出售亏损股票意味着承认自己的错误,同时人们对亏损的厌恶也使得人们扭曲了对事物发生概率的认知。在这样的心态下,自然也就会低估自己的持仓风险。

事实上,这样的心理不仅影响了普通的投资者,同时也在困扰基金经理等专业投资者。这就难怪我们在市场上会常常看到“助涨杀跌”的现象,反倒是所谓理性投资的行为变得极为罕见。投资市场也就此将“羊群效应”演绎得淋漓尽致。

或许,用一个古老的命题来阐释里昂的“投资心理学”再恰当不过:“繁荣滋生松懈,松懈滋生不可信的数据,最终这些不可信的数据又引发时代的衰败”。通俗地讲,当经济繁荣、股价高昂时,监管和会计部门在心理上就容易放松警惕,总是以为人们的债务很容易偿还,财富也会一直增长而会弥补过去的欠缺。在这种全社会心态都放松的背景下,企业就会偷工减料,尽力钻制度的漏洞去套取利润,并形成虚假繁荣的景象。但当其中某个环节(常常是一家或一类企业:1998年是长期资本管理公司,2000年是互联网企业,2008年则是雷曼兄弟)出了麻烦时,所有这一切就像多米诺骨牌那样被推倒,局面立刻变得一发而不可收拾——音乐总有停止的时候,非理性繁荣也终有退潮的那一刻。

“心理因素”和“经济因素”共同构成了里昂的“投资世界”。这两个因素互相作用,也使得投资市场无法成为 “绝对有效的市场”,而只能是 “相对有效市场”。因此,处在这个市场里的人,必须学会在公开信息里判断出其他投资者的心理,推测他们手中的筹码和底牌,并判断出他们下一步的行动以及市场未来的方向。正因此,在里昂看来,投资更像是一门艺术而非精密科学,并且“没有哪一套理论能够完美地阐明市场,未来也从来都不是过去的简单重复”。

在《股价为什么会上涨》里,里昂将投资的要义简明地归纳为两个方面,但我们也会察觉,还有其他因子和观念在影响着他的决策。我们可以看到,他对历史和艺术也有极浓厚的兴趣,在政治和社会领域也有独特的想法。在这一点上,里昂的理念和查理·芒格似乎又有着某种共通之处。查理·芒格将投资的诀窍归结为“lollapalooza”效应——当一个人掌握思维模型的数量到一定程度时,就能产生一种思维上的奇观,届时无论在投资还是其他事业,他都能取得惊人的成果。

当然,最重要的是,无论是里昂还是芒格,他们都在教导我们,投资就是一个漫长的信息积累和分析过程,从来没有什么“悬崖深处的秘籍”能让我们一夜暴富。正如芒格所说:“我们赚钱,靠的是记住浅显的,而不是掌握深奥的。我们从来不试图成为非常聪明的人,而是持续地试图别变成蠢货”。

-------------------------------------------------------------------------------------------------------------------------

如果您喜欢我的文章,请帮忙在“朋友圈”转发,并推荐“严杰夫的约柜”。:)

投资世界是由心理和经济两大因素构建的 - 严杰夫 - 多余的话
 

原文链接: http://jeffyanguang.blog.163.com/blog/static/12215523820174375258535
走廊网: 文化频道 | 生活频道 | 创意&视觉频道 | IT频道

八张图告诉你2017年Q1互联网人才趋势

$
0
0

2017 年的春季招聘季刚刚过去,在这 2016 资本寒冬年后的第一个招聘旺季,互联网招聘市场正悄然变化。其中最大的一个变化是:招募互联网高端人才的主力,正在从中小型创业公司向中大型公司转移。


100offer汇总了 2017 年第一季度 100offer 上高端互联网人才的面试邀请数据,下面就用八张图,为你展现这个春天里,互联网人才流动的「变」与「不变」。



一、互联网行业薪资波折上升



整体来看,整个行业的薪资依然保持良好的增长率。但在 2017 年的招聘旺季,因供给增加,人才稀缺度减弱,薪资环比下降。


二、现在的互联网人更想去大公司还是小公司?



很显然,小公司的吸引力在削弱,大公司则重拾人才招聘的主动权。造成这种现象的原因,除了寒冬后互联网人心态的变化,还有企业可开出薪资的实力变化。前文提到,高端互联网人薪资在 Q1 同比增长 21%,但是这个涨幅更多由 C 轮以后的,更大规模的公司拉动。



招聘需求也在向大公司集中。前几年,大量获得融资后的初创公司有实力用高薪、期权抢夺人才,但是否有持续的供血能力,才是屹立不倒的关键。


三、这个春天,互联网哪个领域最高薪?



传统强势领域电商、互金、移互依然有不错的薪资增长。数据与信息服务领域涨幅偏低,也许需要警惕所谓的大数据过热。O2O 领域则出现罕见的负增长。


四、哪个职位更受欢迎?



Java、Scala、算法、前端依然是最热岗位。


五、互联网哪个城市更高薪?



那么,在这些城市买一个 80 平的房子,不吃不喝至少攒几年?



去年,在阿里、网易等巨头的拉动下,杭州互联网人薪资一度领先北京。在刚刚过去的 2017 年 Q1,北京薪资又重回首位。但不变的是买房难度,北京互联网人实际生存压力依然高于其他城市。而拥有大公司、高薪资、低房价的杭州,依然是个不错的去处,至少目前是这样。

下载虎嗅APP,第一时间获取深度独到的商业科技资讯,连接更多创新人群与线下活动

深入理解 MessageQueue

$
0
0

原文链接: https://pqpo.me/2017/05/03/learn-messagequeue/

Android 中有两个非常重要的知识点,分别是Binder机制和Handler机制。前者用于跨进程通讯,并且通过 ServiceManager 给上层应用提供了大量的服务,而后者用于进程内部通讯,以消息队列的形式驱动应用的运行。之前的文章已经多次分析了Binder相关的内容,复杂程度远高于Handler,之后还会继续分析Binder。说到Handler,做安卓开发的一定都不会陌生,一般用于切换线程。其涉及到的类还有Looper,MessageQueue,Message 等。其中MessageQueue是事件驱动的基础,本文会重点分析MessageQueue,其他内容会简单带过,可以参考生产者-消费者模式。

从Handler的入口开始分析:

Looper.prepare();
1.创建一个Looper,并且是线程私有的:sThreadLocal.set(new Looper(quitAllowed));
2.初始化Handler:mHandler = new Handler();,在构造函数中会获取线程私有的Looper,如获取不到会报错。
3.开启无限循环:Looper.loop();。
在loop方法中主要代码如下:

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
从MessageQueue中获取待处理的Message(阻塞线程)
交给与之关联的Handler处理
回收Message,供Message.obtain()复用
其中msg中的target是在Handler发送消息的时候赋值的:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException this + " sendMessageAtTime() called with no mQueue");
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
发送的消息最终入队列到了MessageQueue。
简单总结一下Handler消息机制的工作原理:

创建与线程绑定的Looper,同时会创建一个与之关联的MessageQueue用于存放消息
开启消息循环,从MessageQueue中获取待处理消息,若无消息会阻塞线程
通过Handler发送消息,此时会将Message入队列到MessageQueue中,并且唤醒等待的Looper
Looper获取的消息会投递给对应的Handler处理
可以看到其中与MessageQueue相关的也就两个操作,一个是入队列(MessageQueue是链表结构),一个是出队列,这正是本文介绍的重点。
MessageQueue的创建:

MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
nativeInit()方法实现为android_os_MessageQueue_nativeInit():
[android_os_MessageQueue.cpp]

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast(nativeMessageQueue);
}
这里会创建一个native层的MessageQueue,并且将引用地址返回给Java层保存在mPtr变量中,通过这种方式将Java层的对象与Native层的对象关联在了一起。这种在Java层保存Native层对象引用地址来实现关联的方式,在Android源代码中会经常看到。
然后看一下Native层MessageQueue的构造方法:

NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
也创建了一个Looper,并且也是与线程绑定的,事实上这个Looper与Java层的Looper并没有多大关系,一个是处理Native层时间的,一个是处理Java层事件的。
Java层的Looper会通过调用MessageQueue的next方法获取下一个消息,先看主要部分,后面省略了一部分IdleHandler的处理逻辑,用于空闲的时候处理不紧急事件用的,有兴趣的自行分析:

Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
这里有必要提一下MessageQueue的数据结构,是一个单向链表,Message对象有个next字段保存列表中的下一个,MessageQueue中的mMessages保存链表的第一个元素。
循环体内首先调用nativePollOnce(ptr, nextPollTimeoutMillis),这是一个native方法,实际作用就是通过Native层的MessageQueue阻塞nextPollTimeoutMillis毫秒的时间。
1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
暂时知道这些就可以继续向下分析了,native方法后面会讲到。
如果msg.target为null,则找出第一个异步消息,什么时候msg.target是null呢?看下面代码:

private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;

Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
这个方法直接在MessageQueue中插入了一个Message,并且未设置target。它的作用是插入一个消息屏障,这个屏障之后的所有同步消息都不会被执行,即使时间已经到了也不会执行。
可以通过public void removeSyncBarrier(int token)来移除这个屏障,参数是post方法的返回值。
这些方法是隐藏的或者是私有的,具体应用场景可以查看ViewRootImpl中的void scheduleTraversals()方法,它在绘图之前会插入一个消息屏障,绘制之后移除。
回到之前的next方法,如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息),可以通过setAsynchronous(boolean async)设置为异步消息。
继续往下,如果有消息需要处理,先判断时间有没有到,如果没到的话设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis);阻塞;
否则把消息返回给调用者,并且设置mBlocked = false代表目前没有阻塞。
如果阻塞了有两种方式唤醒,一种是超时了,一种是被主动唤醒了。根据生产消费模式,生产者有产品的时候一般情况下会唤醒消费者。那么MessageQueue入队列的时候应该会去唤醒,下面看一下MessageQueue入队列的方法,截取了主要逻辑:

boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
上面的代码主要就是加入链表的时候按时间顺序从小到大排序,然后判断是否需要唤醒,如果需要唤醒则调用nativeWake(mPtr);来唤醒之前等待的线程。
再总结一下MessageQueue获取消息和加入消息的逻辑:
获取消息:
1.首次进入循环nextPollTimeoutMillis=0,阻塞方法nativePollOnce(ptr, nextPollTimeoutMillis)会立即返回
2.读取列表中的消息,如果发现消息屏障,则跳过后面的同步消息,总之会通过当前时间,是否遇到屏障来返回符合条件的待处理消息
3.如果没有符合条件的消息,会处理一些不紧急的任务(IdleHandler),再次进入第一步
加入消息:
1.加入消息比较简单,按时间顺序插入到消息链表中,如果是第一个那么根据mBlocked判断是否需要唤醒线程,如果不是第一个一般情况下不需要唤醒(如果加入的消息是异步的需要另外判断)
到这里其实关于MessageQueue已经分析的差不多了,其中有两个native方法没有涉及到分别是nativePollOnce,nativeWake,其实之前结论已经给出了,两个方法都会传入mPtr,在native层对应的是NativeMessageQueue的引用地址。
感兴趣的可以继续往下看,先看一下nativePollOnce的实现:
[android_os_MessageQueue.cpp]

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
通过传进来的ptr获取NativeMessageQueue对象的指针,然后调用NativeMessageQueue对象的pollOnce方法:
[android_os_MessageQueue.cpp]

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
调用的是Looper的pollOnce方法,这个Native层的Looper是在初始化NativeMessageQueue的时候创建的。
[Looper.cpp]

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); int ident = response.request.ident; if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
先是处理native层的Response,这个直接跳过,最终调用pollInner

int Looper::pollInner(int timeoutMillis) {
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
}

// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;

// We are about to idle.
mPolling = true;

struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

// No longer idling.
mPolling = false;
// Acquire lock.
mLock.lock();
// Rebuild epoll set if needed.
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
result = POLL_ERROR;
goto Done;
}

// Check for poll timeout.
if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}
// Handle all events.
for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeEventFd) { if (epollEvents & EPOLLIN) { awoken(); } else { ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
// Invoke pending message callbacks.
mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
{ // obtain handler
sp handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
handler->handleMessage(message);
} // release handler

mLock.lock();
mSendingMessage = false;
result = POLL_CALLBACK;
} else {
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
// Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++) { Response& response = mResponses.editItemAt(i); if (response.request.ident == POLL_CALLBACK) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
removeFd(fd, response.request.seq);
}
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
这个方法有点长,首先会根据Native Message的信息计算此次需要等待的时间,再调用

int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
来等待事件发生,其中是epoll是Linux下多路复用IO接口select/poll的增强版本,具体可以自行查阅相关文章,查考:Linux IO模式及 select、poll、epoll详解
如果epoll_wait返回了,那么可能是出错返回,可能是超时返回,可能是有事件返回,如果是前两种情况跳转到Done处。
如果有事件发生,会判断事件是否是mWakeEventFd(唤醒的时候写入的文件)做不同处理。在Done处会处理Native事件,还有Response。
总结一下就是,Java层的阻塞是通过native层的epoll监听文件描述符的写入事件来实现的。
最后看一下nativeWake:

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
和之前一样,也是通过long类型的ptr获取NativeMessageQueue对象的指针,再调用wake方法:

void NativeMessageQueue::wake() {
mLooper->wake();
}
调用的也是Looper的方法:

void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal: %s", strerror(errno));
}
}
}
重点是write(mWakeEventFd, &inc, sizeof(uint64_t)),写入了一个1,这个时候epoll就能监听到事件,也就被唤醒了。

【腾讯Bugly干货分享】Android减包 - 减少APK大小

$
0
0

本文是对Google官方文档 Reduce APK Size 的翻译,点击“阅读原文”可以查看英文原文。

译者简介:damonxia(夏正冬),天天P图Android工程师

用户经常会避免下载看起来体积较大的应用,特别是在不稳定的2G、3G网络或者在以字节付费的网络。这篇文章描述了怎样减少你的APK大小,这会让更多的用户愿意下载你的应用。

理解APK的结构

在讨论怎样减少应用大小之前,先了解APK的结构是有用的。一个APK文件就是ZIP包,其中包含了组成你的应用的所有文件,比如Java类文件,资源文件,和一个包含被编译资源的文件。

一个APK包含了以下目录:

  • META-INF/: 包含CERT.SF和CERT.RSA签名文件,也包含了MANIFEST.MF文件。(译注:校验这个APK是否被人改动过)
  • assets/: 包含了应用的资源,这些资源能够通过AssetManager对象获得。
  • res/: 包含了没被被编译到resources.arsc的资源。
  • lib/: 包含了针对处理器层面的被编译的代码。这个目录针对每个平台类型都有一个子目录,比如armeabi, armeabi-v7a, arm64-v8a, x86, x86_64和mips。

一个APK也包含了以下文件,其中只有AndroidManifest.xml是强制的:

  • resources.arsc: 包含了被编译的资源。该文件包含了 res/values目录的所有配置的XML内容。打包工具将XML内容编译成二进制形式并压缩。这些内容包含了语言字符串和styles,还包含了那些内容虽然不直接存储在resources.arsc文件中,但是给定了该内容的路径,比如布局文件和图片。
  • classes.dex: 包含了能被Dalvik/Art虚拟机理解的DEX文件格式的类。
  • AndroidManifest.xml: 包含了主要的Android配置文件。这个文件列出了应用名称、版本、访问权限、引用的库文件。该文件使用二进制XML格式存储。(译注:该文件还能看到应用的minSdkVersion, targetSdkVersion等信息)

译注:使用APK Analyzer能够清晰地看出以上文件的内容,具体请看:使用APK Analyzer分析你的APK。

减少资源个数和尺寸

APK的大小会影响应用加载的速度,使用的内存大小,消耗的电量大小。一个最简单的缩小APK大小的方式是减少资源的个数和大小。特别地,你能移除应用中不再使用的资源,你也能使用可缩放的Drawable对象代替图片文件。这节讨论一些通过减少资源从而减少APK大小的方法。

译注:减少资源个数和缩小资源大小的效果是很显著的,比如有一天发现我组里的项目中还包含了旧版本的引导页视频(1.5M),一下就就减少了1.5M,想想为了减少1.5M你得删多少代码才能办到。

移除不使用的资源

lint是Android Studio中的一个静态代码分析工具,检测在“res/”目录中你的代码没有引用的资源。当lint工具发现了项目中潜在的未使用的资源,它会打印以下类似信息:

res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources]

注意:lint工具不会扫描“asset/”目录,这个目录是通过反射引用的资源,或者链接到应用中的库文件。还有,lint不会移除资源,只会发出警告。

被引用的库中可能会包含没使用的资源。如果你在build.gradle文件中启用shrinkResources,则Gradle能自动移除这些资源。

为了使用shrinkResources,你必须要启用代码混淆。在构建过程中,首先proguard移除了未使用的代码,然后gradle移除未使用的资源。

译注:lint工具还能够检查出未使用的类、类中未使用的方法或变量。

更多关于通过代码混淆和其他方式减包,请看Shrink Your Code and Resources。

在Gradle插件0.7或更高版本,你能申明应用支持的配置。Gradle通过传递resConfigs和defaultConfig给构建系统,构建系统会防止不支持的配置出现在APK中,从而减少APK大小。更多信息请看Remove unused alternative resources。

译注:在hello world工程里,resConfigs配置为“zh”和不配置resConfigs,resources.arsc文件相差了80K。

最小化第三方库中资源的使用

当开发Android应用时,你经常使用第三方库提升应用的可用性和灵活性。比如,你引用Android Support Library提升旧设备的用户体验,或者使用Google Play服务实现文字自动翻译。

如果一个第三方库原本是为服务器或普通电脑设计,会引入许多不需要的对象和方法。为了只引入应用需要的库中的那部分,你可以编辑库文件(如果库的license允许你这么做)。你也能使用另外的针对手机的实现同样功能的库。

注意:代码混淆能清除库中不被使用的代码,但是他不能移除库的大量内部依赖。

只支持部分屏幕密度

Android支持很多设备集,其中包含了各种不同的屏幕密度。在Android 4.4及更高版本,框架支持不同的密度:ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi和xxxhdpi。尽管Android支持所有这些屏幕密度,但你不需要为每个密度都配置相应的资源。

如果你知道某种特定屏幕密度已经很少有用户使用了,那么你可以考虑是否需要为这个屏幕密度配置资源。如果你不包含针对特定屏幕密度的资源,那么Android会自动缩放原本针对其他密度的已有资源。

如果你的应用只需要缩放的图片,你甚至可以把图片存放在drawable-nodpi目录,从而节省更多空间。我们推荐每个应用都应该至少包含xxhdpi的图片。

更多关于屏幕密度的信息,请看Screen Sizes and Densities。

减少动画帧数

使用帧动画会大大增加APK的大小。图1显示了目录中构成帧动画的多个PNG文件。每个图片都是动画的一帧。

对于加入动画的每帧,你都增加了APK中图片的个数。图1中,帧动画的帧率是30 FPS。如果帧率降到15 FPS,图片数量将减少一半。

图1:帧动画的每一帧图片。

译注:还有一个常见的减包方案是删除帧动画中重复的图片资源,比如第1帧和第3帧的图片一样,那么只保留一个。

使用Drawable对象

一些图片不需要静态的图片资源,框架能在运行时动态地绘制图像。Drawable对象(XML的)只需要占用APK中的一点空间。另外,XML形式的Drawable对象能够产生遵循Material Design设计规范的图像。

重用资源

你能包含一张图片的很多变种,比如染色、阴影、旋转的版本。但是,我们推荐在运行时复用一张图片来定制化他们。

Android提供了很多方式改变资源的颜色。对于Android 5.0及以上,使用android:tint和tintMode属性。对于更低版本,使用ColorFilter类。

你也能够删除那些只是对另一个资源做旋转的资源。下面的代码片段提供了对一个箭头旋转180度。

<?xml version="1.0" encoding="utf-8"?><rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_arrow_expand"
    android:fromDegrees="180"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="180" />

通过代码绘制

你也能通过代码绘制图像,从而减少APK大小。代码方式绘制图像不需要任何空间因为你不再需要在APK中存储图像文件。

压缩PNG文件

AAPT工具能够在构建过程中通过无损压缩优化res/drawable/中的图片资源。比如aapt工具能将需要颜色少于256色的PNG变为8位PNG图,这样能够在保证图片质量的同时减少内存使用。

需要注意aapt有以下局限性:

  • aapt工具不会压缩asset目录的PNG文件。
  • 通过aapt的优化,图片文件会使用少于256色。
  • aapt工具可能会影响已经被压缩过的PNG文件。为了防止这种情况,你可以在gradle文件中设置cruncherEnabled为false禁用aapt对PNG的压缩。
aaptOptions {
    cruncherEnabled = false
}

译注:建议把cruncherEnabled设为false,然后通过tinypng手工压缩PNG图片。

压缩PNG和JPEG文件

你能使用一些工具(比如pngcrush, pngquant, zopflipng)在不降低图像质量的前提下减少PNG文件大小。所有这些工具都能保留图像质量的情况下减少PNG文件大小。

pngcrush工具特别有效:这个工具通过迭代png过滤器和zlib参数,使用每种过滤器和参数的组合压缩图像,并选择最小的那个作为最后的输出。

对于JPEG文件,能使用packJPG压缩JPEG文件。

译注:guetzli是Google最近推出的JPEG编码器,官方宣称相同图片质量时,比libjpeg生成的图片小20–30%。

使用WebP文件格式

你也能使用WebP文件格式存储图片而不是PNG或者JPEG。WebP格式是有损压缩(像JPEG)且有透明通道(像PNG),且压缩率高于JPEG或PNG。

使用WebP文件格式也有一些缺点。第一,低于Android 3.2的版本不支持WebP,第二,WebP的解码时间比PNG长。

注意:Google Play的APK的应用启动图标只能使用PNG格式,而不支持其他格式。

在Android Studio中,能将BMP,JPG,PNG或者静态GIF图片转换成WebP格式。更多信息,请看Create WebP Images Using Android Studio。

使用向量图

你能使用向量图去创建一个分辨率无关的图标。使用向量图能够显著减少APK大小。在Android中向量图是以VectorDrawable对象形式存在的。使用VectorDrawable对象,一个100B的文件能生成一个屏幕大小的清晰图片。

但是,系统需要很长时间渲染VectorDrawable对象,更大的图片需要更长的时间显示在屏幕上。因此只有小图片才考虑使用向量图。

更多关于VectorDrawable对象的信息,请看Working with Drawables。

减少Native和Java代码

有许多方法能够减少Java和Native的代码量。

减少不必要的生成代码

确保理解任何自动生成的代码。比如,许多protocol buffer工具生成了过多的方法和类,这会让你的应用大小翻倍。

移除枚举

一个枚举能让classes.dex文件增加1–1.4K。枚举的加入会快速增加应用体积。我们可以使用@IntDef注解和Proguard代替枚举,它能提供和枚举一样的类型安全转换。

减少Native库的大小

如果你的应用使用了Native代码和Android NDK,你也能通过优化代码减少应用体积,这里介绍的两个技巧是删除调试符号和避免抽取Native库。

移除调试符号

如果应用在开发中并且仍需要调试,那么我们能理解使用调试符号。使用Android NDK提供的arm-eabi-strip工具,能从Native库中删除不必要的调试符号,之后你再编译release包。

避免抽取Native库

在APK中存储未压缩的so文件,并且在Manifest文件的中设置android:extractNativeLibs为false,这会防止在安装时PackageManager将APK中的so文件拷贝到文件系统,避免这种拷贝会让应用在做增量更新时的更新包更小。

维持多个小的APK包

你的APK会包含用户下载了但从未使用的内容,比如地区或语言信息(译注:比如我是中国人,我就不会用到其他语种的资源)。为了给用户创建小的下载包,你能把你的应用拆分成多个APK,这些APK的差别在于一些因素(比如屏幕大小或者GPU纹理支持)。

当一个用户下载了应用,设备根据自身的特性和设置获取正确的APK。这种方式能够让设备不获取设备不需要的资源。比如,如果设备是hdpi的,那么他就不需要xxxhdpi的资源。

更多信息请看Configure APK Splits和Maintaining Multiple APKs。


更多精彩内容欢迎关注 腾讯 Bugly的微信公众账号:

腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!

作者:Tencent_Bugly 发表于2017/5/4 12:39:22 原文链接
阅读:179 评论:0 查看评论

Yeoman学习与实践笔记

$
0
0

Yeoman是Google的团队和外部贡献者团队合作开发的,他的目标是通过Grunt(一个用于开发任务自动化的命令行工具)和Bower(一个HTML、CSS、Javascript和图片等前端资源的包管理器)的包装为开发者创建一个易用的工作流。

Yeoman的目的不仅是要为新项目建立工作流,同时还是为了解决前端开发所面临的诸多严重问题,例如零散的依赖关系。

Yeoman主要有三部分组成:yo(脚手架工具)、grunt(构建工具)、bower(包管理器)。这三个工具是分别独立开发的,但是需要配合使用,来实现我们高效的工作流模式。

下面这幅图很形象的表明了他们三者之间的协作关系。

YOMAN的特性

闪电般的初始化:项目开始阶段,可以基于现有的模板框架(例如:HTML5 Bolierplate、Twitter Bootstrap)进行项目初始化的快速构建。

了不起的构建流程:不仅仅包括JS、CSS代码的压缩、合并,还可以对图片和HTML文件进行优化,同时对CoffeScript和Compass的文件进行编译。

自动编译CoffeScript和Compass:通过LiveReload进程可以对源文件发生的改动自动编译,完成后刷新浏览器。

自动Lint代码:对于JS代码会自动进行JSLint测试,确保代码符合最佳编程实践。

内置的预览服务器:不再需要自己配置服务器了,使用内置的就可以快速预览。

惊人的图片优化:通过使用OptiPNG和JPEGTran来优化图片,减少下载损耗。

杀手级包管理:通过bower search jQuery,可以快速安装和更新相关的文件,不再需要打开浏览器自己搜索了。

PhantomJS单元测试:可以非常方便的使用PhantomJS进行单元测试,一切在项目初始的时候都准备好了。

安装前的准备工作

检查系统中是否安装了:Node.js、Ruby、Compass。

Mac下安装Node.js非常方便,首页提供了一个pkg下载,双击后可以默认安装node、npm到/usr/local/bin下,我们只需要确保/usr/local/bin包含在PATH变量中就可以。

Mac Mountain Lion 下自带了Ruby,所以也就不需要再单独安装了。

Compass安装需要依赖于Ruby Gems,执行下面的步骤:

sudo gem update --system

sudo gem install compass

安装

环境准备好之后,就可以进行安装了,执行:

sudo npm install -g yo grunt-cli bower

安装成功后,会看到下面的提示:

使用

执行 yo webapp 我们就可以快速的创建一个新的Web应用的框架,一个通常的工作流程如下:

项目实践

本篇以一个实际项目为例,通过Yeoman来构建一个简单的Blog页面:

环境准备好之后,我们开始自己的项目开发了,首先为自己的项目准备一个目录,我这里是: rivertown.sinaapp.com。

然后第一步,使用 yo 进行快速的初始化,初始化时会询问我们是否使用Bootstrap和RequireJS框架,我这里都选择了是。完成后一个Web应用的基础框架就建立好了。

初始化的WebApp目录结构如下,app目录是我们项目的主目录,test目录中对应的一些JS的单元测试文件。

注意我们需要安装黄色字体的提示,将npm和bower安装到项目本地。

修改页面文件以及样式。

对页面进行预览和测试。当代码编辑到一定程度,我们就需要对页面进行测试了,这个时候需要Grunt派上用场,执行下面的命令:

很快,浏览器中就可以看到我们的页面了。

更神奇的是,一旦我们编辑了项目源文件之后,切换到Safari窗口,就马上能够看到修改所带来的变化,根本不用手工刷新页面!

如果我们想对JS进行测试,那么必须利用npm将PhantomJS安装到本地,然后执行grunt test。

最后的步骤就是编译生成项目了,执行 grunt 就可以将项目编译生成在 dist 目录下,有可能生成的时候会提示 phantomjs 没有的错误,这个时候执行 grunt --force 强制跳过这一步就可以了。

查看具体的main.css和main.js中的内容,可以发现里面的内容都是经过优化之后的。

使用体验

经过一下午的试用,第一对于有这么一个集成化的构建、开发、编译流程,感觉非常新奇,相比于传统的开发方式。我需要首先确定用哪些框架,然后各个网站收集源代码,进行整合,还要部署一个测试的Web服务器,不管是用(Apache、Nginx还是Xampp),最后还要做代码的优化。而现在使用了Yeoman之后,所有的这些工作都被自动化、流程化了,我只需要按照既定的步骤来做,很多事情Yeoman都帮我做好了,对于前端开发来说,节省了非常多的力气。而且,生成项目的代码质量也都很不错。

遗憾的地方在于目前Yeoman的相关资料还比较少,中文的资料也非常少,对于如何深入的学习和定制化,还需要继续的钻研。

PS:吐槽一下百度,不得不说对于IT工作者来说,百度真的不是一个好的选择,输入Yeoman,除了Infoq的两篇介绍性的内容,其他的内容几乎与我需要的都相隔万里,也许是国内关注Yeoman太少的原因,但愿如此。

五大危害企业的移动端威胁与安全防御措施

$
0
0

如今大多企业员工几乎每天都需要用到移动端的某些应用来完成相关工作,但是一旦恶意攻击者盯上了你手机上的某个应用,那么设备遭受攻击所带来的影响可能就是连锁式的。

一、五大危害企业的移动端威胁

Lookout产品总监David Richardson和他的团队研究总结出五大移动端恶意软件家族,冒充真正的企业应用,引诱员工下载恶意软件。研究显示,这五个活跃的移动恶意软件家族通常通过窃取合法应用的名称和包名称来模拟一些企业应用,例如思科的商务电子邮件应用、ADP、Dropbox、FedEx Mobile、Zendesk、VMware的Horizon Client、Blackboard的Mobile Learn等等。

1. Shuanet

2.jpg

Shuanet能够将其自身自动安装在设备的系统分区上,获得设备root权限,达到进一步安装其它应用的目的。这些应用程序可能是恶意的也可能是良性的,推送至手机,提高恶意软件的下载几率。Shuanet可能也会向设备推送各种小广告。

企业将面临的风险

被root设备的安全状态已经发生改变。很多人会利用root权限对设备进行自定义设置,但是他们往往都不会去做安全性的合理配置,可能也不会进行定期的软件更新。另外,Shuanet这样的恶意软件会在系统分区中自动安装,即使恢复出厂设置也难以去除。最后,安装应用的恶意软件可能会将更多的恶意应用程序植入该设备,使设备及数据暴露在更高的风险中。

受害应用程序举例:ADP Mobile Solutions、CamCard Free、Cisco Business Class Email(BCE)、Duo Mobile、Google Authenticator、VMWare Horizon Client、Zendesk、Okta Verify。

2. AndroRAT

AndroRAT开发初衷是完成一个大学研究项目——创建一个“远程管理工具”,允许第三方控制某设备并从麦克风收集联系人、通话记录、短信、设备位置和音频等信息。但是该工具目前被一些不法份子恶意利用。

企业将面临的风险

隐藏的远程访问软件能够帮助攻击者轻易地从移动设备获取到企业和个人的数据。另外,对某个移动设备的持续性远程访问也会帮助攻击者对感染设备所连接的公司Wi-Fi和VPN展开入侵行动。

受害应用程序 举例Dropbox、Skype、Business Calendar

3. UnsafeControl

unsafecontrol.jpg

UnsafeControl能够收集联系信息并将其下载到第三方服务器,还能够对联系人列表发送垃圾邮件或向其命令和控制(CNC)服务器指定的电话号码发送短信。消息内容也由CNC控制。

企业将面临的风险

像UnsafeControl这样的恶意软件能够窃取联系人信息,这对很多企业来说都属于敏感信息。比如,销售总裁或副总设备上的联系人信息就是一个公司极大的竞争优势与虚拟财富。

受害应用程序 举例FedEx Mobile、Google Keep、远程VNC Pro、Sky Drive、PocketCloud、Skype

4. PJApps

5.jpg

PJApps可能会收集并泄露受害者的电话号码、移动设备的唯一标识符(IMEI)及位置。为了扩大非法盈利范围,它也可能会向一些优质的短信号码发送钓鱼信息。另外, PJApps也能够进一步下载应用程序到相应设备。

企业将面临的风险

像PJApps这样的恶意软件通常利用其功能来获取收益,但同时也具有一定技术相关性,例如手机位置信息带来的威胁,尤其是针对高管们的移动设备。这些信息可能关乎企业的商业计划。该恶意软件将其它应用程序下载至设备的功能其实也为新型恶意软件进入设备提供了通关密语。

受害应用程序 举例CamScanner

5. Ooqqxx

6.jpg

Ooqqxx实际是一个广告网络,将广告推送至通知栏,发送弹窗广告,在主屏幕上创建快捷方式,未经许可下载大型文件。

企业将面临的风险

这些广告会往往会打断员工的正常工作,员工因此可能也会向IT部门提出改进意见,这些行为都在一定程度上影响了公司员工的工作效率。Time is money!

受害应用程序 举例Mobile从Blackboard、Evernote、PocketCloud、Remote Decktop、Adobe Reader、aCalendar

二、从开发者角度谈移动端威胁的安全防御措施

每个人的手机、iPad等智能设备上大概都有26-55个应用程序,通常包括以下这些类型:娱乐和游戏、银行app、一些社交媒体app、瘦身塑形的软件以及网购应用等。

如果只是普通的游戏软件遭到攻击,你也许不会在意。但事实上,很多应用其实都收集了很多你不想让别人看到的个人信息,比如你的位置、银行卡信息和某些照片。

从开发的角度来看,你必须通过某些协议确保应用程序的代码不被黑客入侵。Codal首席执行官Keval Baxi主要从开发者的角度给出了一些保护手机应用安全的干货建议。

1. 使用基于令牌的身份验证方式访问API

很多移动应用程序都没有设计恰当的身份验证方法,这种行为的本质其实就是数据泄露。“令牌”是指一些本身不带任何意义的数据,但令牌系统准确率高,它是保护移动端应用程序的关键方式。基于令牌的身份验证需要验证每个向服务器发送的API请求的真实性,只有通过验证程序才会对请求作出响应。

2. 使用Android KeyChain和iCloud Keychain存储敏感信息

移动设备上的keychain是一个安全的存储容器,能够保存所有应用程序的登录名、用户名和密码等数据。建议开发人员充分利用操作系统的这一功能进行数据存储,而不是通过p-list文件或NSUserDefaults来存储。使用keychain功能也可以为用户带来便利,不需要每次登录都输入用户名密码。

3. 在本地数据库中保存用户数据时对数据进行加密

加密是将数据和明文转换为“密码”的过程,也就是密文。要想读取密文就必须经过解密或使用密钥的过程,因此加密数据保护最有效的方法之一。

4. 登录应用程序时选择指纹锁而不是用户名和密码

e.jpg

苹果公司研究人员表示,指纹匹配的概率是1:50000,而四位数密码的匹配概率是1:10000。因此指纹登录比使用传统密码的方式更加安全。指纹是每个用户独特的生命体征,而密码不是。在iOS版本8之前,苹果公司为开发人员开通了Touch ID的权限,API能够在SDK(软件开发工具包)中使用。

5. 可疑活动的实时通知

f.jpg

当用户在新的设备或新的未知位置登录某应用程序时,可以通过电子邮件或推送通知向用户发送登录异常的消息,完成验证过程。很少会有应用程序达到这一要求,而Gmail是其中之一。登录验证通知能够让用户获知他的账户是否遭到了非法入侵。

6. 始终使用https(SSL)

g1.jpg

将SSL安装到服务器后,开发人员就能够使用HTTPS协议,该协议安全性高,有助于防止入侵者干扰应用程序及其服务器之间的数据传输。

7. 提防逆向工程

g.jpg

开发人员对应用程序进行逆向工程、把数据和源代码移走也不是不可能发生的事。为了防止这种情况的出现,你可以通过更改预处理器中重要类别和方法的名称来迷惑黑客。第二个办法则是在项目完成后对符号表进行拆分。

*参考来源: CSOCSO2,FB小编Carrie编译,转载请注明来自FreeBuf(FreeBuf.COM)

今日头条的流量哪里去了?

$
0
0

「对了,不少头条号作者收入锐减的确是被调参数了,作者曝光权重和广告分成系数两个都被挤压,打压老的作者,扶持新人」,在和一位非常了解今日头条的深喉聊天的时候,他和我透露了这样的一个消息。事实上,在营收和流量增长的压力之下,头条对内容生产者的态度正在发生微妙的变化。

数据大幅增长之后,流量迎来新的分配机制

在不久前QuestMobile公布的移动互联网春季报告中,2017年3月今日头条DAU为7478万,相比半年前的5000万,头条用户增长可以说非常惊人。

但不少头条号作者最近却都在问我一个问题:流量到哪里去了?他们普遍感觉到头条号无论是流量还是分成都出现了大幅下滑,尤其是分成这方面,原本10万阅读平均能有接近100元的分成,现在则下滑了一半。

不久前,一篇《做号者江湖》刷爆了朋友圈,而这也让隐藏在灰色角落里的「做号者」浮出了水面,他们为流量而生,时刻寻找平台机器推荐的各种漏洞,在简单的关键词拼凑之后,在几分钟内生产出一篇爆款文章,最后赚到大量广告分成。

而对头条来说,通关技术手段区分「优质内容生产者」和「做号者」并不困难。做号者追求流量、团队化运作、批量化制造符合大众喜好的内容,他们相对缺乏原创能力,内容生产方式更多是对头部作者的内容进行二次再加工,而这很容易让平台有迹可循。

这或许也是促使头条对算法进行调整的导火索所在。原本的内容分成机制,很大程度上是因为平台需要争取优质内容生产者,但现在大量的分成却被这些做号者通过技术手段和人海战略给赚走了。这并不是平台乐于见到的事情。对他们进行打压,则能够将流量分给那些优质内容生产者,可谓一举两得。

不过据我所知,这次流量下滑的并不是只有「做号者」,雷科技则是其中的一个例子,这是一家致力于报道数码科技资讯的垂直媒体,并长期位于腾讯、UC、网易等平台影响力前十,他们的创始人罗超表示,「雷科技在今日头条的收入和阅读数,这几个月一直在下滑,现在相比之前差不多下降了一半。」

一个最大的可能就是头条将流量导向了其他内容产品,头条内部的朋友私下和我确认了这个猜想,「现在头条数据非常好,整体流量每天都在增长,不过图文阅读数确实在下滑,这主要是因为流量分配的算法出现了调整。现在更多的流量被分配给了问答、微头条、短视频、火山直播这些新的产品上。」

站着生还是躺着死,流量无限的神话为何破灭了?

今日头条CEO张一鸣一直对外喜欢强调「使用时长」这个概念,仅次于微信,这是今日头条最值得骄傲的地方。在《从架构调整看微信指数,或许是马化腾真正焦虑的开始》一文中,我曾写下这样一段话:

天天快报和腾讯新闻虽对今日头条联合绞杀,但寄予厚望的算法产品天天快报在短时间内仍看不到超越的可能性。而更让腾讯焦虑的是今日头条凭借先有的流量和分发能力不断孵化出新的内容产品:头条视频、抖音、火山直播.......微视的失败则证明了腾讯的社交关系并非万能,而拥有天天快报和腾讯新闻的腾讯,本应该足以能和今日头条一样在孵化出一个短视频应用,从现在的结果来看,腾讯尚未能建立起类似头条的生态。

使用时长的居高不下,这是今日头条商业化过程中最吸引广告主的地方。某种程度上只要用户不断的推拉信息流,今日头条就能产生无限的流量,这些流量能被用来孵化旗下各类产品,也可以被源源不断的分配给广告主。

今日头条商业产品副总裁刘思齐在接受阑夕专访时表示,「今日头条的广告收入,在某种意义上是可控的,因为广告库存在理论上有着无穷的增量:如果十条资讯搭配一条广告达不到预期目标,那就灵活调整广告配比,就像搜索引擎那样控制自己的营收增长。解决了消化问题之后,唯一的问题,就是在交易的另一端,要有海量的广告主,来解决供应问题。」

从现在来看,头条的流量分配机制其实也并非万能。为了能够让用户更长时间的停留在平台上,今日头条选择基于移动资讯和内容分发进行横向扩张,并开始发力短视频、问答、社交化,在不久前更是上线了类似微博的产品「微头条」。

这些新产品的出现极大的丰富了今日头条原本单一的社交形态,但又从根本上改变了现有的流量分配机制,后者反过来会对原有图文内容的推荐产生极大影响。

罗超在一篇文章中感慨道,「大家几乎都在反映今日头条最近文章阅读量下滑,一个核心原因是今日头条2017年之后加快产品的扩张步伐。去年公测的问答现已全面铺开、微头条上线直起对标微博、对短视频的重视力度有增无减,最近直接取消了许多自媒体的短视频上传数量限制。当今日头条不断引入更多功能时,就无可避免地面临着用户注意力主动或被动地分散到新业务上的问题。直接的结果是,图文类传统内容的流量更少。间接结果是图文类传统内容的创作者的收入变少了。」

回顾今日头条为何能从门户新闻客户端大战中脱颖而出,其实说白了并不难,今日头条每天赚到的广告收入远远高于获取用户的成本,这使得今日头条每天新增的用户数量要高于流失,竞争对手想要赶超今日头条就意味着持续不断的烧钱投入,对新浪、搜狐、网易来说,这都不是一件划算的生意。

但在意识到内容成为新的流量入口之后,BAT三巨头纷纷选择加码内容分发市场,不同于三大门户,他们有足够的资本和今日头条耗下去,这让今日头条依赖线下渠道的用户增长红利开始消失。

而这场不计成本的烧钱大战已经让今日头条感受到了压力,不久前的10亿美元融资其实就是今日头条加码的保障。但当今日头条将流量更多的分配给其他产品时,图文流量势必大幅下滑,在BAT向市场砸下真金白银时,内容生产者会逃离吗?这或许是当下今日头条一个无解的困局。

短视频和新生态,今日头条面对的两大挑战

最右联合创始人潘乱在最近撰写的《推荐算法的使命是模拟社交》一文中表示,

头条在把基于个性化推荐的内容消费做到极致后,也开始不断努力希望能够将基于关系的信息流融入到主产品里。比如直接照抄微博的微头条,作为建立关系链重要尝试的头条问答,还有最早的关注体系。

对于他的这篇文章,我是这样评价的,「时间会证明头条做社交可能会是个最大的错误,推荐在移动端打败搜索,靠的是基于兴趣和热点的个性化推荐,极大的提升了用户获取信息的效率。但从头条上线的各类社交产品看,很明显是朝着相反的方向努力,这主要是因为头条没有微博关注式的强关系链。」

但社交和推荐确实在慢慢走向趋同,微博和头条也越来越像。但微博和头条某种程度上却有一个本质的区别,那就是内容生产者的出发点。

微博和快手很大程度上是用户先贡献内容,等到内容达到一定量级后,很自然的就发展出了信息流广告这样一种商业模式,而头条从一开始就不具备这样的内容生产机制。头条的生态起源于抓取媒体的内容,在这基础上又诞生了「基于内容分成的头条号」。虽然同样都有大量的内容生产者,但头条上的内容生产者做内容就是为了赚钱。这也是为何我们在头条上总能看到标题党,而在微博上却很少看到的原因所在。

简而言之,头条是先让作者赚到了钱,然后才有了现在庞大的内容生态,这一定程度上也带歪了后来者,导致现在新的内容分发平台如果不为作者提供分成,就很难吸引他们生产内容。而快手和微博则恰恰相反,作者先贡献内容,然后再与平台一起赚钱,快手靠直播、微博靠网红,同样都是跑通了KOL生态。而头条则没有,所以只能分钱。

在《融资10亿美元,今日头条真正的焦虑在哪里?》一文中,我曾经发表过对头条未来的担忧,「头条会很缺钱,这来源于两部分,一是来源于自身战略,随着头条发力短视频、国际化以及各种投资,这些都需要不断投入。二是来源于巨头的压力,巨头给作者的补贴越来越多,大量优质内容作者面临流失的可能性,这迫使今日头条需要不断提高给内容创作者的分成。」

很显然,眼下头条所构建的内容生态并不算很健康,而内容创作者依靠流量分成变现,也不会是一件长久的事情。一个好的生态,一定是平台和作者都能赚到钱。而现在去年今日头条总收入才60亿元,光给作者就要分10多亿,这样的生意头条实在不愿意再继续做下去,调整早已在预料之中。

头条现在想要做的其实是两件事:「打赢短视频这场战役」和「建立新的内容生态」。

不久前搜狐和腾讯就头条上存在大量剪辑短视频侵权一纸诉状将其告上了法庭,但在纠缠于官司的同时,大量的头条号却收到了「视频无限发文」的权限,背后的目的不言而喻,头条想要在短视频上形成突破,还需要更多短视频,至于「侵权」与否,在头条没有打赢这场战争之前,其实并非首先需要考虑的事情。

而在建立新的内容生态上,头条的策略则是频繁调整内容分发机制:降低图文的广告分成系数以及把流量分给图文之外的问答、微头条,至于分配给新作者的逻辑也是一样(新入驻的作者没有原创标签,分成只有普通人的十分之一)。简而言之,新的机制之下主要会是给流量,逐步减少作者的分成,然后引导他们像微博那样,无偿给平台生产内容,然后一起寻找共赢的商业模式。

对今日头条来说,眼前的两大挑战决定着其能否在BAT的联合围剿下突围而出。成,则一飞冲天,成为独立于BAT之外的第四极;败,则要被迫站队,成为BAT生态的一部分。

微信公众号:俊世太保,个人微信:lijunhust

*文章为作者独立观点,不代表虎嗅网立场

本文由俊世太保 授权虎嗅网发表,并经虎嗅网编辑。转载此文请于文首标明作者姓名,保持文章完整性(包括虎嗅注及其余作者身份信息),并请附上出处(虎嗅网)及本页链接。原文链接:http://www.huxiu.com/article/193526.html 未按照规范转载者,虎嗅保留追究相应责任的权利

未来面前,你我还都是孩子,还不去下载虎嗅App猛嗅创新!


Spring boot executable jar/war 原理

$
0
0

Spring boot executable jar/war

spring boot里其实不仅可以直接以 Java -jar demo.jar的方式启动,还可以把jar/war变为一个可以执行的脚本来启动,比如./demo.jar。

把这个executable jar/war 链接到/etc/init.d下面,还可以变为Linux下的一个service。

只要在spring boot maven plugin里配置:

<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><executable>true</executable></configuration></plugin>

这样子打包出来的jar/war就是可执行的。更多详细的内容可以参考官方的文档。

http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#deployment-install

zip 格式里的 magic number

生成的jar/war实际上是一个zip格式的文件,这个zip格式文件为什么可以在shell下面直接执行?

研究了下zip文件的格式。zip文件是由entry组成的,而每一个entry开头都有一个4个字节的magic number:

Local file header signature = 0x04034b50 (read as a little-endian number)

即 PK\003\004

参考:https://en.wikipedia.org/wiki/Zip_(file_format)

zip处理软件是读取到magic number才开始处理。所以在linux/unix下面,可以把一个bash文件直接写在一个zip文件的开头,这样子会被认为是一个bash script。 而zip处理软件在读取这个文件时,仍然可以正确地处理。

比如spring boot生成的executable jar/war,的开头是:

#!/bin/bash
#
# . ____ _ __ _ _
# /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
# ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
# \\/ ___)| |_)| | | | | || (_| | ) ) ) )
# ' |____| .__|_| |_|_| |_\__, | / / / /
# =========|_|==============|___/=/_/_/_/
# :: Spring Boot Startup Script ::
#

在script内容结尾,可以看到zip entry的magic number:

exit 0
PK^C^D

spring boot 的 launch.script

实际上spring boot maven plugin是把下面这个script打包到fat jar的最前面部分。

https://github.com/spring-projects/spring-boot/blob/1ca9cdabf71f3f972a9c1fdbfe9a9f5fda561287/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/loader/tools/launch.script

这个launch.script 支持很多变量设置。还可以自动识别是处于auto还是service不同mode中。

所谓的auto mode就是指直接运行jar/war:

./demo.jar

而service mode则是由操作系统在启动service的情况:

service demo start/stop/restart/status

所以fat jar可以直接在普通的命令行里执行,./xxx.jar 或者link到/etc/init.d/下,变为一个service。

相关文章

h5调用底层接口的一些知识

$
0
0

      之前接触过这方面的知识,一直想写一些关于代码的文字,但考虑到浪费时间,又不具备大神的实力,也不想去把别人的代码照搬过来,所以一直都是空白着的,今天敲代码的时候,有了一个比较好的想法,第一,定位在学习这一块,去学习一些领域的新知识,并分享自己每天的学习成果,第二,总结工作中遇到的代码,解决的问题,拿出自己的demo截图,做出一些文字说明解释,相信通过这两个途径,能够让自己取得更大的发展空间。

h5调用本地摄像头

     前端时间使用HTML5做了一个WEB端APP,其中用到了H5页面调用手机摄像头的功能,当时也是花了不少时间去研究。最终是采用了HTML5plus(HTML5+)的方式完成了该功能,现将具体方法简单介绍下,并讲解下使用的注意事项。放在服务器上然后浏览就可以了,只支持Chrome和Safari核的浏览器,QQ浏览器,Chrome,Safari浏览器都可以。在不同的手机和浏览器上面展现的方式不一样。这是html5官方文档地址,里面有关于h5或js调用摄像头的全部方法 http://www.html5plus.org/doc/zh_cn/camera.html    

    本篇文章是个人学习期间整理的文档,涉及到很多方面的链接,在Android开发过程中,有时需要调用手机自身设备的功能,本文侧重摄像头拍照功能的调用。使用权限:调用手机自身设备功能(摄像头拍照功能),应该确保已经在AndroidManifest.xml中正确声明了对摄像头的使用及其它相关的feature。安卓调用手机拍照功能: http://blog.csdn.net/mahoking/article/details/28225907;对一些class类的介绍是非常详细的。

h5调用微信支付

   用户通过微信外浏览器打开商品页面,进行 微信支付购买商品… 第三方手机浏览器 H5支付浏览器打开的移动网页的主页,通常是由后端人员来实现,在混合式开发之中,则是由前端人员来掉开发的接口,移动端手机支付功能体现在前端页面上逐渐成为一个趋势,h5微信开发调用支付接口文档: https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_2 

    看到这样的一个问题:手机系统自带的浏览器,调用微信支付如何实现(非扫码),翻看了微信支付的api没发现支持h5调支付接口的情况(微信js除外),然后却发现美团的支付成功调用了,这是怎么实现的?H5支付用于非微信浏览器,在IOS上仍体验不佳,具体来讲就是无法自动回调。微信浏览器内的H5支付指公众号支付,微信已将此严格区分;首先要参见官方文档:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_1

       刚开始做的时候,需要配置一些东西,当然这都是一些最基本的东西;需要搭建环境来进行测试看到一篇这样的文章,博主对此总结了一些实质性的经验,学习起来事半功倍; 微信支付配置信息,JSAPI接口,H5调用微信js接口支付,微信公众号支付:http://blog.csdn.net/u010248119/article/details/50638529

      对于微信支付如何绕过应用签名这个问题;如果把微信支付的API封装起来导出JAR包给多个应用使用,怎么绕过这个应用签名,如果绕不过,那岂不是每个应用我都要建个APP了吗?题主提到的需求有两种方案: https://www.zhihu.com/question/46841202 

h5网页中使用打电话功能

    如果需要在移动浏览器中实现拨打电话,发送email,调用sns等功能,jquery mobile提供的接口是一个好办法。 采用url链接的方式,实现在safari ios,android 浏览器,webos 浏览器,塞班浏览器,ie,operamini等主流浏览器,进行拨打电话功能。在做一个微信的微网站中的一个便民服务电话功能的应用,用到移动web页面中列出的电话号码,点击需要实现调用通讯录,网页一键拨号的拨打电话功能。微信开发之移动手机WEB页面(HTML5)Javascript实现一键拨号及短信发送功能: http://www.cnblogs.com/skylaugh/p/3277899.html ;一句话总结html5中页面拨打电话的方式.

      在开发过程中,我们难免会遇到这种情况:H5跳转链接,拨号怎么做?在制作H5的过程中,很多时候需要用到事件功能,比如(链接跳转、拨号、翻页等),这里为大家介绍H5中常用的事件设置方式。 http://blog.sina.com.cn/s/blog_12d5cc7a50102wpzb.html 

h5调用扫一扫的功能

    使用H5调用手机摄像头扫描二维码。可以通过微信接口调用,直接用H5调用没弄过,很多人说兼容性不好,很多人觉得尽量集成的微信里面做 ,微信本身就提供了 扫一扫的js ,直接自己在浏览器里h5 做扫一扫 是没必要 的因为没应用场景,在网上看了一位技术大牛亲测有效的案例:web/html5调用摄像头实现二维码扫描,需要有后端代码的支持:http://blog.csdn.net/xuewufeifang/article/details/49756099

        这是一片以讲解的方式,帮助大家来理解微信官方的文档,同时给出调用扫一扫的例子。微信调用jssdk在网页端实现调用扫一扫,java+jsp; http://blog.csdn.net/u011327333/article/details/50439462 ;微信官方技术文档jssdk: http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html 

HTML5网页如何调用浏览器APP的微信分享功能?

    最近有看到新浪的移动端网页,增加了微信分享功能。在浏览器APP(Safari除外)中可以直接调用微信APP,分享至朋友圈或者聊天窗口。并不像很多网站是通过弹出一个图片提示右上角分享。并不是通过微信接口实现的,而是直接调用了浏览器APP自带的微信分享功能。比如我用安卓手机自带webkit内核浏览器(原生浏览器没有微信分享功能),打开新浪文章页,就不会出现这个微信分享的功能按钮。那么,用HTML5写的页面怎么获得微信接口,来实现一键分享的功能?废话就不多说了,直接上代码: HTML5网页如何调用浏览器APP的微信分享功能:http://blog.csdn.net/web_qdkf/article/details/50442938

        其实在移动端html5手机网站调用微信分享包括 :获取网络类型。调起客户端的图片播放组件。调用微信扫描二维码。判断是否安装对应的应用。发送邮件。分享到微信朋友圈。内容很多,我随意摘取几个,你们感受一下:

        获取“分享给朋友”按钮点击状态及自定义分享内容接口。

h5调用底层接口的一些知识

       获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口。

h5调用底层接口的一些知识

为什么放权的华为、腾讯越做越大,为老板管事的企业都死了?

$
0
0

Mark Suster是国外知名风险投资机构Upfront Ventures合伙人,该文是Mark Suster的观点和看法。

现实生活总总有源源不断地事情冒出来让人分神。通常 只有那些执着(甚至是到了痴迷程度)于最后的产出并且又不会在公司日常的大小事务中都面面俱到的领导者,才能够在竞争的最后脱颖而出,取得令人瞩目的成绩。

对于一家企业的管理者或者创始人,任何人都可以想出非常多的办法去找到并联系上他们,比如通过电子邮箱发邮件,或者是在Twitter,Facebook,领英这样的社交软件商上留言,还有更直接的就是打电话发短信。这些企业家们往往在社会上进行了过度的曝光,但在处理企业所面临的问题时却又面临着分配的资源与精力不足的问题。

每一个管理者麾下都有各种分工不同的团队想要他亲自出面进行定夺,比如工作意见,反馈,决策制定或者会议,每一个部门都觉得自己才是那个应该被优先考虑的部分。领导者们还被寄希望于出现在各种场合,像会场,活动,销售会议并活跃在公众的视野之中。人们希望能够看到企业家发一些博客动态,会转发些什么,有没有做什么演讲。之后可能还会有投资者过来,向你打听公司的最新进展,跟你打电话,甚至是需要定期汇报。

这就是现实生活的可怖之处,很多的管理者就这样掉入了忙碌无为的陷阱之中,好像每天都有许多事,但其实很多对于实现自己的目标都毫无作用。所以我经常建议他们,通过回避过于耀眼的目标和能阻止我们实现自己真正价值的让人分心的事物,来达到“少做事,多实现”这样的效果。

究竟如何“少做事,多实现”?

做的少,并不是说将工作量减少,而是所接触的事情不再那么繁杂。精简你手头上的事情,将精力花在那些你正做的越来越好并且完成质量高的方面。

我曾经见过不少这样的反面案例,比如因为觉得有太多活动可以出席,便彻底成为了一个全职的会议者,好像错过一场都会觉得自己遗失了什么重要的东西;还有人过于关注房间的装饰,一定要给自己员工免费的卡布基诺,但最后依然不能阻止员工在工作两年后跳槽去了新的更火热的创业公司;对于投资人来说更是如此,每天都有无数的天使轮项目找上门,都快要把脑子吵炸。

对此,这里有一些小tips能起到不错的帮助:

1.过去人们总喜欢说给自己定个“To Do List”一项项完成,但这并不完全适用于管理者。将自己想做的那一栏名单再精简一些,不要放置过多的目标,而是尽量让你“已经完成”的一览显得更加充实;

2. 少做一些新业务拓展的合作,而是让你目前正在做的事情变得更加有影响力; 不要招那么多的员工除非你的业务确实突飞猛进非要不可;

3. 选择一个好看又功能配置符合预期的办公场所,但是是以现阶段的公司规模来作为标准挑选,而不是两年之后可能的样子。

你不需要变成一个炙手可热的公众人物,你需要的只是变得成功,这是两码事。 成功往往是来自于只专心在一两件事上,将其做的出色,并通过顾客的反馈,产品的占有率,使用率的提升和年终营收的增长情况来得以体现,并与对手拉开明显的差距。

“漏斗”理论

其实各种事情的集合就像是一个倒置的漏斗,任何新的机会与契机都处在这个漏斗的最上层(“topend of funnel”),但是否能够有所收益也未可知,比如说第一次商务拓展的对话,第一次客户见面,招募环节的第一个面试者,第一次接受采访或者是第一次会上正式讨论商务计划。能够在“漏斗”的顶层接触到令人感兴趣或者是频繁出现的机遇固然很重要,但是最终的评分却取决于“漏斗”的最底端。

漏斗顶层,又被称作吸引人的选择:最初的交涉,一些听起来很有前景的想法,一些对你表示兴趣的合作伙伴,你在各种场合遇到的各种人;

漏斗中间,高概率:想法仔细考虑了,兴趣意向被确认,商业模式合理,双赢的局面;

漏斗底部,闭环:疯狂的聚焦极小部分事情来让想法付诸实现。

为市场部主管的位置进行大规模的筛选确实很好,但是三个月如果你都还没有让合适的人选到位,那么你仍然只是孤军奋战;也许你能找到一群对你非常有兴趣的商务伙伴和你进行对话,但如果最后结束时却没有一个人愿意与你进一步合作,那么这次的谈话就是彻彻底底的无用功。想要把漏斗顶部的一些事情不断推进到漏斗底部所需要花费的精力,时间与难易度与就在顶部不断接受新的机会相比简直不成比例。这也就是为什么 许多的管理者错误的把时间都浪费在了顶层的原因。

我们都知道高管们经常喜欢谈论愿景和他们即将要做的事情,但其实从来没有付诸过实践。最终,他们的这些鬼话连篇会越堆越厚。人们之所以会对这些目标趋之若鹜是因为开一个头远比最终实现它要容易的太多,根本不在一个数量级。

说到这我不禁也想谈谈我自己的一点体会。我在商业上最大的个人成功就是在最关键的事情上固执到痴迷的程度,对于其他事情彻底不管不顾直到我把手头上的事情解决。我知道当有事情需要完成时, 唯一的方式就是聚焦于漏斗的底层,这也就是为什么有些邮件我会在5分钟内回复,有些邮件却是一个星期都没有点开看。

开放本身是一种技能,但是只有能将精力聚焦在“漏斗”底部的人才配的上安心喝咖啡。

如何确定未上市企业的利润真实性?

$
0
0
其实风投和会计师回答这个问题应该更专业,投行也会做一些核查,但是性质上更像是复核而不是基础核查。

投行会核查:
1、制度
比方说收入确认制度。收入确认制度的意思是说,公司卖出去一笔货,在什么情况下公司可以有把握地说“通过卖这笔货我有了一笔收入”。这项制度设定是否正确,是否严格遵照执行,决定了企业账面的收入是否可靠,当然间接的影响到利润。
其他还有一些制度,核查的意义大致类似。

2、财务证据和报表
比方说存货有没有提够跌价准备,大客户有没有变来变去,现金流量健康不健康。还有交易凭证是否真实,存款数量是否真实之类的。
如果是已经快要上市的企业的话,一般纸面上的帐应该都做得四平八稳了,但如果是对财务这一套还不那么熟的企业,是有可能看出问题来的。

3、外部信息
这个是最复杂、最没标准但是也最管用的方法。
比方说和其他可比公司对比,毛利率有没有过高?成本有没有过低?每年生产那么多产品,和原材料采购数量对得上吗?
销售记录上一年多少车,那就可以蹲在工厂门口数数,每天多少车,算一下就知道真假了。蹲点的事情投行和风投都经常干。还有去查电表的,自己的帐和货都很好做,但是如果开工不足的话耗电量是不容易作假的,也是个办法。
大猫针对网络企业的核查方法也属于这一类。
企业任何业务都一定会留下印迹,造假的印迹和真实业务一定会不一样,只要懂行、肯下功夫,要查出漏洞一般也不会很难。

很好的参考案例是媒体对新大地的核查报道,里面研究了价格、毛利率、原材料采购,核查了经销商,甚至还查了中介机构的问题,个人觉得做得还是挺专业的:
http://www.nbd.com.cn/features/109

来源:知乎 www.zhihu.com
作者: Luo Patrick

【知乎日报】千万用户的选择,做朋友圈里的新鲜事分享大牛。 点击下载

此问题还有 43 个回答,查看全部。
延伸阅读:
怎样理解「现金流比利润更重要」?
PPP 模式和传统政府融资模式有何实质性的区别?

苹果非硬件收入就超过腾讯

$
0
0

别说话,快去手机百度关注199IT百家号

不知道从什么时候开始,国内不少手机厂商或电视厂商就鼓吹着硬件免费,服务收费的模式。当然硬件也并非真的免费,其实也就是把价格打的很低,接近毛利为0甚至亏损一点点进行销售。硬件免费,服务收费这个模式的核心套路是用更低的硬件价格去获取海量的用户,然后通过其上面的互联网服务收费。以手机为例,在用较低的价格获取较高的用户后,一般是通过应用商店,自营APP吸引用户使用,从而通过游戏分成,广告等方式实现服务的收费。

国内企业中,小米和乐视是硬件免费,服务收费最为激进的实践者,正是对后向服务收费的信心,小米把手机价格打的很低,一度获取极高的市场份额;乐视在智能电视上也是非常激进,以至于落得个大亏损。

也许他们是时候向苹果好好学习了。

苹果是硬件和服务两手抓,两手收费

以2Q17苹果的财报收入来看,苹果一共赚了529亿美元,其中硬件收入为458.6亿美元,服务收入为70.4亿美元,其规模的确非常惊人。


苹果硬件和服务双丰收并不是靠硬件杀价赢来的。苹果整体的毛利水平接近40%,净利率为21%。说明苹果既能在硬件上赚到钱,也能在服务上赚到钱,核心秘籍是产品好,自带光环,无需杀价。

苹果服务营收规模虽大,但对占比也才刚过10%

苹果一个季度的服务营收就赚到了70多亿美元,相比之下,腾讯去年4季度的营收也才63.6亿美元。苹果靠iOS应用商店,苹果音乐,iCloud等服务的营收就超过了腾讯的规模。

国内前面强调硬件低价免费,重点放在服务收费的企业把自己的品牌拉低不说,对后向服务收费的迷恋也搞得一地鸡毛,用户颇为不满。以某手机厂商在手机和智能电视上过分强调广告变现为例,就已经引发诸多的用户不良反馈和负面。不少所谓买了低价机器的用户一定头痛不已,不知道他们是否已经认识到世上并没有免费的午餐,即便是羊毛出在猪身上,猪也要把你给强奸回来。

而对于国内的互联网硬件厂商而言,重视用户体验和产品品质的苹果才是他们应该效仿的,不管是产品品质还是产品+服务两条腿一起跑的商业模式。

您可能也喜欢的文章:

腾讯:开放平台开发者分成超过20亿

2012年第三季度腾讯和Facebook收入结构对比

腾讯:2016年VR行业生态及风险研究报告

财报显示腾讯越来越像一家游戏公司

Newzoo:2014年科技公司在中国收入排名 苹果营收是腾讯的三倍
无觅

像巴菲特这类做价值投资的人,是怎么了解到一个公司真实的经营情况的?

$
0
0

其实问巴菲特如何做尽职调查已经是一件老生常谈的事情了,今天巴菲特的股东大会已经有不下三次回答这个问题。


根据巴菲特自己所说,他的尽职调查主要分为两个部分吧。

第一个部分,他们在投资或者收购一家公司之前,会列出一个清单,然后把需要了解的点都记录在上面,再根据情况去弄明白。

第二个部分,巴菲特认为,他投资的公司或者收购的公司,花了很高的薪水去聘请哪些管理公司的人才,那么就必须对这些人才了解清楚,也就是说,巴菲特的尽职调查重点还是在对管理层人事上的考察。


根据巴菲特自己说的这两点,其实可以看出巴菲特在了解公司真实情况上,并没有像普通投资者想象的那样锱铢必较的去寻找一个公司的漏洞,然后费劲心力的去判断是否真实。按照巴菲特自己所说,不要把手埋进沙子里,而迷失了对于前方的看法,更重要的是对商业模式、企业盈利前景的判断。


巴菲特在过去,曾经总结过自己大概的11点投资原则,分别是

这个商业模式是否简单可以理解?

是否有一个长期可靠的运营历史

是否有一个良好的长期预期

管理层是否可靠?

管理层能否代表股东的权益?

管理层是否能够抵制股东机构的胁迫?

相较于每股盈利,股权回报率如何?

企业产品的毛利润率如何?

留置的每一现金能否创造至少相同价值的市值

这门生意创造的的价值是什么?

现金流折现后的价格是否合适?


可以看到,投资原则中,将可理解的商业模式,可追溯的历史业绩,业绩可靠的管理层摆在前位,这从很大程度上就已经避免了普通投资者担心的那些有问题的公司,业务可理解,业绩增长,挣钱,然后估值足够便宜,虽然这样的公司在普通市场上很难买到,但是巴菲特也常在市场恐慌的时候出手,而且换手率也很低,根据目前伯克希尔哈撒韦年报显示,公司的持仓都是知名企业:



唯一被现在人比较有说辞的,大概就是IBM了吧,巴菲特在公开场合说过两句,减持IBM的原因之一,在于IBM前进的道路上有很强力的竞争对手,虽然 IBM 做的也很不错,但是这个竞争的环境是他之前没有考虑到的,而这也是尽职调查很难弄明白。



来源:知乎 www.zhihu.com
作者: Cody

【知乎日报】千万用户的选择,做朋友圈里的新鲜事分享大牛。 点击下载

此问题还有 9 个回答,查看全部。

考试后这样分析,才是学习进步的关键!

$
0
0
 



考试的功能有两种:检验和选拔。

除了中考、高考、竞赛类考试以外,其余几乎都是检验学生对知识的掌握情况,从中发现问题,帮助学生查漏补缺、调整学习方法。

所以,考后试卷分析其实是考试的一部分,或者说,与分数的获得相比,考后试卷分析才是真正收获的手段。

我们的老师和家长如果能与孩子照做文章所讲述的方式分析试卷,孩子一定会有进步的!


第一步:四个分析策略

 



所谓考后试卷分析,是指考试后订正试卷中出现的错误,分析考试的收获以及考试暴露出的问题,然后归类,逐一进行对照并制订出自我提高的措施与方法。所以,试卷分析要讲究以下4个策略:

策略1:从逐题分析到整体分析

从每一道错题入手,分析错误的知识原因、能力原因、解题习惯原因等。分析思路是:

①这道题考查的知识点是什么?

②知识点的内容是什么?

③这道题是怎样运用这一知识点解决问题的?

④这道题的解题过程是什么?

⑤这道题还有其他的解法吗?在此基础上,学生就可以进行整体分析,拿出一个总体结论了。

通常情况下,学生考试丢分的原因大体有三种,即知识不清、问题情景不清和表述不清。

所谓“知识不清”,就是在考试之前没有把知识学清楚,丢分发生在考试之前,与考试发挥没有关系。

所谓“问题情景不清”,就是审题不清,没有把问题看明白,或是不能把问题看明白。这是一个审题能力、审题习惯问题。

所谓“表述不清”,指的是虽然知识具备、审题清楚,问题能够解决,但表述凌乱、词不达意。上述问题逐步由低级发展到高级。研究这三者所造成的丢分比例,用数字说话,也就能够得到整体结论,找到整体方向了。

策略2:从数字分析到性质分析

要点有三:

①统计各科因各种原因的丢分数值。如计算失误失分、审题不清失分、考虑不周失分、公式记错失分、概念不清失分等。

②找出最不该丢的5~10分。这些分数是最有希望获得的,找出来很有必要。在后续学习中,努力找回这些分数可望可即。如果真正做到这些,那么不同学科累计在一起,总分提高也就很可观了。

③任何一处失分,有可能是偶然性失分,也有可能是必然性失分,学生要学会透过现象看本质,找到失分的真正原因。

策略3:从口头分析到书面分析

在学习过程中,反思十分必要。所谓反思,就是自己和自己对话。这样的对话可能是潜意识的,可能是口头表达,最好书面表达。从潜意识的存在到口头表达是一次进步,从口头表达到书面表达又是一次进步。

书面表达是考后试卷分析的最高级形式。所以,建议学生在考试后写出书面的试卷分析。这个分析是反观自己的一面镜子,是以后进步的重要阶梯。

策略4:从归因分析到对策分析

以上分析,都属现象分析,在此基础上,学生就可以进行归因分析和对策分析。三种分析逐层递进:现象分析回答了“什么样”,归因分析回答“为什么”,对策分析回答“怎么办”。对此,学生要首先做到心中有数,下面将做详细探讨。



第二步:九字诀(马上写、及时析、经常翻)

 



马上写。

首先,学生把做错的题重新抄一遍,然后请教老师或同学,详细写出正确过程和答案,主观性试题还应根据老师讲解的解题思路补充齐全。

及时析。

及时写出对试卷的分析内容,包含以下两步:①综合评价,即哪些题目做得比较好,哪些题目存在失误?②在纠正错题的基础上,对错题进行归类,找准原因,对症下药。

错误原因一般有三种情况:

一、是对教材中的观点、原理理解有误,或理解不广、不深、不透;

二、是对某些题型的解题思路、技巧未能掌握,或不能灵活地加以运用;

三、是表现在答题时的非智力因素方面,如遇到复杂些的论述题,便产生恐惧心理等,从而造成失误。

如果是第一种原因,学生应针对题目所涉及的有关知识要点及原理内容认真地加以复习巩固,真正弄懂弄通,如果是第二种原因,学生应要求自己务必掌握住某一题型的答题要领。

无论是哪一类题型,都有答题思路和方法,但关键是对某一特定试题具体作答的“个性”和“特殊性”,只有细心体会,才会有所感悟和提高。如果是第三种原因,学生应在平时训练中有意识地培养和锻炼自己的良好应试心理素质,努力克服不良心态,在答题时做到从容不迫、沉着冷静。

经常翻。

试卷自我分析写完后,和试卷粘贴在一起,要注意保存。积累多了,可以装订成册。千万不要束之高阁,要经常翻阅复习,以达到巩固知识,加强理解,培养能力,掌握规律的目的。

 



(图文来自网络)

本人是个思维导图爱好者,正在发起《绘制一百幅思维导图作品》的活动,欢迎有兴趣的朋友一起参与,让我们相互分享绘图的技巧和心得,共同体验思维导图给我们带来的乐趣。


登录工程:现代Web应用中的身份验证技术

$
0
0

“登录工程”的前两篇文章分别介绍了 《传统Web应用中的身份验证技术》,以及 《现代Web应用中的典型身份验证需求》,接下来是时候介绍适应于现代Web应用中的身份验证实践了。

登录系统

首先,我们要为“登录”做一个简要的定义,令后续的讲述更准确。之前的两篇文章有意无意地混淆了“登录”与“身份验证”的说法,因为在本篇之前,不少“传统Web应用”都将对身份的识别看作整个登录的过程,很少出现像企业应用环境中那样复杂的情景和需求。但从之前的文章中我们看到,现代Web应用对身份验证相关的需求已经向复杂化发展了。

我们有必要重新认识一下登录系统。登录指的是从识别用户身份,到允许用户访问其权限相应的资源的过程。举个例子,在网上买好了票之后去影院观影的过程就是一个典型的登录过程:我们先去取票机,输入验证码取票;接着拿到票去影厅检票进入。取票的过程即身份验证,它能够证明我们拥有这张票;而后面检票的过程,则是授权访问的过程。之所以要分成这两个过程,最直接的原因还是业务形态本身具有复杂性——如果观景过程是免费匿名的,也就免去了这些过程。

在登录的过程中,“鉴权”与“授权”是两个最关键的过程。接下来要介绍的一些技术和实践,也包含在这两个方面中。虽然现代Web应用的登录需求比较复杂,但只要处理好了鉴权和授权两个方面,其余各个方面的问题也将迎刃而解。在现代Web应用的登录工程实践中,需要结合传统Web应用的典型实践,以及一些新的思路,才能既解决好登录需求,又能符合Web的轻量级架构思路。

解析常见的登录场景

在简单的Web系统中,典型的鉴权也就是要求用户输入并比对用户名和密码的过程,而授权则是确保会话Cookie存在。而在稍微复杂的Web系统中,则需要考虑多种鉴权方式,以及多种授权场景。上一篇文章中所述的“多种登录方式”和“双因子鉴权”就是多种鉴权方式的例子。有经验的人经常调侃说,只要理解了鉴权与授权,就能清晰地理解登录系统了。不光如此,这也是安全登录系统的基础所在。

鉴权的形式丰富多彩,有传统的用户名密码对、客户端证书,有人们越来越熟悉的第三方登录、手机验证,以及新兴的扫码和指纹等方式,它们都能用于对用户的身份进行识别。在成功识别用户之后,在用户访问资源或执行操作之前,我们还需要对用户的操作进行授权。

在一些特别简单的情形中——用户一经识别,就可以无限制地访问资源、执行所有操作——系统直接对所有“已登录的人”放行。比如高速公路收费站,只要车辆有合法的号牌即可放行,不需要给驾驶员发一张用于指示“允许行驶的方向或时间”的票据。除了这类特别简单的情形之外,授权更多时候是比较复杂的工作。

在单一的传统Web应用中,授权的过程通常由会话Cookie来完成——只要服务器发现浏览器携带了对应的Cookie,即允许用户访问资源、执行操作。而在浏览器之外,例如在Web API调用、移动应用和富 Web 应用等场景中,要提供安全又不失灵活的授权方式,就需要借助令牌技术。

令牌

令牌是一个在各种介绍登录技术的文章中常被提及的概念,也是现代Web应用系统中非常关键的技术。令牌是一个非常简单的概念,它指的是在用户通过身份验证之后,为用户分配的一个临时凭证。在系统内部,各个子系统只需要以统一的方式正确识别和处理这个凭证即可完成对用户的访问和操作进行授权。在上文所提到的例子中,电影票就是一个典型的令牌。影厅门口的工作人员只需要确认来客手持印有对应场次的电影票即视为合法访问,而不需要理会客户是从何种渠道取得了电影票(比如自行购买、朋友赠予等),电影票在本场次范围内可以持续使用(比如可以中场出去休息等)、过期作废。通过电影票这样一个简单的令牌机制,电影票的出售渠道可以丰富多样,检票人员的工作却仍然简单轻松。

从这个例子也可以看出令牌并非什么神奇的机制,只是一种很常见的做法。还记得第一篇文章中所述的“自包含的Cookie”吗?那实际上就是一个令牌而已,而且在令牌中写有关于有效性的内容——正如一个电影票上会写明场次与影厅编号一样。可见,在Web安全系统中引入令牌的做法,有着与传统场合一样的妙用。在安全系统中,令牌经常用于包含安全上下文信息,例如被识别的用户信息、令牌的颁发来源、令牌本身的有效期等。另外,在必要时可以由系统废止令牌,在它下次被使用用于访问、操作时,用户被禁止。

由于令牌有这些特殊的妙用,因此安全行业对令牌标准的制定工作一直没有停止过。在现代化Web系统的演进过程中,流行的方式是选用基于Web技术的“简单”的技术来代替相对复杂、重量级的技术。典型地,比如使用JSON-RPC或REST接口代替了SOAP格式的服务调用,用微服务架构代替了SOA架构等等。而适用于Web技术的令牌标准就是Json Web Token(JWT),它规范了一种基于JSON的令牌的简单格式,可用于安全地封装安全上下文信息。

OAuth 2、Open ID Connect

令牌在广为使用的OAuth技术中被采用来完成授权的过程。OAuth是一种开放的授权模型,它规定了一种供资源拥有方与消费方之间简单又直观的交互方法,即从消费方向资源拥有方发起使用AccessToken(访问令牌)签名的HTTP请求。这种方式让消费方应用在无需(也无法)获得用户凭据的情况下,只要用户完成鉴权过程并同意消费方以自己的身份调用数据和操作,消费方就可以获得能够完成功能的访问令牌。OAuth简单的流程和自由的编程模型让它很好地满足了开放平台场景中授权第三方应用使用用户数据的需求。不少互联网公司建设开放平台,将它们的用户在其平台上的数据以 API 的形式开放给第三方应用来使用,从而让用户享受更丰富的服务。

OAuth在各个开放平台的成功使用,令更多开发者了解到它,并被它简单明确的流程所吸引。此外,OAuth协议规定的是授权模型,并不规定访问令牌的数据格式,也不限制在整个登录过程中需要使用的鉴权方法。人们很快发现,只要对OAuth进行合适的利用即可将其用于各种自有系统中的场景。例如,将 Web 服务视作资源拥有方,而将富Web应用或者移动应用视作消费方应用,就与开放平台的场景完全吻合。

另一个大量实践的场景是基于OAuth的单点登录。OAuth并没有对鉴权的部分做规定,也不要求在握手交互过程中包含用户的身份信息,因此它并不适合作为单点登录系统来使用。然而,由于OAuth的流程中隐含了鉴权的步骤,因而仍然有不少开发者将这一鉴权的步骤用作单点登录系统,这也俨然衍生成为一种实践模式。更有人将这个实践进行了标准化,它就是Open ID Connect——基于OAuth的身份上下文协议,通过它即可以JWT的形式安全地在多个应用中共享用户身份。接下来,只要让鉴权服务器支持较长的会话时间,就可以利用OAuth为多个业务系统提供单点登录功能了。

我们还没有讨论OAuth对鉴权系统的影响。实际上,OAuth对鉴权系统没有影响,在它的框架内,只是假设已经存在了一种可用于识别用户的有效机制,而这种机制具体是怎么工作的,OAuth并不关心。因此我们既可以使用用户名密码(大多数开放平台提供商都是这种方式),也可以使用扫码登录来识别用户,更可以提供诸如“记住密码”,或者双因子验证等其他功能。

汇总

上面罗列了大量术语和解释,那么具体到一个典型的Web系统中,又应该如何对安全系统进行设计呢?综合这些技术,从端到云,从Web门户到内部服务,本文给出如下架构方案建议:

推荐为整个应用的所有系统、子系统都部署全程的HTTPS,如果出于性能和成本考虑做不到,那么至少要保证在用户或设备直接访问的Web应用中全程使用HTTPS。

用不同的系统分别用作身份和登录,以及业务服务。当用户登录成功之后,使用OpenID Connect向业务系统颁发JWT格式的访问令牌和身份信息。如果需要,登录系统可以提供多种登录方式,或者双因子登录等增强功能。作为安全令牌服务(STS),它还负责颁发、刷新、验证和取消令牌的操作。在身份验证的整个流程的每一个步骤,都使用OAuth及JWT中内置的机制来验证数据的来源方是可信的:登录系统要确保登录请求来自受认可的业务应用,而业务在获得令牌之后也需要验证令牌的有效性。

在Web页面应用中,应该申请时效较短的令牌。将获取到的令牌向客户端页面中以httponly的方式写入会话Cookie,以用于后续请求的授权;在后绪请求到达时,验证请求中所携带的令牌,并延长其时效。基于JWT自包含的特性,辅以完备的签名认证,Web 应用无需额外地维护会话状态。

在富客户端Web应用(单页应用),或者移动端、客户端应用中,可按照应用业务形态申请时效较长的令牌,或者用较短时效的令牌、配合专用的刷新令牌使用。

在Web应用的子系统之间,调用其他子服务时,可灵活使用“应用程序身份”(如果该服务完全不直接对用户提供调用),或者将用户传入的令牌直接传递到受调用的服务,以这种方式进行授权。各个业务系统可结合基于角色的访问控制(RBAC)开发自有专用权限系统。

作为工程师,我们不免会考虑,既然登录系统的需求可能如此复杂,而大家面临的需求在很多时候又是如此类似,那么有没有什么现成(Out of Box)的解决方案呢?自然是有的。IdentityServer是一个完整的开发框架,提供了普通登录到OAuth和Open ID Connect的完整实现;Open AM是一个开源的单点登录与访问管理软件平台;而Microsoft Azure AD和AWS IAM则是公有云上的身份服务。几乎在各个层次都有现成的方案可用。使用现成的产品和服务,能够极大地缩减开发成本,尤其为创业团队快速构建产品和灵活变化提供更有力的保障。

本文简单解释了登录过程中所涉及的基本原理,以及现代Web应用中用于身份验证的几种实用技术,希望为您在开发身份验证系统时提供帮助。现代Web应用的身份验证需求多变,应用本身的结构也比传统的Web应用更复杂,需要架构师在明确了登录系统的基本原理的基础之上,灵活利用各项技术的优势,恰到好处地解决问题。

登录工程的系列文章到此就全部结束了,欢迎就文章内容提供反馈。


更多精彩洞见,请关注微信公众号:思特沃克

十亿级视频播放技术优化揭密

$
0
0

QCon是由InfoQ主办的全球顶级技术盛会,每年在伦敦、北京、东京、纽约、圣保罗、上海、旧金山召开。自 2007年 3月份首次举办以来,已经有超万名高级技术人员参加过QCon大会。QCon内容源于实践并面向社区,演讲嘉宾依据热点话题,面向 5年以上工作经验的技术团队负责人、架构师、工程总监、高级开发人员分享技术创新和最佳实践。

4月18日性能优化面面观专题会议上,腾讯研发总监王辉以“十亿级视频播放技术优化揭秘”为主题,用QQ空间的日均播放量一年内从千万级突破到十亿级所面临的严峻考验为切入点,为大家分享视频团队在视频组件的整体架构、优化效果衡量、带宽优化、秒开优化、缓冲优化、成功率优化等六个方面的技术实践。

王辉:大家早上好!我叫王辉,来自腾讯,从2009年开始从事QQ空间技术研发,近期主要关注手机短视频、视频直播、AI智能硬件。很高兴能在QCon上与大家一起分享和交流。我今天的话题是“十亿级视频播放技术优化揭密”。主要介绍我们团队在去年一年短视频风口上,我们的视频播放量从5000万到十亿级过程中的一些技术实践,希望我的介绍能给大家做一些借鉴和参考。

众所周知,短视频去年是一个风口,起因是来自Facebook 2015年Q3的财报,财报表明在Facebook平台上每天有80亿次短视频播放,给Facebook带来了强劲的广告收入,正是这个数据给国内核心大公司和创业公司带来的一些新的突破口。其实短视频已经不是一个新的概念,从2006年开始国内就有很多公司在做短视频。随着Facebook吹起短视频风,去年在短视频行业有近百款应用出现,中国网民里面每5个里面就有1个是短视频的用户,短视频成为互联网的流量入口。QQ空间也在这个风口中,从2015年11月份的每天5000万视频播放量,经过一年的耕耘细作,徒增到2016年12月份的10亿量级,现在还在不断增长。

我的演讲主要是按照我们产品迭代的几个关键步骤展开:

首先是快速上线,2015年我也是跟随着大家的体验快速上线了新短视频的体验;

其次面临的是成本问题,在做的过程中做了一些成本的优化工作;

然后是体验优化。在解决成本问题之后,短视频的观看体验要做到极致。比如说视频的秒开、不产生缓冲、不卡、成功率的提升。

快速上线

在开始之前,我先介绍一下我们的团队职责,我们团队负责手机QQ和手机QQ空间两个APP,每个APP有iOS和Android两个团队,一共四个团队,四个团队负责两个APP。在这个项目中,我们四个团队要针对两个平台实现四套逻辑,这里的效率是存在一定的问题。

关于短视频体验,在这之前,我们也只是做到能播放而已,没有做很精细的工作,并且这里的产品观感体验也不是很一致,也不是很好。

技术上,之前也只是做很基础的架构,直接由播放器连接服务器下载数据,达到能播放就可以。之前我们没有积极去做这个事情,导致播放成功率低、失败原因未知、不支持边下边播、缓冲时间比较长等问题,流量浪费也比较严重。在产品上要解决的,是在整个APP里面把所有产品的体验做到一致,比如说每个功能的观看体验,视频浮层的体验,统一观看体验也为我们项目清除了很多障碍。

而这里的技术上的要点是非常关键的,第一个是边下边播,这是基础的要求,是为了加快视频播放速度。第二个是流量的控制,这么大的平台,之前只是做5000万的播放量,如果没有流量控制的云策略,可能到后面流量是无法把控的。第三,刚才讲到团队的现状,团队要负责两个APP,这里要做代码复用,不可能再像之前一样四个团队维护四套代码,第四,我们支持第三方的视频源。第五,需要完善监控上报,对业务知根知底。

可以看到,完成核心技术要点最核心的一点是如何控制视频的下载,传统的方式是播放器直接塞播放地址给播放器,它就可以直接播放,这其实是一个黑盒。我们在中间加了一个本地代理,播放器与服务器的数据请求,我们完全可以把控。在这个过程中,比如说播放器要数据时,可以给它更多的数据,这样能解决它缓冲的问题。有了这层代理之后,架构也更清晰一点。

基于这样的架构,在MODEL一层做一些业务的逻辑,在VideoController这一层做控制视频的播放和下载。有了下载代理之后,就可以通过代理管理下载,在APP里面有很多的视频请求,VideoProxy可以管理这些请求,做流量控制,做预加载,还可以做优先级调度和做监控上报,下载逻辑层则主要关注怎么优化服务器,对接缓存管理层,同时我们抽象出了一个数据层,我的数据源可以是HTTPDataSource,也可以读本地,也可以是来来自腾讯视频的数据源,也可以是第三方APP的数据源,协议层主要是HTTP、HTTPS、HTTP2的解决。

在VideoController的逻辑里,其实都可以放到C层来实现,这样安卓和iOS完全可以通用,这一层的逻辑可以在QQ和QQ空间两个APP里面使用,相当于是我们一套逻辑可以完全复用,不用再开发四套逻辑,我们团队的职能也做了相应调整,之前可能是按团队划分,四个团队负责四个终端,现在可能是按FT的方式划分做视频的团队,iOS做视频的团队可能负责QQ和QQ空间里的业务,安卓也是如此。直播的FT也可以这样划分,iOS的负责iOS的两个APP,安卓的负责安卓的两个APP,这样代码复用更清晰一点,我的团队更专注一点。视频的团队专注视频的研发。

监控上报,肯定是不可缺少的,这是一个成熟的项目必备的要素。

1. 问题定位,老板跟用户反馈说我这个视频播不了,要有一套成熟的问题定位的方式;

2.  耗时统计,用户播放这个视频花多长时间播出来,这也是要了解到的;

3. 成功率统计,外网用户播放视频的成功率是多少?还要通过实时报警,才能及时知道外网发生一些故障。

传统的捞Log方式大家都有,但是这种方式效率太低,需要等用户上线之后才能捞到Log,Log捞到之后还得花时间去分析。我们做法的是在关键问题上做一些插装,把每一类错误和每一个具体的子错误都能定义出来,这样一看错误码就知道播放错误是由什么原因导致的。还可以把每次播放视频的链路所有关键流水上报到统计系统里来,每一次播放都是一组流水,每一条流水里面就包含了例如首次缓冲发生的Seek,或下载的链接是多少,下载的时间是多少,有了这些流水之后,用户反馈播放失败,我首先可以用流水看发生了什么错误?错误在哪一步?每一步信息是什么?几秒钟就可以定位到问题。

有了这个数据上报之后,还可以做一些报表。比如说可以做错误码的报表,有了报表之后就可以跟进哪个错误是在TOP的,负责人是谁,原因是什么,都可以看到。

我们也有自己实时的曲线,可以看到各项数据的情况。在告警方面,基于成功率和失败率的统计,进行实时告警。一出现错误码,微信立即可以收到提醒,提醒说是什么原因导致这次告警,全自动。

成本优化

上线一个月之后,一个坏消息一个好消息。好消息是播放量涨了4倍,坏消息是带宽涨了6倍。带宽优化是每个做视频的人必须要面临的问题,我们也分析这个过程中的原因,发现因为改为边下边播之后用户观看视频的意愿比较强,用户有挑选心理,不是每个视频都去看,看了一下之后不喜欢就划走了,之前下载的那部分其实是浪费的。如果之前不做限速的话,一点开视频就疯狂的下数据,带宽有多大就下多少的数据,这样浪费很严重。

我们采取的第一个策略是进行流量控制。在高峰期播放到第10秒时,预下载N秒数据,下载到N秒就停下来。然后,可以做多级限速。一开始不限速,下载到合适时机做1倍码率限速。高峰期时预加载的数据会少一些,防止高峰期时带宽占用明显,这是初级的策略。最终我们也有码率切换的策略。这对用户的观看体验影响比较大,这也是之前必备的一个策略。

上线这个策略之后,对带宽的优化还是比较明显的。在高峰期时从18:00到凌晨1点带宽下降25.4%,这个是我们不断灰度最终确定的值。这个值会影响播放缓冲,因为数据少的话必定会卡顿,在卡顿之间和流量之间取了一个最优值,最终是25.4%.

但这样肯定是不够的,因为流量涨的还是很明显的,我们想到H.265,压缩率相对于H.264提升了30%-50%,但它的复杂度也是呈指数级上升。复杂度导致它的编解码耗时更长,占用资源也更长。如果把H.265用在客户端上的话,可能要评估一些点,比如说在编码上面,现在手机上没有做H.265硬件支持的,相对于H.264的耗时3-7倍,之前耗时可能是10分钟,而现在可能需要到70分钟左右。解码的硬件支持H.265的也很少,耗时差不多是一样的。解码是可行的,你可以采用软解的方式,这个带来的问题是CPU占用非常高,可能之前H.264占20%的CPU,H.265占70%、80%左右,带来的问题是发热和耗电。

结论,解码是可行的,但是编码不用考虑,在移动客户端不可行的情况下,那编码就要放在后台来做了。

为了解决如何在我们手机上能够解码的问题,对手机的解码能力做一次评估。我在合适的时机做一次大规模的浮点数运算,将数据上传到后台服务器进行云适配。如果当前的指数满足H.265条件的话,可以给你下载H.265视频给你播放。从而保证软件解码柔性可用,针对视频源规格按机型适配降级,保证用户视频播放体验。

经过我们的统计,外网上有94%的手机还是支持H.265解码的。支持1080P手机的解码占46%.

编码只能在后台做,如果在视频后台进行全面编码的话,是不现实的。因为编码复杂度呈指数级上升,拿后台服务器进行编码也是不可行的。我们的做法是只用热点视频进行后台转码,不是所有视频都去编码,对观看量在TOP N的视频进行编码,只需要编码少量的视频就可以带来流量优化效果,因为TOP N就占了全网80-90%的流量。

因为热点视频的热点转化很快,可能前几分钟是热点,后几分钟就不是热点,因为社交网络的传播非常快。我们给后台的要求是转码速度一定要快,在之前没有优化时,转一个10分钟的视频要半个小时左右。后来我们做了分布式处理之后,转10分钟的视频只用两三分钟。一些短视频最长5分钟左右,只要监测到这个视频很热的话,1分钟之内就能转出来,就是H.265了。

同样,在H.265编码器上做了一些优化,比如说编码速度和码率的都会有提升。

上线H.265优化之后,我们的带宽进一步下降,节省了31%左右。

秒开优化

带宽问题解决之后,面临的下一个问题是体验优化。用户最想要的是视频能立马播出来。我们定了一个秒开技术指标,只要这个视频从到我的视野范围,到视频播出来之间的耗时在一秒以内。这也是对标Facebook的体验,Facebook一打开动态,视频是能立即播出来的,不需要等待就能播,这个体验其实很顺畅。核心的流程主要是三个步骤:1、客户端的耗时;2、下载数据;3、等待播放。

这里主要有两个大的耗时点,第一下载视频数据耗时;第二个是客户端的耗时,下载视频数据耗时的话,主要是下载数据量和下载的速度。

这里有一个很直接的问题,播放器需要下载多少数据才能播放?我们可以看一下MP4,MP4其实是一个比较灵活的容器格式,每个东西都是用Box表达的,每个Box又可以嵌入到另外一个Box。MP4主要由MOOV和Mdata组成,MOOV是囊括了所有的视频关键信息,肯定是先把MOOV下载完之后才能找视频数据才能播起来。不巧的是,在我们外网会发现有5%左右用户上传的视频,它的MOOV是在尾部的。后来也发现,有很多安卓手机比如说山寨机,一些摄像头处理的厂商可能比较偷懒,因为他们只有在你采集完信息之后才能知道他所有的信息,他可能把所有的信息放在尾部。对于MP4来说,一开始把头部下载了,下载头部时可能找不到这个MOOV,就猜测这个MOOV在尾部,我可能就有一次请求去探测这个头部到底在哪?这个探测的话基本做法是去尾部探测。如果MOOV在其他地方的话,这次播放肯定是失败的。现在主流的系统都是去尾部进行一次探测。

比如安卓某些手机是无法自定义Range,那就需要下载完整个文件才能播放。我们的处理方式,用户在后台做一次转码修复,客户端采集后做一次转码修复。

再看一下Mdata,这是视频的原数据。目前大部分是H.264编码,H.264通过真预测的方式来进行视频编码,这里有一个GOP概念,也是在直播里面经常谈的。一般的视频需要下载歌完整的GOP数据才可以播,可以看到在这个里面需要下载多少数据才能播呢?每个播放器的行为也不一样。大家可以看到iOS要下载一个完整的GOP才可以播。像FFmpegBasedPlayer的话我可能只需要关键帧就可以播出来。安卓是比较尴尬的一个系统,在6.0级以下,可能需要5秒视频数据才可以播起来。如果说是需要下载5秒数据才可以播起来的话,那肯定是非常慢的。我们这里的一个策略会采用FFmpegBasedPlayer自己来做解码,这里要关注功能性和耗电的问题。

解决了Mdata之后,你会发现如果我的数据在头部,拿关键信息进行播放的话,其实我播放的数据量非常小的。

对于下载优化的话,会有一个防盗链的请求,通过HTTP拿到真实的才可以下载数据。在手机上执行HTTP请求是非常耗时的,这里我们走私有长连接通道做这个事情。

关于优化下载链路,这里也是谈的比较多的,一般也是直接输出IP地址,利用IP地址做跑马的策略,兼顾性能的效率,这个是用的比较多的方式。

进一步思考,按照普遍600K码率的话,我们统计到现在APP上面下载的平均速度是400K左右,这样计算的话,可能在安卓上面播放一个视频的话,需要将近0.9秒左右才可以下载到你需要的数据。如果码率再进一步提升的话,可能会更大,这其实我们也做了一些场景分析,会发现我们是社交网站,它有好友动态,视频在好友动态里播放,或者是在视频浮层里播放,我们的选择是预加载的策略,这也是常见的策略。我们会在当前看的这条动态时会预加载后面视频的关键信息,比如说会加载头部信息和需要播放的数据,来进行预加载。比如说在播放当前视频时,我的视频在加载一定数据之后会加载下一秒预加载数据,这些都可以做到的。

预加载有一个问题,我们之前踩了一个坑,可能预加载视频时还是要优先图片的。视频当然重要,但是社交网络上的图片也更重要,可能在预加载视频时会考虑到图片的一些任务,还有视频封面之类。

优化效果也是比较明显,经过刚才几个策略,一个是我们对头和播放器的处理,我们对防盗链的处理,还有对下载链路的处理和预加载,这样我们的耗时大幅度减少了,之前是1.8秒降到0.6秒左右。客户端的性能也是让人容易忽视的问题,发现有些用户虽然有视频的缓存,但是播起来还是很慢,这其实是客户端性能的影响。因为视频涉及到的BU和流程比较多,在这个过程中还要更关注到客户端的影响,要分析下客户端是哪些在抢占你的视频播放资源,我们之前犯过一些错误,md5会卡住一些流程,或者是HttpParser会阻止你的任务,会导致视频播放更慢。

在优化视频播放过程中,我们在4月份也做直播。直播这里面插入个事情,我们要播放直播的视频流,是HLS的视频,在好友动态里面可以观看直播的内容。HLS在安卓上面体验非常差,因为安卓3.0之后对HLS基本没有做的优化工作,这里每次安卓上播放HLS需要等待6-9秒。分析发现它的处理也不是很得当,因为安卓系统请求链路较长,串行下载,需要下载3-4片TS才能启动播放,下载3个分片的话,耗时就会很久。之前提到我们这里有代理,有了代理之后做事情方便很多了,通过里获取M3U8,解析M3U8里面有哪些文件,可以做并行下载,只让他下载一次M3U8,这样下载速度大幅度提升。回到刚才架构上,有了下载代理层的话,你可以做HLS的加速和管理,可以加入HLS的视频源。

HLS缓冲耗时也是很明显的,之前需要6秒左右,现在1.6秒左右就可以播起来。整体从之前的2秒左右现在优化到700m秒,80%用户都可以在1秒内播视频。

还有一个是用户比较关注的问题,观看视频时卡,观看一会卡了一下,loading数据,loading完以后又卡,这个体验非常差,我们希望所有的视频都不卡。其实这有两个播放场景,一个是正常场景,边下边看,数据在下载。对于正常场景下载时会做一些带宽调整,在低速时会做切换IP的处理,比如说当前连通IP的耗时比较久的话,会做一些处理,也会对网络进行速度限制。

针对Seek场景的话,如果用户拖动的话,文件缓存系统是连续存储系统的话,必然会造成拖到这里时,后面的缓存数据是没有办法下载到系统里面来的。

我们就对存储做了一次重构,支持文件空洞。会按照一兆的方式对文件进行碎片划分,好处是我可以分段存储,我可以允许逻辑空洞的,拖动的话也可以在后面存储,也不需要数据库,我可以知道这是从哪个位置到哪个位置的存储。这样淘汰缓存也高效一点,并可以制定更灵活的缓存策略。这里可以淘汰更低密度的文件,还可以做的事情是对文件加密。这里产生卡顿的用户里面,90%是因为进行拖动,拖动之后又没有缓存数据,所以这里有可能导致发生缓冲。统计效果也是比较明显的,上了分片缓存之后,之前的缓存概率是4.6%左右,最后下降到0.48%,基本上看不到发生缓冲的场景。

成功率优化,也是我们比较关键的指标。成功率优化没有捷径,可能是Case by Case各个击破。之前我们进行编码,有几百个错误码,错误码原因进行上报,每次进行循环,一个个错误码进行解决。下载常见的错误码,DNS劫持是比较多的,一些网络运营商会劫持你的请求。

这个在国内是比较常见的劫持,有的小运营商按月会劫持你的视频内容,可能直接污染你的DNS让你查找不到CDN,这是比较多的,还有一些网络不稳定的影响导致。更隐藏的会直接污染你的视频内容,让你视频内容是错误的。播放比较多的可能是一些编码的原因,刚才提到一些手机采集出来的视频在低端手机上播不出来,我们会对这些视频进行修复。

逻辑上的问题,因为播放器有状态的,可能开发人员比较多,每个人过来加一个逻辑的话,会导致播放状态出现问题。

我们做的播放器错误解决方法:

HOOK播放器接口与回调,实现播放器状态机,监控插放器API的调用是否合法,不合法直接告警或Crash。帮助开发快速定位问题,同时减轻测试同事的负担,封装成UI组件,使其它开发不必理解播放器。

最终优化的成果是这样的,下载成功率优化前是97.1%,优化后是99.9%。播放成功率优化前是97.0%,优化后是99.9%。首次缓冲耗时优化前是1.95s,优化后是0.7s。二次缓冲概率优化前是4.63%,优化后是0.48%。数据还是很可观的。

我的演讲基本是这些,欢迎大家关注我们团队的公众账号,也会分享一些技术文章。   

Q&A

问题1:刚才您提到已经开始尝试用265了,能透露一下265你们播放的在整体中占多大的比例?

王辉:现在热视频里面80%都是H.265。10亿里面会有70%、80%左右。

问题2:推进的还是挺靠前的。刚才你提到要判断手机的H.265能力,用大规模的浮点运算,首先我先了解一下你们用的什么浮点运算的Mark?其次,为什么要用浮点运算?其实视频编解码里面几乎全部都是整数运算。

王辉:浮点运算能代表你这个手机性能,其实我们是评估性能的,不是评估H.265转码,如果性能达到的话,解码也是可以达到的。

问题3:如果想针对解码做评估的话,我建议整数运算。你可以确认一下,首先视频编解码的标准规定是没有浮点运算,不同的编解码时限可能会插入少量的浮点运算,大部分是整数运算。

王辉:我们初步做的时候是判断手机有没有运算能力来做的浮点运算判断。

主持人:感谢各位嘉宾的提问,感谢王辉老师给大家带来的讲解。

ofo员工爆料内部大范围贪腐,他们都是如何薅平台羊毛的?

$
0
0

看似无限风光的ofo,背后似乎隐忧重重。

 

继创始团队被资本架空的消息传出之后,ofo内部贪腐的消息也甚嚣尘上。近日,实名社交平台脉脉上,有ofo前员工爆料称:这个公司从高层到基层,贪腐现象严重。

 


而另外一个现任员工则侧面证实了这个观点,并表示,一个城市经理可以每月可以贪好几万,就连学校运营都可以贪个几万十几万。

 

从爆料信息来看,ofo内部贪腐的主要方式有两个——“吃空饷”、“吃回扣”。

 


“吃空饷”现象集中在线下运营业务领域。据悉,ofo运维团队的招聘主要由区域运营专员负责,修车、摆车师傅都是由运营独立负责招聘和结算工资。 根据拉勾网等招聘网站显示,运营师傅的月薪在4000-6000左右,主要工作内容是996的找车搬车(工时朝九晚九,每周工作6天)。

 

由于区域经理全权负责招聘和发薪,所以想在这一项上贪污的方式很简单,只需要向上级多上报5-6个修车师傅名额即可,每月就可以多拿两三万的费用,且上级不会追查。如果此现象属实,那么ofo目前如此高的损坏率也不足为奇了。

 

而“吃回扣”的现象则体现在供应链端。尽管ofo的slogan是“不生产单车、只连接单车”,但CEO戴威在此前的采访中也坦陈,来源于C端共享的自行车占比极低,与供应链合作生产的单车仍占绝对主流。

 

根据内部爆料称 ,ofo某城市的供应链被架空,在采购上做主的是维修仓库主管,因为供应商是自己的好友,就把十年前的旧胎当做新胎采购回来,并直接进行组装

 

对于此爆料,ofo资深副总裁南楠也在其后回应:如果你真是一名有正义感的ofo员工,希望联系我,公司会认真处理你反馈的问题。

 

但随后,“尤二姐”就立即回应南楠,认为ofo实际的贪腐情况比爆料中更为严峻。“(你)不要觉得这是一个城市或者某个城市(的问题),全国每个城市都是这样。还是应该花心思投入安装智能锁,花那么多钱到人上面,终将一层一层被拿掉。”

 

而作为竞争对手的摩拜,在线下运维上选择了自营模式。 据业内人士透露,摩拜在北京一地招聘的运维团队规模就在80人左右,找车、挪车的师傅属于临时工,尚不计算在其中

 

显然,ofo目前正忙于开城市、铺单车、寻找代言人。然而,如果脉脉平台爆料属实,目前ofo的当务之急显然是补全公司的制度漏洞,通过技术的方式来监控车辆情况。全面安装GPS定位和电子锁应该是第一步。

 

“共享”单车走到今天,赛道中的每个选手都已经难对资本和用户讲出全新的故事。那么用户体验、现金流运转就显得至关重要。对于ofo来说,“攘外必先安内”,想要维持第一梯队的地位,就得立刻处理那些在平台上“薅羊毛”的自家员工。


下载虎嗅APP,第一时间获取深度独到的商业科技资讯,连接更多创新人群与线下活动

Java常见面试题之Forward和Redirect的区别

$
0
0

阅读目录

一:间接请求转发(Redirect)
二:直接请求转发(Forward)

用户向服务器发送了一次HTTP请求,该请求可能会经过多个信息资源处理以后才返回给用户,各个信息资源使用请求转发机制相互转发请求,但是用户是感觉不到请求转发的。根据转发方式的不同,可以区分为直接请求转发(Forward)和间接请求转发(Redirect),那么这两种转发方式有何区别呢?本篇在回答该问题的同时全面的讲解两种请求转发方式的原理和区别。

【出现频率】

☆☆☆☆

【关键考点】

  • 请求转发的含义;
  • Forward转发请求的原理;
  • Redirect转发请求的原理。

【考题分析】

Forward和Redirect代表了两种请求转发方式:直接转发和间接转发。

直接转发方式(Forward),客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。

间接转发方式(Redirect)实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。

举个通俗的例子:

  直接转发就相当于:“A找B借钱,B说没有,B去找C借,借到借不到都会把消息传递给A”;

  间接转发就相当于:”A找B借钱,B说没有,让A去找C借”。

下面详细阐述一下两者的原理:

一:间接请求转发(Redirect)

间接转发方式,有时也叫重定向,它一般用于避免用户的非正常访问。例如:用户在没有登录的情况下访问后台资源,Servlet可以将该HTTP请求重定向到登录页面,让用户登录以后再访问。在Servlet中,通过调用response对象的SendRedirect()方法,告诉浏览器重定向访问指定的URL,示例代码如下: 

......
//Servlet中处理get请求的方法
public void doGet(HttpServletRequest request,HttpServletResponse response){
//请求重定向到另外的资源
    response.sendRedirect("资源的URL");
}
........

上图所示的间接转发请求的过程如下:

  • 浏览器向Servlet1发出访问请求;
  • Servlet1调用sendRedirect()方法,将浏览器重定向到Servlet2;
  • 浏览器向servlet2发出请求;
  • 最终由Servlet2做出响应。

二:直接请求转发(Forward)

直接转发方式用的更多一些,一般说的请求转发指的就是直接转发方式。Web应用程序大多会有一个控制器。由控制器来控制请求应该转发给那个信息资源。然后由这些信息资源处理请求,处理完以后还可能转发给另外的信息资源来返回给用户,这个过程就是经典的MVC模式。

javax.serlvet.RequestDispatcher接口是请求转发器必须实现的接口,由Web容器为Servlet提供实现该接口的对象,通过调用该接口的forward()方法到达请求转发的目的,示例代码如下:

......
    //Servlet里处理get请求的方法
 public void doGet(HttpServletRequest request , HttpServletResponse response){
     //获取请求转发器对象,该转发器的指向通过getRequestDisPatcher()的参数设置
   RequestDispatcher requestDispatcher =request.getRequestDispatcher("资源的URL");
    //调用forward()方法,转发请求      
   requestDispatcher.forward(request,response);    
}
......

上图所示的直接转发请求的过程如下:

  • 浏览器向Servlet1发出访问请求;
  • Servlet1调用forward()方法,在服务器端将请求转发给Servlet2;
  • 最终由Servlet2做出响应。

技巧:其实,通过浏览器就可以观察到服务器端使用了那种请求转发方式,当单击某一个超链接时,浏览器的地址栏会出现当前请求的地址,如果服务器端响应完成以后,发现地址栏的地址变了,则证明是间接的请求转发。相反,如果地址没有发生变化,则代表的是直接请求转发或者没有转发。

问:直接转发和间接转发的原理及区别是什么?

答:Forward和Redirect代表了两种请求转发方式:直接转发和间接转发。对应到代码里,分别是RequestDispatcher类的forward()方法和HttpServletRequest类的sendRedirect()方法。

对于间接方式,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。它本质上是两次HTTP请求,对应两个request对象。

对于直接方式,客户端浏览器只发出一次请求,Servlet把请求转发给Servlet、HTML、JSP或其它信息资源,由第2个信息资源响应该请求,两个信息资源共享同一个request对象。

相关文章

Facebook提出全新CNN机器翻译:准确度超越谷歌而且还快九倍(已开源)

$
0
0

选自code.facebook

作者:Jonas Gehring、Michael Auli、David Grangier、Denis Yarats、Yann N. Dauphin 

机器之心编译

参与:吴攀、微胖、蒋思源


去年谷歌在机器翻译上取得了连续不断的突破,参阅《 重磅 | 谷歌翻译整合神经网络:机器翻译实现颠覆性突破》和《 重磅 | 谷歌神经机器翻译再突破:实现高质量多语言翻译和 zero-shot 翻译》。谷歌的方法用到了文本处理惯用的循环神经网络。近日,Facebook 也宣布在神经机器翻译上取得了重大进展,在超过了去年谷歌研究的水平的同时还实现了显著的速度提升。而和谷歌的方法不一样,Facebook 的方法采用了一种完全基于卷积神经网络的架构。机器之心对 Facebook 博客上的介绍文章进行编译,同时在文末附上了该研究论文的摘要介绍,另外该研究的相关源代码和模型也已经在 GitHub 上开源。


  • 论文地址:https://s3.amazonaws.com/fairseq/papers/convolutional-sequence-to-sequence-learning.pdf

  • GitHub 项目地址:https://github.com/facebookresearch/fairseq


Facebook 的使命是让世界更加开放和互联,让每个人都能以自己偏好的语言享受视频和博文——当然,准确度和速度要尽可能最高。因此,语言翻译就显得很重要了。


今天,FAIR 团队推出了一项研究成果:使用一种全新的卷积神经网络(CNN)进行语言翻译,结果以 9 倍于以往循环神经网络(CNN)的速度实现了目前最高准确率。[1] 另外,你可以在 GitHub 开源许可下下载到 FAIR 序列模型工具包(fairseq)源代码和训练过的系统,研究人员可以打造用于翻译、文本摘要以及针对其他任务的定制化模型。


为什么是 CNN?


几十年前,最初由 Yann LeCun 开发的 CNN 已经成功用于多个机器学习领域,比如图像处理。不过,对于文本应用来说,因为 RNN 的高准确度,其已经当仁不让地成为了被最广泛采用的技术和语言翻译的最佳选择。


尽管历史表明,在语言翻译任务上,RNN 胜过 CNN,但其内在设计是有局限性,只要看看它是如何处理信息的就能明白这一点。计算机的翻译办法是:阅读某种语言句子,然后预测在另一种语言中相同含义的语词排列顺序。RNN 运行严格遵照从左到右或者从右到左的顺序,一次处理一个单词。这一运行方式并不天然地契合驱动现代机器学习系统的高度并行的 GPU 硬件。由于每个单词必须等到网络处理完前一个单词,因此计算并不是完全并行的。对比之下,CNN 能够同时计算所有元素,充分利用了 GPU 的并行,计算也因此更高效。CNN 的另一个优势就是以分层的方式处理信息,因此,捕捉数据中的复杂关系也更容易些。


在之前的研究中,被用于翻译任务的 CNN 的表现并不比 RNN 出色。然而,鉴于 CNN 架构潜力,FAIR 开始研究将 CNN 用于翻译,结果发现了一种翻译模型设计,该设计能够让 CNN 的翻译效果也很出色。鉴于 CNN 更加出色的计算效率,CNN 还有望扩大翻译规模,将世界上 6,500 多种语言(世界语言种类大约为 6,900 多种——译者注)纳入翻译范围。


在速度上达到当前最佳


我们的研究结果表明,与 RNN [2] 相比,我们的系统在由机器翻译协会(WMT)提供的广泛使用的公共基准数据集上达到了新的最佳效果。特别是,CNN 模型在 WMT 2014 英语-法语任务(该度量标准被广泛用于判断机器翻译的准确度)上超过了之前最佳结果 1.5 BLEU。我们的模型在 WMT 2014 英语-德语任务上提高了 0.5 BLEU,在 WMT 2016 英语-罗马尼亚语上提高了 1.8 BLEU。


对于实际应用,神经机器翻译的一个考量因素是我们为系统提供一个句子后,它到底需要多长时间进行翻译。FAIR CNN 模型在计算上十分高效,它要比强 RNN 系统快九倍左右。许多研究聚焦于量化权重或浓缩(distillation)等方法来加速神经网络,而它们同样也可被用于本 CNN 模型,甚至提速的效果还要大些,表现出了显著的未来潜力。


利用多跳注意(multi-hop attention)和门控(gating)来改善翻译效果


在我们模型架构中,一个明显不同的组件就是多跳注意,这个机制就像人类翻译句子时会分解句子结构:不是看一眼句子接着头也不回地翻译整个句子,这个网络会反复「回瞥(glimpse)」句子,选择接下来翻译哪个单词,这点和人类更像:写句子时,偶然回过头来看一下关键词。[3] 多跳注意是这一机制的增强版本,可以让神经网络多次「回瞥」,以生成更好的翻译效果。多次「回瞥」也会彼此依存。比如,头次「回瞥」关注动词,那么,第二次「回瞥」就会与助动词有关。


在下图中,我们给出了该系统读取法语短语(编码)并输出其英语翻译(解码)的情形。我们首先使用一个 CNN 运行其编码器以为每个法语词创建一个向量,同时完成计算。接下来,其解码器 CNN 会一次得到一个英语词。在每一步,该注意都会「回瞥」原法语句子来确定翻译句子中最相关的下一个英语词。解码器中有两个所谓的层,下面的动画给出了每层中注意完成的方式。绿线的强度表示了该网络对每个法语词的关注度。当该网络被训练时,其一直可以提供翻译,同时也可以完成对英语词的计算。




我们的系统另一方面是门控(gating),其控制了神经网络中的信息流。在每一个神经网络中,信息流也就是通过隐藏单元的数据。我们的门控机制将具体控制哪些信息应该需要传递到下一个神经元中,以便产生一个优良的翻译效果。例如,当预测下一个词时,神经网络会考虑迄今为止完成的翻译。而门控允许放大翻译中一个特定的方面或取得广义的概览,这一切都取决于神经网络在当前语境中认为哪个是适当。


未来开发


这种方法是一种可选的机器翻译架构,也为其它文本处理任务开启了新的大门。比如说,在对话系统中的多跳注意(multi-hop attention)让神经网络可以关注到对话中距离较远的部分(比如两个分开的事实),然后将它们联系到一起以便更好地对复杂问题作出响应。


以下为相关论文的摘要介绍:


论文:卷积序列到序列学习(Convolutional Sequence to Sequence Learning)


序列到序列学习(sequence to sequence learning)的普遍方法是通过循环神经网络将一个输入序列映射到一个可变长度的输出序列。我们引入了一种完全基于卷积神经网络的架构。相比于循环模型,其在训练阶段中所有元素上的计算都是完全并行的,且其优化更简单,因为非线性的数量是固定的且独立于输入的长度。我们使用门控线性单元简化了梯度传播(gradient propagation),而且我们为每个解码器层都装备了一的单独的注意模块(attention module)。我们在 WMT'14 英语-德语翻译和 WMT'14 英语-法语翻译上的准确度表现都超过了 Wu et al. (2016) 的深度 LSTM 设置,且在 GPU 和 CPU 上的速度都实现了一个数量级的提升。


图 1:训练中批处理(batching)的图示。顶部是英语源句子被编码,同时我们为 4 个德语目标词计算所有的注意值(中间)。我们的注意只是解码器上下文表征(底部左侧)和编码器表征之间的点积。我们为解码器上下文增加了由该注意模块计算出来的条件输入(中部右侧),其可以预测目标词(底部右侧)。S 型和乘法框表示门控线性单元。


博客文章参考文献


[1] Convolutional Sequence to Sequence Learning. Jonas Gehring, Michael Auli, David Grangier, Denis Yarats, Yann N. Dauphin.(即本论文)

[2] Google‘s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation. Yonghui Wu, Mike Schuster, Zhifeng Chen, Quoc V. Le, Mohammad Norouzi, Wolfgang Macherey, Maxim Krikun, Yuan Cao, Qin Gao, Klaus Macherey, Jeff Klingner, Apurva Shah, Melvin Johnson, Xiaobing Liu, Łukasz Kaiser, Stephan Gouws, Yoshikiyo Kato, Taku Kudo, Hideto Kazawa, Keith Stevens, George Kurian, Nishant Patil, Wei Wang, Cliff Young, Jason Smith, Jason Riesa, Alex Rudnick, Oriol Vinyals, Greg Corrado, Macduff Hughes, Jeffrey Dean. Technical Report, 2016. 参考机器之心文章《 重磅 | 谷歌翻译整合神经网络:机器翻译实现颠覆性突破(附论文)

[3] Neural Machine Translation by Jointly Learning to Align and Translate. Dzmitry Bahdanau, Kyunghyun Cho, Yoshua Bengio. International Conference on Learning Representations, 2015. 地址:https://arxiv.org/abs/1409.0473


原文链接:https://code.facebook.com/posts/1978007565818999/a-novel-approach-to-neural-machine-translation/


点击阅读原文,报名参与机器之心 GMIS 2017 ↓↓↓

Viewing all 15896 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>