优大网

4 / 15

Elasticsearch源码分析之一——使用Guice进行依赖注入与模块化系统

elasticsearch使用google开源的依赖注入框架guice,这个项目号称比spring快100倍,具体性能没有测试过,不过由于其代码比较简洁,比spring快很有可能,是不是快那么多就不知道了。先介绍下guice的基本使用方法。

elasticsearch是直接把guice的源码放到自己的包内(es把很多开源项目的代码都直接集成到自己项目中,省得依赖一堆的jar包,也使es的jar包达到差不多10M),在org.elasticsearch.common.inject目录下。
Guice主要是使用Module这个接口来确定各个接口和它们对应的实现。这个Module是个单例的抽象接口,通过bind(A).to(B)来绑定指定实例到这个模块中,下面看下Guice官方文档中的例子:
public class BillingModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(TransactionLog.class).to(DatabaseTransactionLog.class);
    bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
    bind(BillingService.class).to(RealBillingService.class);
  }
}
上面定义了一个订单模块,扩展AbstractModule这个抽象类。这个模块里面有三个实例:交易日志、支付过程和账单服务。通过bind(“interface”).to(“implement”)来使接口和实现绑定。
public class RealBillingService implements BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;
  @Inject
  public RealBillingService(CreditCardProcessor processor,
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }
  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);
      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}
上面类是BillService接口的实现类。其中要注意的就是@Inject这个注释。Guice的Injector类会扫描@Inject这类注释,找到方法中传入参数的实例进行注入。如上面的CreditCardLog和TransactionLog。
publicstaticvoidmain(String[] args) {
    Injector injector = Guice.createInjector(newBillingModule());
    BillingService billingService = injector.getInstance(BillingService.class);
    ...
  }
最后,在main方法中使用Injector进行注入与获取实例。这就是使用Guice进行依赖注入的一个简单例子。elasticsearch里面的组件基本都是用上面的方式进行模块化管理,elasticsearch对guice进行了简单的封装,通过ModulesBuilder类构建es的模块,一个es节点包括下面模块:
PluginsModule:插件模块
SettingsModule:设置参数模块
NodeModule:节点模块
NetworkModule:网络模块
NodeCacheModule:缓存模块
ScriptModule:脚本模块
JmxModule:jmx模块
EnvironmentModule:环境模块
NodeEnvironmentModule:节点环境模块
ClusterNameModule:集群名模块
ThreadPoolModule:线程池模块
DiscoveryModule:自动发现模块
ClusterModule:集群模块
RestModule:rest模块
TransportModule:tcp模块
HttpServerModule:http模块
RiversModule:river模块
IndicesModule:索引模块
SearchModule:搜索模块
ActionModule:行为模块
MonitorModule:监控模块
GatewayModule:持久化模块
NodeClientModule:客户端模块

接下来的文章会分析其中一些重要的模块。

摘自:http://www.searchtech.pro/articles/2013/02/15/1360942810308.html

分布式搜索Elasticsearch源码分析之二–索引过程源码概要分析

elasticsearch的索引逻辑简单分析,这里只是理清主要的脉络,一些细节方面以后的文章或会阐述。
假如通过java api来调用es的索引接口,先是构造成一个json串(es里表示为XContent,是对要处理的内容进行抽象),在IndexRequest里面指定要索引文档到那个索引库(index)、其类型(type)还有文档的id,如果没有指定文档的id,es会通过UUID工具自动生成一个uuid,代码在IndexRequest的process方法内。
if (allowIdGeneration) {
     if (id == null) {
         id(UUID.randomBase64UUID());
         opType(IndexRequest.OpType.CREATE);
     }
 }
然后使用封装过netty的TransportService通过tcp协议发送请求到es服务器(rest的话就是通过http协议)。
服务器获得TransportAction后解析索引请求(TransportShardReplicationOperationAction)。到AsyncShardOperationAction.start()方法开始进行分片操作,先读取集群状态,把目标索引及其分片信息提取出来,根据索引数据的id、类型以及索引分片信息进行哈希取模,确定把该条数据分配到那个分片。
private int shardId(ClusterState clusterState, String index, String type, @Nullable String id, @Nullable String routing) {
     if (routing == null) {
         if (!useType) {
             return Math.abs(hash(id) % indexMetaData(clusterState, index).numberOfShards());
         } else {
             return Math.abs(hash(type, id) % indexMetaData(clusterState, index).numberOfShards());
         }
     }
     return Math.abs(hash(routing) % indexMetaData(clusterState, index).numberOfShards());
 }
并找到数据要分配到的分片的主分片,先把索引请求提交到主分片处理(TransportIndexAction.shardOperationOnPrimary)。
判断是否必须要指定routing值
MappingMetaData mappingMd = clusterState.metaData().index(request.index()).mappingOrDefault(request.type());
  if (mappingMd != null && mappingMd.routing().required()) {
      if (request.routing() == null) {
          throw new RoutingMissingException(request.index(), request.type(), request.id());
      }
  }
判断索引操作的类型,索引操作有两种,一种是INDEX,当要索引的文档id已经存在时,不会覆盖原来的文档,只是更新原来文档。一种是CREATE,当索引文档id存在时,会抛出该文档已存在的错误。
if (request.opType() == IndexRequest.OpType.INDEX)
调用InternalIndexShard进行索引操作。
Engine.Index index = indexShard.prepareIndex(sourceToParse)
        .version(request.version())
        .versionType(request.versionType())
        .origin(Engine.Operation.Origin.PRIMARY);
indexShard.index(index);
通过(InternalIndexShard)查找与请求索引数据类型(type)相符的mapping。对要索引的json字符串进行解析,根据mapping转换为对应的解析结果ParsedDocument 。
public Engine.Index prepareIndex(SourceToParse source) throws ElasticSearchException {
    long startTime = System.nanoTime();
    DocumentMapper docMapper = mapperService.documentMapperWithAutoCreate(source.type());
    ParsedDocument doc = docMapper.parse(source);
    return new Engine.Index(docMapper, docMapper.uidMapper().term(doc.uid()), doc).startTime(startTime);
}
最后调用RobinEngine中的相关方法(添加或修改)对底层lucene进行操作,这里是写入到lucene的内存索引中(RobinEngine.innerIndex)。
if (currentVersion == -1) {
       // document does not exists, we can optimize for create
       if (index.docs().size() > 1) {
           writer.addDocuments(index.docs(), index.analyzer());
       } else {
           writer.addDocument(index.docs().get(0), index.analyzer());
       }
   } else {
       if (index.docs().size() > 1) {
           writer.updateDocuments(index.uid(), index.docs(), index.analyzer());
       } else {
           writer.updateDocument(index.uid(), index.docs().get(0), index.analyzer());
       }
   }
写入内存索引后还会写入到Translog(Translog是对索引的操作日志,会记录没有持久化的操作)中,防止flush前断电导致索引数据丢失。
Translog.Location translogLocation = translog.add(new Translog.Create(create));
主分片索引请求完就把请求发给副本进行索引操作。最后把成功信息返回给客户端。
摘自:http://www.searchtech.pro/articles/2013/02/15/1360941961206.html

关于AlertDialog.getWindow().setContentView(view)不能弹出输入法

可以阅读官方文档:http://developer.android.com/reference/android/app/Dialog.html

其中有一段:

Note: Activities provide a facility to manage the creation, saving and restoring of dialogs. See onCreateDialog(int)onPrepareDialog(int, Dialog)showDialog(int), anddismissDialog(int). If these methods are used, getOwnerActivity() will return the Activity that managed this dialog.

Often you will want to have a Dialog display on top of the current input method, because there is no reason for it to accept text. You can do this by setting theWindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM window flag (assuming your Dialog takes input focus, as it the default) with the following code:

 getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
         WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
这是默认情况下隐藏软键盘的方法,要重新显示软键盘,要执行下面这段代码:

alertDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);

AlertDialog.setView()则不会出现以上问题。

另外:为了防止弹出输入法时把后面的背景挤变形,可以在Manifest里添加:

android:windowSoftInputMode=”adjustPan|stateHidden”

 

Google的9行代码

The 9 lines of code of Google

 sonic0002    Date : 2014-08-15 20:29:52  


Are you still remembering the then hot debated news about Oracle suing Google allegedly copying a small portion of codes from Oracle’s Java in 2010. At that time, Oracle experts estimated that Google owes Oracle between $1.4 billion and $6 billion in damages if liable. But the court thought Oracle was eligible only for statutory damages for that copying, which were not expected to exceed a few hundred thousand dollars. At last, Oracle agreed the zero damage result.

Are you curious about which portion of codes Oracle claimed were stolen? Actually, it’s only a really small portion of it, around 9 lines of codes. These codes are for range check, it’s really a common implementation which a general programmer can write.

Joshua Bloch is the developer who wrote this piece of code. Joshua Bloch was a Sun developer(then acquired by Oracle) for the development of Java APIs. Later he joined Google in 2004 and worked on Android in 2008.  While working for Google, he still contributed to the OpenJDK One of the things he contributed was a much faster implementation for sorting arrays, based on the algorithm TimSort used in Python. Both the old and new algorithm had the rangeCheck method in common, so he just copied it from the old implementation, as “a temporary measure”. But later somehow this temporary measure ended up in Android.

Below are the 9 lines of codes copied:

1
2
3
4
5
6
7
8
9
private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {
     if (fromIndex > toIndex)
          throw new IllegalArgumentException("fromIndex(" + fromIndex +
               ") > toIndex(" + toIndex+")");
     if (fromIndex < 0)
          throw new ArrayIndexOutOfBoundsException(fromIndex);
     if (toIndex > arrayLen)
          throw new ArrayIndexOutOfBoundsException(toIndex);
}

Easy enough so that most programmers can write in just a few minutes or less. But the thing is not about the codes, it’s about the attitude and incentive. Nowadays many API developers are not allowed to read other companies’ similar APIs before implementing their own because the management fears that the developers may be interfered by the source codes they read and write the same without noticing it. They would be able to refer to the source code after they complete implementing their own API however.

Be careful when you are using open source codes especially when you are working for a company which gains its profit by selling commercial software.

Black Hat USA安全隐患盘点及黑客奥斯卡颁奖

摘要:Black Hat USA 2014已落下帷幕,这里带大家回顾会议中最值得关注的安全隐患,以及黑客奥斯卡奖Pwnie Awards的颁奖结果。

【编者按】Black Hat USA 2014已于8月2-7日在拉斯维加斯举行,会议汇聚了大量的全球知名安全专家。Black Hat安全技术大会是世界上最能够了解未来安全趋势的信息峰会,每届会议都可以称得上安全解决方案提供商前行的指引。本年度重点关注的安全领域又有什么不同?一年一度的黑客奥斯卡奖项又花落谁家,这里一起盘点。

以下为译文:

作为业内知名的信息安全会议,Black Hat 2014为各类解决方案提供商定义了一个明确的方向。 资深安全专家,Beyond Trust CTO Marc Maiffret指出,新兴技术一般是安全研究员关注的重点,会议上,大量利用新服务(或产品)漏洞窃取资源或进行其他危险攻击的途径被揭发,产业因此可以尽早的进行调整。本文着重盘点第十八届Black Hat上被揭露的安全漏洞,其中十个足以改变当下安全解决方案提供商的产品布局,并产生深远影响。

1. 数据库安全隐患——历时长久的攻防战

 

David Litchfield,一位英国的安全研究员,知名于揭露Oracle、微软等公司RDBMS软件中的巨大漏洞和结构缺陷。在Black Hat 2014上,David展示了Oracle旗舰数据库管理系统中存在的新漏洞。

该漏洞基于Oracle新版数据库12c中大肆宣扬的功能“数据校订(data redaction)”,这个功能被设计用于保护敏感数据。演讲中,David表示数据校订功能内含有大量安全漏洞,攻击者可以利用这些漏洞来绕过安全特性,并查看社会保险号、信用卡号等敏感信息。David表示,除下虚假的安全感,这个功能对企业没有任何帮助。

2. 云安全隐患——影响的不只是可用性

 

阿根廷顾问公司Bonsai Information Security创始人Andres Riancho通过详细研究指出,配置错误和基本漏洞都可能泄露托管于云上的应用程序数据、账户凭证等信息。Andres演示了AWS云基础设施可能被利用访问账户凭证、日志文件,并最终窃取用户账号。他还开发了一个专门的工具,这个工具可以自动浏览元数据,为攻击者访问敏感资源提供重要线索。他提倡安全专家与开发者共同审视当下的架构,及做好未来项目使用IaaS资源时的准备。

3. 医疗设备隐患——需要重点对待的领域

 

随着微型、强大的嵌入式系统投入到胰岛素泵、心脏起搏器等医疗设备,这个领域的安全就必须得到重点关注,因为这些设备越来越多的连接到互联网,在Black Hat 2014上漏洞管理厂商Rapid7研究员Jay Radcliffe指出。

Jay认为,漏洞可能被用于针对高风险个体,如政治领袖等。Jay专注于医疗设备安全,并成功黑了自己的胰岛素泵。Jay认为,这些设备面对一个非常普遍的问题——安全软件更新,因此无法更好地防御数据泄露。

4. POS系统隐患——Chip-And-PIN漏洞

 

Crowdome安全研究员兼NCR Retail企业安全架构师Nir Valtman表示,零售终端系统正面临内存搜读(memory-scraping)恶意软件的威胁,他表示可以在当下POS系统上运行的内存搜读恶意软件已非常普遍,其中更有许多已在大量目标数据窃取行动中得以验证,这些软件涉及身份认证、信息检索等多个功能。Nir研究过市面上大多数的付费安全系统,致力降低它们对系统性能的影响,他表示,取代只从技术方面着手,为零售商提供更多的信息服务同样重要。

期间,在另一个Black Hat会话上,剑桥大学密码学专家Ross Anderson表示,美国零售商最终将全部使用Chip-And-PIN,但是这项技术同样拥有漏洞和局限性。

5. Email安全隐患——雅虎加入谷歌行列

 

低等级数据加密或者无力及过时的安全协议在会议上被研究员多次提起,但其中影响力最大的报告无疑来自雅虎,该公司宣布将为用户提供一种终端到终端的加密技术。Google于今年6月发布了这项技术,提供了一个浏览器插件来加密数据。

世界范围内的雅虎和谷歌云服务用户都将享受到该技术带来的隐私和安全保障,其主要针对Edward Snowden揭秘的美国政府监控行为。雅虎CIO Alex Stamos表示,这项技术将在2015年放出。

6. 汽车软件隐患——岌岌可危的产业

 

Charlie Miller及Chris Valasek指出,当下,汽车制造厂商为敏感软件添加越来越多的监视及触发器,导致了愈来愈多的安全隐患。同时,他们还公布了关于汽车网络数据的分析,并确认了20个风险最高的模型。

车辆正在增加愈来愈多的无线能力,这无疑让远程攻击变为可能。Charlie当下是Twitter的安全工程师,他表示,他们正在寻找安全防御最好的汽车厂商。他们发现,2014 Jeep Cherokee和2015 Cadillac Escalade的风险最高,而2006 Ford Fusion和2010 Range Rover Sport看起来最为安全。

除此之外,Qualsys的研究员Silvio Cesare还展示了智能钥匙安全系统的缺陷,只使用一个简单的工具就可以锁车、开车,或者打开汽车的后备箱。

7. OTA升级导致了一系列的安全隐患


 

Accuvant Labs研究员Mathew Solnik和Marc Blanchou指出,网络运营商和设备制造商在更新手机、平板等无线设备时,无线机制中往往存在安全漏洞。该实验室研究员表示攻击者可以劫持运营商的远程控制能力,并在广泛的设备管理协议中利用。

此外,研究人员还指出运营商管理网络性能工具Carrier IQ可以监控手机上的所有流量。同时,这个设备管理工具让手机更容易被攻击。该应用可以用于运行远程代码,并绕过操作系统的本地防御。在全球范围内,70%到90%的手机内部都存在这个设备管理软件,由于这个易受攻击的OMA-DM协议,其他的设备,如笔记本、无线热点、物联网设备等也都存在风险。

8. USB安全隐患——威胁指数直线上升

USB一直是大量恶意软件的传播媒介,但是安全研究员Karsten Nohl和Jakob Lell指出USB隐患当下已经更加危险,未来借助USB的攻击将更具威胁。研究员演示了如何借助攻击监视网络流量,以及恶意软件在启动过程中如何夺取控制权。USB攻击非常难以察觉,因为当其被连接到笔记本或台式机时,它通常会被作为一个新设备,而恶意软件设计者可以轻易利用窃取来的证书欺骗设备制造商。

鉴于大多数的驱动硬件制造商都不会采取任何措施保护固件,同时恶意软件解决方案也不会扫描驱动固件以检查恶意行为,USB给了好事者无限可能。幸运的是,目前这种类型攻击在现实中尚未出现。

9. 能源管理平台——更大的威胁产生


来自德国IT安全公司ERNW的研究员揭露了Cisco Systems EnergyWise套件中的安全漏洞,他指出攻击者可以利用这些漏洞破坏机构的电力系统。该信息同样可以被恶意攻击者用来窃取数据,Cisco EnergyWise被设计用于读取每台机架的电压、功率、电流及每小时用电数,同时也会读一些温度、湿度、气流等数据。

10. 机场和飞机上的安全设备存在重大隐患


Qualys漏洞研究主管Billy Rios表示,机场中大量使用的扫描设备存在高危漏洞,攻击者可以利用这些漏洞访问和控制一些关键功能。Billy指出了Transportation Security Administration批准的两个设备,并要求厂商对底层软件做重大调整。Billy表示,在一个制造商的箱包扫描设备中,身份验证证书以纯文本格式存储,并缺少远程控制管理,其默认密码同样是一个非常大的隐患。

同时,IOI研究员Ruben Santamarta表示,攻击者可以通过飞机上提供的Wi-Fi和空中娱乐系统黑进飞机卫星通讯系统,从而控制飞机航线和安全系统。

在上述十个领域之外,监控录像机、Tor网络、路由器、NSA、酒店系统等也是研究员重点关注领域。

除此之外,2014黑客奥斯卡奖Pwnie Awards同样值得关注。本次Pwnie Awards共分8个类别,下面我们一起看各个类别漏洞提名以及最终得奖者(白色为提名,红色标注为最终获奖者),其中Heartbleed可以说是臭名昭著了:

最佳服务器端漏洞:

 

  • Abusing JSONP with Rosetta Flash (CVE-2014-4671)
  • Heartbleed (CVE-2014-0160)
  • IPMI: Sold Down the River
  • Embedded Device Hacking

 

最佳客户端漏洞:

 

  • Google Chrome Arbitrary Memory Read Write Vulnerability (CVE-2014-1705)
  • Heartbleed (CVE-2014-0160)
  • Pwn4Fun Safari vulnerability (CVE-2014-1300)
  • Goto Fail (CVE-2014-1266)

 

最佳提权漏洞

 

  • AFD.sys Dangling Pointer Vulnerability (CVE-2014-1767)
  • VirtualBox VM Breakout using 3D Acceleration (CVE-2014-0981)
  • Linux Futex Bug (CVE-2014-3153)
  • evasi0n iOS 7.0 jailbreak
  • Pangu iOS 7.1 Jailbreak

 

最具创新性研究

 

  • Hardware-assisted Memory Corruptions
  • Bypassing Windows 8.1 Mitigations using Unsafe COM Objects
  • RSA Key Extraction via Low-Bandwidth Acoustic Cryptanalysis
  • Windows 8 UEFI Secure Boot Bypasses
  • Hacking Blind

 

响应最烂的厂商

 

  • OpenCart PHP Object Injection Vulnerability
  • Fired, I?
  • AVG Remote Administration Insecure “By Design”
  • General Motors

 

最佳歌曲奖

 

  • “I’m a C I Double S P”
  • “Memory Corruption”
  • “Expect Us (We Are Anonymous)”
  • “Security Kate”
  • “The SSL Smiley Song”

 

最经典输家

 

  • Goto Fail
  • Heartbleed
  • Target Breach
  • (ISC)2 Optional Membership Fee

 

最经典破坏

 

  • Heartbleed (CVE-2014-0160)
  • Target Breach
  • Inputs.io
  • Mt. Gox

Javascript 匿名函数与封装

迷惑了一会儿不同JS库的封装后,终于有了点头绪。大致就是:

创建一个自调用匿名函数,设计参数window,并传入window对象。

而这个过和的目的则是,

使得自身的代码不会被其他代码污染,同时也可以不污染其他代码。

jQuery 封装

于是找了个早期版本的jQuery,版本号是1.7.1里面的封装代码大致是下面这样的

(function( window, undefined ) {
var jQuery = (function() {console.log('hello');});
window.jQuery = window.$ = jQuery;
if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
    define( "jquery", [], function () { return jQuery; } );
}
})( window );

其中的

console.log('hello');

是用以验证是否按开头说的这样工作,于是我们就可以在window中调用jQuery

window.$

或者是

window.jQuery

于是我们就可以创建一个类似的封装

(function(window, undefined) {
    var PH = function() {

    }
})(window)

相比于上面只是少了两步

  • 定义jQuery的符号及全局调用
  • 异步支持

于是找了下更早期的jQuery的封装,方法上大致是一样的, 除了。。

if (typeof window.jQuery == "undefined") {
    var jQuery = function() {};
    if (typeof $ != "undefined")
        jQuery._$ = $;

    var $ = jQuery;
};

很神奇的判断方法,以致于我们没有办法重写上一步的jQuery。于是只好看看最新的jQuery的封装是怎样的。于是就打开了2.1.1,发现除了加了很多功能以外,基本上思想还是不变的

(function(global, factory) {

    if (typeof module === "object" && typeof module.exports === "object") {
        module.exports = global.document ?
            factory(global, true) :
            function(w) {
                if (!w.document) {
                    throw new Error("jQuery requires a window with a document");
                }
                return factory(w);
        };
    } else {
        factory(global);
    }

}(typeof window !== "undefined" ? window : this, function(window, noGlobal) {
    var jQuery = function() {
        console.log('jQuery');
    };
    if (typeof define === "function" && define.amd) {
        define("jquery", [], function() {
            return jQuery;
        });
    };
    strundefined = typeof undefined;
    if (typeof noGlobal === strundefined) {
        window.jQuery = window.$ = jQuery;
    };
    return jQuery;
}));

在使用浏览器的情况下

typeof module ="undefined"

所以上面的情况是针对于使用Node.js等的情况下判断的,这也表明jQuery正在变得臃肿。

Backbone 封装

打开了Backbone看了一下

(function(root, factory) {

    if (typeof define === 'function' && define.amd) {
        define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
            root.Backbone = factory(root, exports, _, $);
        });

    } else if (typeof exports !== 'undefined') {
        var _ = require('underscore');
        factory(root, exports, _);

    } else {
        root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
    }

}(this, function(root, Backbone, _, $) {
    Backbone.$ = $;
    return Backbone;

}));

除了异步支持,也体现了其对于jQuery和underscore的依赖,百

        define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
            root.Backbone = factory(root, exports, _, $);
        });

表明backbone是原生支持requirejs的。

Underscore 封装

于是,又看了看Underscore,发现这个库又占领了一个符号 _

(function() {
    var root = this;
    var _ = function(obj) {
        if (obj instanceof _) return obj;
        if (!(this instanceof _)) return new _(obj);
        this._wrapped = obj;
    };

    if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            exports = module.exports = _;
        }
        exports._ = _;
    } else {
        root._ = _;
    }

    if (typeof define === 'function' && define.amd) {
        define('underscore', [], function() {
            return _;
        });
    }
}.call(this));

总体上也和差不多都是匿名函数,除了最后用的是call()方法。

http://www.phodal.com/blog/javascript-anonymous-function-encapsulation/

如何看待自己写的烂代码

如果你不是入行不久的新程序员,你很可能会遇到一些你曾经写过的老代码,看到它们,你可能会有这样的反应:

哦,shit!这是什么?当时我脑袋进水了?

我就这样过。我的朋友和同事们都经历过。你很可能也发生过这样的事情。

最近我的一些前同事联系到我,问我是否能帮助他们做一些前端开发工作。我想着挣一些外快也不错,而且,这个公司里我曾经工作过两年半,他们都是优秀的程序员。

昨晚,我遇到了一些之前在那个公司里任职时自己写的JavaScript老代码,下面是我的反应:

  1. 怀旧追思: 哇塞!这是当年我写的啊!
  2. 羞耻: 我靠,怎么写成这样。应该使用更好的方法。真惭愧,当时怎么会把这样的代码放到产品中。
  3. 困:不早了,我该睡觉了。
  4. 骄傲:这段代码运行的良好——虽然不是最佳算法。更妙的是,我是5年前写的它,计算一下,它至少被成功的执行了70亿次!

早上醒来时我感觉好多了,不再为我那不是很完美的代码而忧愁。写代码不是为了美,是为了价值。所以,下次,当你再看到自己的一年前、5年前、甚至更多年前写的让人难为情的“烂代码”时,如果它们还在产品库中,还在生产着价值,你应该为它们骄傲!

程序员经典幽默之恶搞对联

看了文章的标题,各位程序员千万别误会,程序员这种死板的生物怎么可能会写对联。下面的这些对联都非常有趣,看到别人这样恶搞自己也不免会淡淡的一笑,哎,苦逼的程序员。

对联一

上联:受苦受累起得比鸡还早。

下联:累死累活干得比驴还多。

横批:禽兽不如。

对联二

上联:一个项目两部电脑三餐盒饭之为四千工资搞得五脏俱损六神无主仍然七点起床八点开会处理九个漏洞十分辛苦。

下联:十年编码九年加班八面无光忙的七窍生烟到头六亲不认五体投地依旧四肢酸软三更加班只为两个臭钱一身孤苦。

横批:苦逼程序员。

对联三

上联:为系统而生,为框架而死,为debug奋斗一辈子!

下联:吃符号的亏,上大小写的当,最后死在需求上!

横批:杯具程序员。

对联四

上联:这个其实很简单。

下联:原理细节我不管。

横批:立马上线。

对联五

上联:我这儿没干啥它自己就好了。网络这事吧不明觉厉。

下联:你那儿不行吗我运行正常呀!需求想改呀十动然拒。

横批:细思恐极

对联六

上联:足不出户一台电脑打天下

下联:窝宅在家两只巧手定乾坤

横批:我最碉堡

 

.NET技术+25台服务器怎样支撑世界第54大网站

http://www.csdn.net/article/2014-07-22/2820774-stackoverflow-update-560m-pageviews-a-month-25-servers

摘要:同时使用Linux和Windows平台产品,大量使用静态的方法和类,Stack Overflow是个重度性能控。同时,取代横向扩展,他们坚持着纵向扩展思路,因为“硬件永远比程序员便宜”。

【编者按】StackOverflow是一个IT技术问答网站,用户可以在网站上提交和回答问题。当下的StackOverflow已拥有400万个用户,4000万个回答,月PV5.6亿,世界排行第54。然而值得关注的是,支撑他们网站的全部服务器只有25台,并且都保持着非常低的资源使用率,这是一场高有效性、负载均衡、缓存、数据库、搜索及高效代码上的较量。近日,High Scalability创始人Todd Hoff根据Marco Cecconi的演讲视频“ The architecture of StackOverflow”以及Nick Craver的博文“ What it takes to run Stack Overflow”总结了StackOverflow的成功原因。


免费订阅“CSDN大数据”微信公众号,实时了解最新的大数据进展!

CSDN大数据,专注大数据资讯、技术和经验的分享和讨论,提供Hadoop、Spark、Imapala、Storm、HBase、MongoDB、Solr、机器学习、智能算法等相关大数据观点,大数据技术,大数据平台,大数据实践,大数据产业资讯等服务。


以下为译文

意料之中,也是意料之外,Stack Overflow仍然重度使用着微软的产品。他们认为既然微软的基础设施可以满足需求,又足够便宜,那么没有什么理由去做根本上的改变。而在需要的地方,他们同样使用了Linux。究其根本,一切都是为了性能。

另一个值得关注的地方是,Stack Overflow仍然使用着纵向扩展策略,没有使用云。他们使用了384GB的内存和2TB的SSD来支撑SQL Servers,如果使用AWS的话,花费可想而知。没有使用云的另一个原因是Stack Overflow认为云会一定程度上的降低性能,同时也会给优化和排查系统问题增加难度。此外,他们的架构也并不需要横向扩展。峰值期间是横向扩展的杀手级应用场景,然而他们有着丰富的系统调整经验去应对。该公司仍然坚持着Jeff Atwood的名言——硬件永远比程序员便宜。

Marco Ceccon曾提到,在谈及系统时,有一件事情必须首先弄明白——需要解决问题的类型。首先,从简单方面着手,StackExchange究竟是用来做什么的——首先是一些主题,然后围绕这些主题建立社区,最后就形成了这个令人敬佩的问答网站。

其次则是规模相关。StackExchange在飞速增长,需要处理大量的数据传输,那么这些都是如何完成的,特别是只使用了25台服务器,下面一起追根揭底:

状态

 

  • StackExchange拥有110个站点,以每个月3到4个的速度增长。
  • 400万用户
  • 800万问题
  • 4000万答案
  • 世界排名54位
  • 每年增长100%
  • 月PV 5.6亿万
  • 大多数工作日期间峰值为2600到3000请求每秒,作为一个编程相关网站,一般情况下工作日的请求都会高于周末
  • 25台服务器
  • SSD中储存了2TB的SQL数据
  • 每个web server都配置了2个320G的SSD,使用RAID 1
  • 每个ElasticSearch主机都配备了300GB的机械硬盘,同时也使用了SSD
  • Stack Overflow的读写比是40:60
  • DB Server的平均CPU利用率是10%
  • 11个web server,使用IIS
  • 2个负载均衡器,1个活跃,使用HAProxy
  • 4个活跃的数据库节点,使用MS SQL
  • 3台实现了tag engine的应用程序服务器,所有搜索都通过tag
  • 3台服务器通过ElasticSearch做搜索
  • 2台使用了Redis的服务器支撑分布式缓存和消息
  • 2台Networks(Nexus 5596 + Fabric Extenders)
  • 2 Cisco 5525-X ASAs
  • 2 Cisco 3945 Routers
  • 主要服务Stack Exchange API的2个只读SQL Servers
  • VM用于部署、域控制器、监控、运维数据库等场合

 

平台

 

  • ElasticSearch
  • Redis
  • HAProxy
  • MS SQL
  • Opserver
  • TeamCity
  • Jil——Fast .NET JSON Serializer,建立在Sigil之上
  • Dapper——微型的ORM

 

UI

 

  • UI拥有一个信息收件箱,用于新徽章获得、用户发送信息、重大事件发生时的信息收取,使用WebSockets实现,并通过Redis支撑。
  • 搜索箱通过 ElasticSearch 实现,使用了一个REST接口。
  • 因为用户提出问题的频率很高,因此很难显示最新问题,每秒都会有新的问题产生,从而这里需要开发一个关注用户行为模式的算法,只给用户显示感兴趣的问题。它使用了基于Tag的复杂查询,这也是开发独立Tag Engine的原因。
  • 服务器端模板用于生成页面。

 

服务器

 

  • 25台服务器并没有满载,CPU使用率并不高,单计算SO(Stack Overflow)只需要5台服务器。
  • 数据库服务器资源利用率在10%左右,除下执行备份时。
  • 为什么会这么低?因为数据库服务器足足拥有384GB内存,同时web server的CPU利用率也只有10%-15%。
  • 纵向扩展还没有遇到瓶颈。通常情况下,如此流量使用横向扩展大约需要100到300台服务器。
  • 简单的系统。基于.Net,只用了9个项目,其他系统可能需要100个。之所以使用这么少系统是为了追求极限的编译速度,这点需要从系统开始时就进行规划,每台服务器的编译时间大约是10秒。
  • 11万行代码,对比流量来说非常少。
  • 使用这种极简的方式主要基于几个原因。首先,不需要太多测试,因为Meta.stackoverflow本来就是一个问题和bug讨论社区。其次,Meta.stackoverflow还是一个软件的测试网站,如果用户发现问题的话,往往会提出并给予解决方案。
  • 纽约数据中心使用的是Windows 2012,已经向2012 R2升级(Oregon已经完成了升级),Linux系统使用的是Centos 6.4。

 

SSD

 

  • 默认使用的是Intel 330(Web层等)
  • Intel 520用于中间层写入,比如Elastic Search
  • 数据层使用Intel 710和S3700
  • 系统同时使用了RAID 1和RAID 10(任何4+以上的磁盘都使用RAID 10)。不畏惧故障发生,即使生产环境中使用了上千块2.5英寸SSD,还没碰到过一块失败的情景。每个模型都使用了1个以上的备件,多个磁盘发生故障的情景不在考虑之中。
  • ElasticSearch在SSD上表现的异常出色,因为SO writes/re-indexes的操作非常频繁。
  • SSD改变了搜索的使用方式。因为锁的问题,Luncene.net并不能支撑SO的并发负载,因此他们转向了ElasticSearch。在全SSD环境下,并不需要围绕Binary Reader建立锁。

 

高可用性

 

  • 异地备份——主数据中心位于纽约,备份数据中心在Oregon。
  • Redis有两个从节点,SQL有2个备份,Tag Engine有3个节点,elastic有3个节点,冗余一切,并在两个数据中心同时存在。
  • Nginx是用于SSL,终止SSL时转换使用HAProxy。
  • 并不是主从所有,一些临时的数据只会放到缓存中
  • 所有HTTP流量发送只占总流量的77%,还存在Oregon数据中心的备份及一些其他的VPN流量。这些流量主要由SQL和Redis备份产生。

 

数据库

 

  • MS SQL Server
  • Stack Exchange为每个网站都设置了数据库,因此Stack Overflow有一个、Server Fault有一个,以此类推。
  • 在纽约的主数据中心,每个集群通常都使用1主和1只读备份的配置,同时还会在Oregon数据中心也设置一个备份。如果是运行的是Oregon集群,那么两个在纽约数据中心的备份都会是只读和同步的。
  • 为其他内容准备的数据库。这里还存在一个“网络范围”的数据库,用于储存登陆凭证和聚合数据(大部分是stackexchange.com用户文件或者API)。
  • Careers Stack Overflow、stackexchange.com和Area 51等都拥有自己独立的数据库模式。
  • 模式的变化需要同时提供给所有站点的数据库,它们需要向下兼容,举个例子,如果需要重命名一个列,那么将非常麻烦,这里需要进行多个操作:增加一个新列,添加作用在两个列上的代码,给新列写数据,改变代码让新列有效,移除旧列。
  • 并不需要分片,所有事情通过索引来解决,而且数据体积也没那么大。如果有filtered indexes需求,那么为什么不更高效的进行?常见模式只在DeletionDate = Null上做索引,其他则通过为枚举指定类型。每项votes都设置了1个表,比如一张表给post votes,1张表给comment votes。大部分的页面都可以实时渲染,只为匿名用户缓存,因此,不存在缓存更新,只有重查询。
  • Scores是非规范化的,因此需要经常查询。它只包含IDs和dates,post votes表格当下大约有56454478行,使用索引,大部分的查询都可以在数毫秒内完成。
  • Tag Engine是完全独立的,这就意味着核心功能并不依赖任何外部应用程序。它是一个巨大的内存结构数组结构,专为SO用例优化,并为重负载组合进行预计算。Tag Engine是个简单的windows服务,冗余的运行在多个主机上。CPU使用率基本上保持在2-5%,3个主机专门用于冗余,不负责任何负载。如果所有主机同时发生故障,网络服务器将把Tag Engine加载到内存中持续运行。
  • 关于Dapper无编译器校验查询与传统ORM的对比。使用编译器有很多好处,但在运行时仍然会存在fundamental disconnect问题。同时更重要的是,由于生成nasty SQL,通常情况还需要去寻找原始代码,而Query Hint和parameterization控制等能力的缺乏更让查询优化变得复杂。

 

编码

 

  • 流程

 

 

  • 大部分程序员都是远程工作,自己选择编码地点
  • 编译非常快
  • 然后运行少量的测试
  • 一旦编译成功,代码即转移至开发交付准备服务器
  • 通过功能开关隐藏新功能
  • 在相同硬件上作为其他站点测试运行
  • 然后转移至Meta.stackoverflow测试,每天有上千个程序员在使用,一个很好的测试环境
  • 如果通过则上线,在更广大的社区进行测试

 

 

  • 大量使用静态类和方法,为了更简单及更好的性能
  • 编码过程非常简单,因为复杂的部分被打包到库里,这些库被开源和维护。.Net 项目数量很低,因为使用了社区共享的部分代码。
  • 开发者同时使用2到3个显示器,多个屏幕可以显著提高生产效率。

 

缓存

 

  • 缓存一切
  • 5个等级的缓存
  • 1级是网络级缓存,缓存在浏览器、CDN以及代理服务器中。
  • 2级由.Net框架 HttpRuntime.Cache完成,在每台服务器的内存中。
  • 3级Redis,分布式内存键值存储,在多个支撑同一个站点的服务器上共享缓存项。
  • 4级SQL Server Cache,整个数据库,所有数据都被放到内存中。
  • 5级SSD。通常只在SQL Server预热后才生效。
  • 举个例子,每个帮助页面都进行了缓存,访问一个页面的代码非常简单:

 

 

  • 使用了静态的方法和类。从OOP角度来看确实很糟,但是非常快并有利于简洁编码。
  • 缓存由Redis和Dapper支撑,一个微型ORM

 

 

  • 为了解决垃圾收集问题,模板中1个类只使用1个副本,被建立和保存在缓存中。监测一切,包括GC操。据统计显示,间接层增加GC压力达到了某个程度时会显著的降低性能。
  • CDN Hit 。鉴于查询字符串基于文件内容进行哈希,只在有新建立时才会被再次取出。每天3000万到5000万Hit,带宽大约为300GB到600GB。
  • CDN不是用来应对CPU或I/O负载,而是帮助用户更快的获得答案

 

部署

 

  • 每天5次部署,不去建立过大的应用。主要因为

 

 

  • 可以直接的监视性能
  • 尽可能最小化建立,可以工作才是重点

 

 

  • 产品建立后再通过强大的脚本拷贝到各个网页层,每个服务器的步骤是:

 

 

  • 通过POST通知HAProxy下架某台服务器
  • 延迟IIS结束现有请求(大约5秒)
  • 停止网站(通过同一个PSSession结束所有下游)
  • Robocopy文件
  • 开启网站
  • 通过另一个POST做HAProxy Re-enable

 

 

  • 几乎所有部署都是通过puppet或DSC,升级通常只是大幅度调整RAID阵列并通过PXE boot安装,这样做非常快速。

 

协作

 

  • 团队

 

 

  • SRE (System Reliability Engineering):5人
  • Core Dev(Q&A site)6-7人
  • Core Dev Mobile:6人
  • Careers团队专门负责SO Careers产品开发:7人

 

 

  • Devops和开发者结合的非常紧密
  • 团队间变化很大
  • 大部分员工远程工作
  • 办公室主要用于销售,Denver和London除外
  • 一切平等,些许偏向纽约工作者,因为面对面有助于工作交流,但是在线工作影响也并不大
  • 对比可以在同一个办公室办公,他们更偏向热爱产品及有才华的工程师,他们可以很好的衡量利弊
  • 许多人因为家庭而选择远程工作,纽约是不错,但是生活并不宽松
  • 办公室设立在曼哈顿,那是个人才的诞生地。数据中心不能太偏,因为经常会涉及升级
  • 打造一个强大团队,偏爱极客。早期的微软就聚集了大量极客,因此他们征服了整个世界
  • Stack Overflow社区也是个招聘的地点,他们在那寻找热爱编码、乐于助人及热爱交流的人才。

 

编制预算

 

  • 预算是项目的基础。钱只花在为新项目建立基础设施上,如此低利用率的 web server还是3年前数据中心建立时购入。

 

测试

 

  • 快速迭代和遗弃
  • 许多测试都是发布队伍完成的。开发拥有一个同样的SQL服务器,并且运行在相同的Web层,因此性能测试并不会糟糕。
  • 非常少的测试。Stack Overflow并没有进行太多的单元测试,因为他们使用了大量的静态代码,还有一个非常活跃的社区。
  • 基础设施改变。鉴于所有东西都有双份,所以每个旧配置都有备份,并使用了一个快速故障恢复机制。比如,keepalived可以在负载均衡器中快速回退。
  • 对比定期维护,他们更愿意依赖冗余系统。SQL备份用一个专门的服务器进行测试,只为了可以重存储。计划做每两个月一次的全数据中心故障恢复,或者使用完全只读的第二数据中心。
  • 每次新功能发布都做单元测试、集成测试盒UI测试,这就意味着可以预知输入的产品功能测试后就会推送到孵化网站,即meta.stackexchange(原meta.stackoverflow)。

 

监视/日志

 

  • 当下正在考虑使用http://logstash.net/做日志管理,目前使用了一个专门的服务将syslog UDP传输到SQL数据库中。网页中为计时添加header,这样就可以通过HAProxy来捕获并且融合到syslog传输中。
  • Opserver和Realog用于显示测量结果。Realog是一个日志展示系统,由Kyle Brandt和Matt Jibson使用Go建立。
  • 日志通过HAProxy负载均衡器借助syslog完成,而不是IIS,因为其功能比IIS更丰富。

 

关于云

 

  • 还是老生常谈,硬件永远比开发者和有效率的代码便宜。基于木桶效应,速度肯定受限于某个短板,现有的云服务基本上都存在容量和性能限制。
  • 如果从开始就使用云来建设SO说不定也会达到现在的水准。但毫无疑问的是,如果达到同样的性能,使用云的成本将远远高于自建数据中心。

 

性能至上

 

  • StackOverflow是个重度的性能控,主页加载的时间永远控制在50毫秒内,当下的响应时间是28毫秒。
  • 程序员热衷于降低页面加载时间以及提高用户体验。
  • 每个独立的网络提交都予以计时和记录,这种计量可以弄清楚提升性能需要修改的地方。
  • 如此低资源利用率的主要原因就是高效的代码。web server的CPU平均利用率在5%到15%之间,内存使用为15.5 GB,网络传输在20 Mb/s到40 Mb/s。SQL服务器的CPU使用率在5%到10%之间,内存使用是365GB,网络传输为100 Mb/s到200 Mb/s。这可以带来3个好处:给升级留下很大的空间;在严重错误发生时可以保持服务可用;在需要时可以快速回档。

 

学到的知识

1. 为什么使用MS产品的同时还使用Redis?什么好用用什么,不要做无必要的系统之争,比如C#在Windows机器上运行最好,我们使用IIS;Redis在*nix机器上可以得到充分发挥,我们使用*nix。

2. Overkill即策略。平常的利用率并不能代表什么,当某些特定的事情发生时,比如备份、重建等完全可以将资源使用拉满。

3. 坚固的SSD。所有数据库都建立在SSD之上,这样可以获得0延时。

4. 了解你的读写负载。

5. 高效的代码意味着更少的主机。只有新项目上线时才会因为特殊需求增加硬件,通常情况下是添加内存,但在此之外,高效的代码就意味着0硬件添加。所以经常只讨论两个问题:为存储增加新的SSD;为新项目增加硬件。

6. 不要害怕定制化。SO在Tag上使用复杂查询,因此专门开发了所需的Tag Engine。

7. 只做必须做的事情。之所以不需要测试是因为有一个活跃的社区支撑,比如,开发者不用担心出现“Square Wheel”效应,如果开发者可以制作一个更更轻量级的组件,那就替代吧。

8. 注重硬件知识,比如IL。一些代码使用IL而不是C#。聚焦SQL查询计划。使用web server的内存转储究竟做了些什么。探索,比如为什么一个split会产生2GB的垃圾。

9. 切勿官僚作风。总有一些新的工具是你需要的,比如,一个编辑器,新版本的Visual Studio,降低提升过程中的一切阻力。

10. 垃圾回收驱动编程。SO在减少垃圾回收成本上做了很多努力,跳过类似TDD的实践,避免抽象层,使用静态方法。虽然极端,但是确实打造出非常高效的代码。

11. 高效代码的价值远远超出你想象,它可以让硬件跑的更快,降低资源使用,切记让代码更容易被程序员理解。

原文链接: StackOverflow Update: 560M Pageviews A Month, 25 Servers, And It’s All About Performance(编译/仲浩 审校/魏伟)

java程序执行js脚本

public class ExecJs {

    /**
     * 记录日志类
     */
    private Logger log = Logger.getLogger(ExecJs.class);
    /**
     * 后置处理,执行js脚本
     * @param js
     * @throws Exception
     */
    public void execJs(String js, Map<String,Object> map) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("execJs js : " + js);
            Iterator<Entry<String, Object>> it = map.entrySet().iterator();
            while (it.hasNext()) {
                Entry<String, Object> entry = (Entry<String, Object>) it.next();
                log.info("EXECJS MAP : " + entry.getKey() + "---" + entry.getValue());
            }// end while
        }// end if
        if ("".equals(js) || js == null) {
            log.info("EXECJS ERROR : JAVASCRIPT CONTENT IS NULL");
        } else if(map == null || map.size()<=0){
            log.info("EXECJS ERROR : MAP CONTENT IS NULL");
        } else {
            // 获取脚本引擎
            ScriptEngineManager mgr = new ScriptEngineManager();
            ScriptEngine engine = mgr.getEngineByName("javascript");
            // 绑定数据
            ScriptContext newContext = new SimpleScriptContext();
            Bindings bind = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
            bind.putAll(map);
            try {
                engine.setBindings(bind, ScriptContext.ENGINE_SCOPE);
                engine.eval(js);
            } catch (Exception e) {
                log.info("EXECJS EXCEPTION : EXECUTE JAVASCRIPT EXCEPTION", e);
                throw (e);
            }// end try
        }// end if
    }
}
调用例子
boolean flag = false;
String js = “var a = 1; var b = a + aKey;println(b);”;
Map<String,Object> map = new HashMap<String,Object>();
map.put(“aKey”, “aValue”);
try {
flag = execJs.execJs(js, map);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
« 较早的 文章 较新的 文章 »

Copyright © 2024 优大网 浙ICP备13002865号

回到顶部 ↑