<<上篇 | 首页 | 下篇>>

《搜索引擎评分指南》阅读心得

#《搜索引擎评分指南》阅读心得  

##1、 搜索词的三种分类  
搜索引擎把搜索词分为三类:Know,Do和Go,以下是关于三类搜索的简单解释:

*   Know,信息性搜索,这类用户想要了解某些信息,如“南京五一天气怎样”,“南京五一有哪些地方好玩”等。
*   Do,事务型搜索,这类用户想要完成某个目标或参与某个网上活动。他的目标可能是下载,购买,娱乐等,比如“美图秀秀下载”,“植物大战僵尸online”,“cheap mp3 player”等。  
*   Go,导航型搜索,这类用户是要到达某个特定网页,比如“中国制造网”,“优酷”,“新浪微博”等。  

另外,很多搜索词会有不止一种用户意图,比如“ipad”,用户可能想要ipad的简介(Know型),可能想要到达ipad的官网页面(Go型),也可能是想要购买ipad(Do型)。
**搜索词分类的作用很多,可自行扩展。下面简单列举了2种:**

*   便于关键词分析。将关键词按用户意图区分,可以更了解这些关键词背后的含义,有利于对关键词做更好的布局。
*   方便关键词拓展。可以根据关键词的3种分类去对照自身网站,看是否有关键词拓展的空间。

**注:**
关键词分类对电商类网站尤其重要,比如电商类要将事务型搜索关键词作为网站转化的重点;还有淘宝客,可以说最核心的就是如何找到转化率高的长尾关键词;我之前工作过的医疗网站也是,寥寥几个事务型关键词(多为地区类病种词)几乎可以占据每天转化量的一半。




##2、用户意图  
之前一直以为影响网页排名的只有2个因素:**网页与搜索词的相关性**和**网页的重要性**。看了文档才知道有个更高级的因素,那就是**用户意图**。其实也很容易理解:搜索引擎的目的就是满足用户的搜索需求,因此搜索引擎会先根据**用户搜索词**判断**用户意图**(简单分为Do,Go和Know型),然后给出符合需求的一类网页,接着才会根据相关性和重要性对这一类页面进行排序。
例如用户搜索”宫保鸡丁“,而我有个网页是”宫保鸡丁的来历“,虽然我的网页相关性很强(关键词位置,密度,内外链等),也有很多高质量外链,但考虑到搜索这个词的大多数用户都想要知道”宫保鸡丁的做法“,只有极少数是想了解”宫保鸡丁的来历“,所以第一步就落后了,之后也不太可能有很好的排名。除非用户搜索”宫保鸡丁的来历“,这个网页因为很符合用户的需求,排名才会靠前。
也就是说,**要想让某个关键词的排名靠前,首先要分析这个关键词,了解搜索这个关键词的用户想要得到什么信息,然后根据用户需求去组织页面,这样才可能获得理想排名**。也就是说,如果网页是”宫保鸡丁的来历“,那还是不要把精力放在”宫保鸡丁“这个排名上了,因为匹配度太低了。 
**注:**
这东西说来很虚,但用户和搜索引擎都喜欢,可以看看夜息哥哥这篇文章http://www.imyexi.com/?p=853,花很大精力搞卡片分拣就只是为了满足用户需求,在侧边栏放上用户需要的链接。这些链接虽然不起眼,但想想如果有成千上万个页面都放上了用户更感兴趣的链接,那效果就不可同日而语,这其实也是产品,运营,体验的工作。


##3、 网页评级
谷歌根据用户意图与页面的匹配程度,将搜索结果分为5种类型:**至关重要型**,**实用型**,**相关型**,**基本相关型**与**离题或无价值型**。

*   至关重要型。一个页面可以满足用户的所有搜索意图,多用于导航型搜索。  
*   实用型。页面质量高,可满足大多数用户的需求。  
*   相关型。跟搜索需求相匹配,可以满足部分用户的需求。  
*   基本相关型。对大多数用户不那么有价值,但多少跟搜索需求相关。  
*   离题或无价值型。跟搜索请求不匹配,或者没有实际价值,对绝大多数用户没有帮助。

结合**用户意图**的解释可以得到2点:  
1.  只有符合用户意图,满足用户主体需求的页面,评级才会高,排名才会靠前。比如搜索”宫保鸡丁“时为什么”宫保鸡丁的来历“排名较低,因为他没有满足搜索者的主体需求,只是跟搜索词算是**基本相关**。
2.  如果页面内容与关键词匹配度不高,评级低,无法获得很好的排名。例如“宫保鸡丁的来历”想要获得“宫保鸡丁”的排名。

**注:**
相对于用户意图,那些关键词密度,H标签什么的简直弱爆了。

##4、 关键词堆砌  
谷歌对关键词堆砌的分类:  

*   关键词在页面上重复多次  
*   关键词的相关词在页面上重复多次  
*   关键词的拼写错误在页面上重复多次  
*   页面上存在大量胡乱的随机的关键词
*   页面上存在大量程序自动生成的文本,并没有任何意义。  

谷歌对关键词堆砌的处理方式:当页面上关键词数量过多,干扰了用户的正常访问,那就会认定为关键词堆砌;而如果没有察觉到关键词数量的影响,那将不被认为是堆砌。  

**注:**
有时候我们把搜索引擎想象的太弱智了,不是吗?!



##5、 对网页语言的判断  

谷歌已有很成熟的技术去判断网页语言,而不仅仅依靠HTTP响应头部的Charset和Content-Language。比如Google Translate的API里就有相应的功能,能识别网页的语言。(具体可以看<a href="https://developers.google.com/translate/v2/getting_started?hl=zh-CN">谷歌翻译API</a>里的detect source language一项。)  
也就是说,**如果Charset和Content-Language设置为中文,不一定会被谷歌认为是中文网页,只是会干扰谷歌的判断**。  
另外,这不表示Charset和Content-Language设置错误没有影响,Charset和Content-Language的合理设置会让网页更加规范,也避免了用户在浏览网页时弹出其他字符集下载。
 

阅读全文……

标签 :

关于通过H5页面唤NNtive户端的介绍

本文档用于介绍通过H5端唤起本地NN客户端的研究过程!刚进新公司,导师让研究下5页面唤NNtive户端的课题,后面公司客户端产品可能会用到这方面的技术,所以研究了下,写成文章,保密需要,去掉了和具体客户端绑定的内容,希望对那些想了解这方面知识的人有用!

背景

  • 目前通过H5页面唤起native App的场景十分常见,比如常见的分享功能;一方面,对于用户而言,相同的内容在native app上比H5体验更好,操作更加方便,另一方面,对于app运营来说,可以增加app的用户粘性度。

  • 当前native客户端内置webview中,比较常用的是通过schema打开登陆页、触发分享入口的显示;而在外部浏览器或者webview中唤醒公司的客户端目前还没有太多尝试,有据此展开研究的必要性,以便日后在真实的需求中使用!

唤醒native APP 的几种方式

在Android端,常用的方式是Schame + Android Itent,在IOS端,常用的方式是Schema + Universal links(IOS9+); 使用的前提都是客户端程序实现了schema协议。

下面对这3种方式做简要的介绍:

Schema

在Android和IOS浏览器中(非微信浏览器),可以通过schema协议的方式唤醒本地app客户端;schema协议在App注册之后,与前端进行统一约定,通过H5页面访问某个具体的协议地址,即可打开对应的App客户端 页面;

访问协议地址,目前有3种方式,以打开NN客户端登录页为例:

  1. 通过a标签打开,点击标签是启动
    <a href="ftnn:login">打开登录页</a> 
  1. 通过iframe打开,设置iframe.src即会启动
    <iframe src="ftnn:login"></iframe> 
  1. 直接通过window.location 进行跳转
    window.location.href= "ftnn:login"; 

Android上注册schema协议,可以参考博文:Android手机上实现WebApp直接调起NativeApp

注:由于微信的白名单限制,无法通过schema来唤起本地app,只有白名单内的app才能通过微信浏览器唤醒,这个问题我目前没有找到合适的解决办法!

Android Intent

在Android Chrome浏览器中,版本号在chrome 25+的版本不在支持通过传统schema的方法唤醒App,比如通过设置window.location = "xxxx://login"将无法唤醒本地客户端。需要通过Android Intent 来唤醒APP; 使用方式如下:

  1. 构件intent字符串:
    intent:     login                                           // 特定的schema uri,例如login表示打开NN登陆页     #Intent;        package=cn.xxxx.xxxxxx;                       // NN apk 信息       action=android.intent.action.VIEW;            // NN apk 信息       category=android.intent.category.DEFAULT;     // NN apk 信息       component=[string];                           // NN apk 信息,可选       scheme=xxxx;                                  // 协议头       S.browser_fallback_url=[url]                  // 可选,schema启动客户端失败时的跳转页,一般为下载页,需编码     end;  
  1. 构造一个a标签,将上面schame 字符串作为其href值,当点击a标签时,即为通过schema打开某客户端登陆页,如果未安装客户端,则会跳转到指定页,这里会跳转到下载页;

    <a href="intent://loin#Intent;scheme=ftnn;package=cn.futu.trader;category=android.intent.category.DEFAULT;action=android.intent.action.VIEW;S.browser_fallback_url=http%3A%2F%2Fa.app.qq.com%2Fo%2Fsimple.jsp%3Fpkgname%3Dcn.futu.trader%26g_f%3D991653;end">打开登录页</a> 

Universal links

Universal links为 iOS 9 上一个所谓 通用链接 的深层链接特性,一种能够方便的通过传统 HTTP 链接来启动 APP, 使用相同的网址打开网站和 APP;通过唯一的网址, 就可以链接一个特定的视图到你的 APP 里面, 不需要特别的 schema;

在IOS中,对比schema的方式,Universal links有以下优点:

  1. 通过schema启动app时,浏览器会有弹出确认框提示用户是否打开,而Universal links不会提示,体验更好;

  2. Universal link可在再微信浏览器中打开外部App;

    网易新闻客户端IOS 9上目前采用这种Universal links方式

针对这部分内容可以参考博文:

实现过程

首先,通过浏览器是无法判断是否安装了客户端程序的,因此整体的思路就是:尝试去通过上面的唤起方式来唤起本地客户端,如果唤起超时,则直接跳转到下载页;整个实现过程围绕这一点展开。

在不考虑IOS9 Universal links唤醒方式的条件下,可以分为这几个步骤;

  1. 生成schema字符串

首先判断浏览器UA,如果为Chrome for Android,则必须安装 Android Intent的方式来组织schema字符串;如果为其他浏览器,则按照普通的schema方式来返回即可; 

注意参数中包含的url地址需要进行encodeURIComponent编码

2 .通过iframe或者a标签来加载schema

由于无法确定是否安装了客户端,因此通过window.location = schema的方式可能导致浏览器跳转到错误页;所以通过iframe.src或a.href载入schema是目前比较常见的方法;

相比于iframe和a,通过设置其diaplay为none来进行隐藏,这样即便链接错误也不会对当前页构成影响,但是对于a标签,在未安装客户端的情况下,仍然会存在提示访问不存在的情况(比如opera),所以在选取上的优先级是:iframe>a>window.location,只有在iframe.href 无法调用schema的情况下,才采用a.href的方式。

经过非全面测试:

  • Android系统上,Chrome for Android无法通过iframe.src 来调用schema,而通过a.href 的方式可以成功调用,而针对chrome内核的浏览器如猎豹,360,小米浏览器, opera对于iframe.src和a.href的方式都能支持,所以对chrome及先关的内核的浏览器采用a.href的方式来调用scheme;对于其他浏览器,如UC,firefox,mobile QQ,sogou浏览器则采用iframe.src的方式调用schema。对于微信浏览器,则直接跳转到下载页。其他未经测试的浏览器,默认采用iframe.src来调用schema;
  • IOS 9系统上,Safari浏览器无法通过iframe.src的方式调用schema,对于UC,Chrome,百度浏览器,mobileQQ只能通过a.href的方式进行调用schema;对于微信浏览器,默认跳转到下载页;

代码如下:

3 .处理客户端未安装的情况

前面提到无法确定客户端程序是否安装,所以在通过iframe和a调用schema时,会设置一个settimeout,超时,则跳转到下载页;

此处的超时时间设置也十分关键,如果超时时间小于app启动时间,则未待app启动,就是执行setimeout的方法,如果超时时间较长,则当客户端程序未安装时,需要较长时间才能执行settimeout方法进入下载页。

代码中,进入到setimeout时,对跳转过程再次进行了限定;当浏览器因为启动app而切换到后台时,settimeout存在计时推迟或延迟的问题,此时,如果从app切换回浏览器端,则执行跳转代码时经历的时间应该大于setimeout所设置的时间;反之,如果本地客户端程序未安装,浏览器则不会进入后台程序,定时器则会准时执行,故应该跳转到下载页!

在实际测试过程,当通过schema成功唤起客户端,再次返回浏览器时,发现页面已跳转至下载页面,因此对已设置的settimeout需要做一个清除处理;

当本地app被唤起,app处于设备可视窗口最上层,则浏览器进入后台程序页面会隐藏掉,会触发pagehide与visibilitychange事件,此时应该清除setimeout事件,于此同时,document.hide属性为true,因此setimeout内也不做跳转动作,防止页面跳转至下载页面; 此时,有几个事件比较关键:

pagehide: 页面隐藏时触发  visibilitychange: 页面隐藏没有在当前显示时触发,比如切换tab,也会触发该事件  document.hidden 当页面隐藏时,该值为true,显示时为false 

为了尽可能的兼容多的浏览器,所以讲这几个事件都进行绑定! 代码如下。


测试结果

  1. Android平台(小米3 手机测试)

    • 经测试,可唤起chrome,Firefox,uc,360,mibrowser,sogou,liebao,mobileQQ浏览器;
    • 新版opera浏览器采用webkit内核,但是当客户端未安装时跳转下载页会会出错,提示页面不存在;
    • 微信不支持登陆,直接做了跳转到下载页处理;
    • Android上启动相对比较慢,导致很容易启动超时而跳转到下载页面;
    • 测试页面在本机,百度浏览器会上报检测url合法性,导致唤醒不成功

    2 . IOS平台(ip4,ip6+,ipad mini2)

    • os7上Safari可用,其他浏览器为测试,条件限制;
      • Safari,UC浏览器,Chrome 浏览器能唤起nn客户端,但是Safari会有 是否打开的提示;
      • QQ webviwe上能打开,偶尔会失败;
      • IOS上启动速度相对较快

相关代码

对代码进行简单的封装,代码如下,在使用时需要针对当前的app做必要设置,采用UMD的写法:

代码见tool-nativeSchema.js

调用方式:

// COMMONJS 的方式引用,不能直接在浏览器中运行,需要打包转换 var nativeSchema = require("tool-nativeSchema.js");  // Amd的方式 require(["tool-nativeSchema.js"],function(nativeSchema){  });  // 直接引入 <script type="text/javascript" src="xxxx/tool-nativeSchema.js"></script> 
// 使用 nativeSchema.loadSchema({     // 某个schema协议,例如login,     schema: "",      //schema头协议,     protocal:"xxx",      //发起唤醒请求后,会等待loadWaiting时间,超时则跳转到failUrl,默认3000ms     loadWaiting:"3000",      //唤起失败时的跳转链接,默认跳转到应用商店下载页     failUrl:"xxx",      // Android 客户端信息,可以询问 Android同事     apkInfo:{         PKG:"",         CATEGORY:"",         ACTION:""     } }); 

研究意义

便于通过相关H5页面进入Native客户端,提升用户体验,提升App用户粘度; 对于未安装客户端的用户,可引导进入下载通道,如下场景图:

存在的问题

  1. 在没有安装客户端程序的时候,opera无法跳转到指定页的失败页;
  2. 通过微信唤醒客户端目前不可行,Android上需要微信设置白名单;IOS上,需要微信设置白名单或者通过Universal links(IOS9+)协议;
  3. 尚未对IOS9的 Universal links协议进行功能测试。
  4. 代码中使用的各种时间如:settimeout定时时间均根据本机测试进行的调整,普遍性需要进一步验证

 

标签 : , ,

记一次java native memory增长问题的排查 - Axb的自我修养

一段监控DirectBuffer的代码,以提高DirectBuffer的可用性

/**
 * 监控DirectBuffer,提高DirectBuffer可用性
 * 使用MaxDirectMemorySize可以指定DirectBuffer的最大可用空间,DirectBuffer的缓存空间不在堆上分配,
 * 因此,可以使应用程序突破最大堆内存限制,对DirectBuffer的读写操作比普通Buffer快,但是对它的创建
 * 与销毁比普通Buffer要慢
 * @throws Exception
 */
@Test
public void monitorDirectBufferUsage() throws Exception {
Class c = Class.forName("java.nio.Bits");
Field maxMemory = c.getDeclaredField("maxMemory");
maxMemory.setAccessible(true);//通过反射取得私有数据
Field reservedMemory = c.getDeclaredField("reservedMemory");
reservedMemory.setAccessible(true);
synchronized(c) {
Long maxMemoryValue = (Long)maxMemory.get(null);
Long reservedMemoryValue = (Long) reservedMemory.get(null);
System.out.println("maxMemoryValue : " + maxMemoryValue);//总大小
System.out.println("reservedMemoryValue : " + reservedMemoryValue);//剩余大小
}
}

 

排查

首先怀疑是java heap的问题,查看heap占用内存,没有什么特殊。

$ jmap -heap pid

然后又怀疑是directbuffer的问题,jdk1.7之后对directbuffer监控的支持变得简单了一些,使用如下脚本:

 

 

发现directbuffer虽然在增长,但是也只有百兆左右。full gc之后缩小到十几兆,可以忽略。

查看java线程的情况,虽然线程数很多,但是内存增长时线程数基本没有什么变化。

$ jstack pid |grep 'java.lang.Thread.State' |wc -l
或者
$ cat /proc/pid/status |grep Thread

对java做了一次heap dump,使用eclipse的MAT查看堆内使用情况,没有发现明显有哪个对象数量有明显异常,heap的大小也只有几百兆。

$ jmap -dump:file=/tmp/heap.bin

发现stack dump里的global jni reference一直在增长,怀疑是jni调用存在内存溢出。

$ jstack pid |grep JNI

查找了jar包里的.so/.h等c文件,发现jruby、jthon等jar包里有jni相关的文件。

$ wtool jarfind *.so .

上网发现确实有不少jruby内存溢出的issue。把这些jar包直接删掉之后观察global jni reference数量还是在涨,内存增长情况也没有改善。

之后突然想到full gc的问题,对增长中的java进程做了一次full gc,global jni reference数量由几千个下降到几十个,但是占用内存还是没有变化,排除掉global reference的可能性。

用pmap查看进程内的内存情况,发现java的heap和stack大小都没啥变化,但是定期会多出来一个64M左右的内存块。

$ pmap -x pid |lesspmap截图

使用gdb观察内存块里的内容,发现里面有一些接口的返回值、mc的返回值、还有一些类名等等

gdb: dump memory /tmp/memory.bin 0x7f6b38000000 0x7f6b38000000+65535000
$ hexdump -C /tmp/memory.bin$ strings /tmp/memory.bin |lesshexdump截图

上网搜索后发现有人遇到过这个问题,在这个网页里有ibm对64M问题的研究。依照网站上说的办法,把MALLOC_ARENA_MAX参数调成1,发现virtual memory正常了,res也小了1G左右。同时hadoop的issue里也有一些性能方面的测试,发现MALLOC_ARENA_MAX=4的时候性能会提升,但是他们也说不清楚为什么。

修改之后程序启动时的virtual memory明显降低,res也降低到了3.2g: memory

本来以为到这里应该算是解决了,但是这个程序跑了几天之后内存依然在上涨,只是内存块由很多64M变成了一个2g+的普通native heap。

继续寻找线索,在一些关于MALLOC_ARENA_MAX这个参数的讨论里也发现一些关于glibc的其它参数。比如M_TRIM_THRESHOLD和M_MMAP_THRESHOLD或者MALLOC_MMAP_MAX_,试用之后发现依然没有效果。

试着从glibc的malloc实现上找问题,比如这里这里,同样没有什么进展。

尝试用strace和ltrace查找malloc调用,发现定期有32k的内存申请,但是无法确定是从哪调用的。

尝试用valgrind查找内存泄露,但是jvm跑在valgrind上几分钟就crash了。

在网上查到了一个关于thread pool用法错误有可能导致内存溢出的问题,可以写一个小程序重现:

 

 

但是用btrace挂了一天也没有发现有错误的调用,源代码里也没找到类似的用法。

重新用MAT在heap dump里查找是否有native reference,发现finalizer队列里有很多java.util.zip.Deflater的实例,上网搜索发现这个类有可能导致native内存溢出,使用的jesery框架里有这个问题导致gzip异常的issue;用btrace监视发现有大量这个类的构造函数被调用,但是经过几次full gc的观察,每次full gc后finalizer队列里的Deflater数量都会减少到个位数,但是内存依然在上涨;同时排查了线上配置,发现没有开启gzip。

也发现了有人说SunPKCS11有可能导致内存泄露,但是也没发现有相关java对象。

尝试把Xss参数调到256k,运行几天后发现内存维持在5.7g左右,比较稳定,但是从各种角度都无法解释为什么xss调小会影响native heap的大小。

怀疑是JIT的问题,用-Xint或者-XX:-Inline方式启动之后发现内存依然增长。

本来排查到这里已经绝望了,但是最后想到是不是JDK本身有什么bug?

查看jdk的changelog,发现线上使用的1.7-b15的版本比较老,之后有一些对native memory leak的修复。尝试用新的jdk1.7-u71启动应用,内存竟然稳定下来了!

在升级jdk、限制directbuffer大小为256M、调整MALLOC_ARENA_MAX=1后,4倍流量的tcpcopy运行几天后内存占用稳定在5G;只升级了jdk,其它参数不变,运行一天后内存为5.4G,是否上涨还有待观察。对比之前占用6.8G左右,效果还是比较明显的。

4.其它参考资料

 

阅读全文……

标签 : ,