mongo数据库表怎么增加字段_百度知道

mikel阅读(5)

来源: mongo数据库表怎么增加字段_百度知道

mongodb常用操作语句

  1、现有表以及数据添加字段
  db.tbGoodsConsultant.update({}, {$set:{nFlagState:0}}, false, true);
  2、给表字段添加索引
  db.tbGoodsConsultant.ensureIndex({nFlagState:1});
  3、增加数据
  > db.food.save({"name":"jack","address":{"city":"Shanghai","post":021},"phone":[138,139]});
  > db.food.save({"uid":"","AL":['','']});
  4、删除表、数据库
  > db.users.drop();
  > db.dropDatabase();
  5、创建索引、数字1表示升序 -1 表示降序
  > db.user.ensureIndex({"lId":1,"name":-1});
  > db.system.indexes.find();
  6、删除索引
  > db.mycoll.dropIndex(name)
  7、去掉重复数据
  > db.user.distinct('name');
  8、排序sort 1:ASC -1:DESC
  >db.user.find().sort({"age":1});
  9、查询name中包含mongo的数据 %y%
  > db.user.find({name:/y/});
  10、查询name中以d开头的 like 'd%'
  > db.user.find({name:/^d/});
  11、查询指定列name、age数据(name也可以用true||false,true和name:1等同)
  > db.user.find({},{name:1,age:1});
  12、查询2条以后的数据
  > db.user.find().skip(2);
  13、查询在2-10之间的数据
  > db.user.find().limit(10).skip(2);

chrome谷歌浏览器-DevTool开发者工具-详细总结 – Nirvana_zsy – 博客园

mikel阅读(6)

来源: chrome谷歌浏览器-DevTool开发者工具-详细总结 – Nirvana_zsy – 博客园

chrome的开发者工具可以说是十分强大了,是web开发者的一大利器,作为我个人而言平时用到的几率很大,相信大家也很常见,但是不要仅仅停留在点选元素看看样式的层面上哦,跟着我的总结一起学习实践一下吧(能帮到你的话随便copy好了,开源时代赛高,感谢我就给我留个评论吧。但是码字就码了一两个小时,中午的午休都没了还啪啪啪的敲键盘打扰同事休息,一定要好好学啊)。

 

目录:


 

一、概述

1.官方文档

2.打开方法:

3.前言:

二、九个模块:

1.设备模式Device Mode

2.元素面板Elements

3.控制台面板Console

4.源代码面板Sources

5.网络面板Network

6.性能面板Performance  以前的版本叫Timeline 

7.内存面板Memory  以前的版本叫分析面板 

8.应用面板Application 以前版本叫资源面板 

9.安全面板Security 

三、 注 

 


 

一、概述

1.官方文档

https://developers.google.com/web/tools/chrome-devtools/ (需科学上网)

 

2.打开方法:

*在Chrome菜单中选择 更多工具 > 开发者工具

*在页面元素上右键点击,选择 “检查”

*使用 快捷键 Ctrl+Shift+I (Windows) 或 Cmd+Opt+I (Mac)

 

3.前言:

chrome的开发者工具可以说是十分强大了,是web开发者的一大利器,作为我个人而言平时用到的几率很大,相信大家也很常见,但是不要仅仅停留在点选元素看看样式的层面上哦,跟着我的总结一起学习实践一下吧(能帮到你的话随便copy好了,开源时代赛高,感谢我就给我留个评论吧。但是码字就码了一两个小时,中午的午休都没了还啪啪啪的敲键盘打扰同事休息,一定要好好学啊)。

 

二、九个模块:

 

1.设备模式Device Mode

支持响应式:可以自己调节页面大小;

 

可以按照设备选择用户代理/设备,也可以在devtools setting中增加自定义设备,模仿设备触摸、滚动事件,支持横竖屏,支持选择是否显示设备外框;

 

菜单中可以开启媒体查询(查看css中@media设置的各个查询);

 

可调节DPR(设备像素比,逻辑像素/物理像素,带Retina屏的设备像素密度高,DPR大),在响应式中可以设置该值,在选择了特定设备时不可选,因为特定的设备的DPR值固定;

 

“throttling”可以模拟不同的网络环境,可以限制网速;

 

能模拟替换地理位置,模拟加速度计(设备方向);

2.元素面板Elements

 

能在元素面板查看/选择当前页的DOM树,也可以在页面上点选;

选中节点后会在元素面板左边显示元素源代码所在位置,右边会显示节点的各级样式,盒模型(Computed),事件,断点,属性;

DOM代码和样式可以实时修改调试(修改后再刷新页面,修改就失效了,可以选择保存修改,下面sources面板会提到);

可以在每行代码前面右键添加断点,这点很实用。断点有三种类型:属性改变,子节点改变,节点删除;

可以右键节点选择节点的状态:active :hover :visited :focus,当你无法模拟这些状态时,这个功能就很实用;

3.控制台面板Console

在控制台能显示浏览器的消息,其中消息分为info,verbose(长消息),error,warning四个等级;

相同的消息是默认堆叠的,可以设置成按时间戳(timestamps)显示;

可以选择是否保留历史记录Preverse Log,是否显示网络消息和XHR请求;

可以用filter过滤器筛选信息;

可以选择执行环境:top(默认)、 iframe等;

可以在控制台执行代码段,chrome浏览器内置了一些函数可以使用,有意思的是你可以直接在控制台里定义修改它的函数再使用;

4.源代码面板Sources

 

在这里可以看到一些源文件;

类似元素面板中设置断点,这里也可以在每一行代码前设置断点,利用这些断点使代码运行到适当的时候或位置停下来,以便查看特定时刻的变量值、调用堆栈、样式等;

 

不过这里的断点设置比前面的更强,可以自定义断点条件,运行到这行代码表达式为true就会停下来;

中间部分左下角有一个{}大括号图标,可以将压缩的代码格式化(这个很实用啊666);

右边部分也是关于断点的,可以在这里查看所有的断点,选择一系列事件断点,比如动画,键盘,Load事件,XHR事件等等;

5.网络面板Network

 

网络面板分为上下五个窗格。

第一行是control窗格,窗格中的选项用于控制network面板的外观和具体功能;

功能包括录制屏幕,录制后将会以类似“ppt”的形式将录制的每一帧显示出来(我平时调试动画会用到,很实用),可以拉动时间轴;

可以选择打开filter过滤器,控制在Request Table窗格(第四个) 中显示哪些下载资源,支持正则表达式过滤,支持选择不同类型资源;

值得一提的是“Disable cache”这个选项,用于控制是否支持缓存(当你在服务器多次更改css,js文件然后需要在浏览器查看更改效果的时候,你可以打开这个选项,以防浏览器使用的旧的缓存的文件,那样就看不到更改效果了);

类似设备模式中的throttling,这里也可以模拟网络环境;

第三个“Overview”窗格,这里面的图标显示了资源检索时间的时间线(资源多的话像瀑布一样),你可以点选时间线的一段时间查看;

第四个“Request Table”窗格就列出了检索的每一个资源,以及他们的加载时间,资源类型,服务器返回状态码(比如200),大小,鼠标移到右边的彩色条上就能看的详细的瀑布图,更厉害的是右键资源,有很多操作,比如复制资源请求/响应的报头、报文以及URL(报头报文不知道的了解一下计算机网络里面的http协议);

6.性能面板Performance以前的版本叫Timeline

在这个面板你可以录制一段操作,然后查看录制期间的一些页面性能信息,录制前有几个选项可选,包括网络环境模拟,CPU速度模拟,是否开启录制期间截屏;

性能面板包含四个窗格:

 

第一个是控制窗格,包含一些录制选项:CPU速度模拟,网络环境模拟,录制期间是否截图,是否开启js记录等等,录制要注意几点:

  *尽可能保持记录简短。简短的记录通常会让分析更容易。

  *避免不必要的操作。避免与您想要记录和分析的活动无关联的操作(鼠标点击、网络加载,等等)。例如,如果您想要记录点击 Login 按钮后发生的事件,请不要滚动页面、加载图像,等等。

  *停用浏览器缓存。记录网络操作时,最好从 DevTools 的 Settings 面板或 Network conditions 抽屉式导航栏停用浏览器的缓存。

  *停用扩展程序。Chrome 扩展程序会给应用的 Timeline 记录增加不相关的噪声。 以隐身模式打开 Chrome 窗口或者创建新的 Chrome 用户个人资料,确保您的环境中没有扩展程序。

第二窗格是Overview,是录制页面性能的汇总,包含以下三个图表:

FPS。每秒帧数。绿色竖线越高,FPS 越高。 FPS 图表上的红色块表示长时间帧,很可能会出现卡顿。如果录制开启了截屏,当鼠标悬停在overview窗格上时,会显示出每一帧的图片。

CPU。 CPU 资源。此面积图指示消耗 CPU 资源的事件类型。

NET。每条彩色横杠表示一种资源。横杠越长,检索资源所需的时间越长。 每个横杠的浅色部分表示等待时间(从请求资源到第一个字节下载完成的时间)。

深色部分表示传输时间(下载第一个和最后一个字节之间的时间)。

横杠按照以下方式进行彩色编码:

HTML 文件为蓝色。

脚本为黄色。

样式表为紫色。

媒体文件为绿色。

其他资源为灰色。

 

 

另外,按 Cmd+F (Mac) 或 Ctrl+F (Windows / Linux) 可以打开一个查找工具栏。键入想要检查的事件类型的名称,如 Event。

搜索工具栏仅适用于当前选定的时间范围。选定时间范围以外的任何事件都不会包含在结果中。利用上下箭头,可以按照时间顺序在结果中移动,第一个结果表示选定时间范围内最早的事件,最后一个结果表示最后的事件。每次按向上或向下箭头会选择一个新事件,可以在 Details 窗格中查看其详细信息。按向上和向下箭头等同于在火焰图中点击事件。

第三个窗格是火焰图。 CPU 堆叠追踪的可视化。

可以在火焰图上看到一到三条垂直的虚线。蓝线代表 DOMContentLoaded 事件。 绿线代表首次绘制的时间。 红线代表 load 事件。

开始记录前,不要开启Disable JS samples复选框,以便在时间线记录中捕捉 JavaScript 堆栈。 启用 JS 分析器后,火焰图会显示调用的每个 JavaScript 函数。

火焰图可以用鼠标滚轮调整大小;

 

 

第四个是Details窗格。选择事件后,此窗格会显示与该事件有关的更多信息。 未选择事件时,此窗格会显示选定时间范围的相关信息。

7.内存面板Memory以前的版本叫分析面板

在此面板录制,可以使用CPU 分析器识别开销大的js函数

CPU 分析器准确地记录调用了哪些函数和每个函数花费的时间。也可以将配置文件可视化为火焰图。(执行js比较卡的时候可以用这个工具来查找原因);

火焰图分为以下两部分:

概览:整个记录的鸟瞰图。 条的高度与调用堆栈的深度相对应。 所以,栏越高,调用堆栈越深。

调用堆栈:这里可以详细深入地查看记录过程中调用的函数。 横轴是时间,纵轴是调用堆栈。 堆栈由上而下组织。所以,上面的函数调用它下面的函数,以此类推。

 

 

高调用堆栈不一定很重要,只是表示调用了大量的函数。但宽条表示调用需要很长时间完成。 这些需要优化。

鼠标悬停在上面可以看到函数详情。

!此外,除了用内存面板查看函数的内存堆栈情况外,还可以使用 Chrome 任务管理器作为内存问题调查的起点。

任务管理器是一个实时监视器,可以告诉您页面当前正在使用的内存量。

按 Shift+Esc 或者转到 Chrome 主菜单并选择 More tools > Task manager,打开任务管理器。

右键点击任务管理器的表格标题并启用 JavaScript memory

下面两列是与页面的内存使用有关的不同信息:

Memory 列表示原生内存。DOM 节点存储在原生内存中。 如果此值正在增大,则说明正在创建 DOM 节点。

JavaScript Memory 列表示 JS 堆。此列包含两个值。 您感兴趣的值是实时数字(括号中的数字)。 实时数字表示您的页面上的可到达对象正在使用的内存量。 如果此数字在增大,要么是正在创建新对象,要么是现有对象正在增长。

8.应用面板Application以前版本叫资源面板

查看和修改Local Storage与Session Storage:

 

可以从 Local Storage 窗格和Session Storage窗格中检查、修改和删除键值对(KVP);

使用 IndexedDB 窗格可以检查、修改和删除 IndexedDB 数据。

展开 IndexedDB 窗格时,IndexedDB 下的第一个级别是数据库。 如果存在多个活动的数据库,会看到多个条目。 在下面的屏幕截图中,页面只有一个活动的数据库。

点击数据库的名称可以查看该数据库的安全源、名称和版本。

展开数据库可以查看其键值对 (KVP)。

 

使用 Start from key 文本字段旁的箭头按钮可以在 KVP 的页面之间移动。

展开值并双击可以编辑该值。在您添加、修改或删除值时,这些更改不会实时更新。

 

在 Start from key 文本字段中输入键可以过滤出值小于该值的所有键。

 

在添加、修改或删除值时,这些更改不会实时更新。 点击 refresh 按钮 () 可以更新数据库。

点击 Clear Object Store 按钮 () 可以删除数据库中的所有数据。 从 Clear storage 窗格中,点击一次按钮注销服务工作线程并移除其他存储与缓存也可以实现此目标。

使用 Web SQL 窗格可以查询和修改 Web SQL 数据库:

点击数据库名称可以打开该数据库的控制台。从这里,可以对数据库执行语句。

 

点击数据库表可以查看该表的数据。

无法从这里更新值,但是可以通过数据库控制台(参见上文)更新。

点击列标题可以按该列排序表格。

在 Visibile columns 文本字段中输入一个由空格分隔或逗号分隔的列名称列表可以仅显示列表中包含的列。

使用 Application Cache 窗格可以检查通过 Application Cache API (HTML5中新加的API)创建的资源和规则:

 

每一行表示一个资源。

Type 列的值为以下值之一:

Master。资源上指示此缓存为其主文件的 manifest 属性。

Explicit。此资源在清单中明确列出。

Network。指定此资源的清单必须来自网络。

Fallback。Resource 列中的网址作为另一个网址(未在 DevTools 中显示)的回退网址形式列出。

表格底部拥有指示网络连接和应用缓存状态的状态图标。 应用缓存可能拥有以下状态:

IDLE。缓存没有新更改。

CHECKING。正在提取清单并检查有无更新。

DOWNLOADING。正在将资源添加到缓存中。

UPDATEREADY。存在新版本的缓存。

OBSOLETE。正在删除缓存

清除服务工作线程、存储、数据库和缓存:

有时,只需要擦除给定源的所有数据。利用 Application 面板上的 Clear Storage 窗格,可以选择性地注销服务工作线程、存储和缓存。要清除数据,只需启用想要擦除的组件旁的复选框,然后点击 Clear site data。操作将擦除 Clear storage 标签下所列源的所有数据。

 

9.安全面板Security

使用 Security Overview 可以一目了然的查看当前页面是否安全。

点击 View certificate 检查各个源以查看连接和证书详情(安全源)或找出具体哪些请求未受保护(非安全源)。

Security 面板可以区分两种非安全页面。

如果请求的页面通过 HTTP 提供,则主源会被标记为不安全。

如果请求的页面通过 HTTPS 检索,但页面会继续使用 HTTP 检索其他源的内容,然后页面仍然会被标记为不安全。这称为混合内容页面。 混合内容页面仅受部分保护,因为 HTTP 内容可以被嗅探器获取到且易受到中间人攻击

使用左侧面板可以检查各个安全或非安全源。点击安全源查看该源的连接和证书详情。

三、

Author:nirvana-zsy

Time:2016.6.26

写在最前面 – 每天5分钟玩转容器技术(1) – CloudMan – 博客园

mikel阅读(12)

来源: 写在最前面 – 每天5分钟玩转容器技术(1) – CloudMan – 博客园

《每天5分钟玩转容器技术》是一个有关容器技术的教程,有下面两个特点:

  1. 系统讲解当前最流行的容器技术。
    从容器的整个生态环境到各个具体的技术,从整体到细节逐一讨论。
  2. 重实践并兼顾理论。
    从实际操作的角度带领大家学习容器技术。

为什么要写这个?

简单回答是:容器技术非常热门,但门槛高

容器技术是继大数据和云计算之后又一炙手可热的技术,而且未来相当一段时间内都会非常流行。

对 IT 行业来说,这是一项非常有价值的技术。而对 IT 从业者来说,掌握容器技术是市场的需要,也是提升自我价值的重要途径。

拿我自己的工作经历来说,毕业后的头几年是做 J2EE 应用开发。后来到一家大型IT公司,公司的产品从中间件到操作系统,从服务器到存储,从虚拟化到云计算都有涉及。

我所在的部门是专门做 IT 基础设施实施服务的,最开始是做传统的 IT 项目,包括服务器配置,双机 HA 等。随着虚拟化技术成熟,工作上也开始涉及各种虚拟化技术的规划和实施,包括 VMWare,KVM,PowerVM等。后来云计算兴起,在公司业务和个人兴趣的驱动下,开始学习和实践 OpenStack,在这个过程中写了《每天5分钟玩转OpenStack》教程并得到大家的认可。

现在以 Docker 为代表的容器技术来了,而且关注度越来越高,这一点可以从 google trend 中 Docker 的搜索上升趋势(蓝色曲线)中清楚看到。

每一轮新技术的兴起,无论对公司还是个人既是机会也是挑战。

我个人的看法是:如果某项新技术未来将成为主流,就应该及早尽快掌握。 因为:

  1. 新技术意味着新的市场和新的需求。
    初期掌握这种技术的人不会很多,而市场需求会越来越大,因而会形成供不应求的卖方市场,物以稀为贵,这对技术人员将是一个难得的价值提升机会。
  2. 学习新技术需要时间和精力,早起步早成材。

机会讲过了,咱们再来看看挑战。

新技术往往意味着技术上的突破和创新,会有不少新的概念和方法。
而且从大数据,云计算和容器技术来看,这些新技术都是平台级别,覆盖的技术范围非常广,包括了计算、网络、存储、高可用、监控、安全等多个方面,要掌握这些新技术对 IT 老兵尚有不小难道,更别说新人了。

由于对技术一直保持着很高的热诚和执着,在掌握了 OpenStack 相关 IaaS 技术后,我便开始调研 PaaS 技术栈。正好这时 Docker 也越来越流行,自然而然便开始了容器相关技术的学习研究和实践。

学习容器技术的过程可以说是惊喜不断,经常惊叹于容器理念的先进和容器生态环境的完整和强大。很多传统软件开发和运维中的难题在容器世界里都能轻松解决,也渐渐理解了容器为何如此受到青睐。

不夸张的说,容器为我打开了一扇通往另一个软件世界的大门,让我沉浸其中,激动不已。高兴之余,我也迫不及待地想把我所看到所学到和所想到的有关容器的知识介绍给更多的人,让更多的 IT 工程师能够从容器技术中受益。

我希望这个教程也能为大家打开这扇门,降低学习的曲线,系统地学习和掌握容器技术。写给谁看?

写给谁看?

这套教程的目标读者包括:

软件开发人员

相信微服务架构(Microservice Architectur)会逐渐成为开发应用系统的主流。而容器则是这种架构的基石。市场将需要更多能够开发出基于容器的应用程序的软件开发人员。

IT 实施和运维工程师

容器为应用提供了更好的打包和部署方式。越来越多的应用将以容器的方式在开发、测试和生产环境中运行。掌握容器相关技术将成为实施和运维工程师的核心竞争力。

我自己

我坚信最好的学习方法是分享。编写这个教程同时也是对自己学习和实践容器技术的总结。对于知识,只有把它写出来并能够让其他人理解,才能说明真正掌握了这项知识。

包含哪些内容?

如下图,三大块:

下面分别介绍各部分包含的内容。

启程

“启程”会介绍容器的生态系统,让大家先从整体上了解容器都包含那些技术,各种技术之间的相互关系是什么,然后再来看我们的教程都会涉及生态中的哪些部分。

为了让大家尽快对容器有个感性认识,我们会搭建实验环境并运行第一个容器,为之后的学习热身。

容器技术

这是教程的主要内容,包含“容器核心知识”和“容器进阶知识”两部分。

核心知识主要回答有关容器 what, why 和 how 三方面的问题。其中以 how 为重,将展开讨论架构、镜像、容器、网络和存储。

进阶知识包括将容器真正用于生成所必须的技术,包括多主机管理、跨主机网络、监控、数据管理、日志管理和安全管理。

容器平台技术

容器平台技术在生态环境中占据着举足轻重的位置,对于容器是否能够落地,是否能应用于生产至关重要。我们将详细讨论容器编排引擎、容器管理平台和基于容器的 PaaS,学习和实践业界最具代表性的开源产品。

怎样的编写方式?

我会继续采用《每天5分钟玩转OpenStack》的方式,通过大量的实验由浅入深地探讨和实践容器技术,力求达到如下目标:

  1. 快速上手:以最直接、最有效的方式让大家把容器用起来。
  2. 循序渐进:由易到难,从浅入深,详细分析容器的各种功能和配置使用方法。
  3. 理解架构:从设计原理和架构分析入手,深入探讨容器的架构和运行机理。
  4. 注重实践:以大量实际操作案例为基础,让大家能够掌握真正的实施技能。

在内容的发布上还是通过微信公众号(cloudman6)每周 1、3、5 定期分享。 欢迎大家通过公众号提出问题和建议,进行技术交流。

为什么叫《每天5分钟玩转容器技术》?

为了降低学习的难度并且考虑到移动端碎片化阅读的特点,每次推送的内容大家只需要花5分钟就能看完(注意这里说的是看完,有时候要完全理解可能需要更多时间),每篇内容包含1-3个知识点,这就是我把教程命名为《每天5分钟玩转容器技术》的原因。虽然是碎片化推送,但整个教程是系统、连贯和完整的,只是化整为零了。

好了,今天这5分钟算是开了个头,下次我们正式开始玩转容器技术。

二维码+指纹.png

超好用的Redis管理及监控工具,使用后可大大提高你的工作效率! – marko39 – 博客园

mikel阅读(14)

来源: 超好用的Redis管理及监控工具,使用后可大大提高你的工作效率! – marko39 – 博客园

Redis做为现在web应用开发的黄金搭担组合,大量的被应用,广泛用于存储session信息,权限信息,交易作业等热数据。做为一名有10年以上JAVA开发经验的程序员,工作中项目也是广泛使用了Redis,工作中也遇到了Redis的数据可视化不便、Redis的数据查看维护困难、Redis状态监控运维不易等问题。

相信大家在工作中也会遇到我说的这些问题,在工作中大家可能都装过一些可视化的工具,客户端工具,状态监控工具。例如Redis Desktop Manager,Redis Live,Redis browser等,这些工具开发语言也五花八门,php, ruby,python, qt等,安装前置条件也一堆,安装过程的痛苦,装过就知道了。

由于我也是一线的开发,也深切的感受到了没有顺手Redis管理工具带来的不便,经过一段时间的加班加点,挑灯开发,一款用JAVA语言开发的Redis管理及监控工具treeNMS横空出世了。

下面我就详细给大家介绍一下treeNMS的安装及各项功能。

 

1、  安装

相信大家windows下安装工具都得心应手,但Linux中安装就头大了吧,treeNMS管理工具,直接到http://www.treesoft.cn/dms.html下载,是用JAVA开发的,基于WEB方式对Redis管理,windows环境下载解压即可使用,Linux环境中也只需将软件复制过去,配置JAVA环境就可以使用了。MAC系统中也可以直接复制过去用,前提是有JAVA运行环境。

因为是基于WEB方式的,所以可以直接在服务器上布署一份,那么大家都可以用浏览器直接访问操作,避免了每个开发人员都要安装工具软件的麻烦,工作效率大大提高。

 

2、  运行及参数初始化

按说明运行startup.bat文件,软件就运行起来了show一下后台主页。

主面右上角有一个参数配置按钮,点击后直接进行连接信息的填写及连接测试。直接在线编辑连接信息的好处是,无需访问远程服务器,直接就完成修改连接信息。

 

3、状态监控

Redis做为缓存数据库,对内存占用率等指标很有必要定期监控,目前市面上多是国外的Redis监控软件,并且大部分功能较单一,例如Redis Live, 展示的指标有限,而treeNMS提供了详细的Redis状态值 ,达68项之多,并且有状态实时监控。

 

4、 Redis数据的查看,编辑,维护

说到数据查看,之前我也用过Redis Desktop Manager,这是单机版本的,也是国人用JAVA开发的,挺好的,就是数据大时,不稳定。单机版的问题就是团队中相关人员都要安装,服务器远程管理Redis不便,treeNMS就解决了这些问题。

详细数据的展示,查看,编辑,新增,删除等,支持string,list ,set,zset,hash等数据类型。

有些情况会看到展示的数据是乱码,这是由于存入的数据已压缩或序列化处理过,这种类型的数据是展示时就会变成乱码是正常现象。

 

5、json数据格式化

JSON格式的数据具有占用空间小,更易解析等优点,大量数据会以JSON格式存入Redis中。

treeNMS提供了JSON数据格式化的功能,支持格式化,缩进,层级控制,查看,方便数据分析。 做开发,测试都会用到这个功能的。 

 

6、在线数据备份及还原

Redis是支持数据存储及持久化的,treeNMS 支持在线redis数据备份及下载,有个细节:程序要与redis安装在同一台电脑中,才能备份哦。

 

总结:有了这款treeNMS软件,就可以轻松驾驭redis了,也希望国产软件能引领技术革新,走向世界!

 

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的研发动力!欢迎各位转载.

基于SignalR的小型IM系统 – 程序诗人 – 博客园

mikel阅读(14)

来源: 基于SignalR的小型IM系统 – 程序诗人 – 博客园

这个IM系统真是太轻量级了,提供的功能如下:

1.聊天内容美化

2.用户上下线提示

3.心跳包检测机制

4.加入用户可群聊

下面来一步一步的讲解具体的制作方法。

开篇准备工作

首先,巧妇难为无米之炊,这是总所周知的。这里我们需要两个东西,一个是ASP.NET MVC4项目;另一个是Signalr组件。

新建一个ASP.NET MVC4项目,然后通过以下命令安装Signalr组件:

1
Install-Package Microsoft.AspNet.SignalR -Version 1.1.3

这样我们就将组件安装完毕了。

后台交互部分

接着在项目中,新建一个文件夹名称为Hubs,在这个文件夹下面新建一个名称为IChatHub的接口,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface IChatHub
   {
       //服务器下发消息到各个客户端
       void SendChat(string id, string name, string message);
       //用户上线通知
       void SendLogin(string id, string name);
       //用户下线通知
       void SendLogoff(string id, string name);
       //接收客户端发送的心跳包并处理
       void TriggerHeartbeat(string id, string name);
   }

其中,SendChat方法主要用户Signalr后端向前台发送数据;SendLogin方法主要用于通知用户上线;SendLogoff方法主要用于通知用户下线;而TriggerHeartbeat方法主要用于接收前端发送的心跳包并做处理,以便于判断用户是否断开连接(有时候用户直接关闭浏览器或者在任务管理器中关闭浏览器,是无法检测用户离线与否的,所以这里引入了心跳包机制,一旦用户在20秒之后未发送任何心跳包到后端,则视为掉线)。

接下来添加一个ChatHub的类,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class ChatHub:Hub, IChatHub
    {
        private IList<UserChat> userList = ChatUserCache.userList;
        public void SendChat(string id, string name, string message)
        {
            Clients.All.addNewMessageToPage(id, name + " " + DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"), message);
        }
        public void TriggerHeartbeat(string id, string name)
        {
            var userInfo = userList.Where(x => x.ID.Equals(id) && x.Name.Equals(name)).FirstOrDefault();
            userInfo.count = 0;  //收到心跳,重置计数器
        }
        public void SendLogin(string id,string name)
        {
            var userInfo = new UserChat() { ID = id, Name = name };
            userInfo.action += () =>
            {
                //用户20s无心跳包应答,则视为掉线,会抛出事件,这里会接住,然后处理用户掉线动作。
                SendLogoff(id, name);
            };
            var comparison = new ChatUserCompare();
            if (!userList.Contains<UserChat>(userInfo, comparison))
                userList.Add(userInfo);
            Clients.All.loginUser(userList);
            SendChat(id, name, "<====用户 " + name + " 加入了讨论组====>");
        }
        public void SendLogoff(string id,string name)
        {
            var userInfo = userList.Where(x => x.ID.Equals(id) && x.Name.Equals(name)).FirstOrDefault();
            if (userInfo != null)
            {
                if (userList.Remove(userInfo))
                {
                    Clients.All.logoffUser(userList);
                    SendChat(id, name, "<====用户 " + name + " 退出了讨论组====>");
                }
            }
        }
    }

这个类的设计思想有如下几个部分:

首先,所有用户的登陆信息,我持久化到了缓存集合中:IList<UserChat>,这个缓存集合的定义如下:

1
2
3
4
public static class ChatUserCache
    {
        public static IList<UserChat> userList = new List<UserChat>();
    }

这样,用户登陆信息就会保存到内存中,一旦有新用户进来或者是旧用户退出,我就可以通过新增条目或者删除条目来维护这个列表,维护完毕,将这个列表推到前端。这样前台用户就能实时看到,哪些用户上线,哪些用户下线了。

其次,心跳包检测机制部分,前端用户每隔5秒钟会发送一次心跳包到处理中心,处理中心收到心跳包,会将实体类的计数器置为0;也就是说,如果用户登陆正常,那么用户实体中的计数器每隔5秒钟自动置为0;但是如果用户不按正常渠道退出(直接关闭浏览器或者在任务管理器中关闭浏览器),那么用户实体中的计数器就会一直递增,直到加到第20秒,然后会抛出事件,提示当前用户已经断开连接。

用户实体设计如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class UserChat
    {
        public UserChat()
        {
            count = 0;
            if (Timer == null) Timer = new Timer();
            Timer.Interval = 1000;  //1s触发一次
            Timer.Start();
            Timer.Elapsed += (sender, args) =>
            {
                count++;
                if (count >= 20)
                    action();  //该用户掉线了,抛出事件通知
            };
        }
        private readonly Timer Timer;
        public event Action action;
        
        public string ID { get; set; }
        public string Name { get; set; }
        //内部计数器(每次递增1),如果服务端每5s能收到客户端的心跳包,那么count被重置为0;
        //如果服务端20s后仍未收到客户端心跳包,那么视为掉线
        public int count{get;set;}
    }

当用户意外退出,会有一个action事件抛出,我们在SendLogin方法中进行了接收,当这个事件抛出,就会立马触发用户的Logoff事件,通知掉线:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void SendLogin(string id,string name)
       {
           var userInfo = new UserChat() { ID = id, Name = name };
           userInfo.action += () =>
           {
               //用户20s无心跳包应答,则视为掉线,会抛出事件,这里会接住,然后处理用户掉线动作。
               SendLogoff(id, name);
           };
           var comparison = new ChatUserCompare();
           if (!userList.Contains<UserChat>(userInfo, comparison))
               userList.Add(userInfo);
           Clients.All.loginUser(userList);
           SendChat(id, name, "<====用户 " + name + " 加入了讨论组====>");
       }

这就是处理中心的所有内容了。

需要注意的是,在ChatHub类中,SendChat方法,TriggerHeartbeat方法,SendLogin方法,SendLogoff方法都是Singnalr处理对象所拥有的方法,而addNewMessageToPage方法,loginUser方法,logoffUser方法则是其回调方法。也就是说,当你在前台通过SendChat方法向处理中心发送数据的时候,你可以注册addNewMessageToPage方法来接收处理中心返回给你的数据。

image

前端逻辑及布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="tb" class="easyui-panel panel" title="专家在线咨询系统" >
    <div id="messageboard">
        <ul id="discussion"></ul>
    </div>
    <div id="userContainer">
        <ul id="userList"></ul>
    </div>
    <div id="messagecontainer" >
        <textarea id="message" class="rte-zone" rows="3"></textarea>
        <div>
            <input type="button" id="send" class="btn" value="发送" />
            <input type="button" id="close" class="btn" value="关闭" /><input type="hidden" id="displayname" />
        </div>
    </div>
</div>
@section scripts{
    <style>
    .panel{padding:5px;height:auto;min-height:650px;}
    .current{color:Green;}
    .rte-zone{width:815px;margin:0;padding:0;height:160px;border:1px #999 solid;clear:both}
    .rte-toolbar{width:800px;margin-top:10px;}
    .rte-toolbar div{float:left;width:100%;}
    .rte-toolbar a,.rte-toolbar a img{border:0}
    .rte-toolbar p{float:left;margin:0;padding-right:5px}
    #messageboard{border:1px solid #B6DF7D;float:left;width:800px;padding:10px;height:450px;overflow:auto;border-radius:10px; -moz-box-shadow:2px 2px 5px #333333; -webkit-box-shadow:2px 2px 5px #333333; box-shadow:2px 2px 5px #333333;}
    #userContainer{border:1px solid #B6DF7D;float:right;width:200px;height:565px;padding:5px;border-radius:10px; -moz-box-shadow:2px 2px 5px #333333; -webkit-box-shadow:2px 2px 5px #333333; box-shadow:2px 2px 5px #333333;}
    #messagecontainer{float:left;width:800px;}
    #messagecontainer div{float:right;}
    #message{border:1px solid #B6DF7D;width:815px; height:70px;margin-top:5px;border-radius:10px; -moz-box-shadow:2px 2px 5px #333333; -webkit-box-shadow:2px 2px 5px #333333; box-shadow:2px 2px 5px #333333;}
    #userList li{border-bottom:1px solid #B6DF7D;cursor:pointer;}
    #userList li:hover{background-color:#ccc;}
    .btn{width:75px;height:25px;}
    </style>
    <script src="../../Content/JQueryplugin/JQuery.rte.js" type="text/JavaScript"></script>
    <!--Reference the SignalR library. -->
    <script src="../../Scripts/JQuery.signalR-1.1.4.min.js" type="text/JavaScript"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="../../signalr/hubs"></script>
    <script>
        $(function () {
            $('.rte-zone').rte("css url", "http://batiste.dosimple.ch/blog/posts/2007-09-11-1/");
            //添加对自动生成的Hub的引用
            var chat = $.connection.chatHub;
            //调用Hub的callback回调方法
            //后端SendChat调用后,产生的addNewMessageToPage回调
            chat.client.addNewMessageToPage = function (id, name, message) {
                $('#discussion').append('<li style="color:blue;">' + htmlEncode(name) + '</li><li> ' + htmlEncode(message) + '</li>')
            };
            //后端SendLogin调用后,产生的loginUser回调
            chat.client.loginUser = function (userlist) {
                reloadUser(userlist);
            };
            //后端SendLogoff调用后,产生的logoffUser回调
            chat.client.logoffUser = function (userlist) {
                reloadUser(userlist);
            };
            $('#displayname').val(prompt('请输入昵称:', ''));
            //启动链接
            $.connection.hub.start().done(function () {
                var userid = guid();
                var username = $('#displayname').val();
                //发送上线信息
                chat.server.sendLogin(userid, username);
                //点击按钮,发送聊天内容
                $('#send').click(function () {
                    var chatContent = $('#message').contents().find('.frameBody').html();
                    chat.server.sendChat(userid, username, chatContent);
                });
                //点击按钮,发送用户下线信息
                $('#close').click(function () {
                    chat.server.sendLogoff(userid, username);
                    $("#send").css("display", "none");
                });
                //每隔5秒,发送心跳包信息
                setInterval(function () {
                    chat.server.triggerHeartbeat(userid, username);
                }, 5000);
            });
        });
        //重新加载用户列表
        var reloadUser = function (userlist) {
            $("#userList").children("li").remove();
            for (i = 0; i < userlist.length; i++) {
                $("#userList").append("<li><img src='../../Content/images/charge_100.png' />" + userlist[i].Name + "</li>");
            }
        }
        //div内容html化
        var htmlEncode = function (value) {
            var encodedValue = $('<div />').html(value).html();
            return encodedValue;
        }
        //guid序号生成
        var guid = (function () {
            function s4() {
                return Math.floor((1 + Math.random()) * 0x10000)
                           .toString(16)
                           .substring(1);
            }
            return function () {
                return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
                       s4() + '-' + s4() + s4() + s4();
            };
        })();
    </script>
}

在如上代码中:

第49行,加载一个富文本编辑器

第51行,添加对自动生成的proxy的引用

第56行~第68行,注册回调方法,以便于更新前台UI

第73行,打开处理中心hub

第79行,发送用户上线信息

第94行,每隔5秒钟发送一次心跳包

如此而已,非常简便。

运行截图

打开界面,首先提示输入用户昵称:

image

输入完毕之后,用户上线:

image

后续两个用户加入进来:

image

用户聊天内容记录:

image

用户“浅浅的”正常退出:

image

用户“书韵妍香”非正常退出:

image

 

点击下载

参考文章:Tutorial: Getting Started with SignalR 1.x

 

切不要忘记在Global.asax中添加映射:

RouteTable.Routes.MapHubs();

Apache MINA实战之 牛刀小试 – kongxx的专栏 – 博客频道 – CSDN.NET

mikel阅读(12)

 

来源: Apache MINA实战之 牛刀小试 – kongxx的专栏 – 博客频道 – CSDN.NET

本文链接:http://blog.csdn.NET/kongxx/article/details/7520259

Apache的MINA是一个被用来构建高性能和高可伸缩性应用程序的网络应用框架,它提供了一套统一的建立在Java NIO之上的事件驱动的异步API。

对于MINA框架的了解,MINA官方的几篇文章是必须要看的,如下:

* Application Architecture http://mina.apache.org/mina-based-application-architecture.html
* Server Architecture http://mina.apache.org/server-architecture.html
* Client Architecture http://mina.apache.org/client-architecture.html

其中几个主要的组件如下:

I/O Service – 用来处理I/O流,对于Server端就是IOAcceptor的实现类接受Client端的连接请求,对于Client端就是IoConnector的实现类来建立到Server端的连接。

I/O Filter Chain – 用来过滤或转化数据。对于Server端和Client端来说都是IoFilter接口的实现类,MINA自己内建了很多IoFilter接口的实现类。具体可以参考官方文档。

I/O Handler – 用来处理真正业务逻辑的类。对于Server端和Client端来说都是IoHandler接口的实现类,通常来说需要自己编写。

由于Server端和Client端都是基于以上三个组件的,因此对于Server端和Client端编程来说就都有类似的代码结构。

对于Server端来说:

1. 创建I/O service – 这里就是创建IOAcceptor类监听端口。

2. 创建I/O Filter Chain – 这里就是告诉使用那些IoFilter。
3. 创建I/O Handler – 自己的业务逻辑。

对于Client端来说:

1. 创建I/O service – 这里就是创建IOConnector类来建立到Server端的连接。

2. 创建I/O Filter Chain – 这里就是告诉使用那些IoFilter。

3. 创建I/O Handler – 自己的业务逻辑。

下面来通过一个例子看看MINA是怎样工作的。由于大多数应用都是基于TCP/IP的应用,所以这里也就不再说UDP/IP了。

这里我使用了Maven来创建了一个简单Java应用程序,具体步骤请参Maven的官方手册。这里只是将我用到的maven配置文件pom.xml列出,方便下面及后续文章使用。具体pom.xml文件内容如下:

  1. <project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
  2.     xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd”>
  3.     <modelVersion>4.0.0</modelVersion>
  4.     <groupId>com.google.code.garbagecan.minastudy</groupId>
  5.     <artifactId>minastudy</artifactId>
  6.     <packaging>jar</packaging>
  7.     <version>1.0-SNAPSHOT</version>
  8.     <name>minastudy</name>
  9.     <url>http://maven.apache.org</url>
  10.     <dependencies>
  11.         <dependency>
  12.             <groupId>org.apache.mina</groupId>
  13.             <artifactId>mina-core</artifactId>
  14.             <version>2.0.4</version>
  15.         </dependency>
  16.         <dependency>
  17.             <groupId>org.apache.mina</groupId>
  18.             <artifactId>mina-filter-compression</artifactId>
  19.             <version>2.0.4</version>
  20.         </dependency>
  21.         <dependency>
  22.             <groupId>org.slf4j</groupId>
  23.             <artifactId>slf4j-api</artifactId>
  24.             <version>1.3.0</version>
  25.         </dependency>
  26.         <dependency>
  27.             <groupId>org.slf4j</groupId>
  28.             <artifactId>slf4j-log4j12</artifactId>
  29.             <version>1.3.0</version>
  30.         </dependency>
  31.     </dependencies>
  32. </project>

首先来看Server端的代码

  1. package com.google.code.garbagecan.minastudy.sample1;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.nio.charset.Charset;
  5. import org.apache.mina.core.service.IoAcceptor;
  6. import org.apache.mina.core.service.IoHandlerAdapter;
  7. import org.apache.mina.core.session.IdleStatus;
  8. import org.apache.mina.core.session.IoSession;
  9. import org.apache.mina.filter.codec.ProtocolCodecFilter;
  10. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
  11. import org.apache.mina.filter.logging.LoggingFilter;
  12. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
  13. import org.slf4j.Logger;
  14. import org.slf4j.LoggerFactory;
  15. public class MyServer {
  16.     private static final Logger logger = LoggerFactory.getLogger(MyServer.class);
  17.     public static void main(String[] args) {
  18.         IoAcceptor acceptor = new NioSocketAcceptor();
  19.         acceptor.getFilterChain().addLast(“logger”new LoggingFilter());
  20.         acceptor.getFilterChain().addLast(“codec”new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName(“UTF-8”))));
  21.         acceptor.setHandler(new IoHandlerAdapter() {
  22.             @Override
  23.             public void sessionCreated(IoSession session) throws Exception {
  24.             }
  25.             @Override
  26.             public void sessionOpened(IoSession session) throws Exception {
  27.             }
  28.             @Override
  29.             public void sessionClosed(IoSession session) throws Exception {
  30.             }
  31.             @Override
  32.             public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
  33.             }
  34.             @Override
  35.             public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
  36.                 logger.error(cause.getMessage(), cause);
  37.                 session.close(true);
  38.             }
  39.             @Override
  40.             public void messageReceived(IoSession session, Object message) throws Exception {
  41.                 logger.info(“Received message “ + message);
  42.                 session.write(message);
  43.             }
  44.             @Override
  45.             public void messageSent(IoSession session, Object message) throws Exception {
  46.                 logger.info(“Sent message “ + message);
  47.             }
  48.         });
  49.         try {
  50.             acceptor.bind(new InetSocketAddress(10000));
  51.         } catch (IOException ex) {
  52.             logger.error(ex.getMessage(), ex);
  53.         }
  54.     }
  55. }

1. 首先创建I/O Service,这里使用的是NioSocketAcceptor类来创建了一个IoAcceptor实例。

2. 创建I/O Filter Chain,这里使用了两个IoFilter,一个是LoggingFilter用来记录日志和打印事件消息,另一个是ProtocolCodecFilter实例用来编码数据,这里其实就是将传递的数据编码成文本。

3. 创建I/O Handler,不要害怕,看起来代码多,其实就是一个实现了IoHandler接口的子类,自己需要实现其中的一些方法,这里方法比较多,但是我在这里只实现了messageSent,messageReceived和exceptionCaught方法。

4. 最后就是让IoAcceptor类实例绑定端口实现监听。

下面看看Client端的代码

  1. package com.google.code.garbagecan.minastudy.sample1;
  2. import java.net.InetSocketAddress;
  3. import java.nio.charset.Charset;
  4. import org.apache.mina.core.RuntimeIoException;
  5. import org.apache.mina.core.future.ConnectFuture;
  6. import org.apache.mina.core.service.IoConnector;
  7. import org.apache.mina.core.service.IoHandlerAdapter;
  8. import org.apache.mina.core.session.IdleStatus;
  9. import org.apache.mina.core.session.IoSession;
  10. import org.apache.mina.filter.codec.ProtocolCodecFilter;
  11. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
  12. import org.apache.mina.filter.logging.LoggingFilter;
  13. import org.apache.mina.transport.socket.nio.NioSocketConnector;
  14. import org.slf4j.Logger;
  15. import org.slf4j.LoggerFactory;
  16. public class MyClient {
  17.     private static final Logger logger = LoggerFactory.getLogger(MyClient.class);
  18.     public static void main(String[] args) {
  19.         IoConnector connector = new NioSocketConnector();
  20.         connector.setConnectTimeoutMillis(10 * 1000);
  21.         connector.getFilterChain().addLast(“logger”new LoggingFilter());
  22.         connector.getFilterChain().addLast(“codec”new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName(“UTF-8”))));
  23.         connector.setHandler(new IoHandlerAdapter() {
  24.             @Override
  25.             public void sessionCreated(IoSession session) throws Exception {
  26.             }
  27.             @Override
  28.             public void sessionOpened(IoSession session) throws Exception {
  29.                 for (int i = 0; i < 10; i++) {
  30.                     session.write(“Hello user_” + i);
  31.                 }
  32.                 session.write(“Bye”);
  33.             }
  34.             @Override
  35.             public void sessionClosed(IoSession session) throws Exception {
  36.             }
  37.             @Override
  38.             public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
  39.             }
  40.             @Override
  41.             public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
  42.                 logger.error(cause.getMessage(), cause);
  43.                 session.close(true);
  44.             }
  45.             @Override
  46.             public void messageReceived(IoSession session, Object message) throws Exception {
  47.                 logger.info(“Received message “ + message);
  48.                 if (message.toString().equalsIgnoreCase(“Bye”)) {
  49.                     session.close(true);
  50.                 }
  51.             }
  52.             @Override
  53.             public void messageSent(IoSession session, Object message) throws Exception {
  54.                 logger.info(“Sent message “ + message);
  55.             }
  56.         });
  57.         IoSession session = null;
  58.         try {
  59.             ConnectFuture future = connector.connect(new InetSocketAddress(“localhost”10000));
  60.             future.awaitUninterruptibly();
  61.             session = future.getSession();
  62.         } catch (RuntimeIoException e) {
  63.             logger.error(e.getMessage(), e);
  64.         }
  65.         session.getCloseFuture().awaitUninterruptibly();
  66.         connector.dispose();
  67.     }
  68. }

 

1. 首先创建I/O Service,这里使用的是NioSocketConnector类来创建了一个IoConnector实例,并设置连接超时为10秒。

2. 创建I/O Filter Chain,和服务器端同样设置了两个IoFilter,一个是LoggingFilter用来记录日志和打印事件消息,另一个是ProtocolCodecFilter实例用来编码数据,这里其实就是将传递的数据编码成文本。

3. 创建I/O Handler,也不要害怕,看起来代码多,其实也是一个实现了IoHandler接口的子类,并且自己实现了sessionOpened,messageSent,messageReceived和exceptionCaught方法。实现sessionOpened方法是为了在建立连接后向Server端发送消息。另外看一下messageReceived方法实现,在接收到服务器端的消息后关闭会话。从而可以使Client程序最终能够退出。

4. 最后就是IoConnector实例类连接远端的Server。

 

下面测试一下上面的程序,首先运行MyServer类,然后运行MyClient类,就可以分别在各自的终端上看到事件日志以及发送/接收的消息了

Android IM即时通信开发总结及代码 – 普洛提亚 – 博客园

mikel阅读(12)

来源: Android IM即时通信开发总结及代码 – 普洛提亚 – 博客园

IM即时通信的最大成功实例应该就是微信了吧,,,这次的IM即时通信是建立在Bmob上的,借用了人家的服务端和IM、Android SDK

传送:

Andorid快速入门

详细开发文档

IM快速入门

它的文档组织不是很好,我觉得基本上就是这三个链接比较好,所有的链接都来自这个页面

http://docs.bmob.cn/Android/developdoc/index.html?menukey=develop_doc&key=develop_Android

然后就是要自己看类库文档,不得不说,写得太简单了。。还好有demo和问答,问答其实回答的还是很快的。

源码下载

快速入门相关源码下载

案例教程和源码是快速入门的最简单方法,Bmob也为大家准备了相关的案例教程和源码,欢迎大家下载和查看。

为方便大家更好的理解Bmob SDK能够做的事情,我们还特意为大家提供了一些源码,大家可以下载之后,嵌入Bmob的AppKey,再打包运行。

阅读源码是一种良好的习惯!!

即时聊天案例源码:https://github.com/bmob/BmobIMSDK4Android

图文社区案例源码:https://git.oschina.net/v7/Wonderful 这个案例是猿圈媛圈开发团队提供的。

校园小菜案例源码:https://github.com/bmob/Shop 这个案例是湖工大的朋友提供的。

社交分享案例源码:https://github.com/bmob/bmob-android-social-share 这个是金刚锁开发者提供的

第三方登录案例源码:https://github.com/bmob/bmob-android-demo-thirdpartylogin 包含第三方登录和登录后获取用户信息的源码

 

[附:

其实有很多强大的后端云,比如这个github上,基于环信的例子:

https://github.com/huangfangyi/FanXin2.0_IM

https://code.csdn.net/diyangxia/imsample/tree/master

http://blog.csdn.net/diyangxia/article/details/45393599

]

 

 

因为以前没有开发经验,所以这次开发还是很吃力的,看源码差点要了我的命。。

我看的当然是即时通讯的案例源码,这次开发主要注意这么几个事情

1.IM的sdk,事实上,很多在网上用的demo都比较老了,但是bmob的更新比较快,所以我建议还是多读读看,我看的是2016年7月份的最新IM sdk写的, 新的demo功能比较简单,主要是正常的一些消息的传送,这里主要注意两个点,一个就是消息接收机制,消息接收机制有两个,一个是全局消息接收机制,形如:

public class DemoMessageHandler extends BmobIMMessageHandler

这个在IM快速开发文档也是说明了的。还有一个就是局部消息接收机制:

implements MessageListHandler
@Override
public void onMessageReceive(List<MessageEvent> list) {
    Logger.i("聊天页面接收到消息:" + list.size());
    //当注册页面消息监听时候,有消息(包含离线消息)到来时会回调该方法
    for (int i=0;i<list.size();i++){
        addMessage2Chat(list.get(i));
    }
}
 

这个是什么呢,就是一个Activity如果implements了这个类,那么重写这个消息接收函数,当处于这个Activity且有消息通知时,就会执行这个onMessageReceive函数。

还有一个要注意的点就是消息自定义,因为bmob封装的消息类型比较少,如果要扩展的,就要了解消息的自定义机制,这些在IM的快速开发文档里有介绍,比如这个添加好友的自定义消息类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package cn.bmob.imdemo.bean;
import android.text.TextUtils;
import org.json.JSONObject;
import cn.bmob.imdemo.Config;
import cn.bmob.imdemo.db.NewFriend;
import cn.bmob.newim.bean.BmobIMExtraMessage;
import cn.bmob.newim.bean.BmobIMMessage;
import com.orhanobut.logger.Logger;
/**添加好友请求
 * @author :smile
 * @project:AddFriendMessage
 * @date :2016-01-30-17:28
 */
public class AddFriendMessage extends BmobIMExtraMessage{
    public AddFriendMessage(){}
    /**将BmobIMMessage转成NewFriend
     * @param msg 消息
     * @return
     */
    public static NewFriend convert(BmobIMMessage msg){
        NewFriend add =new NewFriend();
        String content = msg.getContent();
        add.setMsg(content);
        add.setTime(msg.getCreateTime());
        add.setStatus(Config.STATUS_VERIFY_NONE);
        try {
            String extra = msg.getExtra();
            if(!TextUtils.isEmpty(extra)){
                JSONObject json =new JSONObject(extra);
                String name = json.getString("name");
                add.setName(name);
                String avatar = json.getString("avatar");
                add.setAvatar(avatar);
                add.setUid(json.getString("uid"));
            }else{
                Logger.i("AddFriendMessage的extra为空");
            }
        catch (Exception e) {
            e.printStackTrace();
        }
        return add;
    }
    @Override
    public String getMsgType() {
        return "add";
    }
    @Override
    public boolean isTransient() {
        //设置为true,表明为暂态消息,那么这条消息并不会保存到本地db中,SDK只负责发送出去
        //设置为false,则会保存到指定会话的数据库中
        return true;
    }
}

 

2. Android开发文档

因为涉及到使用了bmob的服务端,所以我的所有表、所有内容是直接存储在了云上的,如何把消息送到云端、如何从云端读取数据,这个直接看前面的开发文档就好了。都有很详细的说明了,但是,希望库文档能继续完善。。

 

3. 代码结构

由于对Android开发不太熟悉,对各种组件、各种View(Listview、RecyclerView。。)、适配器的理解花费了很多时间,基本上来讲,一个Activity类有一个xml文件,至少一个,然后有一个适配器处理时事件;xml的话,要注意嵌套关系很复杂,就是一个xml吧,有很多个xml组成,完了还有很多布局关系。。T.T

 

4. 事件处理

这里说的事件处理主要是Activity和Activity之间的,startActivity、startActivityForResult、还有这个可以看下EventBus使用详解(一)——初步使用EventBus

把这个四个搞明白了,还有啥看不懂的demo。。

 

代码

代码下载:

part1:http://download.csdn.net/detail/u014576894/9584903

part2:http://download.csdn.net/detail/u014576894/9584898

代码说明:

这是小组作业,如果大家要用的话,请园内联系我好嘛,或者发邮件给我pengjing.parents@qq.com

这个代码是建立在Android Studio2015上的,基本的功能就是IM通信和朋友圈,朋友圈的小视频功能有点bug,主要是下载视频失败,没有修改好;服务端借助了Bmob的后端云,如果要用的话,可以在AndroidManifest.xml里面把application ID改成你自己申请的,具体的看上面的快速开发文档,如果上面的东西你不懂的话,那就不要用了,肯定问题百出的。。

微服务架构:动态配置中心搭建 – 风中程序猿 – 博客园

mikel阅读(13)

来源: 微服务架构:动态配置中心搭建 – 风中程序猿 – 博客园

在微服务架构中,服务之间有着错综复杂的依赖关系,每个服务都有自己的依赖配置,在运行期间很多配置会根据访问流量等因素进行调整,传统的配置信息处理方式是将配置信息写入xml、.properties等配置文件中,和应用一起打包,每次修改配置信息,都需要重新进行打包,效率极低,动态配置中心就是为了解决这一问题。动态配置中心也是一个微服务,我们把微服务中需要动态配置的配置文件存放在远程git私有仓库上,微服务会去服务器读取配置信息,当我们在本地修改完代码push到git服务器,git服务器端hooks自动检测是否有配置文件更新,如果有,git服务端通过消息队列给配置中心发消息,通知配置中心刷新配置文件。因此微服务读取到的就是最新的配置信息,实现了运行期动态配置。理解了配置中心的原理,下面来介绍应用Spring Cloud框架的configserver搭建动态配置中心的整个过程。

1、Git私有仓库搭建

       由于所有配置文件放在Git远程私有仓库上,我们需要搭建Git私有仓库。

1)安装git

# cd /etc/yum.repos.d/

# wget http://geekery.altervista.org/geekery-el5-x86_64.repo

# cd /data/soft/

# wget http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.x86_64.rpm

# rpm -ivh rpmforge-release-0.5.2-2.el5.rf.x86_64.rpm

# yum -y install git

# git –version

2)搭建git仓库

a、创建一个git用户,用来运行git服务

命令:$ sudo adduser git

b、创建证书登录

收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥加入到/home/git/.ssh/authorized_keys文件里,一行一个。

c、初始化git仓库

选定或创建一个目录作为Git仓库,如:/home/git/microservice.git,将此目录初始化为git仓库

命令:$ sudo git init –bare microservice.git

 

                 $ sudo chown -R git:git microservice.git

       d、克隆远程仓库

       命令:$ git clone git@192.168.0.136:microservice.git

       3)在本地安装git客户端

       以上操作完成Git服务端私有仓库搭建后,需要在本地安装Git客户端,并且把公钥加入到服务端的/.ssh/authorized_keys配置文件中,这样就可以在本地克隆服务端代码并向服务端进行推送了。本地Git客户端的安装可参照Git教程。

       2、安装消息队列

       在远程机器上安装消息队列(rabbitmq)并启动。

       

       3、配置中心相关配置文件

       配置中心(配置服务,例如:sample-config)的配置文件application.yml,添加如下配置:

       

       1)configserver的git配置

       configserver根据配置的Git服务器地址,去服务器上读取相应的配置文件信息。

       uri: 用户名、远程Git服务器地址、私有仓库地址

username: Git服务器用户名(搭建仓库时创建的用户)

password: 用户密码

2)消息队列配置

当Git服务端检测到配置文件有更新时,会通过消息队列通知configserver刷新配置文件。

rabbitmq配置:

host: 消息队列的地址

port: 消息队列端口

username: 用户名

password: 密码 

       3)Dockerfile配置

由于采用ssh协议访问Git服务器时,需要手动输入确认连接信息,因此在这里我们需要屏蔽此检查。我们需要新建一个配置文件config(无后缀名)跟Dockerfile一起放在容器中,并在Dockerfile中进行配置(新建/.ssh目录,将config文件添加到此目录下)。

部署在容器中的文件:

config文件配置:

Dockerfile配置:

4、在服务中添加消息总线依赖配置

       在需要将配置文件放到配置中心进行动态配置的服务中,添加消息总线的配置。

在服务的pom.xml文件中添加依赖:

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-bus-amqp</artifactId>

</dependency>

5、在Git服务端编写服务端hook

       当本地Git客户端向服务端仓库推送更新时,Git服务端需要检测配置文件是否有更新,如果有,则需要向configserver发请求通知它刷新配置文件。这就需要用到服务端hook。在Git服务端的/.git/hooks/目录下,创建post-receive文件(无后缀名),并添加相应的脚本,当Git客户端推送更新,服务端更新完文件后会自动执行此post-receive文件脚本。

6、在本地Git客户端提交修改并推送到Git服务端

在本地更新配置文件后,提交到本地Git仓库,然后将本地更新push到Git服务端。

相关Git命令:

$ git add xxx.xml xx.yml ——-将修改的配置文件xxx.xml、xx.yml添加到暂存区

$ git commit -m “xxxxxx”—–将暂存区文件提交到本地Git仓库

$ git push origin master ——将本地更新推送到服务端

以上就是利用configserver实现动态配置中心的整个过程,整个配置完成后就可以在本地修改配置文件push到Git服务器,我们的微服务就可以动态读取到最新的配置了。

 

Meteor-Android-DDP通信 – 简书

mikel阅读(12)

来源: Meteor-Android-DDP通信 – 简书

版本要求

配置

  • 将需要的包加入项目
    • 在根目录下的build.gradle里加如下两段代码
      • 申明Gradle仓库地址
        allprojects {
          repositories {
              maven { url "https://jitpack.io" }
          }
        }
      • 申明Gradle的依赖
        dependencies {
          compile 'com.github.delight-im:Android-DDP:v3.1.2'
        }
  • AndroidManifest.xml 中添加许可:
     <uses-permission android:name="android.permission.INTERNET" />

使用

  • 创建DDP client实例
    public class MyActivity extends Activity implements MeteorCallback {
    
        private Meteor mMeteor;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // 创建一个实例
            mMeteor = new Meteor(this, "ws://example.meteor.com/websocket");
    
            // 注册回调函数
            mMeteor.addCallback(this);
    
            // 建立连接
            mMeteor.connect();
        }
    
        public void onConnect(boolean signedInAutomatically) { }
    
        public void onDisconnect() { }
    
        public void onDataAdded(String collectionName, String documentID, String newValuesJson) {
            // 自行解析json数据 (不推荐)
            // 或者
            // 直接用数据库语句操作 (后面会有详细介绍) (推荐)
        }
    
        public void onDataChanged(String collectionName, String documentID, String updatedValuesJson, String removedValuesJson) {
            // 自行解析json数据 (不推荐)
            // 或者
            // 直接用数据库语句操作 (后面会有详细介绍) (推荐)
        }
    
        public void onDataRemoved(String collectionName, String documentID) {
            // 自行解析json数据 (不推荐)
            // 或者
            // 直接用数据库语句操作 (后面会有详细介绍) (推荐)
        }
    
        public void onException(Exception e) { }
    
        @Override
        public void onDestroy() {
            mMeteor.disconnect();
            mMeteor.removeCallback(this);
            // or
            // mMeteor.removeCallbacks();
    
            // ...
    
            super.onDestroy();
        }
    
    }
  • 单例模式
    • 在一开始就创建一个实例
      MeteorSingleton.createInstance(this, "ws://example.meteor.com/websocket")
      // 代替
      // new Meteor(this, "ws://example.meteor.com/websocket")
    • 实例可以直接用 (通过 Activity 实例)
      MeteorSingleton.getInstance()
      // 代替
      // mMeteor
    • 所有API方法都可以通过MeteorSingleton.getInstance() 调用。
  • 注册一个回调函数
    // MeteorCallback callback;
    mMeteor.addCallback(callback);
  • 取消一个回调函数
    mMeteor.removeCallbacks();
    // or
    // // MeteorCallback callback;
    // mMeteor.removeCallback(callback);
  • 向 collection 中添加数据
    Map<String, Object> values = new HashMap<String, Object>();
    values.put("_id", "my-id");
    values.put("some-key", "some-value");
    
    mMeteor.insert("my-collection", values);
    // or
    // mMeteor.insert("my-collection", values, new ResultListener() { });
  • 更新 collection 中的数据
    Map<String, Object> query = new HashMap<String, Object>();
    query.put("_id", "my-id");
    
    Map<String, Object> values = new HashMap<String, Object>();
    values.put("some-key", "some-value");
    
    mMeteor.update("my-collection", query, values);
    // or
    // mMeteor.update("my-collection", query, values, options);
    // or
    // mMeteor.update("my-collection", query, values, options, new ResultListener() { });
  • 删除 collection 中的数据
    mMeteor.remove("my-collection", "my-id");
    // or
    // mMeteor.remove("my-collection", "my-id", new ResultListener() { });
  • 从后端订阅数据
    String subscriptionId = mMeteor.subscribe("my-subscription");
    // or
    // String subscriptionId = mMeteor.subscribe("my-subscription", new Object[] { arg1, arg2 });
    // or
    // String subscriptionId = mMeteor.subscribe("my-subscription", new Object[] { arg1, arg2 }, new SubscribeListener() { });
  • 取消订阅
    mMeteor.unsubscribe(subscriptionId);
    // or
    // mMeteor.unsubscribe(subscriptionId, new UnsubscribeListener() { });
  • 调用后端自定义的方法
    mMeteor.call("myMethod");
    // or
    // mMeteor.call("myMethod", new Object[] { arg1, arg2 });
    // or
    // mMeteor.call("myMethod", new ResultListener() { });
    // or
    // mMeteor.call("myMethod", new Object[] { arg1, arg2 }, new ResultListener() { });
  • 与后端断开连接
    mMeteor.disconnect();
  • 创建新账户 (后端依赖包 accounts-password)
    mMeteor.registerAndLogin("john", "john.doe@example.com", "password", new ResultListener() { });
    // or
    // mMeteor.registerAndLogin("john", "john.doe@example.com", "password", profile, new ResultListener() { });
  • 通过 username 登录 (依赖后端包 accounts-password)
    mMeteor.loginWithUsername("john", "password", new ResultListener() { });
  • 通过邮箱登录 (依赖后端包 accounts-password)
    mMeteor.loginWithEmail("john.doe@example.com", "password", new ResultListener() { });
  • 检查是否已经登录 (依赖后端包 accounts-password)
    mMeteor.isLoggedIn();
  • 获取 user ID (前提是已登录) (依赖后端包 accounts-password)
    mMeteor.getUserId();
  • 登出 (依赖后端包 accounts-password)
    mMeteor.logout();
    // or
    // mMeteor.logout(new ResultListener() { });
  • 检查客户端是否连接
    mMeteor.isConnected();
  • 手动重连 (一般不需要)
    mMeteor.reconnect();

利用数据库语句处理数据

配置数据库

InMemoryDatabaseDatabase 实例的唯一子类,代码如下:

mMeteor = new Meteor(this, "ws://example.meteor.com/websocket", new InMemoryDatabase());

此时,所有数据都自动从后端接收,并解析完成存入数据库。不需要手动解析JSON!

接收数据时,注意一些状态, onDataAdded, onDataChanged or onDataRemoved

访问数据库

Database database = mMeteor.getDatabase();

这些和后续的方法,都支持链式调用。

通过集合名从数据库获取集合

// String collectionName = "myCollection";
Collection collection = mMeteor.getDatabase().getCollection(collectionName);

获取数据库中所有的集合名字

String[] collectionNames = mMeteor.getDatabase().getCollectionNames();

获取数据库中集合数量

int numCollections = mMeteor.getDatabase().count();

通过ID从集合中取数据

// String documentId = "wjQvNQ6sGjzLMDyiJ";
Document document = mMeteor.getDatabase().getCollection(collectionName).getDocument(documentId);

获取集合中所有数据的ID

String[] documentIds = mMeteor.getDatabase().getCollection(collectionName).getDocumentIds();

获取集合中数据数量

int numDocuments = mMeteor.getDatabase().getCollection(collectionName).count();

在集合中查找数据

以下所有方法都支持链式调用,并随意组合。

// String fieldName = "age";
// int fieldValue = 62;
Query query = mMeteor.getDatabase().getCollection(collectionName).whereEqual(fieldName, fieldValue);
// String fieldName = "active";
// int fieldValue = false;
Query query = mMeteor.getDatabase().getCollection(collectionName).whereNotEqual(fieldName, fieldValue);
// String fieldName = "accountBalance";
// float fieldValue = 100000.00f;
Query query = mMeteor.getDatabase().getCollection(collectionName).whereLessThan(fieldName, fieldValue);
// String fieldName = "numChildren";
// long fieldValue = 3L;
Query query = mMeteor.getDatabase().getCollection(collectionName).whereLessThanOrEqual(fieldName, fieldValue);
// String fieldName = "revenue";
// double fieldValue = 0.00;
Query query = mMeteor.getDatabase().getCollection(collectionName).whereGreaterThan(fieldName, fieldValue);
// String fieldName = "age";
// int fieldValue = 21;
Query query = mMeteor.getDatabase().getCollection(collectionName).whereGreaterThanOrEqual(fieldName, fieldValue);
// String fieldName = "address";
Query query = mMeteor.getDatabase().getCollection(collectionName).whereNull(fieldName);
// String fieldName = "modifiedAt";
Query query = mMeteor.getDatabase().getCollection(collectionName).whereNotNull(fieldName);
Document[] documents = mMeteor.getDatabase().getCollection(collectionName).find();
// int limit = 30;
Document[] documents = mMeteor.getDatabase().getCollection(collectionName).find(limit);
// int limit = 30;
// int offset = 5;
Document[] documents = mMeteor.getDatabase().getCollection(collectionName).find(limit, offset);
Document document = mMeteor.getDatabase().getCollection(collectionName).findOne();

可以组合到一起,如下:

Document document = mMeteor.getDatabase().getCollection("users").whereNotNull("lastLoginAt").whereGreaterThan("level", 3).findOne();

通过属性名查找属性

// String fieldName = "age";
Object field = mMeteor.getDatabase().getCollection(collectionName).getDocument(documentId).getField(fieldName);

获取所有属性名

String[] fieldNames = mMeteor.getDatabase().getCollection(collectionName).getDocument(documentId).getFieldNames();

获取属性数量

int numFields = mMeteor.getDatabase().getCollection(collectionName).getDocument(documentId).count();

Mongodb基础用法及查询操作[转载] – 周国选 – 博客园

mikel阅读(17)

来源: Mongodb基础用法及查询操作[转载] – 周国选 – 博客园

插入多条测试数据
> for(i=1;i<=1000;i++){
… db.blog.insert({“title”:i,”content”:”mongodb测试文章。”,”name”:”刘”+i});
… }

db.blog.list.find().limit(10).forEach(function(data){print(“title:”+data.title);})   循环forEach 用法

 

db.blog.findOne();  取一条数据

db.blog.find();取多条数据
db.blog.remove(); 删除数据集
db.blog.drop();删除表

删除一个数据库:
1.use dbname
2.db.dropDatabase()
======================================查询操作=============================
db.blog.find() 相当于select * from blog
db.blog.find({“age”:27}) 相当于select * from blog where age=’27’
db.blog.find({“age”:27,”name”:”xing”}) 相当于select * from blog where age=’27’ and name=”xing”
db.blog.find({},{“name”:1}) select name from blog ,如果name:0则是不显示name字段
db.blog.find().limit(1) 相当于select * from blog limit 1

db.blog.find().sort({_id:-1}).limit(1)相当于select * from blog order by _id desc limit 1
db.blog.find().skip(10).limit(20) 相当于select * from blog limit 10,20
skip用的时候,一定要注意要是数量多的话skip就会变的很慢,所有的数据库都存在此问题,可以不用skip进行分页,用最后一条记录做为条件
db.blog.find({“age”:{“$gte”:18,”$lte”:30}})     select * from blog  where age>=27 and age<=50
$gt   >
$gte  >=
$lt   <
$lte  <=
$ne   !=
$in : in
$nin: not in
$all: all
$not: 反匹配

查询 creation_date > ‘2010-01-01’ and creation_date <= ‘2010-12-31’ 的数据
db.users.find({creation_date:{$gt:new Date(2010,0,1), $lte:new Date(2010,11,31)});

db.blog.find().sort({_id:-1})  相当于select * from blog  order by _id desc  按_id倒序取数据  1为正序,多个条件用,号分开如{name:1,age:-1}
db.blog.find({“_id”:{“$in”,[12,3,100]}})  相当于select * from blog where _id in (12,3,100)
db.blog.find({“_id”:{“$nin”,[12,3,100]}})  相当于select * from blog where _id not in (12,3,100)
db.blog.find({“$or”:[{“age”:16},{“name”:”xing”}]}) 相当于select * from blog where age = 16 or name = ‘xing’
db.blog.find({“id_num”:{“$mod”:[5,1]}}) 取的是id_num mod 5 = 1 的字段,如id_num=1,6,11,16
db.blog.find({“id_num”:{“$not”:{“$mod”:[5,1]}}}) 取的是id_num mod 5 != 1 的字段,如除了id_num=1,6,11,16等所有字段,多于正则一起用

$exists判断字段是否存在

db.blog.find({ a : { $exists : true }}); // 如果存在元素a,就返回
db.blog.find({ a : { $exists : false }}); // 如果不存在元素a,就返回
$type判断字段类型 
查询所有name字段是字符类型的
db.users.find({name: {$type: 2}});
查询所有age字段是整型的
db.users.find({age: {$type: 16}});
db.blog.find({“z”:null}) 返回没有z字段的所有记录
db.blog.find({“name”:/^joe/i}) 查找name=joe的所有记录,不区分大小写

db.blog.distinct(‘content’)  查指定的列,并去重
查询数组
db.blog.find({“fruit”:{“$all”:[“苹果”,”桃子”,”梨”]}})   fruit中必需有数组中的每一个才符合结果
db.blog.find({“fruit”:{“$size”:3}})  fruit数组长度为3的符合结果
db.blog.find({“$push”:{“fruit”:”桔子”}})相当于db.blog.find({“$push”:{“fruit”:”桔子”},”$inc”:{“size”:1}})
$slice 可以按偏移量返回记录,针对数组。如{“$slice”:10}返回前10条,{“$slice”:{[23,10]}}从24条取10条
如果对象有一个元素是数组,那么$elemMatch可以匹配内数组内的元素

db.people.find({“name.first”:”joe”,”name.last”:”schmoe”}) 子查询如:{“id”:34,”name”:{“first”:”joe”,”last”:”schmoe”}}

db.blog.find({“comments”:{“$elemMatch”:{“author”:”joe”,”score”:{“$gte”:5}}}}) 查joe发表的5分以上的评论,注意comments为二维数组
$where 在走投无路的时候可以用,但它的效率是很低的。

游标用法
cursor.hasNext()检查是否有后续结果存在,然后用cursor.next()将其获得。
>while(cursor.hasNext()){
var obj = cursor.next();
//do same
}

 

 

http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-ConditionalOperators%3A%3C%2C%3C%3D%2C%3E%2C%3E%3D 手册

 

> use blog
> db.blog.insert({“title”:”华夏之星的博客”,”content”:”mongodb测试文章。”});
> db.blog.find();
{ “_id” : ObjectId(“4e29fd262ed6910732fa61df”), “title” : “华夏之星的博客”, “content” : “mongodb测试文章。” }
> db.blog.update({title:”华夏之星的博客”},{“author”:”星星”,”content”:”测试更新”});
> db.blog.find();
{ “_id” : ObjectId(“4e29fd262ed6910732fa61df”), “author” : “星星”, “content” : “测试更新” }

 

 

db.blog.insert不带括号则显示源码
db.blog.insert();插入
db.blog.update();更新

> db.blog.update({title:”华夏之星的博客”},{“author”:”星星”,”content”:”测试更新”});

update默认情况下只能对符合条件的第一个文档执行操作,要使所有的匹配的文档都得到更新,可以设置第四个参数为 true

> db.blog.update({title:”华夏之星的博客”},{“author”:”星星”,”content”:”测试更新”},false,true);

> db.runCommand({getLastError:1}) 可以查看更新了几条信息,n就是条数

 

 备份blog数据库到/soft目录
/usr/local/webserver/mongodb/bin/mongodump -d blog -o /soft

还原数据库

/usr/local/webserver/mongodb/bin/mongorestore -d blog -c blog   /soft/blog/blog.bson

导出数据库(备份出来的数据是二进制的,已经经过压缩。)
/usr/local/webserver/mongodb/bin/mongodump -h 127.0.0.1 -port 27017 -d demo -o /tmp/demo
导入数据
/usr/local/webserver/mongodb/bin/mongorestore -h 127.0.0.1  -port 27017 –directoryperdb /tmp/demo

5) $all
$all和$in类似,但是他需要匹配条件内所有的值:
如有一个对象:

{ a: [ 1, 2, 3 ] }

下面这个条件是可以匹配的:

db.things.find( { a: { $all: [ 2, 3 ] } } );

但是下面这个条件就不行了:

db.things.find( { a: { $all: [ 2, 3, 4 ] } } );

6) $size
$size是匹配数组内的元素数量的,如有一个对象:{a:[“foo”]},他只有一个元素:
下面的语句就可以匹配:

db.things.find( { a : { $size: 1 } } );

官网上说不能用来匹配一个范围内的元素,如果想找$size<5之类的,他们建议创建一个字段来保存元素的数量。

 

8) $type

$type 基于 bson type来匹配一个元素的类型,像是按照类型ID来匹配,不过我没找到bson类型和id对照表。

db.things.find( { a : { $type : 2 } } ); // matches if a is a string
db.things.find( { a : { $type : 16 } } ); // matches if a is an int

9)正则表达式
mongo支持正则表达式,如:

db.customers.find( { name : /acme.*corp/i } ); // 后面的i的意思是区分大小写

10) 查询数据内的值
下面的查询是查询colors内red的记录,如果colors元素是一个数据,数据库将遍历这个数组的元素来查询。

db.things.find( { colors : “red” } );

11) $elemMatch
如果对象有一个元素是数组,那么$elemMatch可以匹配内数组内的元素:

> t.find( { x : { $elemMatch : { a : 1, b : { $gt : 1 } } } } )
{ “_id” : ObjectId(“4b5783300334000000000aa9”),
“x” : [ { “a” : 1, “b” : 3 }, 7, { “b” : 99 }, { “a” : 11 } ]
}

$elemMatch : { a : 1, b : { $gt : 1 } } 所有的条件都要匹配上才行。

注意,上面的语句和下面是不一样的。

> t.find( { “x.a” : 1, “x.b” : { $gt : 1 } } )

$elemMatch是匹配{ “a” : 1, “b” : 3 },而后面一句是匹配{ “b” : 99 }, { “a” : 11 }
12) 查询嵌入对象的值

db.postings.find( { “author.name” : “joe” } );

注意用法是author.name,用一个点就行了。更详细的可以看这个链接: dot notation

举个例子:

> db.blog.save({ title : “My First Post”, author: {name : “Jane”, id : 1}})

如果我们要查询 authors name 是Jane的, 我们可以这样:

> db.blog.findOne({“author.name” : “Jane”})

 

 

 

 
mongodb目前没有或(or)操作符,只能用变通的办法代替,可以参考下面的链接:

http://www.mongodb.org/display/DOCS/OR+operations+in+query+expressions

http://www.bumao.com/index.php/mongo_and_php

 

http://www.php.net/manual/en/mongocursor.count.php