Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 11807 articles
Browse latest View live

基于springboot的freemarker创建指定格式的word文档

$
0
0

       在web或其他应用中,经常我们需要导出或者预览word文档,比较实际的例子有招聘网站上预览或者导出个人简历,使用POI导出excel会非常的方便,但是如果想导出word,由于其格式控制非常复杂,故而使用POI将会非常麻烦,而FreeMarker则可以较好的解决这个问题;并且,根据FreeMarker的实现原理,预览word也会变得非常简单。

       FreeMarker主要有三个部分:模板,数据源以及数据的存储。可想而知,在导出word的时候,我们必须得告诉FreeMarker我们需要导出的word的格式以及将要填充到这个word中的数据,因而模板和数据源是我们需要准备的部分。这里需要另外说明的是,FreeMarker关心的不是模板文件的类型或具体内容,其关心的是模板文件中的ftl标签和其中获取数据的表达式(这部分将在后续进行讲解)。FreeMarker的强大之处也就在这个位置,这里的模板可以是任意类型的模板,而数据源由我们按照指定的格式封装即可。那么也就是说,对于预览操作,我们如果事先制作一个html模板,点击预览后由FreeMarker向按照该模板向新建的html文件中填充数据,接着在前台js中新开窗口将该html文件(填充数据后即为一个静态页面)显示出来即可达到预览的效果。

       这里我们以word文件的导出为例来讲解FreeMarker的使用,我们使用的IDE为Intellij IDEA,框架为springboot,项目是使用Maven构建的。

       模板文件的创建可以使用word2007及以上版本完成,首先我们创建一个如下格式的word文档:

       创建后将该文件以xml格式存储


       使用xml文本编辑器打开该xml文件,检查其中的取值表达式是否发生格式错误,如果发生格式错误就将其中错误的部分删除,使其恢复我们填写的格式(格式错误一般会发生在取值表达式中含有特殊字符的时候)。

        如图中所示,${user.password}就发生了格式错误,我们将中间错误部分删除后如下:

        接着将该模板文件另存为UserList.ftl,文件格式为全部文件:

       将该文件复制到项目中,打开并格式化,找到其中${user.username}和${user.password},仔细分析该ftl文件可以发现,在word文档中表格的每一行在xml文件中即为一个<w:tr></w:tr>标签,而在该标签中,每一个<w:tc></w:tc>则对应一个单元格。了解这个之后,我们就要使用ftl语法对该模板文件进行改造。这里我们需要导出的是一个用户列表的文件,每个用户包含一个用户名和密码,那么这里用户所在的这一行(<w:tr></w:tr>)就需要使用ftl中的<#list></#list>标签包含起来:

<#list users as user>
<w:tr>
<w:tc>
...
用户名:
...
</w:tc>
<w:tc>
...
${user.username}
...
</w:tc>
<w:tc>
...
密码:
...
</w:tc>
<w:tc>
...
${user.password}
...
</w:tc>
</w:tr>
</#list>

        到此为止,我们的ftl模板就制作完毕了。接下来我们创建后台服务端的代码,实体类创建如下:

 

public class User {
  private String username;
  private String password;

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public User() {
  }

  public User(String username, String password) {
    this.username = username;
    this.password = password;
  }
}

        Controller层创建如下:

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/word")
public class WordController {

  @Autowired
  private WordService wordService;

  @RequestMapping(value = "/createUserListWord", method = RequestMethod.GET)
  public ResponseEntity<Void> createUserListWord() {
    wordService.createUserListWord();
    return ResponseEntity.ok().build();
  }
}
        Service层的接口及其实现类如下:
public interface WordService {
  void createUserListWord();
}
 
import javax.transaction.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Service;
@Service
public class WordServiceImpl implements WordService {
  public void createUserListWord() {
    Map<?, ?> root = initData();  //数据源对象
    String template = "/template/UserList.ftl";  //模板文件的地址
    String path = "E:\\UserList.doc";  //生成的word文档的输出地址
    WordUtil.process(root, template, path);
  }

  private Map<?, ?> initData() {
    Map<String, Object> root = new HashMap<String, Object>();

    List<User> users = new ArrayList<User>();
    User zhangsan = new User("张三", "123");
    User lisi = new User("李四", "456");
    User wangwu = new User("王五", "789");
    users.add(zhangsan);
    users.add(lisi);
    users.add(wangwu);

    root.put("users", users);
    root.put("title", "用户列表");

    return root;
  }
}
        这里需要说明的一点是,在FreeMarker中,数据一般是以Map,List以及实体类对象的形式存储,这里数据的初始化函数中则将三种形式的数据存储方式都用到了。在模板中取值的时候,对于Map对象中的数据,使用${key}即可获取,这里key表示Map中的键,对于List,则可以使用下标的方式,也可以使用循环的方式,这里我们是将User对象存储于List中,在模板中则可以使用users[i]来获取List中第i个User对象,如users[i].username;也可以使用循环来对List集合进行遍历,如
<#list users as user>
  ${user.username}</#list>
        这里users表示存储List的Map的key的值。

 

       最后则是FreeMarker中生成word文档的核心函数:

import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public final class WordUtil {
  private static Configuration configuration = null;

  private WordUtil() {
    throw new AssertionError();
  }

  /**
   * 根据模板生成相应的文件
   * @param root 保存数据的map
   * @param template 模板文件的地址
   * @param path 生成的word文档输出地址
   * @return
   */
  public static synchronized File process(Map<?, ?> root, String template, String path) {

    if (null == root ) {
      throw new RuntimeException("数据不能为空");
    }

    if (null == template) {
      throw new RuntimeException("模板文件不能为空");
    }

    if (null == path) {
      throw new RuntimeException("输出路径不能为空");
    }

    File file = new File(path);
    String templatePath = template.substring(0, template.lastIndexOf("/"));
    String templateName = template.substring(template.lastIndexOf("/") + 1, template.length());

    if (null == configuration) {
      configuration = new Configuration(Configuration.VERSION_2_3_23);  // 这里Configurantion对象不能有两个,否则多线程访问会报错
      configuration.setDefaultEncoding("utf-8");
      configuration.setClassicCompatible(true);
    }
    configuration.setClassForTemplateLoading(WordUtil.class, templatePath);

    Template t = null;
    try {
      t = configuration.getTemplate(templateName);
      Writer w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));
      t.process(root, w);  // 这里w是一个输出地址,可以输出到任何位置,如控制台,网页等
      w.close();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return file;
  }

}
        至此,使用FreeMarker生成word文档的核心代码已经全部书写完毕,最后打开浏览器访问http://localhost:8081/word/createUserListWord,在E盘根目录下就会生成一个word文档,其内容如下:

        这只是FreeMarker的一个简单应用,关于FreeMarker生成html文件,这里有一点需要说明,word2007及以上版本保存文件的格式可以为xml文件,也可以为html文件,检查该html文件中取值表达式的格式无误之后按照上述步骤也可以生成我们需要的html文件,但是生成的html文件的格式不一定是我们需要的格式,并且在修改ftl模板的时候,由于模板中标签元素的样式等较多,因而修改较为复杂。若想达到预览的效果,我们可以不使用上述方法生成的html模板,而是手动书写一份格式一致的html文件,然后保存为ftl格式模板,这个过程并不复杂,并且可读性较强。
       以上就是本篇博客的全部内容,希望大家喜欢。


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




京东CTO张晨:如何用机器人客服赚出15000 台 iPhone6

$
0
0
作者: 张宇婷

以下内容根据 GIF2016 极客公园创新大会年度商业变量论坛京东集团 CTO 张晨演讲《在数据的王国里,用更好的体验创造更大的价值》整理而成。


最近流行一个视频,讲的是 1999 年,刘强东在中关村 4 平方米店铺里面,从 7 个员工开始做京东,一个公司从零到有的过程。

有人觉得:「京东好像是个电商,电商有什么技术?不需要有技术。」

从1999 年 4 平方米的柜台到京东今天的规模,京东逐渐建立了自己的技术体系,随着规模的扩大,京东用了很多开源的技术。逐渐成为开源技术的推动者和贡献者,在京东里面我们特别强调塑造开放、分享、透明的技术文化。

机器人客服节约出 15000 台 iPhone6

京东电商拥有中国电商领域里面最完整、最精准、价值链最长的数据。这些数据覆盖了从下单到物流的每一个环节。

大数据帮助京东的同时,也给用户创造了巨大价值。京东拥有 12 万人员工,提高效率、降低成本成为必须。

有时我们在想,微软的小冰跟你聊天聊得很开心,但是怎么把这个技术放到客服当中去,做实时响应、做个性化的应答甚至主动关怀用户。这才是对用户真正重要的,对产品重要的。

京东个用了 JIMI 机器人,通过人工智能,部分商品回答满意度已经超过人工客服。机器人对每个产品功能的了解绝对是超过客服的,机器人可以在最短的时间里面根据用户的习性给出最精准的回答。这一点,为京东节约的成本大概是 15000 台 iPhone6。

智能捕获对电商来说是非常重要。在京东自营商品中,如何根据海量的销售、品类促销的策略,如何根据各个仓库的部署来搭建最佳的捕获模型?通过大数据分析,捕获效率可以超过 50%。用智能取代人力,随着人口老龄化,要让更多的智能机器人代替人工。

3 分钟内审批贷款

京东金融通过大数据分析,提供面向供应商的产品——金宝贝。通过大数据的分析可以在 3 分钟之内审批贷款,京东白条能及时决定用户的白条使用额。数据统计发现用了白条的人消费能力提升百分之百。

对电商来讲,最不愿意做的事情是拆单。拆单不仅增加成本,还给用户造成不好的体验,一个单要让用户等待好几次快递。但是像京东这么大的公司,每年发展很快,全国有将近 250 个仓库,没有一个仓库能够容纳得下所有海量货品。随着京东提供的服务越来越多,这个拆单的比例也在增加。

怎么通过大数据减少拆单的可能性?我们做了测试,通过数据分析,决定什么样的货应该放在什么样的仓库,怎么样布置全国的仓库最合理,把拆单率从 14% 降到了 6% 以下。

这个过程大数据的影响是巨大的。减少拆单为公司降低了巨大的成本,也提升了用户体验。

一个数据可以服务一个家庭 16 年

大数据对京东最重要的是创造了好的用户体验,为什么?数据不在于多少,在于数据的质量。有人告诉我每天产生这么多的数据,没用!怎么样精准把用户画像画起来是非常难的事情。

京东有数据能够把这个做好,举个最简单的例子,如果我今天在网上看到一个家庭、一个帐号在开始买奶瓶、婴儿车,我就知道这个家庭、这个帐号将有个 BABY,如果这个信号有的话,之后你会发现这个 BABY 是男的还是女的,根据它给孩子买的玩具,光这一个信息就可以一路服务这个家庭 18 年,知道这个城市小孩最喜欢什么、小孩该读什么样的书,玩什么样的玩具、放暑假了该去哪里玩,精准的数据可以给用户提供非常好的服务。

再举个例子,别人老说买东西越便宜越好,是这样吗?从零到一的时候也许觉得越便宜越好,但当你的物质不再贫乏,就会追求更高品质的生活。为什么那么多人要到国外买东西回来?为什么要买品牌?我们怎么给电商用户打造品质生活?

iPhone6s 的未买先送实验

另外一个有趣的实验,京东从购买力指数发现,北京望京地区的购买力相对较高,黄村地区的购买力低。

比如买电饭煲,如果一个用户在黄村搜索电饭煲,你最好让他看到的结果是他这个群体里面买得最多的电饭煲款式。但望京的用户,应该为给他打造品质生活,买最便宜的东西对电商来说,往往是亏的。让有消费能力的人买个便宜的东西,他拿到之后心里也是不开心的,

京东跟腾讯合作了小区画像。把群体精准到是一个社区,而不仅仅是大望京区,从而提高用户体验。iPhone 首发时候大家都想最快买到手。那我们怎么做?京东是「未买先送」。未买先送是根据消费能力的预测,京东在北京有 200 多个配送点,在用户没有下单的时候已经把 iPhone 放到了这些配送点。这个结果是什么?iPhone6S售卖的时候,京东首发配送记录是 12 分 20 秒,这是给用户创造极致的用户体验,是根据每个区域的需求精准的定位。

关键在于提升用户体验

动态定价。季节性、生命周期、友商价格都在影响动态定价。同样一个产品,根据不同价格的定位,价格定的准,促销量和毛利会有很大的增长。动态定价在经营管理中有很多模型可以深挖。

大数据优化配送途径。京东通过优化途径算法,让仓库物质堆放更合理。我们把算法放进仓库的时候,100 个中小仓库用了之后效果明显,单品从 22 秒降到 16 秒,平均产生 6 秒的节省,每单节省 6 秒钟,合起来是巨大的效率提高!

青龙物流。京东在全国用大数据技术选择最优的配送途径,可以根据时效率跟负载率等,可以在不同情况下使用不同的方案。

京东智圈是帮助商圈做一些智能数据分析。京东有很多线上数据,怎么用线上数据帮助线下商家也创造价值?这个测试的目的是根据行业、人群、环境做些具体分析。比如说现在有两家店,在两个不同的区,一个店是在知春路,另一个是其他的地方。同样的两个店,经营效果非常不一样。

京东智圈能够告诉两家店主这种不同的原因。比如,店面周围是什么样的环境、用户平常注重的品牌是什么、周围的用户平常在网上线上买什么东西。两家店主可以根据这些数据,决定自己的采购策略、促销策略。如果这个社区里面的用户对某些品牌不敏感,就算进了这些品牌,即使打了折可能也卖不出去。

最后给大家提几点建议,大数据不仅仅是降低成本,关键是能够提高用户体验、创造价值。我曾经说过,我一直相信即使是广告,如果这个广告是跟你兴趣爱好相关,它会变成价值。所以大数据能够提升用户体验。把数据用好核心在哪里?是精准建模分析,精准第一的。第三个,京东逐渐开放大数据的能力和内容,帮助更多合作伙伴一起成长。

国内常用静态资源 CDN 公共库加速服务

$
0
0

静态资源 CDN 公共库是指一些服务商将我们常用的 JavaScript 库存放到网上,方便开发者直接调用,并且还对其提供 CDN 加速,这样一来可以让用户加速访问这些资源,二来还可节约自己服务器的流量。国内提供静态资源 CDN 公共库加速服务服务的厂商还是有蛮多的,今天就给大家介绍一下:

百度静态资源公共库

http://cdn.code.baidu.com/

提供的库比较全,但是不支持 https,使用了 https 加密站点使用会出现警告。

新浪云计算公共库

http://lib.sinaapp.com/

支持的库比较少,但是支持 https。

360 网站卫士常用前端公共库

http://libs.useso.com/

除了提供前端库之外,只需替换一下域名,360 就可以让你继续使用 Google 提供的前端公共库和免费字体库。但是同样最大的问题,不支持 https。

七牛云存储开放静态文件

http://staticfile.org/

国内目前最全的前端库了,使用 https 要换用另外一个域名: https://dn-staticfile.qbox.me/

又拍云JS库

http://jscdn.upai.com/

只提供 jQuery,MOOTOOLS,MODERNIZR,DOJO和EMBER这5个库,而且已经很早没更新了,一般建议不要使用了。

总结

还有 CDNJSGoogle Hosted Libraries其他一些公共 CDN 库,但是国内访问速度不快,这里也就不提了。

个人建议使用 七牛云存储开放静态文件的最好了,目前 七牛云存储 WordPress 插件也使用 StaticFile 来替换 jQuery 库的。

如果你需要让你的用户访问 Google 提供的前端公共库和免费字体库,只能使用 360 网站卫士常用前端公共库

>>>继续阅读 国内常用静态资源 CDN 公共库加速服务的全文 ...

© 我爱水煮鱼 / RSS 订阅 / 长期承接 WordPress 项目

什么才算是真正的编程能力?

$
0
0

本文综合整理自知乎同名问答帖。题主的问题补充如下:

还在读书,也在实验室帮忙做了些东西,自己也搭过几个网站。在周围人看来似乎好像我很厉害,做了那么多东西,但是我发现这些东西虽然是我做的,但是实际上我手把手自己写的代码却并没有多少,很多都是用开源的东西,我写的代码无非是把别人的东西整合下,类似于胶水一样的工作。

我之前所认为的编程是全手动一行一行敲代码,但是现在我发现哪怕是工程上,也有很多人是复制黏贴来解决问题的,并且提倡不要重复造轮子。

但是靠谷歌和复制别人的轮子,虽然我做出了很多东西,可是我并不觉得自己能力上有提升,倒是利用搜索引擎的能力的确提升了不少。而学校里另外一部分在搞ACM的人,他们每天都在刷题练算法,但单凭我个人的感受感觉他们似乎对工程上有些东西并不了解,或许算法的能力才算是实打实的编程能力?那”胶水”的能力和整合轮子的能力算不算编程能力呢?

所以我现在就很困惑,所谓的编程能力到底是什么,我该如何提升自己的编程能力?

下面是 刘贺 的回复: (已征得同意)

非常好的一个问题。这可能是我在知乎见到过的问编程有关的问题中问得最好的一个了。我非常喜欢这个问题。

计算机科学有两类根本问题。一类是理论:算法,数据结构,复杂度,机器学习,模式识别,等等等。一类是系统:操作系统,网络系统,分布式系统,存储系统,游戏引擎,等等等等。

理论走的是深度,是在追问在给定的计算能力约束下如何把一个问题解决得更快更好。而系统走的是广度,是在追问对于一个现实的需求如何在众多的技术中设计出最多快好省的技术组合。

搞ACM的人,只练第一类。像你这样的更偏向于第二类。其实挺难得的,但很可惜的是第二类能力没有简单高效的测量考察方法,不像算法和数据结构有ACM竞赛,所以很多系统的苗子都因为缺少激励和正确引导慢慢就消隐了。

所以比尔盖茨才会说,看到现在学编程的人经常都把编程看作解各种脑筋急转弯的问题,他觉得很遗憾。

做系统,确实不提倡“重复发明轮子”。但注意,是不提倡“重复发明”,不是不提倡“重新制造”。恰恰相反的,我以为,系统的编程能力正体现在“重新制造”的能力。

能把已有的部件接起来,这很好。但当你恰好缺一种关键的胶水的时候,你能写出来吗?当一个已有的部件不完全符合你的需求的时候,你能改进它吗?如果你用的部件中有bug,你能把它修好吗?在网上繁多的类似功能的部件中,谁好谁坏?为什么?差别本质吗?一个开源代码库,你能把它从一个语言翻译到另一个语言吗?从一个平台移植到另一个平台吗?能准确估计自己翻译和移植的过程需要多少时间吗?能准确估计翻译和移植之后性能是会有提升还是会有所下降吗?

系统编程能力体现在把已有的代码拿来并变成更好的代码,体现在把没用的代码拿来并变成有用的代码,体现在把一个做好的轮子拿来能画出来轮子的设计蓝图,并用道理解释出设计蓝图中哪些地方是关键的,哪些地方是次要的,哪些地方是不容触碰的,哪些地方是还可以改进的。

如果你一点不懂理论,还是应该学点的。对于系统性能的设计上,算法和数据结构就像在自己手头的钱一样,它们不是万能的,但不懂是万万不行的。

怎么提高系统编程能力呢?土办法:多造轮子。就像学画画要画鸡蛋一样,不是这世界上没有人会画鸡蛋,但画鸡蛋能驯服手指,感受阴影线条和笔触。所以,自己多写点东西吧。写个编译器?渲染器?操作系统?web服务器?web浏览器?部件都一个个换成自己手写的,然后和已有的现成部件比一比,看看谁的性能好,谁的易用性好?好在哪儿?差在哪儿?为什么?

更聪明一点的办法:多拆轮子。多研究别人的代码是怎么写的。然而这个实践起来经常很难。原因:大部分工业上用的轮子可能设计上的思想和技术是好的,都设计和制造过程都很烂,里面乱成一团,让人乍一看毫无头绪,导致其对新手来说非常难拆。这种状况其实非常糟糕。所以,此办法一般只对比较简单的轮子好使,对于复杂的轮子,请量力而行。

轮子不好拆,其实是一个非常严重的问题。重复发明轮子固然是时间的浪费,但当轮子复杂而又不好拆的时候,尤其是原来造轮子的人已经不在场的时候,重新发明和建造轮子往往会成为无奈之下最好的选择。这是为什么工业界在明知道重复发明/制造轮子非常不好的情况下还在不断重复发明/制造轮子的根本原因。

程序本质是逻辑演绎的形式化表达,记载的是人类对这个世界的数字化理解。不能拆的轮子就像那一篇篇丢了曲谱的宋词一样,能读,却不能唱。

鄙人不才,正在自己研究怎么设计建造一种既好用又好拆的轮子。您没那么幸运,恐怕是等不到鄙人的技术做出来并发扬光大了。在那之前,多造轮子,多拆好拆的小轮子,应该是提高编程能力最好的办法了。

 

(文章属个人观点,与本人工作雇主无关。)

(小编转注:http://zhuanlan.zhihu.com/shanhu 这是刘贺的专栏。)

 

下面是 mu mu 的回复: (已征得同意)

懂得取舍。

在有限的时间内,几乎没有系统可以做到完美。要快,要安全,高并发,易扩展,效率高,容易读,高内聚,低耦合…

大到一个网站,小到几个class,工程师都要清楚,要取什么,舍什么,这并不是那么容易的事。我们都有自己的性格,有的求新,有的求稳,有的求快,但具体到一个项目时,知道如何取舍对这个项目最好,很重要。

学校里的作业,没人在意你是不是写在一个大的main()里面,能跑就行。但做项目的时候,太多的东西要考虑,有时候,宁可简单易读,也不用快那么一点点;有时候,要做太多看不到的工作,却丝毫马虎不得;有时候,写了不如不写,留白也是一个学问。

曾经接手个项目,里面几乎所有的class,每个都有interface,各种继承,各种实现,理由是灵活性高,易扩展。真的易扩展吗?

我不知道。没多久,客户的需求就改了,各种拎不清的继承实现都化为乌有,一大半要重写。

问题在哪里?

不是编程不好,而是取舍的不好。在那个阶段,为30%的需求,花200%的努力,追求设计的滴水不漏,却舍弃快速实现,取得反馈的时机,这就是失误。需求总会变,客户看到越早,修改越早,影响越小。

很聪明的人,也可能做出很难用的系统,不一定是编程不好,可能是不愿,或不屑于取舍。不同的阶段,不同的项目,要取舍的东西也不同。编程只是手段,目的是解决问题,能力高不高,要看问题解决的好不好。不在于使用了什么高端算法,或是复杂的框架。

懂得如何取舍并不容易,需要对问题。

 

 

http://www.techug.com/programming-ability



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



浅谈 Spark 应用程序的性能调优

$
0
0

Spark是基于内存的分布式计算引擎,以处理的高效和稳定著称。然而在实际的应用开发过程中,开发者还是会遇到种种问题,其中一大类就是和性能相关。在本文中,笔者将结合自身实践,谈谈如何尽可能地提高应用程序性能。

分布式计算引擎在调优方面有四个主要关注方向,分别是CPU、内存、网络开销和I/O,其具体调优目标如下:

  • 提高CPU利用率。

  • 避免OOM。

  • 降低网络开销。

  • 减少I/O操作。

第1章 数据倾斜

数据倾斜意味着某一个或某几个Partition中的数据量特别的大,这意味着完成针对这几个Partition的计算需要耗费相当长的时间。

如果大量数据集中到某一个Partition,那么这个Partition在计算的时候就会成为瓶颈。图1是Spark应用程序执行并发的示意图,在Spark中,同一个应用程序的不同Stage是串行执行的,而同一Stage中的不同Task可以并发执行,Task数目由Partition数来决定,如果某一个Partition的数据量特别大,则相应的task完成时间会特别长,由此导致接下来的Stage无法开始,整个Job完成的时间就会非常长。

要避免数据倾斜的出现,一种方法就是选择合适的key,或者是自己定义相关的partitioner。在Spark中Block使用了ByteBuffer来存储数据,而ByteBuffer能够存储的最大数据量不超过2GB。如果某一个key有大量的数据,那么在调用cache或persist函数时就会碰到spark-1476这个异常。

下面列出的这些API会导致Shuffle操作,是数据倾斜可能发生的关键点所在

  1. groupByKey

  2. reduceByKey

  3. aggregateByKey

  4. sortByKey

  5. join

  6. cogroup

  7. cartesian

  8. coalesce

  9. repartition

  10. repartitionAndSortWithinPartitions


图1: Spark任务并发模型

def rdd: RDD[T]
}

// TODO View bounds are deprecated, should use context bounds
// Might need to change ClassManifest for ClassTag in spark 1.0.0
case class DemoPairRDD[K <% Ordered[K] : ClassManifest, V: ClassManifest](
  rdd: RDD[(K, V)]) extends RDDWrapper[(K, V)] {
  // Here we use a single Long to try to ensure the sort is balanced, 
  // but for really large dataset, we may want to consider
  // using a tuple of many Longs or even a GUID
  def sortByKeyGrouped(numPartitions: Int): RDD[(K, V)] =
    rdd.map(kv => ((kv._1, Random.nextLong()), kv._2)).sortByKey()
    .grouped(numPartitions).map(t => (t._1._1, t._2))
}

case class DemoRDD[T: ClassManifest](rdd: RDD[T]) extends RDDWrapper[T] {
  def grouped(size: Int): RDD[T] = {
    // TODO Version where withIndex is cached
    val withIndex = rdd.mapPartitions(_.zipWithIndex)

    val startValues =
      withIndex.mapPartitionsWithIndex((i, iter) => 
        Iterator((i, iter.toIterable.last))).toArray().toList
      .sortBy(_._1).map(_._2._2.toLong).scan(-1L)(_ + _).map(_ + 1L)

    withIndex.mapPartitionsWithIndex((i, iter) => iter.map {
      case (value, index) => (startValues(i) + index.toLong, value)
    })
    .partitionBy(new Partitioner {
      def numPartitions: Int = size
      def getPartition(key: Any): Int = 
        (key.asInstanceOf[Long] * numPartitions.toLong / startValues.last).toInt
    })
    .map(_._2)
  }
}

定义隐式的转换

  implicit def toDemoRDD[T: ClassManifest](rdd: RDD[T]): DemoRDD[T] = 
    new DemoRDD[T](rdd)
  implicit def toDemoPairRDD[K <% Ordered[K] : ClassManifest, V: ClassManifest](
    rdd: RDD[(K, V)]): DemoPairRDD[K, V] = DemoPairRDD(rdd)
  implicit def toRDD[T](rdd: RDDWrapper[T]): RDD[T] = rdd.rdd
}

在spark-shell中就可以使用了

import RDDConversions._

yourRdd.grouped(5)

第2章 减少网络通信开销

Spark的Shuffle过程非常消耗资源,Shuffle过程意味着在相应的计算节点,要先将计算结果存储到磁盘,后续的Stage需要将上一个Stage的结果再次读入。数据的写入和读取意味着Disk I/O操作,与内存操作相比,Disk I/O操作是非常低效的。

使用iostat来查看disk i/o的使用情况,disk i/o操作频繁一般会伴随着cpu load很高。

如果数据和计算节点都在同一台机器上,那么可以避免网络开销,否则还要加上相应的网络开销。 使用iftop来查看网络带宽使用情况,看哪几个节点之间有大量的网络传输。

图2是Spark节点间数据传输的示意图,Spark Task的计算函数是通过Akka通道由Driver发送到Executor上,而Shuffle的数据则是通过Netty网络接口来实现。由于Akka通道中参数spark.akka.framesize决定了能够传输消息的最大值,所以应该避免在Spark Task中引入超大的局部变量。


图2: Spark节点间的数据传输

第1节 选择合适的并发数

为了提高Spark应用程序的效率,尽可能的提升CPU的利用率。并发数应该是可用CPU物理核数的两倍。在这里,并发数过低,CPU得不到充分的利用,并发数过大,由于spark是每一个task都要分发到计算结点,所以任务启动的开销会上升。

并发数的修改,通过配置参数来改变spark.default.parallelism,如果是sql的话,可能通过修改spark.sql.shuffle.partitions来修改。

第1项 Repartition vs. Coalesce

repartition和coalesce都能实现数据分区的动态调整,但需要注意的是repartition会导致shuffle操作,而coalesce不会。

第2节 reduceByKey vs. groupBy

groupBy操作应该尽可能的避免,第一是有可能造成大量的网络开销,第二是可能导致OOM。以WordCount为例来演示reduceByKey和groupBy的差异

reduceByKey
    sc.textFile(“README.md”).map(l=>l.split(“,”)).map(w=>(w,1)).reduceByKey(_ + _)


图3:reduceByKey的Shuffle过程

Shuffle过程如图2所示

groupByKey
    sc.textFile(“README.md”).map(l=>l.split(“,”)).map(w=>(w,1)).groupByKey.map(r=>(r._1,r._2.sum))


图4:groupByKey的Shuffle过程

建议: 尽可能使用reduceByKey, aggregateByKey, foldByKey和combineByKey

假设有一RDD如下所示,求每个key的均值

val data = sc.parallelize( List((0, 2.), (0, 4.), (1, 0.), (1, 10.), (1, 20.)) )

方法一:reduceByKey

data.map(r=>(r._1, (r.2,1))).reduceByKey((a,b)=>(a._1 + b._1, a._2 + b._2)).map(r=>(r._1,(r._2._1/r._2._2)).foreach(println)

方法二:combineByKey

data.combineByKey(value=>(value,1),
     (x:(Double, Int), value:Double)=> (x._1+value, x._2 + 1),     (x:(Double,Int), y:(Double, Int))=>(x._1 + y._1, x._2 + y._2))

第3节 BroadcastHashJoin vs. ShuffleHashJoin

在Join过程中,经常会遇到大表和小表的join. 为了提高效率可以使用BroadcastHashJoin, 预先将小表的内容广播到各个Executor, 这样将避免针对小表的Shuffle过程,从而极大的提高运行效率。

其实BroadCastHashJoin核心就是利用了BroadCast函数,如果理解清楚broadcast的优点,就能比较好的明白BroadcastHashJoin的优势所在。

以下是一个简单使用broadcast的示例程序。

val lst = 1 to 100 toList
val exampleRDD = sc.makeRDD(1 to 20 toSeq, 2)
val broadcastLst = sc.broadcast(lst)
exampleRDD.filter(i=>broadcastLst.valuecontains(i)).collect.foreach(println)

第4节 map vs. mapPartitions

有时需要将计算结果存储到外部数据库,势必会建立到外部数据库的连接。应该尽可能的让更多的元素共享同一个数据连接而不是每一个元素的处理时都去建立数据库连接。

在这种情况下,mapPartitions和foreachPartitons将比map操作高效的多。

第5节 数据就地读取

移动计算的开销远远低于移动数据的开销。

Spark中每个Task都需要相应的输入数据,因此输入数据的位置对于Task的性能变得很重要。按照数据获取的速度来区分,由快到慢分别是:

  1. PROCESS_LOCAL

  2. NODE_LOCAL

  3. RACK_LOCAL

Spark在Task执行的时候会尽优先考虑最快的数据获取方式,如果想尽可能的在更多的机器上启动Task,那么可以通过调低spark.locality.wait的值来实现, 默认值是3s。

除了HDFS,Spark能够支持的数据源越来越多,如Cassandra, HBase,MongoDB等知名的NoSQL数据库,随着Elasticsearch的日渐兴起,spark和elasticsearch组合起来提供高速的查询解决方案也成为一种有益的尝试。

上述提到的外部数据源面临的一个相同问题就是如何让spark快速读取其中的数据, 尽可能的将计算结点和数据结点部署在一起是达到该目标的基本方法,比如在部署Hadoop集群的时候,可以将HDFS的DataNode和Spark Worker共享一台机器。

以cassandra为例,如果Spark的部署和Cassandra的机器有部分重叠,那么在读取Cassandra中数据的时候,通过调低spark.locality.wait就可以在没有部署Cassandra的机器上启动Spark Task。

对于Cassandra, 可以在部署Cassandra的机器上部署Spark Worker,需要注意的是Cassandra的compaction操作会极大的消耗CPU,因此在为Spark Worker配置CPU核数时,需要将这些因素综合在一起进行考虑。

这一部分的代码逻辑可以参考源码TaskSetManager::addPendingTask

private def addPendingTask(index: Int, readding: Boolean = false) {
  // Utility method that adds `index` to a list only if readding=false or it's not already there
  def addTo(list: ArrayBuffer[Int]) {
    if (!readding || !list.contains(index)) {
      list += index
    }
  }

  for (loc <- tasks(index).preferredLocations) {
    loc match {
      case e: ExecutorCacheTaskLocation =>
        addTo(pendingTasksForExecutor.getOrElseUpdate(e.executorId, new ArrayBuffer))
      case e: HDFSCacheTaskLocation => {
        val exe = sched.getExecutorsAliveOnHost(loc.host)
        exe match {
          case Some(set) => {
            for (e <- set) {
              addTo(pendingTasksForExecutor.getOrElseUpdate(e, new ArrayBuffer))
            }
            logInfo(s"Pending task $index has a cached location at ${e.host} " +", where there are executors " + set.mkString(","))
          }
          case None => logDebug(s"Pending task $index has a cached location at ${e.host} " +", but there are no executors alive there.")
        }
      }
      case _ => Unit
    }
    addTo(pendingTasksForHost.getOrElseUpdate(loc.host, new ArrayBuffer))
    for (rack <- sched.getRackForHost(loc.host)) {
      addTo(pendingTasksForRack.getOrElseUpdate(rack, new ArrayBuffer))
    }
  }

  if (tasks(index).preferredLocations == Nil) {
    addTo(pendingTasksWithNoPrefs)
  }

  if (!readding) {
    allPendingTasks += index  // No point scanning this whole list to find the old task there
  }
}

如果准备让spark支持新的存储源,进而开发相应的RDD,与位置相关的部分就是自定义getPreferredLocations函数,以elasticsearch-hadoop中的EsRDD为例,其代码实现如下。

override def getPreferredLocations(split: Partition): Seq[String] = {
  val esSplit = split.asInstanceOf[EsPartition]
  val ip = esSplit.esPartition.nodeIp
  if (ip != null) Seq(ip) else Nil
}

第6节 序列化

使用好的序列化算法能够提高运行速度,同时能够减少内存的使用。

Spark在Shuffle的时候要将数据先存储到磁盘中,存储的内容是经过序列化的。序列化的过程牵涉到两大基本考虑的因素,一是序列化的速度,二是序列化后内容所占用的大小。

kryoSerializer与默认的javaSerializer相比,在序列化速度和序列化结果的大小方面都具有极大的优势。所以建议在应用程序配置中使用KryoSerializer.

spark.serializer  org.apache.spark.serializer.KryoSerializer

默认的cache没有对缓存的对象进行序列化,使用的StorageLevel是MEMORY_ONLY,这意味着要占用比较大的内存。可以通过指定persist中的参数来对缓存内容进行序列化。

exampleRDD.persist(MEMORY_ONLY_SER)

需要特别指出的是persist函数是等到job执行的时候才会将数据缓存起来,属于延迟执行; 而unpersist函数则是立即执行,缓存会被立即清除。

更多内容可以访问  community.qingcloud.com

Solr安装与配置

$
0
0

需要Java Runtime Environment(JRE) 1.7或更高版本,先验证。

# java -version


如果没有安装好Java环境,需要参考: http://blog.csdn.net/unix21/article/details/18774417


无需安装tomcat,新版solr已经集成jetty
Solr最新版下载地址 http://mirror.bit.edu.cn/apache/lucene/solr/
#wget http://mirror.bit.edu.cn/apache/lucene/solr/5.3.1/solr-5.3.1.tgz

解压压缩包
#tar zxf solr-5.3.1.tgz

创建应用程序和数据目录
#mkdir -p /data/solr /usr/local/solr

创建运行solr的用户并赋权
#groupadd solr
#useradd -g solr solr
#chown -R solr.solr /data/solr /usr/local/solr

安装solr服务
#/usr/solr-5.3.1/bin/install_solr_service.sh /usr/solr-5.3.1.tgz -d /data/solr -i /usr/local/solr

如果没有安装jdk



安装正常


【状态】
#/usr/local/solr/solr/bin/solr status



solr命令用法
定位到solr应用程序目录  
# cd /usr/local/solr/solr
 
查看solr命令选项
# ./bin/solr

--------------
Usage: solr COMMAND OPTIONS
       where COMMAND is one of: start, stop, restart, status, healthcheck, create, create_core, create_collection, delete


  Standalone server example (start Solr running in the background on port 8984):


    ./solr start -p 8984


  SolrCloud example (start Solr running in SolrCloud mode using localhost:2181 to connect to ZooKeeper, with 1g max heap size and remote Java debug options enabled):


    ./solr start -c -m 1g -z localhost:2181 -a "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044"


Pass -help after any COMMAND to see command-specific usage information,
  such as:    ./solr start -help or ./solr stop -help
-----------------------

【启动】
#/usr/local/solr/solr/bin/solr start -help
#/usr/local/solr/solr/bin/solr create -help
【重启】才可以新的索引配置文件生效   重启可以解决CPU占比很高的问题
#/usr/local/solr/solr/bin/solr restart
#/usr/local/solr/solr/bin/solr stop
#crontab -l
#crontab -e
10 3 * * * root /usr/local/solr/solr/bin/solr restart


安装solr服务脚本用法
运行安装脚本
# /usr/solr-5.3.1/bin/install_solr_service.sh


【创建集合】
在这个部分,我们创建一个简单的Solr集合。
Solr可以有多个集合,但在这个示例,我们只使用一个。使用如下命令,创建一个新的集合。我们以solr用户运行以避免任何权限错误。
# su - solr -c "/usr/local/solr/solr/bin/solr create -c gettingstarted -n data_driven_schema_configs"


在这个命令中,gettingstarted是集合的名字,-n指定配置集合。Solr默认提供了3个配置集合。这里我们使用的是schemaless,意思是可以提供任意名字的任意列,类型将会被猜测。


防火墙
-A INPUT -m state --state NEW -m tcp -p tcp --dport 8983 -j ACCEPT


http://192.168.1.197:8983/solr



http://192.168.1.197:8983/solr/#/gettingstarted/query




【删除核心】
#su - solr -c "/usr/local/solr/solr/bin/solr delete -c collection2"

【创建核心】

#su - solr -c "/usr/local/solr/solr/bin/solr create -c collection1 -n data_driven_schema_configs"
#su - solr -c "/usr/local/solr/solr/bin/solr create -c collection2 -n data_driven_schema_configs"



----------------------

【配置数据库】


1.进入你的core(如collection1),新建一个目录lib,拷贝mysql-connector-java-5.1.14.jar到新建的lib目录中。


2.从solr的解压文件目录中找到“/example/example-DIH/solr/db/conf”下的db-data-config.xml文件到你的core(如collection1)conf目录下,并更名为data-config.xml。


3.solrconfig.xml
  <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
  <lst name="defaults">
    <str name="config">data-config.xml</str>
  </lst>
</requestHandler>


4.data-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<dataConfig>
<dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://192.168.1.197:3306/test" user="root" password="123456"/><document>
    <entity name="user" query="select * from user">
          <field column="id" name="id" />
         <field column="name" name="name" />
         <field column="password" name="password" />
     </entity>
 </document>
</dataConfig> 
[Core Admin点Reload]


5.
注意5.3managed-schema就代替之前的schema

schema brower可以查看schema


5.拷贝jar
Error loading class 'org.apache.solr.handler.dataimport.DataImportHandler' 
编译的Solr,不会把data import handler的jar文件包含到war文件里面去的。 
解决方法: 
在编译好的Solr文件夹里,我的是/usr/local/solr/solr-5.3.1/dist,找到solr-dataimporthandler-5.3.1.jar和solr-dataimporthandler-extras-5.3.1.jar,
复制到jetty里,也就是前面建立的lib目录。然后重新reload,Solr就没有问题了




6.dataimport
这时候dataimport才有界面




7.【连接sqlserver】注意不支持域名,必须转换为IP
<dataSource type="JdbcDataSource"    
                  driver="net.sourceforge.jtds.jdbc.Driver"    
                  url="jdbc:jtds:sqlserver://211.152.35.122:49151;DatabaseName=YuanBoWebDB;"    
                  user="panqingqiang"    
                  password="514d1b0bfe6432841ac5a38cc7bfe22b"/>


jTDS驱动兼容性问题 
Java连接SQL Server 2000数据库时,有两种方法:


(1)通过Microsoft的JDBC驱动连接。此JDBC驱动共有三个文件,分别是mssqlserver.jar、msutil.jar和msbase.jar。但是Microsoft官网上已经找不到这些驱动,其提供的sqljdbc.jar和sqljdbc4.jar并不支持SQL Server 2000。


驱动程序名称:com.microsoft.jdbc.sqlserver.SQLServerDriver(即下面的classforname)
数据库连接URL:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=dbname(即下面的url)




(2)通过jTDS驱动连接。下载地址:http://sourceforge.net/projects/jtds/files/jtds/


驱动程序名称:net.sourceforge.jtds.jdbc.Driver(即下面的classforname)
数据库连接URL:jdbc:jtds:sqlserver://localhost:1433/dbname(即下面的url)


http://www.cnblogs.com/snake-hand/archive/2013/06/18/3143159.html




8.【查询】
http://192.168.1.197:8983/solr/collection2/select?q=*%3A*&wt=json&indent=true&start=1&rows=5
http://121.43.153.80:8983/solr/collection2/select?q=*%3A*&wt=json&indent=true&start=1&rows=5


-----
9.【分词】
默认Solr提供的分词组件对中文的支持是不友好的,比如:“VIM比作是编辑器之神”这个句子在索引的的时候,选择FieldType为”text_general”作为分词依据时,分词效果是:它把每一个词都分开了,可以想象如果一篇文章这样分词的搜索的体验效果非常差。
能够和Solr集成的中文分词组件有很多,比如:mmseg4j、IkAnalyzer、ICTCLAS等等。各有各的特点。
作者github
https://github.com/chenlb/mmseg4j-solr
上的分享链接,可靠
http://pan.baidu.com/s/1dD7qMFf
mmseg4j-solr-2.3.0.jar
mmseg4j-core-1.10.0.jar


<fieldtype name="textComplex" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="complex" dicPath="dic"/>
</analyzer>
</fieldtype>
<fieldtype name="textMaxWord" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="max-word" />
</analyzer>
</fieldtype>
<fieldtype name="textSimple" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="simple" dicPath="n:/custom/path/to/my_dic" />
</analyzer>
</fieldtype>


TS_Address: '上海大酒店'  这个分词,居然""不会分词





http://www.tuicool.com/articles/67BFFz
68.罐头不如果汁营养丰富,分别在ik和mmseg下比较分词的差异




10.【注意事项】
Solr使用注意事项:
1:连接字符串要用IP,不能使用域名。
<dataSource type="JdbcDataSource"    
                  driver="net.sourceforge.jtds.jdbc.Driver"    
                  url="jdbc:jtds:sqlserver://112.124.235.156:3499;DatabaseName=yuanbodb;"
          


2:需要设置jetty服务器的内存大小
<init-param>
      <param-name>maxCacheSize</param-name>
      <param-value>1024000000</param-value>
</init-param>
默认是256M。


数据超过1万条会产生Java堆溢出错误
/usr/local/solr/solr-5.3.1/server/etc/ webdefault.xml
java.lang.OutOfMemoryError: Java heap space解决方法


静态html admin-extra.html
E:\VM\test_197\solr\solr-5.3.1\example\example-DIH\solr\solr\conf


如果你需要修改官方分词可以修改mmseg4j-core-1.10.0.jar里的data目录下的分词文件words.dic即可

==============================
参考:
CentOS下安装Solr5.3
http://www.centoscn.com/image-text/install/2015/0918/6190.html


linux集群系列(三):Solr全文搜索服务器部署(Solr-5.2.1)
http://blog.csdn.net/jeffsmish/article/details/46533255


适用于 SQL Server 的 Microsoft JDBC Drivers 4.2、4.1 和 4.0
https://www.microsoft.com/zh-CN/download/details.aspx?id=11774
https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=11774


Solr之搭建Solr5.2.1服务并从Mysql上导入数据
http://www.itnose.net/news/157/6310328


solr主从部署
http://my.oschina.net/gaowm/blog/349334


solr连接数据库配置
http://www.cnblogs.com/madyina/p/4133908.html


[solr] - 数据库导入 
http://www.cnblogs.com/HD/p/3981677.html
[solr] - 索引数据删除
http://www.cnblogs.com/HD/p/3981716.html


从mysql数据库中导入数据到solr4.5
http://blog.csdn.net/zhangyaoming2004/article/details/44804987


全文检索引擎Solr系列——整合中文分词组件mmseg4j
http://www.importnew.com/12860.html


chenlb/mmseg4j-solr 
https://github.com/chenlb/mmseg4j-solr


solr中mmseg4j自定义词库配置
http://blog.csdn.net/alen1985/article/details/8501021


Solr4.7.0中整合中文分词mmseg4j-1.9.1 
http://josh-persistence.iteye.com/blog/2050054?utm_source=tuicool&utm_medium=referral


solr4.7中文分词器(ik-analyzer)配置 
http://blog.csdn.net/clj198606061111/article/details/21289897#

作者:21aspnet 发表于2016/1/20 10:41:00 原文链接
阅读:32 评论:0 查看评论

docker使用场景

$
0
0

Docker应用容器相对于 VM 有以下几个优点:

1、启动速度快,容器通常在一秒内可以启动,而 VM 通常要更久

2、资源利用率高,一台普通PC 可以跑上千个容器,你跑上千个 VM 试试

3、性能开销小, VM 通常需要额外的 CPU 和内存来完成 OS 的功能,这一部分占据了额外的资源

因为VM 的 Hypervisor 需要实现对硬件的虚拟化,并且还要搭载自己的操作系统,自然在启动速度和资源利用率以及性能上有比较大的开销。个人体会较深的两处优点:

1、 快速部署,传统的部署模式是:安装(包管理工具或者源码包编译)->配置->运行;Docker的部署模式是:复制->运行。

2、 可以保证线上与测试环境一致,计划以后上线就直接复制测试使用的docker容器)

 

以下内容介绍了常用的8个Docker的真实使用场景,

原文链接:  8 Ways to Use Docker in the Real World

分别是简化配置、代码流水线管理、提高开发效率、隔离应用、整合服务器、调试能力、多租户环境、快速开发。我们一直在谈Docker,Docker怎么使用,在怎么样的场合下使用?也许本文可以帮到你。有需要交流的地方,可以通过评论与我们交流。



几周前我们参加了  DockerCon ,Dockercon 是首个以Docker为中心的技术大会。它面向开发者以及对在Docker开放平台上构建、交付、运行分布式应用感兴趣的从业者,不论这些开放平台是运行 于自用笔记本上或者是数据中心的虚拟机上。我们参加了这次大会,Flux7是Docker基础的系统集成合作伙伴,同时也是演讲嘉宾。


我们的CEO Aater Suleman和我们的一位客户一同进行了演讲。虽然DockerCon大会十分有趣,但我觉得大会太关注Docker的具体细节,而忽略了Docker的使用场景。所以,在这篇文章中,我想介绍并分享一些Docker的实际应用案例。 

在我们讨论Docker的使用场景之前,先来看看Docker这个工具有什么特别的地方吧。 

Docker提供了轻量级的虚拟化,它几乎没有任何额外开销,这个特性非常酷。 

首先你在享有Docker带来的虚拟化能力的时候无需担心它带来的额外开销。其次,相比于虚拟机,你可以在同一台机器上创建更多数量的容器。 

Docker的另外一个优点是容器的启动与停止都能在几秒中内完成。Docker公司的创始人 Solomon Hykes曾经介绍过Docker在单纯的LXC之上做了哪些  事情,你可以去看看。 

下面是我总结的一些Docker的使用场景,它为你展示了如何借助Docker的优势,在低开销的情况下,打造一个一致性的环境。

 

1. 简化配置

 

这 是Docker公司宣传的Docker的主要使用场景。虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台(软件、系统),Docker在降 低额外开销的情况下提供了同样的功能。它能让你将运行环境和配置放在代码中然后部署,同一个Docker的配置可以在不同的环境中使用,这样就降低了硬件 要求和应用环境之间耦合度。

 

2. 代码流水线(Code Pipeline)管理

 

前一个场景对于管理代码的流水线起到了很大的帮助。代码从开发者的机器到最终在生产环境上的部署,需要经过很多的中间环境。而每一个中间环境都有自己微小的差别,Docker给应用提供了一个从开发到上线均一致的环境,让代码的流水线变得简单不少。

 

3. 提高开发效率

 

这就带来了一些额外的好处:Docker能提升开发者的开发效率。如果你想看一个详细一点的例子,可以参考Aater在  DevOpsDays Austin 2014 大会或者是DockerCon上的演讲。 

不同的开发环境中,我们都想把两件事做好。一是我们想让开发环境尽量贴近生产环境,二是我们想快速搭建开发环境。 

理 想状态中,要达到第一个目标,我们需要将每一个服务都跑在独立的虚拟机中以便监控生产环境中服务的运行状态。然而,我们却不想每次都需要网络连接,每次重 新编译的时候远程连接上去特别麻烦。这就是Docker做的特别好的地方,开发环境的机器通常内存比较小,之前使用虚拟的时候,我们经常需要为开发环境的 机器加内存,而现在Docker可以轻易的让几十个服务在Docker中跑起来。

 

4. 隔离应用

 

有很多种原因会让你选择在一个机器上运行不同的应用,比如之前提到的提高开发效率的场景等。 

我们经常需要考虑两点,一是因为要降低成本而进行服务器整合,二是将一个整体式的应用拆分成松耦合的单个服务(译者注:微服务架构)。如果你想了解为什么松耦合的应用这么重要,请参考Steve Yege的  这篇论文,文中将Google和亚马逊做了比较。

 

5. 整合服务器

 

正如通过虚拟机来整合多个应用,Docker隔离应用的能力使得Docker可以整合多个服务器以降低成本。由于没有多个操作系统的内存占用,以及能在多个实例之间共享没有使用的内存,Docker可以比虚拟机提供更好的服务器整合解决方案。

 

6. 调试能力

 

Docker提供了很多的工具,这些工具不一定只是针对容器,但是却适用于容器。它们提供了很多的功能,包括可以为容器设置检查点、设置版本和查看两个容器之间的差别,这些特性可以帮助调试Bug。你可以在  《Docker拯救世界》的文章中找到这一点的例证。

 

7. 多租户环境

 

另 外一个Docker有意思的使用场景是在多租户的应用中,它可以避免关键应用的重写。我们一个特别的关于这个场景的例子是为IoT(译者注:物联网)的应 用开发一个快速、易用的多租户环境。这种多租户的基本代码非常复杂,很难处理,重新规划这样一个应用不但消耗时间,也浪费金钱。 

使用Docker,可以为每一个租户的应用层的多个实例创建隔离的环境,这不仅简单而且成本低廉,当然这一切得益于Docker环境的启动速度和其高效的  diff命令。 

你可以在  这里了解关于此场景的更多信息。

 

8. 快速部署

 

在虚拟机之前,引入新的硬件资源需要消耗几天的时间。Docker的虚拟化技术将这个时间降到了几分钟,Docker只是创建一个容器进程而无需启动操作系统,这个过程只需要秒级的时间。这正是Google和Facebook都看重的特性。 

你可以在数据中心创建销毁资源而无需担心重新启动带来的开销。通常数据中心的资源利用率只有30%,通过使用Docker并进行有效的资源分配可以提高资源的利用率。

 

http://sungyang.iteye.com/blog/2271917



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



关于物联网的标准之争,你需要知道这些

$
0
0

关于物联网的标准之争,你需要知道这些

作为一年一度的科技风向标,CES告诉我们2016会是物联网之年(也有人说是虚拟现实之年)。不过虽然市面上已经有了智能车门,智能洗衣机,智能门铃,智能灯,而且越来越多的日用品会智能化,但它们还没真正互联,不能实现智能家庭的愿景。

随着智能手机市场的饱和,许多公司也转向做物联网产品,谁做的好谁又能吸引到更多消费者,这都很难说。要了解发展状况,需要知道下面的信息。

标准之战

如果每个公司都遵循同样的标准,消费者应该也会更好过些,但现实不是这样。每个标准都有优缺点,而市场也会逐渐选择使用最多,布置方便,兼容性最强的一个。就是在移动操作系统上Android的发展状况一样。不过物联网的情景不同,开发芯片组,系统和应用的公司都有,还有一些联盟想制定物联网设备的通讯标准。

芯片

关于物联网的标准之争,你需要知道这些

旧式家电都没有蓝牙,WiFi这样的功能,但没有电子元件,家里的产品都无法智能化。公司无论大小,都在开发自己的平台和芯片,其中比较知名的有三星的ARTIK平台,英特尔的IoT平台,联发科的LinkIt Smart 7688以及高通的开发者平台。其它公司则想把自己的芯片放在产品中,如Marvell,博通,Atmel和GainSpan等。

谁能胜出很大程度上取决于谁能找到更好的伙伴,这也让三星,英特尔和高通处于更有利的位置。不过我们也看到苹果A9芯片门让三星在短期内失去了信任,所以随着竞争的加剧,其它公司也有机会。

操作系统

在芯片之后是操作系统。桌面和移动端有微软和Google,在物联网上则出现了Google的Brillo,华为的Lite OS和联发科的OpenWRT。还有一些不太知名的公司,比如MicroEJ为嵌入式系统开发操作系统多年,现在看准了物联网。上述的所有系统都基于现有平台进行了改良,以适用低功耗的硬件;而且都是开源的,想吸引更多人使用。

通信技术

除了系统,进入物联网还可以从通信技术上着手。苹果的HomeKit,LG的LINK,华为的HiLink和Google Weave,通过这些技术框架,物联网设备的手机之间可相互通信。

协议

设备间的通信还需要蓝牙,WiFi和NFC等通信协议,而且这几个已经通过智能手机被我们熟知了。但这就够了?蓝牙和NFC由于传输距离短,很可能不适用智能家居,WiFi是个不错的选择,而且已经应用在了不产家电产品中,但随着设备的增多,信号干扰会是个大问题。

还有一些协议可选,如ZigBee,Z-Wave,EnOcean和DECT。这些协议背后都有自己的支持联盟,以及一些公司,而且都认为自己的协议是物联网的最好选择。标准之争日趋激烈,但最终只会有一两普及。

一个设备控制所有

大公司争夺的焦点在芯片和操作系统,而设备制造商OEM们想的是消费者用自己的手机应用控制所有产品。我们应该会看到新设备与旧设备某种整合,使用这些设备的家庭也会越来越智能,但若要做到真正的设备互联,可能还需要一段时间,除非你愿意一直买同一家公司的产品。

比如三星,它推出的新的冰箱和SUHD TV电视,不能用你现在的手机和电脑控制,而是非得用他们的高端设备才行。有些公司则比较开放,如LG与Google合作,用后者的Weave技术,从而让认证的设备都能兼容。

物联网安全

物联网的未来很吸引人,但也带来了安全隐患。如果家里的所有设备都能联网,那网络安全就是不得不考虑的事。目前已经有了一些标准的设备管理和认证方法,比如用密码和指纹信息等,但一些公司想用更独特的方法解决这一问题。

关于物联网的标准之争,你需要知道这些

在企业端,PFP Cybersecurity能帮助公司检测软件中的零日漏洞。他们开发了一个外部功率监测器,能检测出细微的异常状况(与系统上线前的情景进行对比),从而确定系统是否被更改。这家公司还想与芯片厂商合作,将功率检测芯片嵌入物联网设备的主板。

在消费端,好消息是你还不用过于担心自己的敏感信息,会被家里的智能空调智能咖啡机什么的盗走,但恶意入侵还是会控制智能设备,让他们在你外出的时候运转,轻则费电,重则造成人员损伤。这听起来有点危言耸听,但已经有人入侵了汽车,想想如果对方在你开车时做什么小动作……

Domotz公司主要做智能设备监控服务,公司的产品能监测所有接入家庭网络的设备,并进行连接和警报设定。例如,用户可以通过手机开/关家里的电器,甚至还能在远程协议打开后,远程控制家里的电脑。随着物联网设备的普及,还会有更多类似的公司出现,是个不错的创业方向。

今年应该会有更多的物联网产品出来,不过如果你并不是新技术的狂热爱好者,倒是可以等一段时间再看什么值得买,等到技术标准统一,各产品的通信更简单,否则单一的产品容易让人生腻。

via xda


腾讯林松涛:微信应用号与应用宝“应用+”理念一致

$
0
0

1月17日,2015腾讯应用榜样·应用宝星APP之夜在北京工人体育馆隆重上演,腾讯应用宝在发布2015星APP全民榜的同时,腾讯副总裁林松涛也对移动互联网未来形态做出预测。他表示,应用宝“应用+”与微信应用号理念一致,都提供用户直接触达APP内服务,未来APP形态或被打破。

林松涛认为,移动互联网的的本质并不是APP,而内容和服务才是根本。“所谓APP本身的壳,只是一个形式不是一个目的,也不是用户的诉求。用户用你的APP不是想用这个APP本身,是要你提供的内容和服务。”林松涛表示。

腾讯林松涛:微信应用号与应用宝“应用+”理念一致

图1:腾讯公司副总裁林松涛

1月11日,在“2016微信公开课pro版”现场,张小龙表示微信正在构思推出一种全新的公众号形态“应用号”,取代一些现有APP。应用号推出后,用户关注一个公众号就像安装了一个APP。这样可以让现有更多的APP有一种更轻量的形态,更换手机也云端保存永不丢失。

在林松涛看来,微信应用号与应用宝“应用+”的产品理念十分一致,两者都是把开发者的内容、服务直接触达给用户,缩短用户诉求和用户之间的距离,无需用户去下载APP。而从这一模式而言,未来两款产品在应用分发层面会有很多联动机制。

“在移动应用市场应用分发与推广成本越来越昂贵的行业环境下,应用宝‘应用+’与微信应用号可有效帮助应用开发者减少推广成本并提升分发率。”林松涛表示“应用+”和微信应用号都将使APP营销推广事半功倍。

据透露,截至1月份,在“应用+”内测3个月以来,共有22个类目的上千家开发者申请加入合作,开发者提交的内容数量超过1亿条,涵盖类型包含视频、音乐、壁纸主题等线上类目,以及美甲、美发、保洁等生活服务类目。

目前“应用+”已经覆盖搜索、优惠、抢鲜等多个场景,已为开发者们总体带来了1.5亿的精准用户流量,分发效率也比传统应用分发提高35%。通过“应用+”,APP激活转化率提升超过50%。而某阅读类APP在接入“应用+”后,日下载从3万提升至12万。

2016年,“应用+”将会为开发者开放更多流量场景,重点建设平台内容闭环及4大中心化场景:包括游戏周边信息、生活服务、泛娱乐内容优惠折扣等场景。2016年,预期“应用+”将为开发者带来数十亿的流量。

“通过‘应用+’,用户是通过看内容、服务去找APP,这对APP的分发效率是非常大比例的提升,单个应用的推广成本也大比例下降,而未来应用宝还将帮助开发者打造社交渠道的传播模式,这开发者来说在获得一个用户之后,将产生自来水一样的效应,让用户帮助开发者更好的传播,这个是我们未来的方向,且不会向开发者收费,会帮助开发者成倍数降低推广成本。”林松涛表示。


浅谈CLOSE_WAIT

$
0
0

TCP 有很多连接状态,每一个都够聊十块钱儿的,比如我们以前讨论过 TIME_WAITFIN_WAIT1,最近时不时听人提起 CLOSE_WAIT,感觉有必要梳理一下。

所谓 CLOSE_WAIT,借用某位大牛的话来说应该倒过来叫做 WAIT_CLOSE,也就是说「等待关闭」,如果你还不理解其含义,可以看看 TCP 关闭连接时的图例:

TCP Close

TCP Close

不要被图中的 client 和 server 所迷惑,你只要记住:主动关闭的一方发出 FIN 包,被动关闭的一方响应 ACK 包,此时,被动关闭的一方就进入了 CLOSE_WAIT 状态。如果一切正常,稍后被动关闭的一方也会发出 FIN 包,然后迁移到 LAST_ACK 状态。

通常,CLOSE_WAIT 状态在服务器停留时间很短,如果你发现大量的 CLOSE_WAIT 状态,那么就意味着被动关闭的一方没有及时发出 FIN 包,一般有如下几种可能:

  • 程序问题:代码层面遗漏或者死循环之类的,没有 close 相应的 socket 连接。
  • 响应太慢:对方已经 timeout 了,本方还忙于耗时逻辑,导致 close 被延后。
  • BACKLOG 太大:队列堆积严重,导致多余的请求来不及消费就被关闭了。

如果遭遇了 CLOSE_WAIT 故障,那么需要立刻通过「netstat」或者「ss」命令来判断 CLOSE_WAIT 连接是哪些进程引起的,如果是我们自己写的一些程序,比如用 HttpClient 自定义的蜘蛛,那么八九不离十是忘记了 close 相应的 socket,如果是一些使用广泛的程序,比如 Tomcat 之类的,那么不太可能是它们自身的 BUG,更可能是响应速度太慢或者 BACKLOG 设置过大导致的故障。

此外还有一点需要说明:按照前面图例所示,当被动关闭的一方处于 CLOSE_WAIT 状态时,主动关闭的一方处于 FIN_WAIT2 状态。 那么为什么我们总听说 CLOSE_WAIT 状态过多的故障,但是却相对少听说 FIN_WAIT2 状态过多的故障呢?这是因为 Linux 有一个「tcp_fin_timeout」设置,控制了 FIN_WAIT2 的最大生命周期。坏消息是 CLOSE_WAIT 没有类似的设置,如果不重启进程,那么 CLOSE_WAIT 状态很可能会永远持续下去;好消息是如果连接开启了 keepalive机制,那么可以通过对应的设置来清理无效连接,不过 keepalive 是治标不治本的方法,还是应该对照前面的解释找到问题的症结才对。

本来想多写点的,但是着急下班回家,就写到这吧,结尾推荐两个案例:

写得都比我好,建议大家仔细阅读。

 

Edelman:调查显示人们更信任谷歌新闻

$
0
0

据国外媒体报道, 全球最大公关公司爱德曼(Edelman )连续第二年的调查显示,与新闻媒体相比,越来越多的人相信来自谷歌的新闻,而谷歌不过是对前者的新闻报道进行了聚合。爱德曼对28个国家的3.3万人进行了调查,发现在世界各地,63%的人称他们信任来自“搜索引擎”的新闻和信息,只有53%的人信任“纯在线媒体”。

33a2c5490e899b1

这一结果意味着,就同一篇新闻报道,读者更容易信任他们在谷歌新闻聚合服务上所读到的,而不是首先发表了该新闻报道的网站。这对新闻行业来说不是令人鼓舞的好消息。

造成这一差距的原因之一,可能是新闻媒体针对特定平台对新闻报道进行了包装的这种做法。如针对搜索引擎的新闻报道进行了优化处理,通常篇幅较短和以事实为主(因此更受信任),而在其它平台上共享的新闻报道——特别是社交媒体——则更加方便人们从不同角度对其进行阐述。目前,Facebook成为出版商最大的流量来源,领先于谷歌的搜索引擎。

爱德曼的调查报告还发现,78%的人信任经他们的朋友或家人转发的新闻。相比之下,65%的人相信经学术专家转发的新闻,只有44%的人相信经记者转发的新闻。

将媒体信任度低放在一边,本次调查可以说是证明了谷歌的营销能力。谷歌经常评为最值得信赖的公司之一,在2015年一次调查中被评为全球第二高信誉的公司,仅次于宝马(领先于迪斯尼、苹果和微软等公司)。

当然,谷歌不是唯一的搜索引擎。但根据市场研究公司comScore的数据,谷歌掌控了美国互联网搜索市场64的份额。雅虎和微软的Bing搜索服务远远落后。在美国之外的地区,谷歌在搜索市场上所占份额更大,雅虎和Bing都不太受欢迎的。

自 网易科技

您可能也喜欢的文章:

谷歌新闻实验室:大数据帮你更好地讲故事

Forrester:调查显示谷歌眼镜或成为下一个iPhone

谷歌:调查显示日本智能手机用户最活跃

Yankee Group:调查显示苹果和谷歌智能手机市场行情最理想

谷歌眼镜将引发“第一人称视角”新闻革命
无觅

2015年度中国大数据最具创新场景应用服务提供商排行榜

$
0
0

伴随着互联网的深度发展,巨大的信息流背后产生的海量数据成为一块蕴含财富的宝藏。数据之所以成为新商业经济社会的必争之物,在于它实际场景中的应用价值。数据只有被应用到具体的商业场景和产业生产中才具有价值和意义,企业之所以将建立的交易数据库、客户数据库等视为企业核心竞争力,是因为得数据者得用户、得用户者得天下。企业之间的激烈竞争是商业社会优胜劣汰的必经之路,如何利用好数据,将之转化为有价值的数据财富应用到产业化场景中,是当下企业建立竞争壁垒的首要问题。

以下是互联网周刊发布的《2015年度 中国大数据最具创新场景应用服务提供商》,该榜单整理的是国内最具创新能力的大数据应用服务型企业,从四个维度:技术能力,服务能力,发展潜力以及市场影响力对创新能力进行了综合评分。当然,该排名并不能保证百分百的全面与精准,该榜单梳理的目的主要是帮助大数据生态产业链条上的各家公司更好的发展,以及大数据所服务的客户更加了解大数据。

3417eb9bbd9018068e9018
大数据赋予精准营销能量无穷大

每一次营销,都将形成循环效果。通过定位用户群、分析用户内容偏好、分析用户行为偏好、建立受众分群模型、制定渠道和创意策略、试投放并收集数据、优化确定渠道和创意、正式投放并收集数据、实时调整投放策略、完成投放评估效果等,完整的数据应用过程不断把控营销质量与效果,实现从效果监测转向效果预测。

借助大数据及相关技术,我们可针对不同行为特征的客户进行针对性营销,甚至能从将一个产品推荐给一些合适的客户到将一些合适的产品推荐给一个客户,得以更聚焦客户,进行个性化精准营销。国内在用大数据做精准营销方面最具创新性的企业有集奥聚合,品友互动,百分点,亿赞普,晶赞科技等。

还有,电商是最早利用大数据进行精准营销的行业,除了精准营销,电商可以依据客户消费习惯来提前为客户备货,并利用便利店作为货物中转点,在客户下单15分钟内将货物送上门,提高客户体验。马云的菜鸟网络宣称的24小时完成在中国境内的送货,以及京东刘强东宣传未来京东将在15分钟完成送货上门都是基于客户消费习惯的大数据分析和预测。

电商可以利用其交易数据和现金流数据,为其生态圈内的商户提供基于现金流的小额贷款,电商业也可以将此数据提供给银行,同银行合作为中小企业提供信贷支持。由于电商的数据较为集中,数据量足够大,数据种类较多,因此未来电商数据应用将会有更多的想象空间,包括预测流行趋势,消费趋势、地域消费特点、客户消费习惯、各种消费行为的相关度、消费热点、影响消费的重要因素等。依托 大数据分析,电商的消费报告将有利于品牌公司产品设计,生产企业的库存管理和计划生产,物流企业的资源配制,生产资料提供方产能安排等等,有利于精细化社会化大生产,有利于精细化社会的出现。

大数据更懂交通出行

最近两年,智能手机中的公众交通出行应用软件开始得到发展,大数据不仅会便利在线打车企业,还可以为智慧城市的规划带来积极影响。根据大数据反映的城市各路段拥堵情况、出租车行驶路线的分布等情况,城市管理部门可以更合理地规划城市道路和交通管理方案。

值得注意的是,虽然打车软件企业都在挖掘大数据的价值,但这一市场也在呈现差异化的特点。例如,目前优步青睐派单制,而滴滴快的则大力推广抢单制。究竟哪种方式更适应用户需求,或许还需市场检验。

从整体上看,今年随着打车软件新的解决方案的集中推出,基于大数据的应用也不断增长。可以预见的是,今后围绕着出行大数据的创新和市场竞争的增强,公众出行也会获得更多丰富的选择和便利。

数据安全问题刻不容缓

随着大数据时代的到来,数据的收集和存储更加方便,同时跨境流动更加频繁,安全问题愈发凸显。我国在发展大数据产业的过程中,部分企业收集用户数据的使用权限边界界定不清,用户隐私和权益遭受侵害,一些重要数据被非法倒卖、流向他国,安全威胁已经从个人层面上升至国家安全层面,亟须引起重视。在大数据面前,既要贴心的服务又要保护隐私,这是一个两难的境地。

在传统的PC互联网时代,电脑连接还有明显的边界,需通过线缆连接,这时的安全问题可以靠防病毒、查杀流氓软件、防火墙等进行防御;但进入到互联网新阶段,特别是移动互联网时代,手机等终端打破了对网络边界的定义,手机和个人隐私信息联系在一起,安全问题变得更加严重。

目前用户数据的收集、存储、管理与使用等均缺乏规范,更缺乏监管,主要依靠企业自律,用户无法确定自己隐私信息的用途。而且国内大数据的安全应用仍多停留于想法,仍需进一步探究如何落地。大数据首先应建设一套规范且灵活的建设标准与运行机制,规范化建设可以促进大数据管理过程的正规有序,实现各级各类信息系统的网络互联、数据集成、资源共享,在统一的安全规范框架下运行。

大数据技术的发展带来企业经营决策模式的转变,驱动着行业变革,衍生出新的商机和发展契机。驾驭大数据的能力已被证实为领军企业的核心竞争力,这种能力能够帮助企业打破数据边界,绘制企业运营全景视图,做出最优的商业决策和发展战略。其实,不论是哪个行业的大数据分析和应用场景,可以看到一个典型的特点还是无法离开以人为中心所产生的各种用户行为数据,用户业务活动和交易记录,用户社交数据,这些核心数据的相关性再加上可感知设备的智能数据采集就构成一个完整的大数据生态环境。

目前大部分企业面临的挑战不是缺少数据,而是数据太多。通过新型技术,大数据价值可能被充分地挖掘和利用。大数据要发挥价值,必须与业务融合,能够带来实际业务收益。大数据的应用模式仍处于探索阶段。

您可能也喜欢的文章:

数据客:中国大数据2015大事记

艾瑞咨询:2014年12月热门网络服务数据排行榜Top10

中国移动社交服务综合实力排行榜——信息图

App Annie:2014年7月中国iOS发行商和应用排行榜

2012年第18周中国Android应用下载排行榜
无觅

关于企业服务的投资逻辑,你不得不听的6点干货

$
0
0
本文为我在36 氪股权融资平台《投资分享会》栏目上的分享。
 
我们很关注的一个市场是企业服务,在这一块有非常多的投资布局,例如美味不用等、闪电报销、法大大、易创互联、xberts等等,总计估值达到了数十亿人民币。
 
下面我讲讲我为什么这么看重企业服务这块市场以及我们的投资逻辑。
 
企业服务市场非常重要,主要有以下几个原因:
 
1、市场空间巨大:我们主要来和美国对比。对比Google、Amazon、Facebook和百度、阿里、腾讯,可以看到美国和中国to C领域的巨头规模基本一致。而美国2c和2b的公司市值总体差距不大,美国云端的企业服务市场年增速为47%,领军企业都是几十亿美金的收入和几百亿市值,而中国的玩家还是传统的像用友、金蝶这样缺乏互联网基金的公司,市场空间巨大。
 
2、具备未来独角兽群的潜力:在不长的时间内,美国出现了一批重量级的互联网巨头公司,例如互联网女皇Mary Meeker在今年的互联网报告里就用多个篇幅提到了企业服务领域,以及里面的多个行业领导公司Stripe、Zenefits、Docusign、Slack、Domo、Square、Intercom、Gainsight、Directly、Anaplan、Greenhouse、Checkr、GuideSpark、Envoy,涉及支付、HR、电子签名、IM、招聘等多种服务,这些公司的规模都将达到或者超过独角兽级别。
 
3、中国市场环境适宜:中国中小企业数量巨大,跨越多个行业。为这么多企业去提供企业服务来建立商业模式,具备非常好的市场土壤。
 
为什么企业服务现在有一个很好的市场发展机会?
 
我分析是整体市场环境导致的。
 
第一,之前中国商业环境恶劣以及市场化程度低,巨大的企业需求被压抑。
 
第二,各种云服务、大数据、BI技术的兴起,具备了SAAS成熟的基本土壤。
 
第三,传统软件售卖模式的消亡。像以前卖PC机要经过代理商,经销商,现在基本都是通过直销的形式。软件也是一样,互联网改造了中间环节,带来了更低的费用、更好的服务,所以以前软件售卖的形式日渐式微,让软件服务商可以直接售卖产品和服务。
 
第四,to C产品的迭代。iPhone的出现教育了用户,原来系统和应用可以做的这么美,这么好用。用户的诉求倒逼企业期望获得更好的服务和更好的软件。
 
第五,全社会被服务意识的增强。激烈的竞争让服务提供者越来越为消费者提供更好的服务,像海底捞精神深入人心,改造了整个传统服务行业。消费者都希望得到更好的体验和服务,企业也是一样。
 
第六,传统企业的互联网化诉求加速。触网、智能化、云化,将变成传统企业普适性的需求。
 
企业服务包括SAAS、LAAS、PAAS,ERP、BI,开发服务、招聘服务、运维服务、HR服务,B2B交易平台等多种类型。今年我们看到了针对各行各业的SAAS。针对行业的有针对医院、健身房、水果店、电影院、彩票店、P2P的SAAS,功能性的有报销、销售、HR、招聘、客服等SAAS,像我们之前投资的法大大是电子合同的SAAS,闪电报销则是费用管理SAAS。下面我来深入谈谈时下比较火热的SAAS领域。
 
虽然趋势相近,但是在中国,SAAS发展和美国还有一些不一样的地方。美国市场环境好,收费容易,平台容易做大,像DocuSign在美国服务的价格每月从15到20美元不等,Slack每年每用户平均收费100美元,Zendesk每月的服务费从9到59美元不等,所以它们的年营收很容易做到几千万美元的规模。而在中国需要一个建立信任到付费意愿增强的培养过程,也因为中国行业竞争激烈,所以很多企业服务企业最开始都用免费或者低价的服务来获取用户,积累用户到一定数量后,再利用融资来加快发展。从这个情况来看,中国建立商业模式和获得营收比美国更难,压力比美国更大。例如法大大目前通过非常低的价格来提供优质服务,而美味不用等也并不针对餐厅进行收费。
 
做SAAS产品,有一些重要建议
 
如果是功能性SAAS,需要先针对垂直行业进行拓展和挖掘,摸透行业的场景和拓展模式,把某个行业做专做深,例如法大大首先针对的就是互联网金融行业,目前已经接入了多个行业的领军企业客户。SAAS最开始获取收入较难,但是长期爆发力很强。建立品牌和壁垒之后,SAAS黏性强,规模化快,以及边际成本低的优势就能够体现出来,议价能力以及获取收入的能力会出现大幅度上升,从而业绩会有一个飞速的增长。
 
行业的会议营销是很好的推广途径。例如HR的行业会议、健身行业的行业会议、餐饮行业的行业会议等等。线下要建立一支强大的销售铁军,服务和产品模式是SAAS模式,但是推广的渠道和方式却应该像传统软件一样来做。1000家有效客户是一个坎,翻过这个坎之后客户数量增加就会加速,所以非常重要的就是这第一个阶段。
 
如何判断好的SAAS服务和企业?
 
除了通常的创始人、团队等基本判断条件,还要看几点。
 
第一,和生活服务高频打低频一样,好的企业服务很重要的一个特点同样是高频,例如合同、招聘、报销、销售、客服,都是高频行为,而注册公司、法律服务、商标注册就是低频行为。高频SAAS的黏性以及价值一定大于低频SAAS。
 
第二,SAAS不仅只是传统行业的互联网化和线上化,而是要切实给企业带来实际利益和价值。例如要帮他们去改造供应链,提供新的业务流程,或者降低运营成本,这才能构成核心竞争力和壁垒。
 
第三,满足核心指标。优秀的SAAS服务必须要有极低的客户流失率即极高的客户留存率,这样他们能够持续在单个用户上获取大量的收入,随着用户规模的上升,收入就会出现高速的增长。涉及的核心指标包括CLV(Customer LifeTime Value,客户终身价值),Churn(客户流失率),MRR(MonthlyRecurring Revenue,客户每月收入)以及CAC(CustomerAcquisition Cost,客户获取成本),优秀的SAAS服务客户流失率通常在5%以下,而像Slack甚至低于2%!
 
不太成功的SAAS除了上述提到的服务本身低频,客户流失率高等原因之外,还具有一个特点,就是针对的细分市场规模太小。SAAS本身针对的行业和客户与传统软件基本一致,如果在某个行业只有几家规模很小的软件公司,该行业也不太适合SAAS企业。以功能而论,市场规模公认最大的几个SAAS类别分别是销售、HR服务、IT支持、财务以及营销,这几个方向分别诞生了Salesforce、LinkedIn、Zendesk等巨头公司。以垂直行业而论,医院、便利店、健身工作室都算是市场空间较大的行业,但是如果针对电影院的SAAS就值得商榷。
 
在商业模式的建立方面,企业服务除了本身服务的收费,还有很多想象空间。
 
B2B2C是可行的,企业服务可以先切to B的市场,再利用b端的企业资源拓展C端用户,最后通过C端用户来建立商业模式。例如美味不用等利用餐厅来获取C端用户,青橙科技则通过健身房和健身教练来积累健身爱好者。
 
供应链金融:ERP、B2B交易平台一个最大想象空间是供应链金融,即为所服务的企业提供各种增值的金融服务,这块市场空间和需求很大。
 
以切入点构建服务体系:企业服务有可能作为抓手,获取客户后再提供衍生的增值服务,例如法大大是以电子合同为切入点,最后提供衍生的法律服务。美味不用等以排队来切入,最后来为餐厅提供一整套智能化的解决方案。
 
Q&A
 
主持人:请问蒋总,以您个人的投资经验来看更偏向于投资哪类行业呢?另外,您是怎样看待O2O这个行业呢?
 
蒋宇捷:从个人兴趣爱好、投资经历来看,我个人比较关注投资企业服务、生活服务O2O以及互联网金融的项目,企业服务的有美味不用等、法大大、闪电报销等,O2O的有拼豆夜宵、婚趣网等,互联网金融有财鱼管家、很帅的投资客等。企业服务的投资方法我前面阐述过了,互联网金融是一个大市场,而O2O方向主要是我个人有美食、旅游等方面的爱好。
 
O2O是一个很大的领域,只要是线上线下结合的产品都可以划在里面,包括电商、旅游、教育、医疗等等,所以这里面的市场太大,机会太多了,无论是传统行业的互联网化,还是二三线城市的互联网化,都有巨大的想象空间。
 
主持人:拿美元投资和人民币投资有什么区别吗?是否会涉及将来的退出的方向呢?
 
蒋宇捷:拿美元和人民币肯定是有差别的。一是涉及项目在国内还是国外退出的问题;二是估值模型不太一致,不同项目在国内外资本市场的被接受程度完全不一样,通俗的话说就是在中国和美国值不一样的价钱;三是品牌效应不一致,中国股民对于A股公司是耳熟能详,但是了解的美股、港股公司就寥寥无几了,这和众筹能带来广告效应和忠实粉丝是一样的道理;
 
主持人:您是百度魔图的创始人,那您觉得百度魔图的成功最重要的是靠什么呢?
 
蒋宇捷:创业最重要要看大势,合适的时间点做适合的事情。我最近有一篇文章,是建议创业者要蓝海创业,风口之前创业,这篇文章可以在信天创投的公众号里面看到。魔图最重要是找准了风口,提前看到了2010年智能手机出货量快速上升的趋势,用户用智能手机生产消费图片的巨大市场,魔图用产品解决了这海量的需求。好的市场环境+强烈用户需求+优秀的产品,我认为这是魔图能够快速积累过亿用户,取得成功的关键。
 
主持人:这个问题也是大家普遍关心的问题,就是目前项目普遍的估值较高,您是怎样看待项目估值的问题呢?
 
蒋宇捷:早期投资有一个说法,只有投错了的项目,没有投贵了的项目。从基金的历史统计来看,只有大部分投到了独角兽级别公司的基金才能保持非常高的收益率,没有投到独角兽的基金在资本回报以及影响力上都很难谈的上成功,所以投资本质是在发掘独角兽的过程,是在用少部分项目的成功获取高额回报的过程。所以由此推导出来的基金投资逻辑就是一方面要广泛布局,提升投中独角兽的概率;第二就是项目质量与是否成功是key point,而估值相比起来重要程度更低,所以在投资机构看来,对好的早期项目,一定范围内的估值浮动是可以接受的;第三,估值后继可以动态调整和修复,但是投入的资金往往是不可能收回的,所以基金往往会在估值浮动的情况下,固定投资金额来减小风险。目前早期项目估值偏高的原因是一是基金竞争的加剧,二是之前二级市场的火爆具有向一级市场的传导性。但是对于创业者来说,估值其实没那么重要,估值只是一时账面上的浮盈,大部分项目的结果都是失败,真正决定创业者和公司价值的是最后是否能取得成功。早期估值高一点与项目是否成功,以及最后能获得多少回报之间相关性非常弱,所以踏踏实实的融资其实对创业者心态和业务发展更有利。
 
主持人:现在创投圈寒冬将至的说法让创业者人心惶惶,您是怎么看待今年下半年到明年的创投市场呢?为什么?
 
蒋宇捷:我要强调的是第一创业不是一件好玩的事,需要全身心投入和认真对待;第二是成功的比例低的可怕,但是很多创业者经常忽视这一点,现在的很多创业想法有点过于盲目和逐利了。目前的创投市场的确投资机构会更加谨慎,会控制所投项目数量和金额,但是我认为这反而是一件好事。市场冷静下来能让良币驱逐劣币,能让真正好的产品脱颖而出,价值得到体现,真正有价值的产品是一定会得到认可的,所以如果创业者很踏实,业务也做的实实在在,就不需要有任何担心,一是这样的企业仍然能够获得投资机构的认可,另外就是那些靠概念和忽悠的竞争者会难以为继,这对于好项目来说反而是最大的利好。我们自己仍然会坚持价值投资的理念,继续在市场中投资优秀的项目。
 
自由提问
 
网友提问:1、空间域名算是最传统的2B领域了,而国内之前的百花齐放激烈竞争的状态已经如何了呢?2、连BAT都开始打价格战+服务增值战的时候,中小2B创业企业如何避免在传统的或者新的2B领域被BAT吃掉?3、就客户资源而言,BAT级公司可以把各种2B服务无缝切换到自身的B和C端客户以增加黏性,那缺乏客户资源的初创中小SAAS企业如何能够做大做强?
 
蒋宇捷:to B市场和to C一样是一个大市场,这里面存在众多的细分服务。这个问题我也在最近的一篇文章中有过讨论,就是如何选择赛道和巨头竞争,重点就是不要切入巨头横向切入门槛低的赛道创业,例如出行领域的滴滴打车、硬件领域的小米。但是BAT是不是什么都能做?答案是否定的,针对细分场景、针对细分人群、针对细分行业的企业服务是BAT所不能顾及的领域。BAT不可能把Stripe、Zenefits、Docusign、Slack、Domo、Square、Intercom、Gainsight、Directly、Anaplan、Greenhouse、Checkr、GuideSpark、Envoy都做了,但是这些企业成长为独角兽之后,就有了很大的护城河和行业壁垒。上面是竞争问题。初创中小SAAS企业如何能够做大做强,这个话题就非常大了,能够单独写一篇文章。
 
网友提问:现在犹如百花齐放的创业界,最后谁能结出果实还未可知,然而机构和散户个人的消息具有不对等性,作为个人跟投的小散,怎么能筛选出有价值的标的呢?
 
蒋宇捷:这个问题也很巧,我在杭州的众筹论坛上讲过。作为跟投者,我也投了fir.im和麦客这两个项目。因为一方面这些项目有一流机构的背书,另一方面我自己基本每天都在使用他们的产品,对他们非常熟悉了解。所以我觉得作为跟投人,一方面看领投方的影响力和知名度,一方面投资自己更加熟悉的领域和产品,这可能更加合适。
作者:hfahe 发表于2016/1/20 16:00:07 原文链接
阅读:253 评论:0 查看评论

Chrome采用新压缩算法 提升网页加载速度降低数据流量消耗

$
0
0

谷歌Chrome浏览器很快就会提升网页加载速度并且降低数据流量消耗,这要归功于公司引进的Brotli压缩算法。Brotli压缩算法始于去年九月。谷歌声称和使用已经达到3年时间的Zopfli算法相比,它可以将数据压缩率继续提升26%,谷歌表示,Brotli还可以帮助降低移动设备的电池使用量,达到省电目的。

据谷歌表示,Brotli是一个全新的数据格式,在包装中容纳更多数据,而解压缩速度和其他算法大致相同。如果你希望现在就用上Brotli压缩算法,那么可以下载 Chrome Canary版(谷歌用于测试新功能的浏览器)。

http://static.cnbetacdn.com/article/2016/0120/1e2215e63d00208.jpg

如何使用 Java 构建微服务?

$
0
0

【编者按】微服务背后的大理念是将大型、复杂且历时长久的应用在架构上设计为内聚的服务,这些服务能够随着时间的流逝而演化。本文主要介绍了利用 Java 生态系统构建微服务的多种方法,并分析了每种方法的利弊。

快速预览

  1. 在 Java 生态系统中构建微服务的策略主要有:container-less, self-contained 和 in-container;

  2. Container-less 微服务把应用程序及其所有依赖打包成单一的 jar 文件;

  3. Self-contained 微服务也会将应用及其依赖打包成单一的Jar文件,但它还包含可能含有第三方库的嵌入式框架;

  4. In-container 微服务会打包一个完整的 Java EE 容器,并且它的服务是在 Docker image 中实现。

基于微服务的架构设计是架构师和程序员们面临的一项新挑战。然而,随着语言及工具的不断更新,架构师们完全有能力征服这样的挑战。 Java也不例外,本文探讨了使用Java生态系统来构建微服务的几种不同方式。

介绍

本文不会讨论微服务的好与坏,也不会建议你提前为微服务设计应用程序,或当它们出现在你庞大的应用中时,是否应该剥离这些微服务。

本文介绍的方法并不是唯一的,但应该可以达到抛砖引玉的效果。尽管本文的重点是使用 Java 生态系统来构建微服务,但这些概念同样可以转移到其它语言和技术中。

笔者把本文用到的方法命名为 container-less、self-contained 和 in-containe。这些名称或许不是非常正式,但足以区分相互间的差别。接下来,笔者会详细描述每种方法。

Container-less

在此方法中,开发者会将 JVM 之上的任何事物视为应用程序的一部分。

container-less 方法会启用所谓的单 jar 部署(也可称作“fat jar部署”),这也就意味着,应用程序及其所有依赖都会被打包成单一的jar文件,并且作为独立的Java进程运行。

如何使用 Java 构建微服务?

$ java -jar myservice.jar

该方法的第一个优点就是当对应用的规模进行伸缩时,服务很容易按需求快速启动和停止;另一优点是方便部署,你只需要传递一个 jar 文件即可。

该方法的缺点就是库的兼容性。对于事务支持这类问题,你需要自己来实现,或必须引入第三方库才能实现。而后,如果你需要更多支持,例如持续性问题的支持,你就需要解决第三方库之间的兼容性问题。

Self-contained

另一种单 jar 部署就是使用一个嵌入式框架来构建服务。在此方法中,框架提供了所需服务的实现方法,开发者可以选择在项目中包括哪些服务。

你可能会认为这个方法与 container-less 完全一样,但笔者认为,两者的区别在于,self-contained 方法会提供一套相互兼容的第三方库。所以,该方法不存在库兼容性问题。

如何使用 Java 构建微服务?

该方法可能涉及 Spring Boot、Wildfly Swarm 之类的工具。

Spring Boot

在Java中, Spring BootSpring Cloud Netflix项目对构建微服务提供了很好的支持。 Spring Boot 允许你选择各种 Spring 工具和其它流行的工具,然后把它们和你的应用打包成一个 jar 文件。 Spring Initializr提供了一个简单的复选框列表来完成上面这些事。一个简单的Hello World服务示例如下: Gist Snippet

Wildfly Swarm

在 Java EE 中,和 Spring Boot 相对应是 Wildfly Swarm。它允许你根据自己的需求挑选 Java EE 规范,然后把它们和你的应用程序打包成一个 jar 文件。这里有一个简单的 Hello World 示例: Gist Snippet

self-contained 方法的优点是你可以自主选择用于服务运行的项目。

这种方法的缺点是配置更加复杂,由于它在实际的服务中构建所需的容器功能,由此产生的 jar 文件也会稍大一些。

In-container

虽然在 Java EE 容器中部署微服务的开销似乎很大,然而,一些开发者认为,微服务中的“微”并不表示该服务的小或者简单。

如何使用 Java 构建微服务?

在这些案例中,将 Java EE 容器作为所需平台似乎是合适的。因此,你唯一需要的依赖就是 Java EE API 。注意,由于该依赖的实现是由容器提供的,因此该依赖项已经满足了,这也就意味着所产生的 war 文件是非常精简的,该服务的实现与上面 Wildfly Swarm 的例子是一样的: Gist Snippet

该方法的优点是,容器通过标准 API 提供了经过测试和验证的标准功能的实现。因此,开发者可以完全聚焦于业务功能,并在应用代码之外维护底层代码。

另一个优点是,应用程序代码不依赖 Java EE 应用服务器,无论该应用部署到 GlassFishWildFlyWebLogicWebSphere还是任何与 Java EE 兼容的其他实现系统。

该方法的缺点是你需要把服务部署到容器中,这样就增加了部署的复杂性。

Docker

现在来谈谈 Docker。通过把 Java EE 容器和服务实现打包到 Docker 镜像,你可以得到与单一 jar 部署相似的结果。唯一的不同是服务打包在 Docker 镜像中,而不是在 jar 文件中。

Dockerfile
FROM jboss/wildfly:9.0.1.Final
ADD myservice.war /opt/jboss/wildfly/standalone/deployments

在 Docker 引擎中启动 Docker 镜像可以唤醒服务:

$ docker run -it -p 8081:8080 myorganization/myservice

Snoop

细心的读者可能已经在先前的 Spring Boot 代码段中注意到了 @EnableEurekaClient 注解。该注解在 Eureka中注册服务,使其能够被服务消费者发现。 Eureka 是 Spring Cloud Netflix 包的一部分,并且是一个极易使用和配置服务发现的解决方案。

Java EE 在外部并没有提供这样的功能,但是有一些开源解决方案可以使用,其中一个就是 Snoop它的功能与Eureka相似。要使 Java EE 微服务支持任务查找,唯一要做的是使用 @EnableSnoopClient 注解,如本例所示: Gist Snippet

总结

在构建微服务时, Java 是一个非常好的选择。本文中介绍的任何一种方法都可以实现微服务。当然,最好的方法还是根据服务需求而定。对于简单的服务, container-less 或者 self-contained 服务就是不错的选择。不过,借助 in-container ,开发者可以更快更简单地实现更高级的服务。无论针对哪种服务,Java 生态系统都能提供行之有效的实现方法。

(编译自: https://dzone.com/articles/building-microservices-with-java

OneAPM为您提供端到端的 Java 应用性能解决方案,我们支持所有常见的 Java 框架及应用服务器,助您快速发现系统瓶颈,定位异常根本原因。分钟级部署,即刻体验, Java 监控从来没有如此简单。想阅读更多技术文章,请访问 OneAPM 官方技术博客
本文转自 OneAPM 官方博客


Twitter的分布式自增ID算法Snowflake

$
0
0

在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位 机器ID 10位 毫秒内序列12位。

 

10---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000

在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。

这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。

1</pre>
2
3package com.twitter.service.snowflake
5import com.twitter.ostrich.stats.Stats
6import com.twitter.service.snowflake.gen._
7import java.util.Random
8import com.twitter.logging.Logger
10
16class IdWorker(val workerId: Long, val datacenterId: Long, private val reporter: Reporter, var sequence: Long = 0L)
17extends Snowflake.Iface {
18  private[this] def genCounter(agent: String) = {
19    Stats.incr("ids_generated")
20    Stats.incr("ids_generated_%s".format(agent))
21  }
22  private[this] val exceptionCounter = Stats.getCounter("exceptions")
23  private[this] val log = Logger.get
24  private[this] val rand = new Random
25 
26  val twepoch = 1288834974657L
27 
28 //机器标识位数
29 
30  private[this] val workerIdBits = 5L
31 
32//数据中心标识位数
33  private[this] val datacenterIdBits = 5L
34 
35//机器ID最大值
36  private[this] val maxWorkerId = -1L ^ (-1L << workerIdBits)
37 
38//数据中心ID最大值
39  private[this] val maxDatacenterId = -1L ^ (-1L << datacenterIdBits)
40 
41//毫秒内自增位
42  private[this] val sequenceBits = 12L
43 
44//机器ID偏左移12位
45 
46  private[this] val workerIdShift = sequenceBits
47 
48//数据中心ID左移17位
49  private[this] val datacenterIdShift = sequenceBits workerIdBits
50 
51//时间毫秒左移22位
52  private[this] val timestampLeftShift = sequenceBits workerIdBits datacenterIdBits
53  private[this] val sequenceMask = -1L ^ (-1L << sequenceBits)
54 
55  private[this] var lastTimestamp = -1L
56 
57  // sanity check for workerId
58  if (workerId > maxWorkerId || workerId < 0) {
59    exceptionCounter.incr(1)
60    throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0".format(maxWorkerId))
61  }
62 
63  if (datacenterId > maxDatacenterId || datacenterId < 0) {
64    exceptionCounter.incr(1)
65    throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0".format(maxDatacenterId))
66  }
67 
68  log.info("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
69    timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId)
70 
71  def get_id(useragent: String): Long = {
72    if (!validUseragent(useragent)) {
73      exceptionCounter.incr(1)
74      throw new InvalidUserAgentError
75    }
76 
77    val id = nextId()
78    genCounter(useragent)
79 
80    reporter.report(new AuditLogEntry(id, useragent, rand.nextLong))
81    id
82  }
83 
84  def get_worker_id(): Long = workerId
85  def get_datacenter_id(): Long = datacenterId
86  def get_timestamp() = System.currentTimeMillis
87 
88  protected[snowflake] def nextId(): Long = synchronized {
89    var timestamp = timeGen()
90 
91 //时间错误
92 
93    if (timestamp < lastTimestamp) {
94      exceptionCounter.incr(1)
95      log.error("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp);
96      throw new InvalidSystemClock("Clock moved backwards.  Refusing to generate id for %d milliseconds".format(
97        lastTimestamp - timestamp))
98    }
99 
100    if (lastTimestamp == timestamp) {
101//当前毫秒内,则 1
102      sequence = (sequence  1) & sequenceMask
103      if (sequence == 0) {
104//当前毫秒内计数满了,则等待下一秒
105        timestamp = tilNextMillis(lastTimestamp)
106      }
107    } else {
108      sequence = 0
109    }
110 
111    lastTimestamp = timestamp
112//ID偏移组合生成最终的ID,并返回ID   
113 
114((timestamp - twepoch) << timestampLeftShift) |
115      (datacenterId << datacenterIdShift) |
116      (workerId << workerIdShift) |
117      sequence
118  }
119 
120//等待下一个毫秒的到来 
121 
122protected def tilNextMillis(lastTimestamp: Long): Long = {
123    var timestamp = timeGen()
124    while (timestamp <= lastTimestamp) {
125      timestamp = timeGen()
126    }
127    timestamp
128  }
129 
130  protected def timeGen(): Long = System.currentTimeMillis()
131 
132  val AgentParser = """([a-zA-Z][a-zA-Z\-0-9]*)""".r
133 
134  def validUseragent(useragent: String): Boolean = useragent match {
135    case AgentParser(_) => true
136    case _ => false
137  }
138}
 
 
转载自: http://blog.sina.com.cn/s/blog_6b7c2e660102vbi2.html#userconsent#


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Oracle更新的两种方式(merge/update内联视图)

$
0
0

对于Oracle的两表联合更新的场景(有A、B两表,以A.id=B.id关联,根据B表中的记录更新A表中的相应字段),一般有update内联视图和merge两种方式,下面举例介绍:

创建用例表:

CREATE TABLE test1(ID NUMBER(10),NAME VARCHAR2(20));

INSERT INTO test1 VALUES(1,'lucy');

INSERT INTO test1 VALUES(2,'lily');

 

CREATE TABLE test2(ID NUMBER(10),NAME VARCHAR2(20));

INSERT INTO test2 VALUES(1,'lucy');

INSERT INTO test2 VALUES(2,'hanmeimei');

 

merge方式:

MERGE INTO test1 USING test2

ON (test1.id = test2.id)

WHEN MATCHED THEN UPDATE

  SET test1.name = NVL2(test1.name,test2.name,test1.name);

 

merge方法是最简洁,效率最高的方式,在大数据量更新时优先使用这种方式。

 

update内联视图方式:

使用这种方式必须在test2.id上有主键(这里很好理解,必须保证每一个test1.id对应在test2里只有一条记录,如果test2中有多条对应的记录,怎么更新test1?),一般而言这种方式代价比merge方式稍高。

ALTER TABLE test2 ADD CONSTRAINT pk_test2 PRIMARY KEY(ID);

UPDATE (SELECT a.id aid,a.name aname,b.id bid,b.name bname FROM TEST1 a,test2 b WHERE a.id=b.id) t

SET aname = NVL2(aname,bname,aname);

 

使用并行,加快大量数据更新:

MERGE /*+parallel(test1,4)*/ INTO test1 USING test2

ON (test1.id = test2.id)

WHEN MATCHED THEN UPDATE

  SET test1.name = NVL2(test1.name,test2.name,test1.name);



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



微博分布式存储作业实现方法

$
0
0

可能通过「高可用架构」听说过在微博的系统中,单张 MySQL 在线业务表 60 亿条数据的场景。很多关注互联网架构的工程师也非常关注如何如何设计类似系统。下面是一道微博新兵训练营的分布式存储课堂练习,要设计合格才能上岗。

关注为什么超长列表数据的翻页技术实现复杂的读者请直接参看文末链接。

feeds2

考虑到网上有很多架构师也在讨论,补充题目一些说明如下。

1、访问场景

由于上面题目的应用场景,用户一般情况下,主要查看用户查看自己收到的最新的微博,以及某个特定用户 profile 的所有微博。

  • 收到的微博,考虑微博以拉为主的模式,则需要访问关注用户最近 n 条最新的微博。
  • 用户 profile,需要访问用户历史上所有发表的微博,而且支持分页查看,可以直接跳转到某一页或者某个时间段, 因此需要适当考虑分页的效率(可参考扩展阅读)。

访问特征

  • 从上面描述以及社交网络的用户访问特点来看,用户大部分情况( > 90% )是访问最近 7 天的数据。

不需要考虑的点

  • 此题主要是存储层的设计,因此不需要考虑缓存如何设计。
  • 由于微博是 异步写入的,在某种程度可以起到错峰作用,所以作业暂时不需要考虑写入的峰值。
  • 不需要考虑 id 如何产生,假定已经有发号服务。
  • 不需要考虑用户收到的微博怎么聚合,那个是更上层服务层的职责。

2、设计需要考虑的点

Scale-out 扩展性

  • 将数据拆分到多个独立的单元存储
  • 可以在适当时机进一步拆分,拆分时候需要继续提供在线访问
  • 存储在廉价硬件上,考虑到数据规模比较大,需要适当考虑方案的整体成本,因此不要假定默认全部使用 SSD 存储。

Cost 成本

  • 不同访问级别的数据存储在不同访问速度(成本)的硬件上。

High availability 高可用,以及 Reliability 可靠性 – 复制

在当前场景下,主要通过 MySQL replication 来解决可用性、以及分担读的请求。

3、Sharding 策略

Shard 常用策略

range based:根据用户 uid 来分布,相邻 uid 的数据保存在一起。
hash based:根据某个 hash 函数,将一个用户 uid 的数据保存在指定的分片。

Re-Sharding 拆分设计
当数据持续增长,原先存储的数据(或者访问量)超过当前节点的容量上限,则需要对节点进行进一步拆分。

feeds3

如何确定shard数量

db buffer > hot data

容量规划

  • 预规划: 容纳未来一段时间的数据
  • 2 的指数倍: shard 数量变更简单

Tradeoff

  • 分片多:影响 IO 效率;
  • 分片少:扩容频繁、复杂

4、部分投稿案例

案例一:使用 user id range 作为分片

case1

案例二:使用user id hash作为分片

case2

 

方案三 (via 张亮)
历史数据:
1. 每半年根据日期分库,如:2015.01-2015.06为一个库。每天增加1亿数据,半年180亿,约为0.72T数据,可以保留在1T的磁盘中。
2. 根据 uid 取模分库(表),便于查询和分散数据。

当前 n 日数据:
1. 暂定n为10,存储10亿数据。
2. 根据uid + 权重的hash算法分库。权重可以根据每个uid的微博id数量,粉丝数等指标离线计算。

hash算法需保证:
1. 同一uid需落在一个库。
2. 权重接近的用户尽量均匀的落在不同库。
3. 为了应对突然发生的事件导致访问量激增,需要考虑2级甚至3级分片,而不宜直接做re-sharding导致数据迁移。多级分片可考虑读取一个标记,放在zk中。根据标记确定分片的hash算法加入小时等维度。

查询索引:
1. 增加发帖索引字段,记录每个用户的每个帖子的索引。
2. 增加发帖总数统计表,以用户为维度,每个用户发一次贴则发帖总数++。
3. 增加二级索引表,记录每个用户,每次分片库的发帖索引。如:uid 1的用户,在2015年第一帖是该用户发帖的总数的第10贴,2015年最后一贴是该用户发帖总数的第50贴。
4. 分页查询使用二级索引表,先查到该查哪个真实库(可能是多个),再到真实库中获取数据。

总结:
1. 通过灵活的运用时间维度分片,免去因uid分片数量不足导致的大规模迁移,使用外部flag灵活的控制分片策略。而且用时间维度分片更易做到冷热分离。
分片逻辑可以灵活到,zk中记录时间段,某个时间段内,按月分,某个时间段,按年分,之类。
2. 通过离线计算权重的方式均匀分散数据访问。权重周期性调整,对于调整权重的用户,需要重点考虑当前n日数据的数据迁移方案。但由于调整权重的用户属于少量,所以迁移应该数据变动较小。历史数据不需权重概念,无需数据迁移。
3. 查询使用二级索引。使用修改btree结构去掉二级索引能有效减少数据量,但实现难度较大,可以在之后的局部优化中实现,对总体数据库结构影响不大。
4. 将前n日数据和当天数据整合在一起,之前对微博的场景理解不深,以为有首屏显示这样的概念。

 

5、扩展阅读

关于分页:
为什么超长列表数据的翻页技术实现复杂
为什么超长列表数据的翻页技术实现复杂(二)

Similar Posts:

10款人工智能和机器学习领域方面的开源项目

$
0
0
GraphLab
GraphLab是一种新的面向机器学习的并行框架。GraphLab提供了一个完整的平台,让机构可以使用可扩展的机器学习系统建立大数据以分析产品,该公司客户包括Zillow、Adobe、Zynga、Pandora、Bosch、ExxonMobil等,它们从别的应用程序或者服务中抓取数据,通过推荐系统、欺诈监测系统、情感及社交网络分析系统等系统模式将大数据理念转换为生产环境下可以使用的预测应用程序。( 详情 )
项目主页: http://graphlab.org/
  
Vowpal Wabbit
Vowpal Wabbit (Fast Online Learning)最初是由雅虎研究院建设的一个机器学习平台,目前该项目在微软研究院。它是由John Langford启动并主导的项目。
项目地址: http://hunch.net/~vw/
  
scikits.learn
scikit-learn是一个开源的、构建在SciPy之上用于机器学习的 Python 模块。它包括简单而高效的工具,可用于数据挖掘和数据分析,适合于任何人,可在各种情况下重复使用、构建在 NumPy、SciPy和 matplotlib 之上,遵循BSD 协议。
项目地址: http://scikit-learn.org/stable
  
Theano
Theano是一个python库,用来定义、优化和模拟数学表达式计算,用于高效的解决多维数组的计算问题。它使得写深度学习模型更加容易,同时也给出了一些关于在GPU上训练它们的选项。
项目地址: http://deeplearning.net/software/theano/
  
Mahout
Mahout 是 Apache Software Foundation(ASF) 旗下的一个开源项目,提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更加方便快捷地创建智能应用程序。Mahout包含许多实现,包括聚类、分类、推荐过滤、频繁子项挖掘。此外,通过使用 Apache Hadoop 库,Mahout 可以有效地扩展到云中。
项目主页: http://mahout.apache.org/
  
pybrain
pybrain是Python的一个机器学习模块,它的目标是为机器学习任务提供灵活、易应、强大的机器学习算法。pybrain包括神经网络、强化学习(及二者结合)、无监督学习、进化算法。以神经网络为核心,所有的训练方法都以神经网络为一个实例。
项目主页: http://pybrain.org/
  
OpenCV
OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
  
Orange
Orange 是一个基于组件的数据挖掘和机器学习软件套装,它的功能即友好,又很强大,快速而又多功能的可视化编程前端,以便浏览数据分析和可视化,基绑定了 Python以进行脚本开发。它包含了完整的一系列的组件以进行数据预处理,并提供了数据帐目,过渡,建模,模式评估和勘探的功能。
项目主页: http://orange.biolab.si/
  
NLTK
NLTK(natural language toolkit)是python的自然语言处理工具包。2001年推出,至今发展非常活跃。它的主要作用是为了教学,至今已经在20多个国家60多所高校使用,里面包括了大量的词料库,以及自然语言处理方面的算法实现:分词, 词根计算, 分类, 语义分析等。
项目主页: http://nltk.org/
  
Nupic
Nupic是一个开源的人工智能平台。该项目由Grok(原名 Numenta)公司开发,其中包括了公司的算法和软件架构。 NuPIC 的运作接近于人脑,“当模式变化的时候,它会忘掉旧模式,记忆新模式”。如人脑一样,CLA 算法能够适应新的变化。
项目主页: http://numenta.org/nupic.html

关于Java中几种loadClass的讨论

$
0
0

                                          关于Java中几种loadClass的讨论

java中有几类加载class的方法,本文针对这几个方法,就行源码导读。

 

本文的native源码来源于android4.1.2源码。

 

1. Class.forName(className, true, classLoader);
clazz = Class.forName(className, true, classLoader);
className:要加载的类的全路径名。

classLoader:类加载器。

这个函数会调用native方法,源码:java_lang_Class.cpp
static void Dalvik_java_lang_Class_classForName(const u4* args, JValue* pResult)

{

    StringObject* nameObj = (StringObject*) args[0];

    bool initialize = (args[1] != 0);

    Object* loader = (Object*) args[2];


    RETURN_PTR(dvmFindClassByName(nameObj, loader, initialize));

}


这里调用的是dvmFindClassByName(nameObj, loader, initialize),重点在这句,由于此函数代码比较多,我只罗列了关键语句:


ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,

    bool doInit)

{

    ClassObject* clazz = NULL;

    char* name = NULL;

    char* descriptor = NULL;

    ......

// 初始化的参数在这里体现

    if (doInit)

        clazz = dvmFindClass(descriptor, loader);

    else

        clazz = dvmFindClassNoInit(descriptor, loader);


    .......


}


由于dvmFindClass会先调用dvmFindClassNoInit,下面优先分析dvmFindClassNoInit

由于代码比较多,下面只列出了调用函数关系:

dvmFindClassNoInit--&gt;findClassFromLoaderNoInit--&gt;dvmLookupClass--&gt;dvmHashTableLookup


关键看dvmHashTableLookup这个函数:


void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,

   HashCompareFunc cmpFunc, bool doAdd)
{

    pEntry = &amp;pHashTable-&gt;pEntries[itemHash &amp; (pHashTable-&gt;tableSize-1)];
    pEnd = &amp;pHashTable-&gt;pEntries[pHashTable-&gt;tableSize];
    while (pEntry-&gt;data != NULL) {
        if (pEntry-&gt;data != HASH_TOMBSTONE &amp;&amp;
            pEntry-&gt;hashValue == itemHash &amp;&amp;
            (*cmpFunc)(pEntry-&gt;data, item) == 0)  // 这里是需要ClassObject的关键点
    }

    return result;
}

此函数寻找ClassObject的逻辑是:

通过classname生成hash值,在HashTable中寻找此类的ClassObject,而这个HashTable保存在全局变量DvmGlobals中,ClassObject也就是C++中的对象,在虚拟机的堆中,classname和classloader两者对应唯一的一个ClassObject。看一下关键的比较函数cmpFunc如何判定此ClassObject是classname要寻找的Object

 

    match = (strcmp(clazz->descriptor, pCrit->descriptor) == 0 &&

             (clazz->classLoader == pCrit->loader ||

              (pCrit->loader != NULL &&

 

               dvmLoaderInInitiatingList(clazz, pCrit->loader)) ));

这里通过clazz->descriptor,classLoader 决定是否是要寻找的类对象。

 

所以有关java中ClassLoader内容,不同的类在不同的ClassLoader中加载,对应的不同的ClassObject,有此函数可以得出此结论。

 

回到刚才的函数,如果通过dvmLookupClass寻找不到对应的ClassObject,怎么处理?

会调用Invoke loadClass()进行class加载和初始化。

 

        const Method* loadClass =

            loader->clazz->vtable[gDvm.voffJavaLangClassLoader_loadClass];

        JValue result;

        dvmCallMethod(self, loadClass, loader, &result, nameObj);

        clazz = (ClassObject*) result.l;

 

这段函数还是比较重要的,通过ClassLoader,进行重新加载,dvmCallMethod是java调用native的入口。

 

 

2. ClassLoader Class<?> loadClass(String className, boolean resolve)

 

这个函数可能会被表面定义混淆,这个函数的逻辑也是先在HashTable中需要className对应的ClassObjet,如果寻找不到,在进行加载和初始化。

 

    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {

        Class<?> clazz = findLoadedClass(className);

 

        if (clazz == null) {

            try {

                clazz = parent.loadClass(className, false);

            } catch (ClassNotFoundException e) {

                // Don't want to see this.

            }

 

            if (clazz == null) {

                clazz = findClass(className);

            }

        }

 

        return clazz;

    }

 

一、首先看Class<?> clazz = findLoadedClass(className);这句

 

对应的native源码在java_lang_VMClassLoader.cpp中。

 

static void Dalvik_java_lang_VMClassLoader_loadClass(const u4* args,

    JValue* pResult)

{

    StringObject* nameObj = (StringObject*) args[0];

    bool resolve = (args[1] != 0);

    ClassObject* clazz;

 

    clazz = dvmFindClassByName(nameObj, NULL, resolve);

    assert(clazz == NULL || dvmIsClassLinked(clazz));

    RETURN_PTR(clazz);

}

 

重点看一下dvmFindClassByName

 

ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,

    bool doInit)

{

    ClassObject* clazz = NULL;

    char* name = NULL;

    char* descriptor = NULL;

    if (doInit)  // 这里是这个函数的重点逻辑

        clazz = dvmFindClass(descriptor, loader);

    else

        clazz = dvmFindClassNoInit(descriptor, loader);

}

 

这里如果查找不到,便会返回null。

 

二、clazz = parent.loadClass(className, false);

这里是调用classloader的父类进行加载,其加载逻辑相同。

 

三、 clazz = findClass(className);

如果一和二都不能找到className对应的ClassObject,那么就要通过findClass(className)进行寻找,这个函数的定义在classLoader,而android平台下定义在BaseDexClassLoader,看一下他的函数:

    protected Class<?> findClass(String name) throws ClassNotFoundException {

        Class clazz = pathList.findClass(name);

 

        if (clazz == null) {

            throw new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + originalPath);

        }

 

        return clazz;

    }

 

这里调用的是pathList.findClass(name);

 

继续跟踪函数:

    public Class findClass(String name) {

        for (Element element : dexElements) {

            DexFile dex = element.dexFile;

 

            if (dex != null) {

                Class clazz = dex.loadClassBinaryName(name, definingContext);

                if (clazz != null) {

                    return clazz;

                }

            }

        }

 

        return null;

 

    }

 

这里调用的是dex.loadClassBinaryName(name, definingContext);,对应的native函数为:

dalvik_system_DexFile.cpp

 

static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args,

    JValue* pResult)

{

    StringObject* nameObj = (StringObject*) args[0];

    Object* loader = (Object*) args[1];

    int cookie = args[2];

    ClassObject* clazz = NULL;

    DexOrJar* pDexOrJar = (DexOrJar*) cookie;

    DvmDex* pDvmDex;

    char* name;

    char* descriptor;

 

    name = dvmCreateCstrFromString(nameObj);

    descriptor = dvmDotToDescriptor(name);

    ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",

        descriptor, loader, cookie);

    free(name);

 

    if (!validateCookie(cookie))

        RETURN_VOID();

 

    if (pDexOrJar->isDex)

        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);

    else

        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);

 

    /* once we load something, we can't unmap the storage */

    pDexOrJar->okayToFree = false;

 

    clazz = dvmDefineClass(pDvmDex, descriptor, loader);

 

 

}

 

 clazz = dvmDefineClass(pDvmDex, descriptor, loader);这句是核心,

直接进入Class.cpp中的static ClassObject* findClassNoInit(const char* descriptor, Object* loader,

    DvmDex* pDvmDex)函数,这个函数的简单逻辑是dvmLookupClass寻找class,如果找不到,会创建对应的ClassObject,并且添加到Globals中。

 

 clazz = dvmLookupClass(descriptor, loader, true); // 寻找对应的ClassObject

    if (clazz == NULL) {

        const DexClassDef* pClassDef;

 

        dvmMethodTraceClassPrepBegin();

        profilerNotified = true;

 

#if LOG_CLASS_LOADING

        u8 startTime = dvmGetThreadCpuTimeNsec();

#endif

 

// 通过dex加载对应的类

        if (pDvmDex == NULL) {

            assert(loader == NULL);     /* shouldn't be here otherwise */

            pDvmDex = searchBootPathForClass(descriptor, &pClassDef);

        } else {

            pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);

        }

 

        if (pDvmDex == NULL || pClassDef == NULL) {

            if (gDvm.noClassDefFoundErrorObj != NULL) {

                /* usual case -- use prefabricated object */

                dvmSetException(self, gDvm.noClassDefFoundErrorObj);

            } else {

                /* dexopt case -- can't guarantee prefab (core.jar) */

                dvmThrowNoClassDefFoundError(descriptor);

            }

            goto bail;

        }

 

        /* found a match, try to load it */

        clazz = loadClassFromDex(pDvmDex, pClassDef, loader);

        if (dvmCheckException(self)) {

            /* class was found but had issues */

            if (clazz != NULL) {

                dvmFreeClassInnards(clazz);

                dvmReleaseTrackedAlloc((Object*) clazz, NULL);

            }

            goto bail;

        }

 

        /*

         * Lock the class while we link it so other threads must wait for us

         * to finish.  Set the "initThreadId" so we can identify recursive

         * invocation.  (Note all accesses to initThreadId here are

         * guarded by the class object's lock.)

         */

        dvmLockObject(self, (Object*) clazz);

        clazz->initThreadId = self->threadId;

 

        /*

         * Add to hash table so lookups succeed.

         *

         * [Are circular references possible when linking a class?]

         */

        assert(clazz->classLoader == loader);

        if (!dvmAddClassToHash(clazz)) { 添加创建的类到HashTable

            /*

             * Another thread must have loaded the class after we

             * started but before we finished.  Discard what we've

             * done and leave some hints for the GC.

             *

             * (Yes, this happens.)

             */

            //ALOGW("WOW: somebody loaded %s simultaneously", descriptor);

            clazz->initThreadId = 0;

            dvmUnlockObject(self, (Object*) clazz);

 

            /* Let the GC free the class.

             */

            dvmFreeClassInnards(clazz);

            dvmReleaseTrackedAlloc((Object*) clazz, NULL);

 

            /* Grab the winning class.

             */

            clazz = dvmLookupClass(descriptor, loader, true);

            assert(clazz != NULL);

            goto got_class;

        }

        dvmReleaseTrackedAlloc((Object*) clazz, NULL);

......

}

 

到此整个流程已经介绍完毕。

 

由此看出我们直接调用ClassLoader.loadClass要比调用Class.forName更高效。

 

 

 

 

 

 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Viewing all 11807 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>