Web网页性能管理详解

Standard

原文:http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html

你遇到过性能很差的网页吗?

这种网页响应非常缓慢,占用大量的 CPU 和内存,浏览起来常常有卡顿,页面的动画效果也不流畅。

你会有什么反应?我猜想,大多数用户会关闭这个页面,改为访问其他网站。作为一个开发者,肯定不愿意看到这种情况,怎样才能提高性能呢?

本文将详细介绍性能问题的出现原因,以及解决方法。

一、网页生成的过程

要理解网页性能为什么不好,就要了解网页是怎么生成的。

网页的生成过程,大致可以分成五步。

  • HTML 代码转化成 DOM
  • CSS 代码转化成 CSSOM(CSS Object Model)
  • 结合 DOM 和 CSSOM,生成一棵渲染树(包含每个节点的视觉信息)
  • 生成布局(layout),即将所有渲染树的所有节点进行平面合成
  • 将布局绘制(paint)在屏幕上

这五步里面,第一步到第三步都非常快,耗时的是第四步和第五步。

“生成布局”(flow)和”绘制”(paint)这两步,合称为”渲染”(render)。

二、重排和重绘

网页生成的时候,至少会渲染一次。用户访问的过程中,还会不断重新渲染。

以下三种情况,会导致网页重新渲染。

  • 修改 DOM
  • 修改样式表
  • 用户事件(比如鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等等)

重新渲染,就需要重新生成布局和重新绘制。前者叫做”重排”(reflow),后者叫做”重绘”(repaint)。

需要注意的是,”重绘”不一定需要”重排”,比如改变某个网页元素的颜色,就只会触发”重绘”,不会触发”重排”,因为布局没有改变。但是,”重排”必然导致”重绘”,比如改变一个网页元素的位置,就会同时触发”重排”和”重绘”,因为布局改变了。

三、对于性能的影响

重排和重绘会不断触发,这是不可避免的。但是,它们非常耗费资源,是导致网页性能低下的根本原因。

提高网页性能,就是要降低”重排”和”重绘”的频率和成本,尽量少触发重新渲染。

前面提到,DOM 变动和样式变动,都会触发重新渲染。但是,浏览器已经很智能了,会尽量把所有的变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。

div.style.color = 'blue';
div.style.marginTop = '30px';

上面代码中,div 元素有两个样式变动,但是浏览器只会触发一次重排和重绘。

如果写得不好,就会触发两次重排和重绘。

div.style.color = 'blue';
var margin = parseInt (div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';

上面代码对 div 元素设置背景色以后,第二行要求浏览器给出该元素的位置,所以浏览器不得不立即重排。

一般来说,样式的写操作之后,如果有下面这些属性的读操作,都会引发浏览器立即重新渲染。

  • offsetTop/offsetLeft/offsetWidth/offsetHeight
  • scrollTop/scrollLeft/scrollWidth/scrollHeight
  • clientTop/clientLeft/clientWidth/clientHeight
  • getComputedStyle ()

所以,从性能角度考虑,尽量不要把读操作和写操作,放在一个语句里面。

// bad
div.style.left = div.offsetLeft + 10 + "px";
div.style.top = div.offsetTop + 10 + "px";

// good
var left = div.offsetLeft;
var top  = div.offsetTop;
div.style.left = left + 10 + "px";
div.style.top = top + 10 + "px";

一般的规则是:

  1. 样式表越简单,重排和重绘就越快。
  2. 重排和重绘的 DOM 元素层级越高,成本就越高。
  3. table 元素的重排和重绘成本,要高于 div 元素

四、提高性能的九个技巧

有一些技巧,可以降低浏览器重新渲染的频率和成本。

第一条是上一节说到的,DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。

第二条,如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排。

第三条,不要一条条地改变样式,而要通过改变 class,或者 csstext 属性,一次性地改变样式。

// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";

// good 
el.className += " theclassname";

// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

第四条,尽量使用离线 DOM,而不是真实的网面 DOM,来改变元素样式。比如,操作 Document Fragment 对象,完成后再把这个对象加入 DOM。再比如,使用 cloneNode () 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。

第五条,先将元素设为 display: none (需要 1 次重排和重绘),然后对这个节点进行 100 次操作,最后再恢复显示(需要 1 次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达 100 次的重新渲染。

第六条,position 属性为 absolute 或 fixed 的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。

第七条,只在必要的时候,才将元素的 display 属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden 的元素只对重排有影响,不影响重绘。

第八条,使用虚拟 DOM 的脚本库,比如 React 等。

第九条,使用 window.requestAnimationFrame ()、window.requestIdleCallback () 这两个方法调节重新渲染(详见后文)。

五、刷新率

很多时候,密集的重新渲染是无法避免的,比如 scroll 事件的回调函数和网页动画。

网页动画的每一帧(frame)都是一次重新渲染。每秒低于 24 帧的动画,人眼就能感受到停顿。一般的网页动画,需要达到每秒 30 帧到 60 帧的频率,才能比较流畅。如果能达到每秒 70 帧甚至 80 帧,就会极其流畅。

大多数显示器的刷新频率是 60Hz,为了与系统一致,以及节省电力,浏览器会自动按照这个频率,刷新动画(如果可以做到的话)。

所以,如果网页动画能够做到每秒 60 帧,就会跟显示器同步刷新,达到最佳的视觉效果。这意味着,一秒之内进行 60 次重新渲染,每次重新渲染的时间不能超过 16.66 毫秒。

一秒之间能够完成多少次重新渲染,这个指标就被称为”刷新率”,英文为 FPS(frame per second)。60 次重新渲染,就是 60FPS。

六、开发者工具的 Timeline 面板

Chrome 浏览器开发者工具的 Timeline 面板,是查看”刷新率”的最佳工具。这一节介绍如何使用这个工具。

首先,按下 F12 打开”开发者工具”,切换到 Timeline 面板。

左上角有一个灰色的圆点,这是录制按钮,按下它会变成红色。然后,在网页上进行一些操作,再按一次按钮完成录制。

Timeline 面板提供两种查看方式:横条的是”事件模式”(Event Mode),显示重新渲染的各种事件所耗费的时间;竖条的是”帧模式”(Frame Mode),显示每一帧的时间耗费在哪里。

先看”事件模式”,你可以从中判断,性能问题发生在哪个环节,是 JavaScript 的执行,还是渲染?

不同的颜色表示不同的事件。

  • 蓝色:网络通信和 HTML 解析
  • 黄色:JavaScript 执行
  • 紫色:样式计算和布局,即重排
  • 绿色:重绘

哪种色块比较多,就说明性能耗费在那里。色块越长,问题越大。

帧模式(Frames mode)用来查看单个帧的耗时情况。每帧的色柱高度越低越好,表示耗时少。

你可以看到,帧模式有两条水平的参考线。

下面的一条是 60FPS,低于这条线,可以达到每秒 60 帧;上面的一条是 30FPS,低于这条线,可以达到每秒 30 次渲染。如果色柱都超过 30FPS,这个网页就有性能问题了。

此外,还可以查看某个区间的耗时情况。

或者点击每一帧,查看该帧的时间构成。

七、window.requestAnimationFrame ()

有一些 JavaScript 方法可以调节重新渲染,大幅提高网页性能。

其中最重要的,就是 window.requestAnimationFrame () 方法。它可以将某些代码放到下一次重新渲染时执行。

function doubleHeight (element) {
  var currentHeight = element.clientHeight;
  element.style.height = (currentHeight * 2) + 'px';
}
elements.forEach (doubleHeight);

上面的代码使用循环操作,将每个元素的高度都增加一倍。可是,每次循环都是,读操作后面跟着一个写操作。这会在短时间内触发大量的重新渲染,显然对于网页性能很不利。

我们可以使用window.requestAnimationFrame (),让读操作和写操作分离,把所有的写操作放到下一次重新渲染。

function doubleHeight (element) {
  var currentHeight = element.clientHeight;
  window.requestAnimationFrame (function () {
    element.style.height = (currentHeight * 2) + 'px';
  });
}
elements.forEach (doubleHeight);

页面滚动事件(scroll)的监听函数,就很适合用 window.requestAnimationFrame () ,推迟到下一次重新渲染。

$(window) .on ('scroll', function() {
   window.requestAnimationFrame (scrollHandler);
});

当然,最适用的场合还是网页动画。下面是一个旋转动画的例子,元素每一帧旋转 1 度。

var rAF = window.requestAnimationFrame;

var degrees = 0;
function update () {
  div.style.transform = "rotate (" + degrees + "deg)";
  console.log ('updated to degrees ' + degrees);
  degrees = degrees + 1;
  rAF (update);
}
rAF (update);

八、window.requestIdleCallback ()

还有一个函数 window.requestIdleCallback (),也可以用来调节重新渲染。

它指定只有当一帧的末尾有空闲时间,才会执行回调函数。

requestIdleCallback (fn);

上面代码中,只有当前帧的运行时间小于 16.66ms 时,函数 fn 才会执行。否则,就推迟到下一帧,如果下一帧也没有空闲时间,就推迟到下下一帧,以此类推。

它还可以接受第二个参数,表示指定的毫秒数。如果在指定的这段时间之内,每一帧都没有空闲时间,那么函数 fn 将会强制执行。

requestIdleCallback (fn, 5000);

上面的代码表示,函数 fn 最迟会在 5000 毫秒之后执行。

函数 fn 可以接受一个 deadline 对象作为参数。

requestIdleCallback (function someHeavyComputation (deadline) {
  while(deadline.timeRemaining () > 0) {
    doWorkIfNeeded ();
  }

  if(thereIsMoreWorkToDo) {
    requestIdleCallback (someHeavyComputation);
  }
});

上面代码中,回调函数 someHeavyComputation 的参数是一个 deadline 对象。

deadline 对象有一个方法和一个属性:timeRemaining () 和 didTimeout。

(1)timeRemaining () 方法

timeRemaining () 方法返回当前帧还剩余的毫秒。这个方法只能读,不能写,而且会动态更新。因此可以不断检查这个属性,如果还有剩余时间的话,就不断执行某些任务。一旦这个属性等于0,就把任务分配到下一轮requestIdleCallback

前面的示例代码之中,只要当前帧还有空闲时间,就不断调用 doWorkIfNeeded 方法。一旦没有空闲时间,但是任务还没有全执行,就分配到下一轮requestIdleCallback

(2)didTimeout 属性

deadline 对象的 didTimeout 属性会返回一个布尔值,表示指定的时间是否过期。这意味着,如果回调函数由于指定时间过期而触发,那么你会得到两个结果。

  • timeRemaining 方法返回0
  • didTimeout 属性等于 true

因此,如果回调函数执行了,无非是两种原因:当前帧有空闲时间,或者指定时间到了。

function myNonEssentialWork (deadline) {
  while ((deadline.timeRemaining () > 0 || deadline.didTimeout) && tasks.length > 0)
    doWorkIfNeeded ();

  if (tasks.length > 0)
    requestIdleCallback (myNonEssentialWork);
}

requestIdleCallback (myNonEssentialWork, 5000);

上面代码确保了,doWorkIfNeeded 函数一定会在将来某个比较空闲的时间(或者在指定时间过期后)得到反复执行。

requestIdleCallback 是一个很新的函数,刚刚引入标准,目前只有 Chrome 支持。

九、参考链接

使用脚本便捷地在 Ubuntu 中安装最新 Linux 内核

Standard

原文:http://ubuntuhandbook.org/index.php/2015/08/install-latest-kernel-script/作者: Ji m
译文:LCTT  https://linux.cn/article-6219-1.html译者: mr-ping

--------

想要安装最新的Linux内核吗?一个简单的脚本就可以在Ubuntu系统中方便的完成这项工作。

Michael Murphy 写了一个脚本用来将最新的候选版、标准版、或者低延时版的内核安装到 Ubuntu 系统中。这个脚本会在询问一些问题后从 Ubuntu 内核主线页面 下载安装最新的 Linux 内核包。

通过脚本来安装、升级Linux内核:

1、 点击这个 github 页面 右上角的 “Download Zip” 来下载该脚本(注:此脚本在墙外,我已经搬运回来了,请参见下面。)。

2、鼠标右键单击用户下载目录下的 Zip 文件,选择 “在此展开” 将其解压。

3、右键点击解压后的文件夹,选择 “在终端中打开” 到此文件夹下。

此时将会打开一个终端,并且自动导航到目标文件夹下。如果你找不到 “在终端中打开” 选项的话,在 Ubuntu 软件中心搜索安装 nautilus-open-terminal ,然后重新登录系统即可(也可以再终端中运行 nautilus -q 来取代重新登录系统的操作)。

备注:此脚本如下,你可以将它保存为一个可执行的 shell 脚本:

  1. #!/bin/bash
  2. cd /tmp
  3. if ! which lynx > /dev/null; then sudo apt-get install lynx -y; fi
  4. if [ "$(getconf LONG_BIT)" == "64" ]; then arch=amd64; else arch=i386; fi
  5. function download() {
  6. wget $(lynx -dump -listonly -dont-wrap-pre $kernelURL | grep "$1" | grep "$2" | grep "$arch" | cut -d ' ' -f 4)
  7. }
  8. # Kernel URL
  9. read -p "Do you want the latest RC?" rc
  10. case "$rc" in
  11. y* | Y*) kernelURL=$(lynx -dump -nonumbers http://kernel.ubuntu.com/~kernel-ppa/mainline/ | tail -1) ;;
  12. n* | N*) kernelURL=$(lynx -dump -nonumbers http://kernel.ubuntu.com/~kernel-ppa/mainline/ | grep -v rc | tail -1) ;;
  13. *) exit ;;
  14. esac
  15. read -p "Do you want the lowlatency kernel?" lowlatency
  16. case "$lowlatency" in
  17. y* | Y*) lowlatency=1 ;;
  18. n* | n*) lowlatency=0 ;;
  19. *) exit ;;
  20. esac
  21. # Download Kernel
  22. if [ "$lowlatency" == "0" ]; then
  23. echo "Downloading the latest generic kernel."
  24. download generic header
  25. download generic image
  26. elif [ "$lowlatency" == "1" ]; then
  27. echo "Downloading the latest lowlatency kernel."
  28. download lowlatency header
  29. download lowlatency image
  30. fi
  31. # Shared Kernel Header
  32. wget $(lynx -dump -listonly -dont-wrap-pre $kernelURL | grep all | cut -d ' ' -f 4)
  33. # Install Kernel
  34. echo "Installing Linux Kernel"
  35. sudo dpkg -i linux*.deb
  36. echo "Done. You may now reboot."

4. 当进入终端后,运行以下命令来赋予脚本执行本次操作的权限。

  1. chmod +x *

最后,每当你想要安装或升级 Ubuntu 的 linux 内核时都可以运行此脚本。

  1. ./*

这里之所以使用 * 替代脚本名称是因为文件夹中只有它一个文件。

如果脚本运行成功,重启电脑即可。

恢复并且卸载新版内核

如果因为某些原因要恢复并且移除新版内核的话,请重启电脑,在 Grub 启动器的 高级选项 菜单下选择旧版内核来启动系统。

当系统启动后,参照下边章节继续执行。

如何移除旧的(或新的)内核:

  1. 从 Ubuntu 软件中心安装 Synaptic Package Manager。
  2. 打开 Synaptic Package Manager 然后如下操作:
  • 点击 Reload 按钮,让想要被删除的新内核显示出来.
  • 在左侧面板中选择 Status -> Installed ,让查找列表更清晰一些。
  • 在 Quick filter 输入框中输入 linux-image- 用于查询。
  • 选择一个内核镜像 “linux-image-x.xx.xx-generic” 然后将其标记为removal(或者Complete Removal)
  • 最后,应用变更

重复以上操作直到移除所有你不需要的内核。注意,不要随意移除此刻正在运行的内核,你可以通过 uname -r 命令来查看运行的内核。

对于 Ubuntu 服务器来说,你可以一步步运行下面的命令:

  1. uname -r
  2. dpkg -l | grep linux-image-
  3. sudo apt-get autoremove KERNEL_IMAGE_NAME


via: http://ubuntuhandbook.org/index.php/2015/08/install-latest-kernel-script/

11 Python Libraries You Might Not Know

Standard
原文:http://blog.yhathq.com/posts/11-python-libraries-you-might-not-know.html

There are tons of Python packages out there. So many that no one man or woman could possibly catch them all. PyPialone has over 47,000 packages listed!

Recently, with so many data scientists making the switch to Python, I couldn’t help but think that while they’re getting some of the great benefits of pandasscikit-learn, and numpy, they’re missing out on some older yet equally helpful Python libraries.

In this post, I’m going to highlight some lesser-known libraries. Even you experienced Pythonistas should take a look, there might be one or two in there you’ve never seen!

1) delorean

Delorean is a really cool date/time library. Apart from having a sweet name, it’s one of the more natural feeling date/time munging libraries I’ve used in Python. It’s sort of like moment in javascript, except I laugh every time I import it. The docs are also good and in addition to being technically helpful, they also make countless Back to the Futurereferences.

from delorean import Delorean
EST = "US/Eastern"
d = Delorean(timezone=EST)

2) prettytable

There’s a chance you haven’t heard of prettytable because it’s listed on GoogleCode, which is basically the coding equivalent of Siberia.

Despite being exiled to a cold, snowy and desolate place, prettytable is great for constructing output that looks good in the terminal or in the browser. So if you’re working on a new plug-in for the IPython Notebook, check out prettytable for your HTML __repr__.

from prettytable import PrettyTable
table = PrettyTable(["animal", "ferocity"])
table.add_row(["wolverine", 100])
table.add_row(["grizzly", 87])
table.add_row(["Rabbit of Caerbannog", 110])
table.add_row(["cat", -1])
table.add_row(["platypus", 23])
table.add_row(["dolphin", 63])
table.add_row(["albatross", 44])
table.sort_key("ferocity")
table.reversesort = True
+----------------------+----------+
|        animal        | ferocity |
+----------------------+----------+
| Rabbit of Caerbannog |   110    |
|      wolverine       |   100    |
|       grizzly        |    87    |
|       dolphin        |    63    |
|      albatross       |    44    |
|       platypus       |    23    |
|         cat          |    -1    |
+----------------------+----------+

3) snowballstemmer

Ok so the first time I installed snowballstemmer, it was because I thought the name was cool. But it’s actually a pretty slick little library. snowballstemmer will stem words in 15 different languages and also comes with a porter stemmer to boot.

from snowballstemmer import EnglishStemmer, SpanishStemmer
EnglishStemmer().stemWord("Gregory")
# Gregori
SpanishStemmer().stemWord("amarillo")
# amarill

4) wget

Remember every time you wrote that web crawler for some specific purpose? Turns out somebody built it…and it’s called wget. Recursively download a website? Grab every image from a page? Sidestep cookie traces? Done, done, and done.

Movie Mark Zuckerberg even says it himself

First up is Kirkland, they keep everything open and allow indexes on their apache configuration, so a little wget magic is enough to download the entire Kirkland facebook. Kid stuff!

The Python version comes with just about every feature you could ask for and is easy to use.

import wget
wget.download("http://www.cnn.com/")
# 100% [............................................................................] 280385 / 280385

Note that another option for linux and osx users would be to use do: from sh import wget. However the Python wget module does have a better argument handline.

5) PyMC

I’m not sure how PyMC gets left out of the mix so often. scikit-learn seems to be everyone’s darling (as it should, it’s fantastic), but in my opinion, not enough love is given to PyMC.

from pymc.examples import disaster_model
from pymc import MCMC
M = MCMC(disaster_model)
M.sample(iter=10000, burn=1000, thin=10)
[-----------------100%-----------------] 10000 of 10000 complete in 1.4 sec

If you don’t already know it, PyMC is a library for doing Bayesian analysis. It’s featured heavily in Cam Davidson-Pilon’s Bayesian Methods for Hackers and has made cameos on a lot of popular data science/python blogs, but has never received the cult following akin to scikit-learn.

6) sh

I can’t risk you leaving this page and not knowing about shsh lets you import shell commands into Python as functions. It’s super useful for doing things that are easy in bash but you can’t remember how to do in Python (i.e. recursively searching for files).

from sh import find
find("/tmp")
/tmp/foo
/tmp/foo/file1.json
/tmp/foo/file2.json
/tmp/foo/file3.json
/tmp/foo/bar/file3.json

7) fuzzywuzzy

Ranking in the top 10 of simplest libraries I’ve ever used (if you have 2-3 minutes, you can read through the source), fuzzywuzzy is a fuzzy string matching library built by the fine people at SeatGeek.

fuzzywuzzy implements things like string comparison ratios, token ratios, and plenty of other matching metrics. It’s great for creating feature vectors or matching up records in different databases.

from fuzzywuzzy import fuzz
fuzz.ratio("Hit me with your best shot", "Hit me with your pet shark")
# 85

8) progressbar

You know those scripts you have where you do a print "still going..." in that giant mess of a for loop you call your __main__? Yeah well instead of doing that, why don’t you step up your game and start using progressbar?

progressbar does pretty much exactly what you think it does…makes progress bars. And while this isn’t exactly a data science specific activity, it does put a nice touch on those extra long running scripts.

Alas, as another GoogleCode outcast, it’s not getting much love (the docs have 2 spaces for indents…2!!!). Do what’s right and give it a good ole pip install.

from progressbar import ProgressBar
import time
pbar = ProgressBar(maxval=10)
for i in range(1, 11):
    pbar.update(i)
    time.sleep(1)
pbar.finish()
# 60% |########################################################                                      |

9) colorama

So while you’re making your logs have nice progress bars, why not also make them colorful! It can actually be helpful for reminding yourself when things are going horribly wrong.

colorama is super easy to use. Just pop it into your scripts and add any text you want to print to a color:

10) uuid

I’m of the mind that there are really only a few tools one needs in programming: hashing, key/value stores, and universally unique ids. uuid is the built in Python UUID library. It implements versions 1, 3, 4, and 5 of the UUID standards and is really handy for doing things like…err…ensuring uniqueness.

That might sound silly, but how many times have you had records for a marketing campaign, or an e-mail drop and you want to make sure everyone gets their own promo code or id number?

And if you’re worried about running out of ids, then fear not! The number of UUIDs you can generate is comparable to the number of atoms in the universe.

import uuid
print uuid.uuid4()
# e7bafa3d-274e-4b0a-b9cc-d898957b4b61

Well if you were a uuid you probably would be.

11) bashplotlib

Shameless self-promotion here, bashplotlib is one of my creations. It lets you plot histograms and scatterplots using stdin. So while you might not find it replacing ggplot or matplotlib as your everyday plotting library, the novelty value is quite high. At the very least, use it as a way to spruce up your logs a bit.

$ pip install bashplotlib
$ scatter --file data/texas.txt --pch x

在SQL Server中使用存储过程发送电​​子邮件

Standard

 

简介

这是一个很有意思的讨论话题。现在我们习惯把邮件集成到每一个应用程序中。我们使用SMTP设置在.NET的Web.Config中整合电子邮件,使用Send方法来发送邮件。最近,我遇到了一个有趣的挑战,即如何从SQL Server发送电子邮件。假设我们不得不跟踪成功的有计划的SQL查询执行。我们不能为了检查它是否成功而每次去修改table。如果我们能得到某种形式的通知,来帮助我们知道执行的状态,那就好了。是的,利用预定义的几个存储过程从SQL Server发送邮件,这是可能的。

一起来学学吧。

开始

我们的目的是使用预定义的存储过程来发送邮件。首先,我们需要建立一个账户——这是服务器发送邮件所需的认证信息。一般邮件是通过SMTP(Simple Mail Transfer Protocol)发送的。这些设置将取决于服务器应用程序的需求。请记住配置必须是有效的。

创建一个数据库帐号:

EXEC msdb.dbo.sysmail_add_account_sp
    @account_name = 'SendEmailSqlDemoAccount'
  , @description = 'Sending SMTP mails to users'
  , @email_address = 'suraj.0241@gmail.com'
  , @display_name = 'Suraj Sahoo'
  , @replyto_address = 'suraj.0241@gmail.com'
  , @mailserver_name = 'smtp.gmail.com'
  , @port = 587
  , @username = 'XXXXXX'
  , @password = 'XXXXXX'
Go

请使用正确的认证信息和服务器设置,以便成功地发送邮件,否则邮件就会发送失败,被阻塞在发送队列中。

下一步是创建将用于设置数据库邮件的profile(配置文件)。请看下面:

EXEC msdb.dbo.sysmail_add_profile_sp
    @profile_name = 'SendEmailSqlDemoProfile'
  , @description = 'Mail Profile description'
Go

Profile用于设置邮件配置和邮件发送。

下一步骤是将帐户映射到profile。这是让profile知道,它需要用哪个帐户的认证信息来确保发送成功。

-- 添加帐户到配置文件
EXEC msdb.dbo.sysmail_add_profileaccount_sp
    @profile_name = 'SendEmailSqlDemo'
  , @account_name = 'SendEmailSql'
  , @sequence_number = 1
GO

这样,我们就能成功发送电子邮件了。邮件发送查找片段如下所示:

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'SendEmailSqlDemo2'
  , @recipients = 'suraj.0241@gmail.com'
  , @subject = 'Automated Test Results (Successful)'
  , @body = 'The stored procedure finished successfully.'
  , @importance ='HIGH' 
GO

有时候使用存储过程,并不能得到执行。因此,可以尝试catch块,以及Begin和End处理在一些存储过程中是强制性的。

举个例子,假设我们有一个使用存储过程的SELECT INSERT查询,那么会发生的事情是,我们需要从4个table中选择并插入,这4个table即Users | UserLogin | UserEmployment | Departments

对于每一个新屏幕的创建,我们要操纵和选择用户,根据外键,再次插入到具有不同外键的相同table中,代表特定的屏幕。查询如下:

BEGIN TRY
  BEGIN TRAN
 INSERT INTO
   dbo.[User]
 SELECT
    us.UserName,
	us.UserAddress,
	us.UserPhone,
    @fkScreenID
 FROM
   dbo.[User] as us
 WHERE
   UserID= @userID
 COMMIT TRAN
    END TRY
   BEGIN CATCH
  ROLLBACK TRAN
  END
  END CATCH  //其他table的代码与此类似。添加Try Catch到整个SP执行块(Executing Block)会更好

这里的事件要是失败的话,会转移到Catch块,在Catch块中我们可以让电子邮件一直发送程序以获取相关成功或失败的通知和原因,以及告知哪里失败。这对开发人员非常有帮助。

故障排除邮件

还有一些存储过程能让我们知道邮件是成功的,失败的还是尚在排队中。这真是一个超棒的功能。

要检查邮件是否已经成功发送和发布,我们可以运行以下查询:

select * from msdb.dbo.sysmail_sentitems

它返回的一些列

Email1

在第二个图片中你可以看到,sent_status属性为sent,这表明邮件已成功发送。

为检查可能无法发送的未发送邮件,我们运行以下查询:

select * from msdb.dbo.sysmail_unsentitems

为检查甚至不能重新从队列中发送的失败邮件,我们运行下面的查询: –

select * from msdb.dbo.sysmail_faileditems

有关故障及原因的详细信息,故障查找查询将如下所示:

SELECT items.subject,
    items.last_mod_date
    ,l.description FROM msdb.dbo.sysmail_faileditems as items
INNER JOIN msdb.dbo.sysmail_event_log AS l
    ON items.mailitem_id = l.mailitem_id
GO

结果类似于:

Email3

上面的错误描述为“No Such Host”错误。该错误通常发生在有一些SMTP服务器连接设置错了的时候。我们需要靠自己排除故障——重新检查设置认证信息,然后再试试。如果依然不能工作,那么就需要检查DNS服务器设置,再次重试配置。

结论

这一次我们讨论了如何使用存储过程从我们自己的SQL发送邮件的过程,并证明是可行的。故障排除错误和设置也都很简单。

异常和错误是开发中不可避免的一部分,但处理这些问题却是开发人员的使命挑战。

译文链接:http://www.codeceo.com/article/sql-server-send-mail.html
英文原文:Sending Email Using Stored Procedures in Sql Server
翻译作者:码农网 – 小峰

Create and Connect With Wifi Hotspot in Windows 8

Standard

Apart from Start Orb and classic Start Menu, Windows 7 users test driving the new OS miss the ability to create an ad-hoc wireless network. If you network then you probably have noticed the absence of the option to create such a network in Windows 8 to share files and internet connections. The ad hoc feature of windows 7 merely creates a separate network with similar ip addresses so that other devices may connect to a virtual network. Allowing users to connect to the internet via the routers DNS configuration. Although the new OS comes with Internet Connection Sharing (ICS) allowing other devices to use the internet through your host’s internet connection, there is no option for this to be a wireless connection. In the following tutorial I shall show you how to create such a network in a Windows 8 environment.

Wifi Hotspot in Windows 8

Using Windows Netsh Utility

The Network Shell (netsh) utility is essentially just a tool majorly used to configure network devices in different editions of windows. The support for configuring WLAN using netsh was first introduced in Windows Vista, and it’s now available in Windows Server 2008, Windows 7 and Windows 8. By the netsh wlan command, the hosted network option can be easily used to create an ad hoc wireless network. The Hosted Network is WLAN feature is designed to implement virtualization of physical network adapter. Because it is essential in creating a virtual wireless device, it is used in a wide array of network virtualization devices, such as Virtual WiFi Router, MyPublicWiFi, Marfi etc. So, provided your NIC supports wireless hostednetwork features, you may make your pc a wi-fi hotspot by way of an ad hoc wireless connection.

Both the OS’s support network configuration of wirelessly created networks made using netsh commands. To create a virtual wireless connection by way of the netsh utility, firstly run command prompt with admin privileges. Goto Start Menu> Type in search bar “cmd” and hit enter.

cmd launch Windows 8

 

First step is to check whether your device supports ad hoc connections or not. For this type in the command window:

netsh wlan show drivers

netsh wlan show drivers

 

In the case it says no, you shall have to update your devices driver. When you have support you can move on. To configure the wireless connection type in:

Space

netsh wlan set hostednetwork mode=allow ssid=<network name> key=<passkey>

cmd.exe

Once you allow the new network you can set the vitualization option to deem it an ad hoc connection. To do this type in the command window:

netsh wlan start hostednetwork

netsh wlan start hostednetwork

 

If it says “hosted network couldn’t started”, disable all current wireless devices and try again. Furthermore you may also need to refresh your Device Manager list to install this virtual devices driver.

Once the new host network is up and running, turn on ICS for this new network, to allow other devices to connect to the internet through your internet connection. To enable this ICS option simply go to Network and Internet –> Network Connections, and open Properties dialog of network device (which is connected to internet). Next, open Sharing tab, enable Allow other network users to connect through this computer’s Internet connection option, then choose your new hosted network from the list.

Wi-Fi Properties - Sharing

Once this is done check the new connections IP settings from the TCP/IPv4 Properties. If IP addresses are not assigned then run the following command in command prompt:

netsh wlan start hostednetwork

IP Addresses

 

Once this is all completed now you shall be able to access the internet through a wireless virtual network on your Windows 8 PC. As shown by the screenshot below.

Network and Sharing Center

 

To manually turn the Hosted Network off, enter the following:

 

netsh wlan stop hostednetwork

 

To see the Hosted Network details (see Figure 3), including the MAC addresses of connected users, enter the following:

 

netsh wlan show hostednetwork

 

To change the WPA2-PSK key, you can enter the following:

 

netsh wlan refresh hostednetwork YourNewNetworkPassword

Using keyboard shortcuts in Javascript

Standard

If you want to enhance your web app, Javascript keyboards shortcuts is definitely something to consider. In this article, you’ll learn to use JS keyboard shortcuts, with and without the JQuery framework.

Keyboard shorcuts on the web, let’s talk about it
The most important thing to think about when implementing Javascript keyboard shortcuts on a web app is to avoid redefining shortcuts provided by the client’s browser. Imagine that you’re using a web app, you want to close the tab by pressing Cmd+W, and the web app trigger an action, just because the developer redefined a keyboard shortcut used by your browser…How annoying. This is why I recommend to avoid using the Ctrl (Windows) or Cmd (Mac) key for your web app shortcuts. You should definitely use another key as such as F1, for example.

As you may already know, Javascript have lots of useful event listeners. For keyboard shortcuts, we’ll use onkeyup,
which allow you to perform an action when a key is pressed. Then, we’ll just have to compare the returned value with the keyboard code associated with the key used by one of our custom defined keyboard shortcuts.

Keyboard codes are simple code consisting of 2 or 3 numbers. Each keyboard key have its own keyboard code. For exemple, the Ctrl key code is 17.

For a full reference of keyboard keys, scroll down to the end of this post.

Examples
In the following example, we’re simply going to verify the key pressed down by the user. If the key pressed are Ctrl+S, a function will be triggered.

First example, without JQuery:

var isCtrl = false;
document.onkeyup=function(e) {
if(e.which == 17) isCtrl=false;
}document.onkeydown=function(e){
if(e.which == 17) isCtrl=true;
if(e.which == 83 && isCtrl == true) {
alert(‘Keyboard shortcuts are cool!’);
return false;
}
}Example with the JQuery framework:

var isCtrl = false;$(document).keyup(function (e) {
if(e.which == 17) isCtrl=false;
}).keydown(function (e) {
if(e.which == 17) isCtrl=true;
if(e.which == 83 && isCtrl == true) {
alert(‘Keyboard shortcuts + JQuery are even more cool!’);
return false;
}
});In theses examples, we started by verifying that the Ctrl is pressed by the user. If yes, the initial value of the isCtrl variable is set to true. If the keys are released, isCtrl will be set to false again.

Once done, we have to verify that the second pressed key is S. As the shortcut consists of a keyboard combination, we also have to check that is isCtrl variable have true as a value.

If both the isCtrl variable ius set to true and the second pressed key is S, then we can trigger a Javascript alert to display a message. Of course, in a “real” web app, you’ll use a more useful function

Keyboard codes reference
Key Keyboard code
Backspace 8
Tab 9
Enter 13
Shift 16
Ctrl 17
Alt 18
Pause 19
Capslock 20
Esc 27
Page up 33
Page down 34
End 35
Home 36
Left arrow 37
Up arrow 38
Right arrow 39
Down arrow 40
Insert 45
Delete 46
0 48
1 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
a 65
b 66
c 67
d 68
e 69
f 70
g 71
h 72
i 73
j 74
k 75
l 76
m 77
n 78
o 79
p 80
q 81
r 82
s 83
t 84
u 85
v 86
w 87
x 88
y 89
z 90
0 (numpad) 96
1 (numpad) 97
2 (numpad) 98
3 (numpad) 99
4 (numpad) 100
5 (numpad) 101
6 (numpad) 102
7 (numpad) 103
8 (numpad) 104
9 (numpad) 105
* 106
+ 107
– 109
. 110
/ 111
F1 112
F2 113
F3 114
F4 115
F5 116
F6 117
F7 118
F8 119
F9 120
F10 121
F11 122
F12 123
= 187
Coma 188
Slash / 191
Backslash 220

书法口诀

Standard
每秉笔必在圆正,气力纵横重轻,凝思静虑。当审字势,四面停均,八边俱备;长短合度,粗细折中;心眼准程,疏密被正。
最不可忙,忙则失势;次不可缓,缓则骨痴;又不可瘦,瘦当枯形,复不可肥,肥即质浊。细详缓临,自然备体,此是最要妙处。
贞观六年七月十二日,询书付善奴授诀。
八诀
  丶[点如高峰之坠石。   L[竖弯钩似长空之初月。   一[横若千里之阵云。   丨[竖如万岁之枯藤。   [斜钩劲松倒折,落挂石崖。   [横折钩如万钧之弩发。   丿[撇利剑截断犀象之角牙。   ㄟ[捺一被常三过笔。
澄神静虑,端己正容,秉笔思生,临池志逸。虚拳直腕,指齐掌空,意在笔前,文向思后。
分间布白,勿令偏侧。墨淡则伤神彩,绝浓必滞锋毫。肥则为钝,瘦则露骨,勿使伤于软弱,不须怒降为奇。
四面停匀,八边具备,短长合度,粗细折中。心眼准程,疏密欹正。筋骨精神,随其大小。
不可头轻尾重,无令左短右长,斜正如人,上称下载,东映西带,气宇融和,精神洒落,省此微言,孰为不可也。

逍遥赋

Standard

塵世間,紛紛緣,君求富貴吾尋仙。
有人笑,有人勸,皆說我道盡虛傳。
年少應立功名業,老來應享兒女閑。
世上人人事如此,從何了脫浮沉圈。
宏圖霸業爭名利,神耗氣散一夢間。
紅粉嬌顏絢麗曲,彈指一揮伴塵眠。
人生點滴連成線,縱橫交織出因緣。
因因果果連根奏,塵起塵飛如雲煙。
識破此身無正主,來去順水失命元。
如何解得平生願,弱水江畔望渡船。
昔有秦皇尋仙山,後有漢武燒爐煙。
世間都曉長生好,只奈珍寶識不全。
我性本是空虛鏡,萬相昭然方寸間。
而今反被色欲鎖,六根分別亂方圓。
日久根深習氣重,顛倒乾坤把緣攀。
喜慶世間好貨利,不知結成生死緣。
一朝失了本來我,苦海無邊回頭難。
出世先聖大慈悲,留傳真經指根源。
紅塵萬相磨心境,掃除紅花散欲貪。
窮思多學參天根,從此六識合自然。
明月鏡,還複來,靈苗發露明鏡間。
此是造化真種子,仙凡分判內外變。
養於內,積功久,化作元神居上田。
用於外,歲月悠,為功為業變塵緣。
元神現,道未全,一點神光照大千。
陰陽水火鍛天地,周天符火煉純乾。
無中恍惚見脈理,龍騰鳳舞護法船。
自爾平步赴雲天,始證匠手無虛言。
功成行滿禮三清,入世作業把恩還。
法身穩穩現虛空,混俗處處施方便。
末法紅塵唱道情,聲聲靈音醒德賢。
君家自是英雄漢,自有天福牽靈線。
若是持此盈私利,莫怪神鬼障道淵。
七返九還托辭作,八歸六居證天仙。
無去無來逍遙樂,無生無死無無年。

jQuery Tips: More basic Snippets

Standard

I read this article from Johann Burkard.

Remove a word with jQuery

The simple way – using regular expressions:

var el = $('#id');
el.html(el.html().replace(/word/ig, ""));

Test it!

jQuery timer callback functions

Want to call a method after a certain timeout?

window.setTimeout(function() {
 $('#id').empty();
}, 1000);

Remove this element one second after clicking it.

If you want to call a task periodically, use the Timer plugin for jQuery.

Verify that an element exists in jQuery

Simply test the .length property. Bonus information: This is used in inc.

if ($('#id').length) {
 // do stuff
}

Is there an element with an id of “top”?

jQuery not working in IE 5.0 or 5.5?

jQuery does not support older Internet Explorer versions. To make sure your users do not see JavaScript errors, edit your jquery.js file as follows:

// Put this before the original jQuery code
if (!(window.showModelessDialog && !document.implementation)) {
 (function() {

// Original jQuery code goes here

// Put this after the jQuery code
 })();
}

How to use a plugin with jQuery?

jQuery plugins are included on the page after the main jquery.js file:

<script type="text/javascript" src="jquery-1.1.4.js"></script>
<script type="text/javascript" src="jquery.roflcopter-1.0.js"></script>
<script type="text/javascript" src="jquery.lolcode-2.4.js"></script>

This is the beginner’s version. The advanced version is copying all your JavaScript files into a single file and then compressing it with YUI and /packer/.

Dynamically adding <div> elements with jQuery

…or any other element of course.

$('<div>hello</div>').appendTo(document.body);

Force a Page Reload

This will forcefully reload the current page. No jQuery needed here.

location.reload(true);

If your content might be cached by proxy servers and your URL has no query string, try this:

location.replace(
 location.href.replace(/?.*$/, '') + '?' + Math.random());

Many ad networks use this trick to ensure that ad tags are never cached.

Try it: Reload this page.

Reload an Image

There is no reload or replace method for images. The src property can be set in the same way as the location.href property though.

with($('#image')) {
 src = src.replace(/?.*$/, '') + '?' + Math.random();
}

Roßstein, Buchstein und Leonhardstein

Try it: Reload this awesome photo of my awesome tour to the Schildenstein. In Firebug, you will see the new image being loaded.

Replace a <div> with jQuery

The easiest way, pointed out by Paul, is to use the replaceWith method.

$('#thatdiv').replaceWith('<div>fnuh</div>');

A more confusing way is to insert the new element after the target element’s previouselement and then remove the target element. The with statement makes it shorter.

with ($('#thatdiv')) {
 with (prev()) {
  after('<p>Your new content goes here</p>');
 }
 remove();
}

Try it: Replace this element with something completely different.

Verify if an Element is empty

if ($('#keks').html()) {
 // Do something to remedy the situation
}

Test it: Is this element empty?

Append or Add HTML to an Element

One of the nicest things about jQuery that most methods are named what you’d expect them to be named.

$('#lal').append("<b>lol</b>");

Test it: Append your eBay username and password to this.

When to load jQuery on the page?

I assume most people include the jquery.js in the <head> portion of a page – at least I do. But unless you write CSS overrides on the page from JavaScript (document.write('<style>/<script>…')), putting it at the bottom improves performance.