分类 日常 下的文章

好久没更新mongo运维这块知识了,这次介绍 db.currentOp与db.killOp命令

数据库优化方法有很多,但所有数据库优化都离不开慢查询优化。mysql、mongodb都可以开启慢查询,来对数据库查询进行分析。开启慢查询日志,对性能会有一定的影响。mongoDB 有时我们只想临时看下慢查询日志,应该如何处理。

这时,我们可以用到mongdb的db.currentOp命令 ,他可以列出当前真正跑的op相关信息。

1.查看目前正在执行的所有查询语句

db.currentOp();

2.查询所有操作xxx集合并且执行时间已超过3s的请求

db.currentOp(
   {
     "active" : true,
     "secs_running" : { "$gt" : 3 },
     "ns" : /^xxx\./
   }
)

3. 当然,如果我当前db集群有非常多的集合,我也可以不限制xxx集合

db.currentOp(
   {
     "active" : true,
     "secs_running" : { "$gt" : 3 }
   }
)

currentOp的过滤条件包括

  1. 请求操作类型,insert、update、delete…
  2. 请求对应的connectionId,threadId
  3. 请求是否正在等待锁
  4. 请求执行时间
  5. 请求操作的DB或collection
  6. 请求query的内容
  7. …等等

返回结果如下

{
            "desc" : "conn44266",
            "threadId" : "140419266524928",
            "connectionId" : 44266,
            "client" : "10.10.68.209",
            "active" : true,
            "opid" : 4495651,
            "secs_running" : 25,
            "microsecs_running" : NumberLong(25459008),
            "op" : "command",
            "ns" : "xxxxx",
            "query" : {
                "count" : "c74dc2de71",
                "query" : {
                    "video" : {
                        "$exists" : true
                    },
                    "_isdel" : 0,
                    "deleted" : {
                        "$ne" : true
                    },
                    "verify" : {
                        "$ne" : false
                    }
                }
            },
            "planSummary" : "IXSCAN { video: 1 }",
            "numYields" : 189,
            "locks" : {
                "Global" : "r",
                "Database" : "r",
                "Collection" : "r"
            },
            "waitingForLock" : false,
            "lockStats" : {
                "Global" : {
                    "acquireCount" : {
                        "r" : NumberLong(380)
                    }
                },
                "Database" : {
                    "acquireCount" : {
                        "r" : NumberLong(190)
                    }
                },
                "Collection" : {
                    "acquireCount" : {
                        "r" : NumberLong(190)
                    }
                }
            }
        }

结果

当我们知道某条语句是锁库的罪魁祸首的时候,我们就可以通过另一条语句,干掉对应的请求。

killOp 停止正在执行的查询

用法:

db.killOp(opid)

目前Mongodb手册,还未有一次清掉当前所有查询,文档地址:https://docs.mongodb.com/manual/reference/method/db.killOp/

执行后返回

{ "info" : "attempting to kill op", "ok" : 1 }

image.png

db.killOp(opid)的实现原理如下

每个连接对应的服务线程存储了一个killPending的字段,当发送killOp时,会将该字段置1;请求在执行过程中,可以通过不断的调用OperationContext::checkForInterrupt()来检查killPending是否被设置,如果被设置,则线程退出。

一个请求要支持killOp,必须在请求的处理逻辑里加上checkForInterrupt()检查点才行,否则即使发送了killOp,也只能等待请求完全处理完毕线程才会退出。

比如createIndex的处理逻辑里包含了类似如下的代码,在createIndex的循环过程中,一旦killPending被置1了,createIndex的执行可以在当前循环结束时退出。

while (!createIndexFinished) {
    createIndexForOneElement();
    checkForInterupt();
}

所以发送killOp后,请求要执行到下一个『检查点』线程才会退出,MongoDB在很多可能耗时长的请求中,都加入了checkForInterrupt()检查点,如创建索引,repair database,mapreduce、aggregation等。

批量一次清楚当前慢查询

上面说还未有一次清掉当前所有查询, 不过我们可以通过手动写脚本实现。此脚本由diggzhang大神贡献。文章链接:http://yangcongchufang.com/kill-mongo-ops.html

实现功能:传入自己的IP地址,强制关停自己的异常查询。

打开家目录下的.mongorc.js拷贝下面的killMyRunningOps函数进去,重新打开mongoshell即可加载这个函数(mongoshell启动时会预读这个文件)。

➜  ~ cat ~/.mongorc.js
killMyRunningOps = function (clientIp) {
    var currOp = db.currentOp();
    for (op in currOp.inprog) {
        if (clientIp == currOp.inprog[op].client.split(":")[0]) {
            db.killOp(currentOp.inprog[op].opid)
        }
    }
}

用法很简单,知道自己IP后,调用这个函数:

> killMyRunningOps("12.23.32.21")

篇首:俺自己很low,写的东西也很low,各位前辈已经各种总结的,so,大家轻喷啊,俺只是为了记录一下子。
刚刚不经意间查看关于php加速方面的消息,看到了一句话“lamp结构的瓶颈在于M”,感觉是真知灼见,当然这是在理想状态下的情况。对于实际情况下的生产环境来说,需要解决的问题是多种多样的,l-a-
m-
p在不同阶段需要优化的点是不同的,虽然通过努力最终的瓶颈在于M,但是期间的各种努力也是不容忽视的。下面我就意识流的总结一下,在网站生产环境遇到的各种问题。
以下是之前遇到问题的各种汇总:
1、php内的sql代码性能存在问题
这个存在于两方面,一个是sql本身优化不够,另外一个方面是随着数据量的增加,某些性能问题会逐渐暴露。

2、apache处理模式造成的内存消耗
由于apache是以进程的形式,同步阻塞式的处理http请求,so,当网页本身加载元素过多的话,会造成大量的
http进程并发,消耗服务器大量的内存,导致访问缓慢或者异常。
解决办法很是简单,主要有一下几个方面
a、动态页面转成静态页面。
b、转成lnamp的模式,图片等静态资源由nginx进行处理,减少http进程数量

3、CDN服务的使用
这个并不是性能问题,而是为了解决用户访问的页面加载速度问题,以及不同网络之间的延迟问题。
具体使用效果是非常不错的,会大大的改善各地用户的访问速度,土豪公司可以自建CDN,中小公司
选择第三方服务也是不错的选择。

4、缓存的使用
这个有两个方面的缓存,其中主要的缓存是数据也就是动态方面的缓存
使用的软件主要有redis以及memcache(开源用的爽爽哒)
这两个软件都是针对php与mysql之间的请求进行缓存,减少与mysql的直接交互,提高访问速度。

5、apache的负载均衡,mysql的读写分离
即便增加了CDN、缓存,优化了apache的页面,但是高并发的问题依然会随着用户量的增加而慢慢出现,那么这个时候
就要发大招了,扩!容!硬!件!。永远都好使的大招,用完即生效。我们现在主要使用的是nginx+apache的负载均衡方式。
而mysql的读写分离也so easy,使用主从复制即可。对于及时性要就不是太高的网页访问,问题不大。

6、奇葩问题粗现了!!!
Then,该优化的俺们都优化了,前辈的经验也是各种借鉴,最后俺们发现页面访问还是慢得想让用户注销。问题到底在哪?
又经过了艰苦卓绝的调查与研究,最后发现,上一个离职的哥们开放的一个权限系统(所有网页加载都会用),没有关闭
debug,我去!我一口老血喷在了显示器上啊!苍天啊,我曾经一度怀疑自己是不是在做无用功啊!

这里不是挑起运维与研发的阶级矛盾,而是总结经验教训,以后不在走弯路!
先到这吧!以后有了经验再补充!

1.如何实现基于HTTP协议和TCP协议的RPC调用,它们之间有何差别?
RPC信息协议由两个不同结构组成:调用信息和答复信息。
它们之间通过中间件实现,既是说它们通过使用RPC 代理作为中间件,中间件是他们之间通信的关键桥梁。 RPC 允许进程之间的通信和数据交换,可配置 RPC
代理服务器。
http协议是应用层协议,里面的http协议有能够写出好几本书。

2.常见的分布式系统存储解决方案有哪些?
阿里就有开源分布式项目,其中TFS,Tair,以及OceanBase就是典型的分布式系统存储解决方案。
现在的分布式存储系统、分布式文件系统、分布式缓存、分布式数据库的软件项目就很多。正是因为分布式系统的软件多,免费开源的都十来种,正因此才会产生出这个问题。

还有,分布式文件系统集群Ceph、FastDFS、MFS、Hadoop中的HDFS、MooseFS等等。
分布式文件系统未来发展新星Ceph,Ceph不仅仅是一个文件系统,还是一个有企业级功能的对象存储生态环境。
看到国内很火的开源项目“FastDFS”,这个FastDFS分布式文件夹系统,最关键的是开源的项目,同还是个轻量级的文件系统。
然而,比较关心分布式存储部署是否方便,存储操作是否便捷。

3.常见的Web攻击手段和防御方法?
互联网上目前最常见的DDOS攻击,Web网站代码漏洞,Shell提权,XSS攻击,CRSF攻击, SQL注入攻击,跨站点伪造请求,DNS污染。
DDOS攻击这些主流危害大、最泛滥的攻击。

4.如何进行集群的监控?
淘宝的采集工具Tsar;Twitter 开源项目Zipkin;分布式的监控系统Ganglia 。
还有开源的优秀监控软件 Nagios、Cacti、Zabbix和各种监控插件。
现成的开源监控软件、监控工具非常多可提供选择,关键的一定还是如何使用好它们。关键点是好工具,要懂得这么用,和怎么用好它。再好的工具,使用恰当才能发挥出其最大优势。

5. 说说您读完试读样章后的启发
淘宝大师级人物写的书。
样章主要介绍了常见的分布式系统存储解决方案,文中介绍mysql.hbase,redis,lucene实际应用的经验,包括MySQL的分布式扩展、HBase的API及使用场景、Redis的使用等。memcache
的安装配置例子,讲解基本功能和原理,文章中配图,列举了实践场景中生动的例子。
目前分布式系统在互联网大型网站应用中属于比较新颖的IT技术,对于开放的互联网来说,对于这样一个前沿的大型网站技术架构,这一块具有相当的挑战性。
当前互联网得到迅猛发展,大型网站已经越来越多,数据级别已经要用大数据来形容它了。那么需要面对技术瓶颈问题也迎面而来,新的需求也在跟着变化。

从试读章节可以深刻感受到作者资深的经验和思维的深度,书中以实际工作经历展开论述,对架构技术有系统论述,是一本专门论述的好书。对研究大型网的读者来说,我认为这是一本很好的书,值得推荐细读!

6. 这些大型的互联网应用是设计出来的?还是演化出来的?
大型的互联网应用确实是进化而来,经过一系列复杂的演化过程。
好的大型的互联网网站架构都是“进化”出来,没有适用一切的全能架构吧!有高性能网站架构模式或框架,但没有一成不变的高性能网站架构技术实现!
但是互联网大型网站经过这十多年的发展,总体架构大家都比较相近。现成的技术、方案各家公司都有不同的名称,可他们的核心思想是相似,底层技术百变不离其中。
假如没有大型网站的需求,不是实际生产中遇到瓶颈,急迫需要这些分布式解决方案。如果没有了这些原因,怎么会有以后的事情发生。
看看书中开篇内容,资深技术专家就有如下的阐述:
2008年,TB网随着访问量/数据量的巨增,以及开发人员的增长,原有的架构体系已经无法支撑,于是在那一年TB网将系统改造为了一个大型分布式的网站。作者目前就职于阿里集团,清晰地看到了目前TB这个大型分布式网站的架构体系,这个架构体系其实是非常多方面的技术的融合,要掌握好最重要的首先是看清全貌,但这也是最难的。本书向大家展示了一个大型分布式网站需要的技术的全貌。
互联网在不断演变,需要解决的问题也在变化中。

图床(Image Hosting)是用于存储和分享图片的服务。使用图床可以帮助你在网站、博客、论坛等地方方便地插入图片,而不需要将图片直接上传到目标服务器。以下是使用图床的基本步骤:

1. 选择图床服务

首先,你需要选择一个图床服务。常见的图床服务包括:

  • 七牛云: 提供图片存储和CDN加速服务,适合需要大流量和高可用性的用户。
  • 腾讯云COS: 腾讯云的对象存储服务,支持图片存储和分发。
  • GitHub: 如果你有GitHub账号,可以将图片上传到GitHub仓库,并通过Raw链接分享。

2. 注册账号

大多数图床服务需要你注册一个账号。注册过程通常很简单,只需提供邮箱或手机号即可。

3. 上传图片

登录到图床服务后,你可以通过以下方式上传图片:

  • 网页上传: 在图床服务的网页上找到上传按钮,选择你要上传的图片文件。
  • API上传: 如果你是开发者,可以使用图床提供的API接口,通过代码上传图片。
  • 客户端上传: 有些图床服务提供桌面或移动客户端,你可以通过客户端上传图片。

4. 获取图片链接

图片上传成功后,图床服务会生成一个图片链接。你可以复制这个链接,用于在其他地方插入图片。

5. 插入图片

在需要插入图片的地方(如博客、论坛、Markdown文档等),使用以下格式插入图片:

markdown

复制

![图片描述](图片链接)

例如:

markdown

复制

![示例图片](https://i.imgur.com/example.jpg)

6. 管理图片

大多数图床服务允许你管理上传的图片,包括查看、删除、编辑图片信息等。你可以根据需要对图片进行管理。

存在问题

  • 国内的免费图床有大小,格式限制
  • 一些国内的图床提供几个月,就访问不到了
  • github 图床目前国内已经访问不到了

这里使用Bmob后端云来搭建图床,首先假定你已经有了一个账号,平台每月会给到20G CDN流量。

我们通过JS SDK来搭建简单图床

1.安装SDK

~:gh repo clone bmob/hydrogen-js-sdk

~:cd hydrogen-js-sdk

~:npm install

安装

2.运行

➜  hydrogen-js-sdk git:(master) ✗ npm run dev

运行

3.替换自己的秘钥

打开项目代码,找到main.js

秘钥

4.最终效果

效果

这时候UI自己设计一下,就完成了一个简易图床

5.核心代码

html

<input type="file" id="profilePhotoFileUpload"  multiple="multiple" >

JavaScript

// 获取文件上传控件元素
const fileUploadControl = document.getElementById('profilePhotoFileUpload');

// 当文件选择发生变化时触发该事件处理函数
fileUploadControl.onchange = () => {
  // 获取选择的文件列表
  const pic = fileUploadControl.files;
  
  // 遍历选择的文件并创建Bmob.File对象
  for(let item of pic){
     // 初始化Bmob.File,命名文件并关联文件对象
     file = Bmob.File(item.name, item);
  }
  
  // 保存文件到云端存储服务
  file.save().then(res => {
    // 保存成功后,获取保存结果
    const file = res[0];
    
    // 打印保存文件的数量和文件信息
    console.log(res.length);
    console.log(res,file,file.url);

    // file.url 插入到id为img的层下面最后一个元素图片展示
    document.getElementById('img').appendChild(document.createElement('img')).src = file.url;

    // 把文件url 文本插入到上一个图片后面
    document.getElementById('img').appendChild(document.createTextNode(file.url));


  });
}

教程代码 github 地址:https://github.com/bmob/hydrogen-js-sdk/ 有什么问题可以简书平台留言

为什么需要

简单地说,浏览器插件,可以大大的扩展你的浏览器的功能。包括但不仅限于这些功能:捕捉特定网页的内容,捕捉HTTP报文,捕捉用户浏览动作,改变浏览器地址栏/起始页/书签/Tab等界面元素的行为,与别的站点通信,修改网页内容……给你增加许多想象空间,试想想看,你可以用它来识别一些网站上的广告代码,并直接把这些代码删掉,这样你就不会受到广告的困扰了,没错,如你所愿,这样的插件别人已经开发好了,你可以直接用。不过,也要说浏览器插件的弊端,那就是:会带来一些安全隐患,也可能让你的浏览器变得缓慢甚至不稳定。

为什么是Chrome

因为Chrome的插件开发起来最简单,总体上看没什么新的技术,开发语言就是javascript,web前端工程师能很快上手;而Firefox的插件开发则复杂许多,涉及到环境的搭建和一些WEB以外的技术;IE的插件开发就更复杂了,需要熟悉C++和COM技术,当然还要装微软的Visual
Studio。

这里有篇老外写的文章,对比Chrome、Opera和Firefox的插件开发的:<http://blog.nparashuram.com/2011/10/writing-
browser-extensions-comparing.html>。

应该说Chrome和Opera的插件的开发都不难,但Firefox的则比较棘手,也许你要问,那为什么Firefox的插件是最丰富的?我想这有些历史原因,Chrome出来毕竟比较晚,另外几种浏览器提供的插件的功能也是不尽相同的,OK,我们还是言归正传吧。

需要准备什么

几乎是零需求。Chrome浏览器和一个文本编辑器即可,文本编辑器最好是带语法高亮的那种。谷歌对我们做技术的人来说真是太大度了。

如何开始

强烈建议看看官方的说明:https://developer.chrome.com/extensions/getstarted.html

文章不长,照着文章去做,完成后,你就成功开发了第一个Chrome插件,这个插件会弹出一个小窗口,上面显示些阿猫阿狗的小图片。如图:

这个插件一共有4个文件:

  • manifest.json - 所有插件都要有这个文件,这是插件的配置文件,可看作插件的“入口”。
  • icon.png - 小图标,推荐使用1919的半透明png图片,更好的做法是同时提供一张3838的半透明的png图片作为大图标,在我后面提供的例子中,我就是那么干的。
  • popup.html - 就是你所看到的那个阿猫阿狗的弹出页面。
  • popup.js - 阿猫阿狗页面所引用的javascript文件。

这里千万千万注意了,我当初没仔细看popup.html里有一小段注释,这一小段注释说:出于安全考虑,javascript必须与html分开存放
。而我想嘛,一个小测试程序,没必要分开吧,直接写一起不就行了吗?结果javascript死活执行不了,我翻来覆去找不到原因,还以为弹出的小窗口不支持javascript,在网上搜索了半天又没有结果,最后才发现是这个原因,浪费了许多时间,这个事情也一定程度上说明了:细节决定成败。

manifest.json中的内容也非常显而易见,我选择其中几个属性讲一下:

复制代码



{ "manifest_version": 2, "name": "One-click Kittens", "description": "This extension demonstrates a browser action with kittens.", "version": "1.0", "permissions": [ "https://secure.flickr.com/" ], "browser_action": { "default_icon": "icon.png", "default_popup": "popup.html" }
}

复制代码

"manifest_version":现在应该总是2。

"permissions":很重要的东西,即允许插件做哪些事情,访问哪些站点,假如一个插件的"permissions"里写有“http://*.hacker.com/”,那么这个插件就被允许访hacker.com上的所有内容,包括可能会把你的一些个人信息提交给hacker.com,危险性不言而喻,查看一个插件能访问那些站点的方法是:在chrome的地址栏里输入“chrome://extensions/”(注意:这个页面我们之后要频繁用到,请收藏一下),然后点对应插件的旁边的那个“权限”,如:

"default_popup":用来指定点击小图标后弹出的小窗口中默认显示的是哪个html,这个弹出的小窗口就叫做“popup”。

"browser_action":这是一个浏览器级的动作,也就是说,不管你现在在访问哪个页面,那个小按钮总是显示出来,而我们的插件如果仅仅是针对某些页面的话,就不适合用这个"browser_action"了。下面我们来弄一个只有访问博客园(www.cnblogs.com)才会出现的小按钮。

Page Action

[chrome-plugin-page-action-demo.7z](http://files.cnblogs.com/guogangj/chrome-
plugin-page-action-demo.7z)

这个插件只有4个文件,其中两个还是图标,那就只剩下一个必须的manifest.json和一个background.js了。

mainifest.json:

复制代码



{ "manifest_version": 2, "name": "cnblogs.com viewer", "version": "0.0.1", "background": { "scripts": ["background.js"] }, "permissions": ["tabs"], "page_action": { "default_icon": { "19": "cnblogs_19.png", "38": "cnblogs_38.png" }, "default_title": "cnblogs.com article information" }
}

复制代码

注意:这里是“page_action”而不是“browser_action”属性了。

“permissions”属性里的“tabs”是必须的,否则下面的js不能获取到tab里的url,而这个url是我们判断是否要把小图标show出来的依据。background是什么概念?这是一个很重要的东西,可以把它认为是chrome插件的主程序,理解这个很关键,一旦插件被启用(有些插件对所有页面都启用,有些则只对某些页面启用),chrome就给插件开辟了一个独立的javascript运行环境(又称作运行上下文),用来跑你指定的background
script,在这个例子中,也就是background.js。

background.js

复制代码



function getDomainFromUrl(url){ var host = "null"; if(typeof url == "undefined" || null == url)
          url = window.location.href; var regex = /.*\:\/\/([^\/]*).*/; var match = url.match(regex); if(typeof match != "undefined" && null != match)
          host = match[1]; return host;
} function checkForValidUrl(tabId, changeInfo, tab) { if(getDomainFromUrl(tab.url).toLowerCase()=="www.cnblogs.com"){
          chrome.pageAction.show(tabId);
     }
};

chrome.tabs.onUpdated.addListener(checkForValidUrl); 

复制代码

代码中,我们使用了一个正则表达式去匹配url,获取出其中的domain部分,如果domain部分是“www.cnblogs.com”的话,就把小图标show出来,效果如下:

当然了,你现在点那个小图标的话,是没有任何反应的,我没有像官方提供的那个例子那样提供了popup。OK,现在是时候描述下chrome插件的结构了。

Chrome插件结构

需要声明的是,这个结构图是我自己画的,代表我对Chrome插件的理解,可能并不全面,甚至还不是十分准确,但找来找去找不到现成的,只好自己动手,如有谬误,请不吝指出。

如图,manifest.json作为插件的配置文件,同时可以看作程序的“入口”,因为它指定了显示什么图标,background
script有哪些文件,content script又有哪些文件,pop up的页面是什么,等等。

什么是popup,什么是background script,相信大家都清楚了,那什么是content script呢?content
script就是我们要注入到页面中的脚本,插件允许我们往网页中注入脚本,这是一个多么让人有想象力的功能,其功能之强大无需多解释,总的来说,就是让我们全面干预页面的内容!也许你马上会想到,这可能带来很大的安全隐患,没错,有些恶意插件会窃取你的页面信息,而有些有漏洞的插件则可能让你遭受跨站脚本注入(XSS)的攻击;另一个可能你会想到的问题是:往页面中注入自己的脚本,难道不会跟页面原本的脚本发生冲突吗?能想到这点说明你真的很厉害,如果我们的注入脚本和页面原本的脚本处于同一个运行环境中,确实会发生冲突,所以,Chrome是另外开辟了一个独立的运行空间,供我们的Content
Script使用的,Content
Script能访问DOM的内容,但却不能访问页面原本的脚本(我是说直接访问不行),反之,页面原本的脚本也不能直接访问Content
Script。在图中,浅红色的背景块代表Content
Script的运行环境,而浅蓝色的背景块代表页面运行环境,另外插件的运行环境我用浅绿色表示,注意,这是三个不同的运行环境,调试的时候你会充分体会到它们的不同。

那么,Content Script会在什么时候运行呢?默认情况下,是在网页加载完了和页面脚本执行完了,页面转入空闲的情况下(Document
Idle),但这个是可以改变的,详情可参考https://developer.chrome.com/extensions/content_scripts.html,查看其中的“run_at”。

由于处于不同的运行环境中,Content Script和Background
Script不能直接互相访问,那它们之间如何通信?通过Message!这个之后的代码中会有。

学习资料

理解了Chrome插件结构之后,我相信你完全有能力开发一款自己的插件了,当然了,你得自己去google一些资料,这里我就分享下我的方法。

首先,官方的资料一定得看看,https://developer.chrome.com/extensions/index.html,这个上面的资料得大致浏览一下(不需要全部仔细看),这样你能够明白一些术语,知道如何去寻找你的解决方案。

再则,官方提供的例子,可以看看,https://developer.chrome.com/extensions/samples.html,我发现上面的例子有些已经不能用于新版的Chrome了,但没关系,你只要找你想要的就行了,也不用一个个尝试,就根据你的需要,挑选几个你感兴趣的看看即可。

遇到问题,怎么办?当然是用google去查找问题,但这里我最最最强烈推荐,这简直是解决问题的神器!不多解释了,用过便知。

学习过程基本上就是:看个大概,写点代码,调试调试。就可以了。哦,大前提当然是你得有javascript的基础。(你:呵呵,你在逗我吧!)

我的例子

[chrome-plugin-cnblogs-article-
information.zip](http://files.cnblogs.com/guogangj/chrome-plugin-cnblogs-
article-information.zip)

[chrome-plugin-cnblogs-article-info-
server.zip](http://files.cnblogs.com/guogangj/chrome-plugin-cnblogs-article-
info-server.zip) (服务器端,PHP代码)

好,轮到我的例子登场了。它的功能是这样的:当你浏览博客园的时候,它会启动并尝试获取你浏览的文章的信息(标题、作者和日期),再通过往另一个服务器发送请求的方式,记录和获取你第一次访问这篇文章的时间,把这个时间连带文章的信息,显示在popup上。听起来挺无聊的功能,但关键是为了演示嘛,如图:

这个插件一共有9个文件,新出现的文件有两个(其它相信大家都很熟悉了),一个是“content_script.js”,这就是前面提到的Content
Script,获取和修改页面的内容就靠它了;另一个是“jquery-2.0.0.min.js”,大名鼎鼎的jQuery,我很喜欢用的js库,其理念是“write
less,do
more”,能帮我减少很多代码,这是目前最新的2.0.0版,这个版本跟以前的1.x.x的最大差别就是不再支持IE6、7和8,我个人是十分赞同这种做法的,微软的旧版浏览器都成了Web技术发展的绊脚石了,而且这次我们用的是Chrome浏览器,果断选择最新版了。

另外还有一个服务器端,为了让问题简化,这次我用了php代码,一个php文件就是整个处理了,没有太多繁杂的配置,简洁,这是php最大的优势。系统结构如图:

抓取网页的内容得依靠content_script.js,然后通过sendMessage/onMessage和background.js交换数据,background.js将url信息通过ajax(XMLHttpRequest)发送给localhost,获取此页面的第一次访问的时间,最后,用户点小图标,popup.html出现,popup.html会读取(代码在popup.js中)background.js中的articleData的数据,把它显示出来。这就是整个过程。

我抓取网页数据的方式并不能确保所有的博客园的文章都能被正常获取,这跟用户使用的博客模板有关系,但我尝试下来大多数文章还是可以抓取的,我不去适应所有的模板了,毕竟这只是个演示的demo。

另外还需说明的一点是我使用了jQuery做XMLHttpRequest,post的内容不是传统的html表单形式,而是json数据,所以在服务器端这边,就不能直接用$_REQUEST获取,而是通过读取“php://input”的内容获取。顺便谈谈个人对web
api的一个看法:“统一”大于“灵活”,这是我的观点,我确定我的接口的格式是json,使用utf-8编码,于是就一直用下去,调用者不用考虑用XML还是html表单还是别的,开发者也不必多考虑,让这成为一种统一的约定,在团队协助和以后的开发中会很省事。

调试

程序开发,必定要涉及到调试,记得我刚开始做WEB开发前,问一些做了好久WEB开发的朋友,你们是怎样做javascript调试的,我发觉大多数人竟然回答:用alert一点点试吧——不是不行,是太原始,太低效了,对吧?其实Chrome直接支持javascript的调试,拥有了Chrome,就相当于拥有了一个强大的javascript调试器了。

Chrome打开开发者工具的方法是++_(Windows版),大致如下:_

我们这次需要关心的有“Elements”、“Sources”和“Console”这三个标签。Elements是用来做DOM分析的,功能有点类似Firebug,帮助我们分析页面的内容;而Sources,是我们用来调试javascript的;Console则是我们的Log的输出窗口,也是一个调试利器。

调试Content Script

如我提供的这个例子,可在Sources的“Copntent
Scripts”下看到“content_script.js”然后设断点,执行到断点处时,Chrome会挺住,你可以观察到上面的值,如图:

太cool了,请问你还要一点点alert吗?

调试Background

由于background和content
script并不在同一个运行环境中,因此上面的方法是看不到Background的javascript的。要调试Background,还需要打开插件页,也就是“chrome://extensions”。点对应的插件的“generated
background page.html”,就出现了调试窗口,接下来的操作就跟前面的类似了。如图:

至于你看到ID,“aajnhhjiia……”这一长串东西,这是chrome自动安排的一个ID。

调试Popup

虽然Popup和Background是处于同一运行环境中,但在刚才的Background的调试窗口中是看不到Popup的代码的。调试Popup还需要这样:

然后……就跟前面差不多了。

一些问题

也许有时候你会发觉调试器不是很灵,至少我用下来感觉如此,比如你可能发现断点设不了,或者断点不起作用,或者看不到你自己的javascript文件。我的方法是在插件页中,把对应的插件的“已启用”这个复选框去掉,再重新勾上,然后再点一下“重新加载(Ctrl+R)”,通常能解决问题。当然了,还有些很古怪的问题,还不好重现,总体的解决思路就是重新载入一下,实在不行的话重启浏览器,或者清除浏览器缓存什么的,再试试看。

在做插件调试的时候我还遇到一个十分郁闷的问题,那就是我的Chrome使用了“Go Agent”,关于Go
Agent是用来干嘛的,这个嘛,可以去google一下,我相信绝大多数程序员都会喜欢上它……可由于使用了这个东西,很可能会导致插件的XMLHttpRequest工作不正常,而且可能你会思索半天也找不到原因,好吧,暂时把Go
Agent停用掉,甚至可能你需要重启下Chrome——我的经验。

总结

我还是想说,我觉得Google对我们程序员来说是个很大度的公司,在Chrome这个产品上面就可见一斑。利用Chrome插件技术,我们可以做许多有用的东西,通过本文,相信你已经知道如何去开发一款Chrome插件了,当然了,Chrome插件的功能是很强大的,我用到的仅是冰山一小角。要深入,当然还需要更加充分地利用google和stackoverflow.com了。

原文http://www.cnblogs.com/guogangj/p/3235703.html#t2