IT牛人博客聚合网站 发现IT技术最优秀的内容, 寻找IT技术的价值 http://www.fwnace.com/ zh_CN http://www.fwnace.com/about hourly 1 Sun, 04 Aug 2019 21:58:09 +0800 <![CDATA[在Golangçš„HTTP请求中共享数据]]> http://www.fwnace.com/item/17379.html http://www.fwnace.com/item/17379.html#reviews Fri, 08 Feb 2019 11:30:42 +0800 老王 http://www.fwnace.com/item/17379.html ???????首先,我们需要先明确一下问题的描述:本文所要讨论的共享数据可不是指的 cookie、session 之类的概念,它们描述的是在「请求间」共享数据,而我们关注的是在「请求中」共享数据,也就说是,在每个请求中的各个 middleware 和 handler 之间共享数据。

实际上,我之所以关注这个问题源自 httprouter???????,众所周知,httprouter 是目前 Golang 社区最流行的 HTTP 路由库,不过它有一个问题,其 handler 参数定义如下:

func (http.ResponseWriter, *http.Request, httprouter.Params)

而官方的 http.Handler 参数定义是:

???????func (http.ResponseWriter, *http.Request)

???????也就是说, httprouter 多了一个 httprouter.Params 参数,用来传递路由参数,可惜它破坏了兼容性,关于此问题,官方给出了:

???????The router itself implements the http.Handler interface. Moreover the router provides convenient adapters for http.Handlers and http.HandlerFuncs which allows them to be used as a httprouter.Handle when registering a route. The only disadvantage is, that no parameter values can be retrieved when a http.Handler or http.HandlerFunc is used, since there is no efficient way to pass the values with the existing function parameters. Therefore httprouter.Handle has a third function parameter.

???????大概意思是 httprouter 提供了兼容模式,不过兼容模式不能使用路由参数。那么能不能在保持兼容性的前提下使用路由参数呢,官方有过,计划在新版本中使用 Context 来传递路由参数,但是几年过去了,还没实现。

让我们先顺着 Context 来看看如何在 Golang 的 HTTP 请求中共享数据。

路由的例子有点复杂,我们不妨假设一个简单点儿的例子:设想一下我们需要给每一个请求分配一个请求 ID,并且每个 middleware 或者 handler 都可以拿到此请求 ID。很明显,这个请求 ID 就是我们说的共享数据,下面让我们看看如何用 Context 来实现它:

package main

import (
	"context"
	"fmt"
	"net/http"
)

// RequestContextKey is a context key
type RequestContextKey string

func requestID(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithValue(r.Context(), RequestContextKey("id"), "uuid")
		next(w, r.WithContext(ctx))
	}
}

func test1(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(RequestContextKey("id"))
	w.Write([]byte("request_id: " + id.(string)))
}

func test2(w http.ResponseWriter, r *http.Request) {
	id := fmt.Sprintf("%v", r.Context().Value(RequestContextKey("id")))
	w.Write([]byte("request_id: " + id))
}

func main() {
	http.Handle("/test1", requestID(test1))
	http.HandleFunc("/test2", test2)
	http.ListenAndServe(":8080", nil)
}

本例只用到了两个 Context 方法,分别是:

  • WithValue(parent Context, key, val interface{}) Context
  • Value(key interface{}) interface{}

???????如上可见,key 和 val 都是 interface{},也就是说,你可以使用任意值作为键和值,与此对应的,当你取回值得时候,同样需要做对应的类型转换。

需要着重说明的一点是,最好不要使用基础类型来做 key,而应该使用自定义类型,就好像本例中的 RequestContextKey 类型,为什么要这样做?假设大家都是用 string 之类的基础类型来做 key 的话,那么我们就不容易区分这个 key 到底隶属于谁,很容易出现彼此影响的情况,Context 在读写数据的时候会保证类型安全,不会发生错乱的情况。

???????明白了这些就可以运行代码了,先请求 /test1,再请求 /test2,结果依次是:

  1. request_id: uuid
  2. request_id: <nil>

???????也就是说,我们实现了在 HTTP 请求中共享数据的功能,同时可知 Context 的作用范围是请求级的,不同请求的 Context 不会彼此干扰。

???????让我们把目光回到文章开头提到的 httprouter 身上,虽然它本身 和 http.Handler 有不兼容的问题,但是我们可以通过前面学到的 Context 相关知识来改善此问题:

package main

import (
	"context"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

// RouterContextKey is a context key
type RouterContextKey string

func compatible(next http.HandlerFunc) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		ctx := context.WithValue(r.Context(), RouterContextKey("params"), p)
		next(w, r.WithContext(ctx))
	}
}

func user(w http.ResponseWriter, r *http.Request) {
	p := r.Context().Value(RouterContextKey("params")).(httprouter.Params)
	w.Write([]byte(p.ByName("name")))
}

func main() {
	router := httprouter.New()
	router.GET("/user/:name", compatible(user))
	http.ListenAndServe(":8080", router)
}

???????本文是仓促写于返京的途中,未做严格验证,如有谬误敬请海涵。

0     0

fwnace.com 聚合 | 评论: 0 |

]]>
首先,我们需要先明确一下问题的描述:本文所要讨论的共享数据可不是指的 cookie、session 之类的概念,它们描述的是在「请求间」共享数据,而我们关注的是在「请求中」共享数据,也就说是,在每个请求中的各个 middleware 和 handler 之间共享数据。

实际上,我之所以关注这个问题源自 ,众所周知,httprouter 是目前 Golang 社区最流行的 HTTP 路由库,不过它有一个问题,其 handler 参数定义如下:

???????func (http.ResponseWriter, *http.Request, httprouter.Params)

???????而官方的 http.Handler 参数定义是:

???????func (http.ResponseWriter, *http.Request)

???????也就是说, httprouter 多了一个 httprouter.Params 参数,用来传递路由参数,可惜它破坏了兼容性,关于此问题,官方给出了:

The router itself implements the http.Handler interface. Moreover the router provides convenient adapters for http.Handlers and http.HandlerFuncs which allows them to be used as a httprouter.Handle when registering a route. The only disadvantage is, that no parameter values can be retrieved when a http.Handler or http.HandlerFunc is used, since there is no efficient way to pass the values with the existing function parameters. Therefore httprouter.Handle has a third function parameter.

大概意思是 httprouter 提供了兼容模式,不过兼容模式不能使用路由参数。那么能不能在保持兼容性的前提下使用路由参数呢,官方有过,计划在新版本中使用 Context 来传递路由参数,但是几年过去了,还没实现。

???????让我们先顺着 Context 来看看如何在 Golang 的 HTTP 请求中共享数据。

???????路由的例子有点复杂,我们不妨假设一个简单点儿的例子:设想一下我们需要给每一个请求分配一个请求 ID,并且每个 middleware 或者 handler 都可以拿到此请求 ID。很明显,这个请求 ID 就是我们说的共享数据,下面让我们看看如何用 Context 来实现它:

package main

import (
	"context"
	"fmt"
	"net/http"
)

// RequestContextKey is a context key
type RequestContextKey string

func requestID(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithValue(r.Context(), RequestContextKey("id"), "uuid")
		next(w, r.WithContext(ctx))
	}
}

func test1(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(RequestContextKey("id"))
	w.Write([]byte("request_id: " + id.(string)))
}

func test2(w http.ResponseWriter, r *http.Request) {
	id := fmt.Sprintf("%v", r.Context().Value(RequestContextKey("id")))
	w.Write([]byte("request_id: " + id))
}

func main() {
	http.Handle("/test1", requestID(test1))
	http.HandleFunc("/test2", test2)
	http.ListenAndServe(":8080", nil)
}

本例只用到了两个 Context 方法,分别是:

  • WithValue(parent Context, key, val interface{}) Context
  • Value(key interface{}) interface{}

如上可见,key 和 val 都是 interface{},也就是说,你可以使用任意值作为键和值,与此对应的,当你取回值得时候,同样需要做对应的类型转换。

需要着重说明的一点是,最好不要使用基础类型来做 key,而应该使用自定义类型,就好像本例中的 RequestContextKey 类型,为什么要这样做?假设大家都是用 string 之类的基础类型来做 key 的话,那么我们就不容易区分这个 key 到底隶属于谁,很容易出现彼此影响的情况,Context 在读写数据的时候会保证类型安全,不会发生错乱的情况。

明白了这些就可以运行代码了,先请求 /test1,再请求 /test2,结果依次是:

  1. request_id: uuid
  2. request_id: <nil>

也就是说,我们实现了在 HTTP 请求中共享数据的功能,同时可知 Context 的作用范围是请求级的,不同请求的 Context 不会彼此干扰。

让我们把目光回到文章开头提到的 httprouter 身上,虽然它本身 和 http.Handler 有不兼容的问题,但是我们可以通过前面学到的 Context 相关知识来改善此问题:

package main

import (
	"context"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

// RouterContextKey is a context key
type RouterContextKey string

func compatible(next http.HandlerFunc) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		ctx := context.WithValue(r.Context(), RouterContextKey("params"), p)
		next(w, r.WithContext(ctx))
	}
}

func user(w http.ResponseWriter, r *http.Request) {
	p := r.Context().Value(RouterContextKey("params")).(httprouter.Params)
	w.Write([]byte(p.ByName("name")))
}

func main() {
	router := httprouter.New()
	router.GET("/user/:name", compatible(user))
	http.ListenAndServe(":8080", router)
}

本文是仓促写于返京的途中,未做严格验证,如有谬误敬请海涵。

0     0

fwnace.com 聚合 | 评论: 0??????? |

]]>
0
<![CDATA[找回密码的功能设计]]> http://www.fwnace.com/item/17378.html http://www.fwnace.com/item/17378.html#reviews Thu, 07 Feb 2019 15:14:02 +0800 阮一峰 http://www.fwnace.com/item/17378.html 所有需要登录的网站,都会提供"找回密码"的功能,防止用户忘记密码。

正确设计这个功能,保证安全可靠,并不简单。下面就是安全专家 Troy Hunt 给出的。

一、如何保存密码

一个网站要想保证密码安全,第一步就是以正确的方法保存密码。一般说来,密码有三种保存方式。

(1)明文保存

???????"明文保存"就是用户的密码原文不动地写入数据库。这种方式最不安全,极易泄漏,应该严格禁用。

(2)加密保存

"加密保存"就是使用密钥,将密码加密后,以密文保存进数据库。这种方式虽然有一定的安全性,但是终究还是可以用密钥还原密码。因此,还是存在泄漏的可能,也不推荐使用。

(3)哈希保存

"哈希保存"就是对密码使用哈希算法,将哈希值保存进数据库。为了增加随机性,防止彩虹表这一类的工具,计算哈希的时候,每个用户都有一个不一样的盐值(salt),也会同时保存进数据库。

哈希是单向运算,无法还原,所以即使哈希值泄漏,一般来说,也不会暴露用户的原始密码。

第一条规则:密码永远都要哈希保存。

二、密码重置

如果密码是哈希保存,用户一旦忘记密码,网站也无法知道原始密码是什么,只能让用户重置密码。

第二条规则:找回密码就是让用户重置密码。

???????重置密码又有两种做法。有的网站先自动改成一个随机密码,然后再让用户登录后自己改掉。这样做的风险在于,你必须把随机密码告知用户,通过邮件或短信,这个过程中就有可能泄漏。

第三条规则:重置密码的时候,要给出一个链接,让用户到网页上自己修改密码。

重置链接由于是明文传播,而且直接修改密码,所以必须有失效时间。一般来说,可以设成10分钟失效。

三、用户名还是邮件地址?

重置密码之前,必须知道重置谁的密码。这时需要用户提供,注册时的邮件地址。

第四条规则:重置密码之前,如果用户提供了错误的邮件地址,不要提示他。

这是因为如果提示了,数据库不包含某个邮件地址,就可能像下图那样,泄露用户的隐私,被钓鱼者利用。

???????正确的做法是,不管用户输入什么邮箱,都向该邮箱发邮件。在邮件里说明,有人尝试重置密码,但是他输入的邮箱不在数据库里面。

如果不是采用邮件地址,而是根据用户名识别用户,就没有办法不提示,某个用户名是否存在。某些人的用户名非常特殊,一旦知道该用户名存在,就几乎可以肯定是该人注册的。

第五条规则:重置密码的时候,识别用户最好依靠邮件地址,而不是用户名。

四、过滤用户

为了防止机器人攻击,进入重置密码之前,最好加上 CAPTCHA 识别。

???????此外,还要防止一种情况:张三知道李四的邮箱,然后使用找回密码功能,让系统给李四发出重置密码的邮件。

第六条规则:如果条件允许,重置密码之前,最好请用户回答一些个人问题,或者采用,比如短信验证码。

最后,不要忘了记录 IP 地址,在邮件里面告诉用户,哪个 IP 地址在申请重置你的密码。

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名()
  • 发表日期:2019年2月 7日
  • 更多内容:»
  • 文集:,
  • 社交媒体:,
0     0

fwnace.com 聚合 | 评论: 0??????? |

]]>
所有需要登录的网站,都会提供"找回密码"的功能,防止用户忘记密码。

正确设计这个功能,保证安全可靠,并不简单。下面就是安全专家 Troy Hunt 给出的。

一、如何保存密码

一个网站要想保证密码安全,第一步就是以正确的方法保存密码。一般说来,密码有三种保存方式。

(1)明文保存

"明文保存"就是用户的密码原文不动地写入数据库。这种方式最不安全,极易泄漏,应该严格禁用。

(2)加密保存

"加密保存"就是使用密钥,将密码加密后,以密文保存进数据库。这种方式虽然有一定的安全性,但是终究还是可以用密钥还原密码。因此,还是存在泄漏的可能,也不推荐使用。

(3)哈希保存

"哈希保存"就是对密码使用哈希算法,将哈希值保存进数据库。为了增加随机性,防止彩虹表这一类的工具,计算哈希的时候,每个用户都有一个不一样的盐值(salt),也会同时保存进数据库。

哈希是单向运算,无法还原,所以即使哈希值泄漏,一般来说,也不会暴露用户的原始密码。

第一条规则:密码永远都要哈希保存。

二、密码重置

???????如果密码是哈希保存,用户一旦忘记密码,网站也无法知道原始密码是什么,只能让用户重置密码。

第二条规则:找回密码就是让用户重置密码。

重置密码又有两种做法。有的网站先自动改成一个随机密码,然后再让用户登录后自己改掉。这样做的风险在于,你必须把随机密码告知用户,通过邮件或短信,这个过程中就有可能泄漏。

第三条规则:重置密码的时候,要给出一个链接,让用户到网页上自己修改密码。

重置链接由于是明文传播,而且直接修改密码,所以必须有失效时间。一般来说,可以设成10分钟失效。

三、用户名还是邮件地址?

重置密码之前,必须知道重置谁的密码。这时需要用户提供,注册时的邮件地址。

第四条规则:重置密码之前,如果用户提供了错误的邮件地址,不要提示他。

???????这是因为如果提示了,数据库不包含某个邮件地址,就可能像下图那样,泄露用户的隐私,被钓鱼者利用。

???????正确的做法是,不管用户输入什么邮箱,都向该邮箱发邮件。在邮件里说明,有人尝试重置密码,但是他输入的邮箱不在数据库里面。

如果不是采用邮件地址,而是根据用户名识别用户,就没有办法不提示,某个用户名是否存在。某些人的用户名非常特殊,一旦知道该用户名存在,就几乎可以肯定是该人注册的。

第五条规则:重置密码的时候,识别用户最好依靠邮件地址,而不是用户名。

四、过滤用户

???????为了防止机器人攻击,进入重置密码之前,最好加上 CAPTCHA 识别。

此外,还要防止一种情况:张三知道李四的邮箱,然后使用找回密码功能,让系统给李四发出重置密码的邮件。

第六条规则:如果条件允许,重置密码之前,最好请用户回答一些个人问题,或者采用,比如短信验证码。

???????最后,不要忘了记录 IP 地址,在邮件里面告诉用户,哪个 IP 地址在申请重置你的密码。

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名()
  • 发表日期:2019年2月 7日
  • 更多内容:»
  • 文集:,
  • 社交媒体:,
0     0

fwnace.com 聚合 | 评论: 0??????? |

]]>
0
<![CDATA[Some tips about Python, Pandas, and Tensorflow]]> http://www.fwnace.com/item/17377.html http://www.fwnace.com/item/17377.html#reviews Wed, 06 Feb 2019 10:47:51 +0800 Robin Dong http://www.fwnace.com/item/17377.html ???????There are some useful tips for using Keras and Tensorflow to build models.

???????1. Using applications.inception_v3.InceptionV3(include_top = False, weights = ‘Imagenet’) to get pretrained parameters for InceptionV3 model, the console reported:

Exception: URL fetch failure on https://github.com/fchollet/deep-learning-models/releases/download/v0.5/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5: None -- unknown url type: https

???????The solution is. Just install some packages:

Sudo yum install openssl openssl-devel -y

2. Could we use ‘add’ to merge two DataFrames of Pandas? Let’s try

import pandas as pd

a = pd.DataFrame([[1, 2], [3, 4]], columns = ['first', 'second'])
b = pd.DataFrame([], columns = ['first', 'second'])
print(a+b)

The result is:

first second
0   NaN    NaN
1   NaN    NaN

The operator ‘+’ just works as ‘‘. It try to add all values column by column, but the second DataFrame is empty, so the result of adding a number and a nonexistent value is ‘Nan’.
??????? To merge two DataFrames, we should use ‘append’:

a.append(b)

???????3. Why Estimator of Tensorflow doesn’t print out log?

logging_hook = tf.train.LoggingTensorHook({'step': global_step, 'loss': loss, 'precision': precision, 'recall': recall, 'f1': f1, 'auc': auc},
            every_n_iter = every_n_iter, formatter = formatter_log)
    ....
    if mode == tf.estimator.ModeKeys.EVAL:
        return tf.estimator.EstimatorSpec(mode, loss = loss, evaluation_hooks = [logging_hook])

But the logging_hook hasn’t been run. The solution is just adding one line before running Estimator:

tf.logging.set_verbosity(tf.logging.INFO)

0     0

fwnace.com 聚合 | 评论: 0 |

]]>
There are some useful tips for using Keras and Tensorflow to build models.

???????1. Using applications.inception_v3.InceptionV3(include_top = False, weights = ‘Imagenet’) to get pretrained parameters for InceptionV3 model, the console reported:

Exception: URL fetch failure on https://github.com/fchollet/deep-learning-models/releases/download/v0.5/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5: None -- unknown url type: https

The solution is. Just install some packages:

Sudo yum install openssl openssl-devel -y

???????2. Could we use ‘add’ to merge two DataFrames of Pandas? Let’s try

import pandas as pd

a = pd.DataFrame([[1, 2], [3, 4]], columns = ['first', 'second'])
b = pd.DataFrame([], columns = ['first', 'second'])
print(a+b)

???????The result is:

first second
0   NaN    NaN
1   NaN    NaN

The operator ‘+’ just works as ‘‘. It try to add all values column by column, but the second DataFrame is empty, so the result of adding a number and a nonexistent value is ‘Nan’.
To merge two DataFrames, we should use ‘append’:

a.append(b)

???????3. Why Estimator of Tensorflow doesn’t print out log?

logging_hook = tf.train.LoggingTensorHook({'step': global_step, 'loss': loss, 'precision': precision, 'recall': recall, 'f1': f1, 'auc': auc},
            every_n_iter = every_n_iter, formatter = formatter_log)
    ....
    if mode == tf.estimator.ModeKeys.EVAL:
        return tf.estimator.EstimatorSpec(mode, loss = loss, evaluation_hooks = [logging_hook])

???????But the logging_hook hasn’t been run. The solution is just adding one line before running Estimator:

tf.logging.set_verbosity(tf.logging.INFO)

0     0

fwnace.com 聚合 | 评论: 0 |

]]>
0
<![CDATA[两个老虎]]> http://www.fwnace.com/item/17376.html http://www.fwnace.com/item/17376.html#reviews Wed, 06 Feb 2019 06:11:27 +0800 qyjohn http://www.fwnace.com/item/17376.html

0     0

fwnace.com 聚合 | 评论: 0 |

]]>

0     0

fwnace.com 聚合 | 评论: 0??????? |

]]>
0
<![CDATA[Go Reflect 性能]]> http://www.fwnace.com/item/17370.html http://www.fwnace.com/item/17370.html#reviews Fri, 01 Feb 2019 16:30:08 +0800 鸟窝 http://www.fwnace.com/item/17370.html Go提供了运行时获取对象的类型和值的能力,它可以帮助我们实现代码的抽象和简化,实现动态的数据获取和方法调用, 提高开发效率和可读性, 也弥补Go在缺乏泛型的情况下对数据的统一处理能力。

通过reflect,我们可以实现获取对象类型、对象字段、对象方法的能力,获取struct的tag信息,动态创建对象,对象是否实现特定的接口,对象的转换、对象值的获取和设置、Select分支动态调用等功能, 看起来功能不错,但是大家也都知道一点:使用reflect是有性能代价的!

测试

???????Java中的reflect的使用对性能也有影响, 但是和Java reflect不同, Java中不区分Type和Value类型的, 所以至少Java中我们可以预先讲相应的reflect对象缓存起来,减少反射对性能的影响, 但是Go没办法预先缓存reflect, 因为Type类型并不包含对象运行时的值,必须通过ValueOf和运行时实例对象才能获取Value对象。

对象的反射生成和获取都会增加额外的代码指令, 并且也会涉及interface{}装箱/拆箱操作,中间还可能增加临时对象的生成,所以性能下降是肯定的,但是具体能下降多少呢,还是得数据来说话。

当然,不同的reflect使用的姿势, 以及对象类型的不同,都会多多少少影响性能的测试数据,我们就以一个普通的struct类型为例:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
package testimport (	"reflect"	"testing")type Student struct {	Name  string	Age   int	Class string	Score int}func BenchmarkReflect_New(b *testing.B) {	var s *Student	sv := reflect.TypeOf(Student{})	b.ResetTimer()	for i := 0; i < b.N; i++ {		sn := reflect.New(sv)		s, _ = sn.Interface().(*Student)	}	_ = s}func BenchmarkDirect_New(b *testing.B) {	var s *Student	b.ResetTimer()	for i := 0; i < b.N; i++ {		s = new(Student)	}	_ = s}func BenchmarkReflect_Set(b *testing.B) {	var s *Student	sv := reflect.TypeOf(Student{})	b.ResetTimer()	for i := 0; i < b.N; i++ {		sn := reflect.New(sv)		s = sn.Interface().(*Student)		s.Name = "Jerry"		s.Age = 18		s.Class = "20005"		s.Score = 100	}}func BenchmarkReflect_SetFieldByName(b *testing.B) {	sv := reflect.TypeOf(Student{})	b.ResetTimer()	for i := 0; i < b.N; i++ {		sn := reflect.New(sv).Elem()		sn.FieldByName("Name").SetString("Jerry")		sn.FieldByName("Age").SetInt(18)		sn.FieldByName("Class").SetString("20005")		sn.FieldByName("Score").SetInt(100)	}}func BenchmarkReflect_SetFieldByIndex(b *testing.B) {	sv := reflect.TypeOf(Student{})	b.ResetTimer()	for i := 0; i < b.N; i++ {		sn := reflect.New(sv).Elem() 		sn.Field(0).SetString("Jerry")		sn.Field(1).SetInt(18)		sn.Field(2).SetString("20005")		sn.Field(3).SetInt(100)	}}func BenchmarkDirect_Set(b *testing.B) {	var s *Student	b.ResetTimer()	for i := 0; i < b.N; i++ {		s = new(Student)		s.Name = "Jerry"		s.Age = 18		s.Class = "20005"		s.Score = 100	}}

测试结果:

1234567
BenchmarkReflect_New-4               	20000000	        70.0 ns/op	      48 B/op	       1 allocs/opBenchmarkDirect_New-4                	30000000	        45.6 ns/op	      48 B/op	       1 allocs/opBenchmarkReflect_Set-4               	20000000	        73.6 ns/op	      48 B/op	       1 allocs/opBenchmarkReflect_SetFieldByName-4    	 3000000	       492 ns/op	      80 B/op	       5 allocs/opBenchmarkReflect_SetFieldByIndex-4   	20000000	       111 ns/op	      48 B/op	       1 allocs/opBenchmarkDirect_Set-4                   30000000	        43.1 ns/op	      48 B/op	       1 allocs/op

测试结果

我们进行了两种功能的测试:

  • 对象(struct)的创建
  • 对象字段的赋值

???????对于对象的创建,通过反射生成对象需要70纳秒, 而直接new这个对象却只需要45.6纳秒, 性能差别还是很大的。

对于字段的赋值,一共四个测试用例:

  • Reflect_Set: 通过反射生成对象,并将这个对象转换成实际的对象,直接调用对象的字段进行赋值, 需要73.6纳秒
  • Reflect_SetFieldByName: 通过反射生成对象,通过FieldByName进行赋值, 需要492纳秒
  • Reflect_SetFieldByIndex: 通过反射生成对象,通过Field进行赋值, 需要111纳秒
  • Direct_Set: 直接调用对象的字段进行赋值, 只需要43.1纳秒

???????Reflect_Set和Direct_Set性能的主要差别还是在于对象的生成,因为之后字段的赋值方法都是一样的,这也和对象创建的测试case的结果是一致的。

如果通过反射进行赋值,性能下降是很厉害的,耗时成倍的增长。比较有趣的是,FieldByName方式赋值是Field方式赋值的好几倍, 原因在于FieldByName会有额外的循环进行字段的查找,虽然最终它还是调用Field进行赋值:

12345678910111213141516171819202122232425
func (v Value) FieldByName(name string) Value {	v.mustBe(Struct)	if f, ok := v.typ.FieldByName(name); ok {		return v.FieldByIndex(f.Index)	}	return Value{}}func (v Value) FieldByIndex(index []int) Value {	if len(index) == 1 {		return v.Field(index[0])	}	v.mustBe(Struct)	for i, x := range index {		if i > 0 {			if v.Kind() == Ptr && v.typ.Elem().Kind() == Struct {				if v.IsNil() {					panic("reflect: indirection through nil pointer to embedded struct")				}				v = v.Elem()			}		}		v = v.Field(x)	}	return v}

优化

???????从上面的测试结果看, 通过反射生成对象和字段赋值都会影响性能,但是通过反射的确确确实实能简化代码,为业务逻辑提供统一的代码, 比如标准库中json的编解码、rpc服务的注册和调用, 一些ORM框架比如gorm等,都是通过反射处理数据的,这是为了能处理通用的类型。

https://github.com/golang/go/blob/master/src/encoding/json/decode.go#L946
12345678910
  ......      case reflect.String:	v.SetString(string(s))case reflect.Interface:	if v.NumMethod() == 0 {		v.Set(reflect.ValueOf(string(s)))	} else {		d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex())})          }  ......
https://github.com/jinzhu/gorm/blob/master/scope.go#L495
123456789101112131415161718
     for fieldIndex, field := range selectFields {if field.DBName == column {	if field.Field.Kind() == reflect.Ptr {		values[index] = field.Field.Addr().Interface()	} else {		reflectValue := reflect.New(reflect.PtrTo(field.Struct.Type))		reflectValue.Elem().Set(field.Field.Addr())		values[index] = reflectValue.Interface()		resetFields[index] = field	}	selectedColumnsMap[column] = offset + fieldIndex	if field.IsNormal {		break	}}     }

???????在我们追求高性能的场景的时候,我们可能需要尽量避免反射的调用, 比如对json数据的unmarshal, easyjson就通过生成器的方式,避免使用反射。

123456789101112131415161718192021222324252627282930313233343536373839404142434445
func (v *Student) UnmarshalJSON(data []byte) error {	r := jlexer.Lexer{Data: data}	easyjson4a74e62dDecodeGitABCReflect(&r, v)	return r.Error()}func (v *Student) UnmarshalEasyJSON(l *jlexer.Lexer) {	easyjson4a74e62dDecodeGitABCReflect(l, v)}func easyjson4a74e62dDecodeGitABCReflect(in *jlexer.Lexer, out *Student) {	isTopLevel := in.IsStart()	if in.IsNull() {		if isTopLevel {			in.Consumed()		}		in.Skip()		return	}	in.Delim('{')	for !in.IsDelim('}') {		key := in.UnsafeString()		in.WantColon()		if in.IsNull() {			in.Skip()			in.WantComma()			continue		}		switch key {		case "Name":			out.Name = string(in.String())		case "Age":			out.Age = int(in.Int())		case "Class":			out.Class = string(in.String())		case "Score":			out.Score = int(in.Int())		default:			in.SkipRecursive()		}		in.WantComma()	}	in.Delim('}')	if isTopLevel {		in.Consumed()	}}

其它的一些编解码库也提供了这种避免使用反射的方法来提高性能。

顺带测一下"装箱/拆箱"操作带来的性能影响

12345678910111213141516171819202122232425262728293031
func DirectInvoke(s *Student) {	s.Name = "Jerry"	s.Age = 18	s.Class = "20005"	s.Score = 100}func InterfaceInvoke(i interface{}) {	s := i.(*Student)	s.Name = "Jerry"	s.Age = 18	s.Class = "20005"	s.Score = 100}func BenchmarkDirectInvoke(b *testing.B) {	s := new(Student)	for i := 0; i < b.N; i++ {		DirectInvoke(s)	}	_ = s}func BenchmarkInterfaceInvoke(b *testing.B) {	s := new(Student)	for i := 0; i < b.N; i++ {		InterfaceInvoke(s)	}	_ = s}

测试结果:

12
BenchmarkDirectInvoke-4              	300000000	         5.60 ns/op	       0 B/op	       0 allocs/opBenchmarkInterfaceInvoke-4           	200000000	         6.64 ns/op	       0 B/op	       0 allocs/op

???????可以看到将具体对象转换成 interface{}(以及反向操作)确实回带来一点点性能的影响,不过看起来影响倒不是很大。

0     0

fwnace.com 聚合 | 评论: 0??????? |

]]>
Go提供了运行时获取对象的类型和值的能力,它可以帮助我们实现代码的抽象和简化,实现动态的数据获取和方法调用, 提高开发效率和可读性, 也弥补Go在缺乏泛型的情况下对数据的统一处理能力。

通过reflect,我们可以实现获取对象类型、对象字段、对象方法的能力,获取struct的tag信息,动态创建对象,对象是否实现特定的接口,对象的转换、对象值的获取和设置、Select分支动态调用等功能, 看起来功能不错,但是大家也都知道一点:使用reflect是有性能代价的!

测试

Java中的reflect的使用对性能也有影响, 但是和Java reflect不同, Java中不区分Type和Value类型的, 所以至少Java中我们可以预先讲相应的reflect对象缓存起来,减少反射对性能的影响, 但是Go没办法预先缓存reflect, 因为Type类型并不包含对象运行时的值,必须通过ValueOf和运行时实例对象才能获取Value对象。

对象的反射生成和获取都会增加额外的代码指令, 并且也会涉及interface{}装箱/拆箱操作,中间还可能增加临时对象的生成,所以性能下降是肯定的,但是具体能下降多少呢,还是得数据来说话。

当然,不同的reflect使用的姿势, 以及对象类型的不同,都会多多少少影响性能的测试数据,我们就以一个普通的struct类型为例:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
package testimport (	"reflect"	"testing")type Student struct {	Name  string	Age   int	Class string	Score int}func BenchmarkReflect_New(b *testing.B) {	var s *Student	sv := reflect.TypeOf(Student{})	b.ResetTimer()	for i := 0; i < b.N; i++ {		sn := reflect.New(sv)		s, _ = sn.Interface().(*Student)	}	_ = s}func BenchmarkDirect_New(b *testing.B) {	var s *Student	b.ResetTimer()	for i := 0; i < b.N; i++ {		s = new(Student)	}	_ = s}func BenchmarkReflect_Set(b *testing.B) {	var s *Student	sv := reflect.TypeOf(Student{})	b.ResetTimer()	for i := 0; i < b.N; i++ {		sn := reflect.New(sv)		s = sn.Interface().(*Student)		s.Name = "Jerry"		s.Age = 18		s.Class = "20005"		s.Score = 100	}}func BenchmarkReflect_SetFieldByName(b *testing.B) {	sv := reflect.TypeOf(Student{})	b.ResetTimer()	for i := 0; i < b.N; i++ {		sn := reflect.New(sv).Elem()		sn.FieldByName("Name").SetString("Jerry")		sn.FieldByName("Age").SetInt(18)		sn.FieldByName("Class").SetString("20005")		sn.FieldByName("Score").SetInt(100)	}}func BenchmarkReflect_SetFieldByIndex(b *testing.B) {	sv := reflect.TypeOf(Student{})	b.ResetTimer()	for i := 0; i < b.N; i++ {		sn := reflect.New(sv).Elem() 		sn.Field(0).SetString("Jerry")		sn.Field(1).SetInt(18)		sn.Field(2).SetString("20005")		sn.Field(3).SetInt(100)	}}func BenchmarkDirect_Set(b *testing.B) {	var s *Student	b.ResetTimer()	for i := 0; i < b.N; i++ {		s = new(Student)		s.Name = "Jerry"		s.Age = 18		s.Class = "20005"		s.Score = 100	}}

测试结果:

1234567
BenchmarkReflect_New-4               	20000000	        70.0 ns/op	      48 B/op	       1 allocs/opBenchmarkDirect_New-4                	30000000	        45.6 ns/op	      48 B/op	       1 allocs/opBenchmarkReflect_Set-4               	20000000	        73.6 ns/op	      48 B/op	       1 allocs/opBenchmarkReflect_SetFieldByName-4    	 3000000	       492 ns/op	      80 B/op	       5 allocs/opBenchmarkReflect_SetFieldByIndex-4   	20000000	       111 ns/op	      48 B/op	       1 allocs/opBenchmarkDirect_Set-4                   30000000	        43.1 ns/op	      48 B/op	       1 allocs/op

测试结果

我们进行了两种功能的测试:

  • 对象(struct)的创建
  • 对象字段的赋值

对于对象的创建,通过反射生成对象需要70纳秒, 而直接new这个对象却只需要45.6纳秒, 性能差别还是很大的。

???????对于字段的赋值,一共四个测试用例:

  • Reflect_Set: 通过反射生成对象,并将这个对象转换成实际的对象,直接调用对象的字段进行赋值, 需要73.6纳秒
  • Reflect_SetFieldByName: 通过反射生成对象,通过FieldByName进行赋值, 需要492纳秒
  • Reflect_SetFieldByIndex: 通过反射生成对象,通过Field进行赋值, 需要111纳秒
  • Direct_Set: 直接调用对象的字段进行赋值, 只需要43.1纳秒

Reflect_Set和Direct_Set性能的主要差别还是在于对象的生成,因为之后字段的赋值方法都是一样的,这也和对象创建的测试case的结果是一致的。

如果通过反射进行赋值,性能下降是很厉害的,耗时成倍的增长。比较有趣的是,FieldByName方式赋值是Field方式赋值的好几倍, 原因在于FieldByName会有额外的循环进行字段的查找,虽然最终它还是调用Field进行赋值:

12345678910111213141516171819202122232425
func (v Value) FieldByName(name string) Value {	v.mustBe(Struct)	if f, ok := v.typ.FieldByName(name); ok {		return v.FieldByIndex(f.Index)	}	return Value{}}func (v Value) FieldByIndex(index []int) Value {	if len(index) == 1 {		return v.Field(index[0])	}	v.mustBe(Struct)	for i, x := range index {		if i > 0 {			if v.Kind() == Ptr && v.typ.Elem().Kind() == Struct {				if v.IsNil() {					panic("reflect: indirection through nil pointer to embedded struct")				}				v = v.Elem()			}		}		v = v.Field(x)	}	return v}

优化

从上面的测试结果看, 通过反射生成对象和字段赋值都会影响性能,但是通过反射的确确确实实能简化代码,为业务逻辑提供统一的代码, 比如标准库中json的编解码、rpc服务的注册和调用, 一些ORM框架比如gorm等,都是通过反射处理数据的,这是为了能处理通用的类型。

https://github.com/golang/go/blob/master/src/encoding/json/decode.go#L946
12345678910
  ......      case reflect.String:	v.SetString(string(s))case reflect.Interface:	if v.NumMethod() == 0 {		v.Set(reflect.ValueOf(string(s)))	} else {		d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex())})          }  ......
https://github.com/jinzhu/gorm/blob/master/scope.go#L495
123456789101112131415161718
     for fieldIndex, field := range selectFields {if field.DBName == column {	if field.Field.Kind() == reflect.Ptr {		values[index] = field.Field.Addr().Interface()	} else {		reflectValue := reflect.New(reflect.PtrTo(field.Struct.Type))		reflectValue.Elem().Set(field.Field.Addr())		values[index] = reflectValue.Interface()		resetFields[index] = field	}	selectedColumnsMap[column] = offset + fieldIndex	if field.IsNormal {		break	}}     }

???????在我们追求高性能的场景的时候,我们可能需要尽量避免反射的调用, 比如对json数据的unmarshal, easyjson就通过生成器的方式,避免使用反射。

123456789101112131415161718192021222324252627282930313233343536373839404142434445
func (v *Student) UnmarshalJSON(data []byte) error {	r := jlexer.Lexer{Data: data}	easyjson4a74e62dDecodeGitABCReflect(&r, v)	return r.Error()}func (v *Student) UnmarshalEasyJSON(l *jlexer.Lexer) {	easyjson4a74e62dDecodeGitABCReflect(l, v)}func easyjson4a74e62dDecodeGitABCReflect(in *jlexer.Lexer, out *Student) {	isTopLevel := in.IsStart()	if in.IsNull() {		if isTopLevel {			in.Consumed()		}		in.Skip()		return	}	in.Delim('{')	for !in.IsDelim('}') {		key := in.UnsafeString()		in.WantColon()		if in.IsNull() {			in.Skip()			in.WantComma()			continue		}		switch key {		case "Name":			out.Name = string(in.String())		case "Age":			out.Age = int(in.Int())		case "Class":			out.Class = string(in.String())		case "Score":			out.Score = int(in.Int())		default:			in.SkipRecursive()		}		in.WantComma()	}	in.Delim('}')	if isTopLevel {		in.Consumed()	}}

???????其它的一些编解码库也提供了这种避免使用反射的方法来提高性能。

顺带测一下"装箱/拆箱"操作带来的性能影响

12345678910111213141516171819202122232425262728293031
func DirectInvoke(s *Student) {	s.Name = "Jerry"	s.Age = 18	s.Class = "20005"	s.Score = 100}func InterfaceInvoke(i interface{}) {	s := i.(*Student)	s.Name = "Jerry"	s.Age = 18	s.Class = "20005"	s.Score = 100}func BenchmarkDirectInvoke(b *testing.B) {	s := new(Student)	for i := 0; i < b.N; i++ {		DirectInvoke(s)	}	_ = s}func BenchmarkInterfaceInvoke(b *testing.B) {	s := new(Student)	for i := 0; i < b.N; i++ {		InterfaceInvoke(s)	}	_ = s}

???????测试结果:

12
BenchmarkDirectInvoke-4              	300000000	         5.60 ns/op	       0 B/op	       0 allocs/opBenchmarkInterfaceInvoke-4           	200000000	         6.64 ns/op	       0 B/op	       0 allocs/op

可以看到将具体对象转换成 interface{}(以及反向操作)确实回带来一点点性能的影响,不过看起来影响倒不是很大。

0     0

fwnace.com 聚合 | 评论: 0 |

]]>
0
<![CDATA[每周分享第 42 期]]> http://www.fwnace.com/item/17375.html http://www.fwnace.com/item/17375.html#reviews Fri, 01 Feb 2019 10:08:23 +0800 阮一峰 http://www.fwnace.com/item/17375.html ???????这里记录过去一周,我看到的值得分享的东西,每周五发布。

欢迎投稿,或推荐你自己的项目,请前往 GitHub 的提交 issue。

通知:下周春节假期,周刊暂停一期。

???????呆伯特漫画的作者亚当斯( Scott Adams),有一次谈到自己的成功秘诀。

他的经历其实很普通。小时候喜欢画画,画得还可以,但远远不算优秀。长大以后,在一家公司当经理,管理企业,也是业绩平平。无论是选择当画家,或者继续当公司经理,也许都能够干下去,但应该都不会很成功。于是,他灵机一动,把自己的这两个特点结合起来,选择了另一条路:专门画讽刺企业管理的漫画,结果走红了,成了世界闻名的漫画家。

他说,任何领域最优秀的前5%的人,都能拿到很好的报酬,比如,最优秀的那5%的程序员、面包师、钢琴家、美发师都是高收入的。但是,想要挤进这5%,是很不容易的,需要拼掉其他95%的人。但是,如果标准放宽一点,挤进前25%,普通人经过努力,还是很有希望达到的。

成功的秘诀就是,你必须有两个能达到前25%水平的领域,这两个领域的交集就是你的职业方向。

简单计算就可以知道,两个领域都是前25%,那么交集就是 25% 乘以 25%,等于 6.25%,即很有可能挤进前5%。更进一步,如果在两个领域里面,你都属于前10%的优秀人才,那么在交集里面,就可以达到顶尖的1%。总之,选择交集作为职业方向,你的竞争力会提升一个量级,收入也会随之大涨。

举例来说,袁腾飞是一个中学历史老师,但是表达能力非常好,特别能说,简直能当脱口秀演员。如果他一直当中学历史老师,或者选择说脱口秀(就像黄西那样),可能都不会很成功,竞争者太多了。但是他把两者结合起来,专门在网上视频说历史,讲得就很有意思,非常受欢迎,另一方面这个领域的竞争者也很少。

新闻

1、

???????测绘番茄基因的时候,科学家惊奇地发现,生成辣椒素的基因在番茄里面也存在,只是默认没有激活。也就是说,番茄天生具备辣椒的基因。巴西和爱尔兰的植物生理学家正在研究,如何激活这些基因,使得番茄也能产生辣椒素,使得口味变得像辣椒。

???????这样做的主要好处是,可以使得辣椒产量提高30多倍。辣椒产量是每公顷3吨,生长期4到5个月,而番茄在4个月内每公顷可产出110吨。

2、

???????地球内核是一个液态的金属内核,流动的铁元素导致地球自身带有一个巨大的磁场,而且有南北两个磁极。磁极的具体位置一直在变化(因为铁元素在流动),科学家原定每5年发布一次磁极的具体位置,下一次应该是在2020年。但是磁极的变化突然在最近加速了,不得不提前到2019年1月15日发布新的磁极。因为磁极位置不正确,会对许多方面(比如远洋船舶的导航系统)造成影响。

???????上图是过去100年北半球磁极的位置,深色的点是地理意义的北极点。可以看到红色的磁极一直在向北极点靠近。

3、

日本政府决定,2020年东京奥运会时,东京地铁站将出现机器人服务员,为世界各地的游客服务。这主要是因为,日本人口出生率太低,出现劳动力危机,找不到足够的服务人员。

该机器人身高1米8左右,能够为乘客展示通往洗手间和储物柜的路,提供交通信息,推荐该地区的旅游景点。她能说日语,英语,中文和韩语,还配有触摸屏显示器。

4、

特斯拉推出一款新的家用充电器,可以插在家用的高压插座上,直接给特斯拉汽车充电。它的好处是家里不用为电动汽车,安装专门的充电站了,只要买来这款充电器就开箱即用。

???????它的售价是500美元,直接输出 40A 电流。

5、

???????1月3日,嫦娥四号飞船在月球背面登陆。飞船上带有6种生物种子:棉花、油菜、土豆、拟南芥、酵母和果蝇。它们都放在密封的罐子里,罐子由特殊的铝合金材料制成,直径173毫米,高198.3毫米,内部除了6种生物,还有18毫升水、土壤、空气、热控装置(防止受外界温度影响),形成一个自我维持的生态圈,以及两个记录生物生长状态的摄像头。

嫦娥四号登陆以后,罐子自动加水,让种子结束休眠状态。传回的照片显示,棉花种子已经成功在月球上发芽。月球上如果有植物生长,将来就可以为月球基地的居民提供食物。不过,嫦娥四号随后按照地面控制中心的指令,进入休眠状态,切断能源供应。这些种子在没有供热的情况下,已经在月球的低温中。

6、

???????美国创业公司 Ambrosia 新推出了一项业务,只要支付 8000 美元,就可以注射1升的年轻人血液,12,000美元可以注射2升。

???????该公司正式发布该项业务之前,已经实验了近150人,年龄从35岁到92岁不等。临床试验从2017年开始,旨在了解成年人的静脉充满年轻人的血液时会发生什么。虽然该研究的结果尚未公布,但该公司说结果"非常积极"。目前,尚没有任何这方面的医学研究。

7、

国家统计局宣布,2018年中国新生儿是1523万。尽管已经放开二胎,这个数字是2000年以后的最低值。

???????现在的预测是,2029年之前,中国人口就会开始负增长。中国劳动力过剩、资源紧张的问题,将发生质的变化,而老龄化危机才刚刚开始。

8、

影视剧里面,主人公经常对着流星许愿。日本一家公司准备推出"流星雨服务",在客户指定的时间和地点,制造流星。

总部位于东京的 Astro Live Experiences 公司(ALE)2019年1月18日已将七颗微型卫星送入太空,以提供世界上第一个人造流星雨。每颗微型卫星大约相当于一个大背包的大小,里面携带400颗金属颗粒,每个颗粒直径不到一英寸。在指定的位置和时间,卫星释放金属颗粒,当它们穿过地球的大气层时,颗粒会与大气摩擦燃烧,发出明亮的光芒,形成一个人造的流星雨,大约持续几秒钟,可见范围约为100公里。

根据该公司的说法,每场流星雨服务将消耗约20颗金属颗粒,因此一个卫星可以提供20场活动。根据计划,人造流星雨预计将在2020年对外提供服务,价格还没宣布。

9、一句话新闻

  • 改变策略,允许自家的音乐和电影服务可以在非苹果设备上使用。现在,Apple 音乐可以在亚马逊 Alexa 上听,而 Apple 电影和电视可以在三星、LG、索尼等电视上观看。

  • 发现,嗅觉较好的人也具有较好的方向感。科学家的解释是,嗅觉和方向感在大脑皮层的同一个区域。

  • 发现,2018年,三分之一的美国创业公司不注册 .com 域名,而是选择 .ai、.app、.health、.tech 和 .services 域名。

教程

1、(英文)

???????作为一个 iOS 开发者,作者谈了自己使用标题里面的三种技术,开发 App 的感受。

2、(英文)

一篇 TypeScript 的使用体会,以及一些使用经验。

3、(英文)

???????gRPC 是谷歌提出的一套远程服务调用的解决方案,只能用于服务器之间。本文介绍了 gRPC-Web 这个项目,旨在让浏览器也可以使用 gRPC。

4、(英文)

本文介绍了作者如何配置 Linux 桌面开发环境。

???????5、(英文)

???????一份安全上网清单,从专业角度告诉你,应该做哪些事情,怎样才能安全地使用互联网。

6、(英文)

作者介绍使用一台树莓派,架设个人使用的反向代理服务器、Git 服务、看板服务的过程。

???????7、(英文)

???????作者的家用 NAS 的电路板损坏,导致所有数据都无法读出,他不得不把4块 1TB 硬盘插到台式机里面恢复数据。

8、(英文)

SQLite 有很强的读取性能,作者提出它可以用作小型网站的搜索数据库。

9、(英文)

Dart 是 Flutter 框架的开发语言,语法与 JS 高度类似,也是单线程。本文介绍 Dart 语言的异步操作,可以比较与 JS 的异同。

10、(英文)

???????随着 GPU 日益强大,实时渲染高质量的 3D 动画已经成为现实,这将改变许多行业,个人将可以制作完整的电影。

资源

1、

AdGuard 宣布提供免费的 DNS 服务:76.103.130.130或176.103.130.131,还支持 DNS over HTTPS。

2、

一份简短的入门课程,直接在浏览器里编写代码,学习 JavaScript 语言的基本语法。

3、

常见加密算法的实现演示。作者用容易懂的代码、详细的注释帮助读者理解这些加密算法的实现。

4、

该网站收集 Go 语言的各种应用实例。

5、

一个国内开发者分享阅读与思考的专栏。(@* *投稿)

6、

每日不定时在社交媒体推送一批 GitHub 优秀的开源项目给开发者, 帮助开发者们发现当下最火的开源项目。(@投稿)。

7、(中文)

???????Java 互联网开发的知识笔记,涉及MQ、ES、Redis等周边工具。(@* *投稿)

8、(第二版)

???????麻省理工学院以前用著名的 SICP 作为编程教程,但是该书使用 Scheme 语言作为示例,再加上出版多年,现在已经不用了。作为改进,2001年出版了《如何设计程序》这本教材(缩写为 HTDP),2018年又出版了该书的第二版,使用 Racket 语言的图形界面 DrRacket 作为示例,内容全部开源。

工具

1、

命令行查看图片的工具。

2、

中国程序员容易发音错误的单词。(@* *投稿)

3、

一个用 Markdown 语法做笔记的桌面软件。

4、

???????终于有人觉得 Babel 太慢,用 Rust 语言重写了一遍,速度提高了十几倍,一分钟的编译可以缩短为5秒。下一步该是重写 Webpack 吧。

5、

???????该网站提供 SVG 格式的各种 Logo 文件下载,目前一共有 1,080个公司/项目的3,458个 Logo。

6、

???????这个网站通过请求速度,比较各大 CDN 的性能表现。

7、

???????谷歌官方推出的一个兼容库,将 Flutter 框架的 API 用于开发跨平台的桌面应用。文档说,只要把手机应用的源码改改,就能编译成桌面应用。如果可行的话,Flutter 就是全平台框架了。

8、

???????一个开源的网站统计后端,带有 Web 界面,可以作为 Google Analytics 的替代。

9、

???????Webpack 的一个封装,目的是简化 Webpack 的配置,让大多数情况下可以不写配置文件,直接使用 Webpack。

10、

???????一个 SQLite 数据库的命令行客户端,借鉴了和项目。

文摘

1、

有人提出制造超音速的电动飞机。我认为,这是不太可能的。

???????波音737的起飞重量为72吨,需要96600兆焦耳(MJ)的能量才能达到35,000英尺的巡航高度,这还只是爬升阶段需要的能量。(注:焦耳是能量单位,表示1牛顿的力移动1米所需要的能量。)

现在,假设72吨的重量里面,有25吨是电池,那就意味着在爬升阶段,电池需要提供每公斤3.8MJ的能量。巡航阶段还需要额外的 2~4MJ/kg 的能量。因此,电池至少必须含有 8MJ/kg 的能量。你知道 TNT 炸药的能量是多少吗?4.2MJ/kg。也就是说,这个电池包含的能量是 TNT 的两倍,是现有最好的化学电池的6倍。

???????如果想要实现超音速飞行,那么还需要更多的能量。最乐观的情况下,电池必须提供 15-20MJ/kg 的能量。这时,电池跟炸弹已经没什么两样了,一旦出现任何问题,就会导致非常强大的爆炸。

2、

???????电路板其实就是一块铜片,外面包裹一层树脂。这层树脂外壳又称为阻焊膜,是一种带有彩色颜料的硬化树脂,以丝网印刷的方式涂在铜板上。它的目的是保护下面的电子线路免受潮湿和灰尘的影响,并控制焊料的流动。

这层树脂可以有多种颜色,如蓝色,红色,黄色,黑色和白色,但是通常情况下,它是绿色,这是为什么?

早期,电路板是否印刷正确,依赖于工人的裸眼检查。整天看这么微小的电路,非常伤视力。绿色最有利于减轻视觉疲劳。此外,人眼或视锥细胞对绿光非常敏感。绿色电路板的视觉对比度更大,而对比度越高,错误越容易发现。绿色电路板往往肉眼就可以轻松识别电路的缺陷。

另外,绿色电路板也有更好的性能。目前,绿色阻焊膜是唯一可以可靠地生产 0.1毫米(4mil)焊接掩模坝的颜色。接下来是红色,黄色和蓝色,可以产生0.12毫米,然后才是黑色和白色,只能达到0.15毫米。焊接掩模坝在防止焊桥形成方面很有价值。

最后,绿色是历史上默认的电路板颜色,所有厂家都支持,所以批量生产绿色电路板的成本是最低的。

本周图片

1、

Youtube 有一个频道,专门介绍电影特效是怎么制作的。这一期介绍《神奇动物在哪里》,可以看到完全是绿幕合成,比如下面这个从箱子里钻出来的效果。

???????再比如,抚摸大鸟的效果。

2、

索尼和松下的合资企业 JOLED 在日本 Finetech 展览上,展出了一款柱状 4K 显示器的概念产品。

还有专为地铁/火车的车厢设计的可弯曲显示器。

3、

世界最长的步行路线,是从俄罗斯的马加丹一直到南非的开普敦,全长22387公里。你必须经过16个国家,包括南苏丹,叙利亚和格鲁吉亚,所以这条路线不是很安全。

新奇

1、

???????荷兰一家公司运输自行车时,总是发生很大的损耗。该公司忍无可忍,在包装纸箱上印上了电视机。初看之下都会以为,箱子里面是一台平板电视,运输耗损率因此降低了80%。

本周金句

1、

???????在创业公司工作,很可能报酬更少(公司失败的话),工作更多。如果钱少活多对你没有吸引力,那么我建议你不要加入创业公司。

--

2、

苹果公司的策略是,不要让第三方应用赚到大钱,便宜的第三方应用会让人们更愿意购买 iPhone。所以,它通过应用商店,让第三方开发者的收入减少 30%。

???????--。使用某种产品时,需要配合使用的其他东西,叫做补充品,比如,果酱是面包的补充品。如果补充品非常便宜,将促进所补充的那种产品的销售。因此,聪明的公司会让补充品变得便宜,并形成一个专门的商品市场。

3、

当市场出现大的热潮时,最好的策略通常不是参与这个热潮,而是成为工具提供者。

--

4、

统计发现,城市越大,人们的步行速度越快。

--

欢迎订阅

???????这个专栏每周五发布,同步更新在我的、和。

微信搜索"阮一峰的网络日志 "或者扫描二维码,即可订阅。

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名()
  • 发表日期:2019年2月 1日
  • 更多内容:»
  • 文集:,
  • 社交媒体:,
0     0

fwnace.com 聚合 | 评论: 0??????? |

]]>
这里记录过去一周,我看到的值得分享的东西,每周五发布。

欢迎投稿,或推荐你自己的项目,请前往 GitHub 的提交 issue。

通知:下周春节假期,周刊暂停一期。

呆伯特漫画的作者亚当斯( Scott Adams),有一次谈到自己的成功秘诀。

???????他的经历其实很普通。小时候喜欢画画,画得还可以,但远远不算优秀。长大以后,在一家公司当经理,管理企业,也是业绩平平。无论是选择当画家,或者继续当公司经理,也许都能够干下去,但应该都不会很成功。于是,他灵机一动,把自己的这两个特点结合起来,选择了另一条路:专门画讽刺企业管理的漫画,结果走红了,成了世界闻名的漫画家。

???????他说,任何领域最优秀的前5%的人,都能拿到很好的报酬,比如,最优秀的那5%的程序员、面包师、钢琴家、美发师都是高收入的。但是,想要挤进这5%,是很不容易的,需要拼掉其他95%的人。但是,如果标准放宽一点,挤进前25%,普通人经过努力,还是很有希望达到的。

成功的秘诀就是,你必须有两个能达到前25%水平的领域,这两个领域的交集就是你的职业方向。

???????简单计算就可以知道,两个领域都是前25%,那么交集就是 25% 乘以 25%,等于 6.25%,即很有可能挤进前5%。更进一步,如果在两个领域里面,你都属于前10%的优秀人才,那么在交集里面,就可以达到顶尖的1%。总之,选择交集作为职业方向,你的竞争力会提升一个量级,收入也会随之大涨。

???????举例来说,袁腾飞是一个中学历史老师,但是表达能力非常好,特别能说,简直能当脱口秀演员。如果他一直当中学历史老师,或者选择说脱口秀(就像黄西那样),可能都不会很成功,竞争者太多了。但是他把两者结合起来,专门在网上视频说历史,讲得就很有意思,非常受欢迎,另一方面这个领域的竞争者也很少。

新闻

1、

测绘番茄基因的时候,科学家惊奇地发现,生成辣椒素的基因在番茄里面也存在,只是默认没有激活。也就是说,番茄天生具备辣椒的基因。巴西和爱尔兰的植物生理学家正在研究,如何激活这些基因,使得番茄也能产生辣椒素,使得口味变得像辣椒。

???????这样做的主要好处是,可以使得辣椒产量提高30多倍。辣椒产量是每公顷3吨,生长期4到5个月,而番茄在4个月内每公顷可产出110吨。

2、

???????地球内核是一个液态的金属内核,流动的铁元素导致地球自身带有一个巨大的磁场,而且有南北两个磁极。磁极的具体位置一直在变化(因为铁元素在流动),科学家原定每5年发布一次磁极的具体位置,下一次应该是在2020年。但是磁极的变化突然在最近加速了,不得不提前到2019年1月15日发布新的磁极。因为磁极位置不正确,会对许多方面(比如远洋船舶的导航系统)造成影响。

上图是过去100年北半球磁极的位置,深色的点是地理意义的北极点。可以看到红色的磁极一直在向北极点靠近。

3、

???????日本政府决定,2020年东京奥运会时,东京地铁站将出现机器人服务员,为世界各地的游客服务。这主要是因为,日本人口出生率太低,出现劳动力危机,找不到足够的服务人员。

???????该机器人身高1米8左右,能够为乘客展示通往洗手间和储物柜的路,提供交通信息,推荐该地区的旅游景点。她能说日语,英语,中文和韩语,还配有触摸屏显示器。

4、

特斯拉推出一款新的家用充电器,可以插在家用的高压插座上,直接给特斯拉汽车充电。它的好处是家里不用为电动汽车,安装专门的充电站了,只要买来这款充电器就开箱即用。

它的售价是500美元,直接输出 40A 电流。

5、

1月3日,嫦娥四号飞船在月球背面登陆。飞船上带有6种生物种子:棉花、油菜、土豆、拟南芥、酵母和果蝇。它们都放在密封的罐子里,罐子由特殊的铝合金材料制成,直径173毫米,高198.3毫米,内部除了6种生物,还有18毫升水、土壤、空气、热控装置(防止受外界温度影响),形成一个自我维持的生态圈,以及两个记录生物生长状态的摄像头。

嫦娥四号登陆以后,罐子自动加水,让种子结束休眠状态。传回的照片显示,棉花种子已经成功在月球上发芽。月球上如果有植物生长,将来就可以为月球基地的居民提供食物。不过,嫦娥四号随后按照地面控制中心的指令,进入休眠状态,切断能源供应。这些种子在没有供热的情况下,已经在月球的低温中。

6、

美国创业公司 Ambrosia 新推出了一项业务,只要支付 8000 美元,就可以注射1升的年轻人血液,12,000美元可以注射2升。

???????该公司正式发布该项业务之前,已经实验了近150人,年龄从35岁到92岁不等。临床试验从2017年开始,旨在了解成年人的静脉充满年轻人的血液时会发生什么。虽然该研究的结果尚未公布,但该公司说结果"非常积极"。目前,尚没有任何这方面的医学研究。

7、

???????国家统计局宣布,2018年中国新生儿是1523万。尽管已经放开二胎,这个数字是2000年以后的最低值。

???????现在的预测是,2029年之前,中国人口就会开始负增长。中国劳动力过剩、资源紧张的问题,将发生质的变化,而老龄化危机才刚刚开始。

8、

影视剧里面,主人公经常对着流星许愿。日本一家公司准备推出"流星雨服务",在客户指定的时间和地点,制造流星。

???????总部位于东京的 Astro Live Experiences 公司(ALE)2019年1月18日已将七颗微型卫星送入太空,以提供世界上第一个人造流星雨。每颗微型卫星大约相当于一个大背包的大小,里面携带400颗金属颗粒,每个颗粒直径不到一英寸。在指定的位置和时间,卫星释放金属颗粒,当它们穿过地球的大气层时,颗粒会与大气摩擦燃烧,发出明亮的光芒,形成一个人造的流星雨,大约持续几秒钟,可见范围约为100公里。

根据该公司的说法,每场流星雨服务将消耗约20颗金属颗粒,因此一个卫星可以提供20场活动。根据计划,人造流星雨预计将在2020年对外提供服务,价格还没宣布。

9、一句话新闻

  • 改变策略,允许自家的音乐和电影服务可以在非苹果设备上使用。现在,Apple 音乐可以在亚马逊 Alexa 上听,而 Apple 电影和电视可以在三星、LG、索尼等电视上观看。

  • 发现,嗅觉较好的人也具有较好的方向感。科学家的解释是,嗅觉和方向感在大脑皮层的同一个区域。

  • 发现,2018年,三分之一的美国创业公司不注册 .com 域名,而是选择 .ai、.app、.health、.tech 和 .services 域名。

教程

1、(英文)

???????作为一个 iOS 开发者,作者谈了自己使用标题里面的三种技术,开发 App 的感受。

???????2、(英文)

一篇 TypeScript 的使用体会,以及一些使用经验。

???????3、(英文)

gRPC 是谷歌提出的一套远程服务调用的解决方案,只能用于服务器之间。本文介绍了 gRPC-Web 这个项目,旨在让浏览器也可以使用 gRPC。

???????4、(英文)

本文介绍了作者如何配置 Linux 桌面开发环境。

???????5、(英文)

一份安全上网清单,从专业角度告诉你,应该做哪些事情,怎样才能安全地使用互联网。

6、(英文)

???????作者介绍使用一台树莓派,架设个人使用的反向代理服务器、Git 服务、看板服务的过程。

???????7、(英文)

作者的家用 NAS 的电路板损坏,导致所有数据都无法读出,他不得不把4块 1TB 硬盘插到台式机里面恢复数据。

8、(英文)

???????SQLite 有很强的读取性能,作者提出它可以用作小型网站的搜索数据库。

???????9、(英文)

???????Dart 是 Flutter 框架的开发语言,语法与 JS 高度类似,也是单线程。本文介绍 Dart 语言的异步操作,可以比较与 JS 的异同。

10、(英文)

???????随着 GPU 日益强大,实时渲染高质量的 3D 动画已经成为现实,这将改变许多行业,个人将可以制作完整的电影。

资源

1、

???????AdGuard 宣布提供免费的 DNS 服务:76.103.130.130或176.103.130.131,还支持 DNS over HTTPS。

2、

一份简短的入门课程,直接在浏览器里编写代码,学习 JavaScript 语言的基本语法。

3、

???????常见加密算法的实现演示。作者用容易懂的代码、详细的注释帮助读者理解这些加密算法的实现。

4、

该网站收集 Go 语言的各种应用实例。

5、

???????一个国内开发者分享阅读与思考的专栏。(@* *投稿)

6、

???????每日不定时在社交媒体推送一批 GitHub 优秀的开源项目给开发者, 帮助开发者们发现当下最火的开源项目。(@投稿)。

???????7、(中文)

???????Java 互联网开发的知识笔记,涉及MQ、ES、Redis等周边工具。(@* *投稿)

8、(第二版)

???????麻省理工学院以前用著名的 SICP 作为编程教程,但是该书使用 Scheme 语言作为示例,再加上出版多年,现在已经不用了。作为改进,2001年出版了《如何设计程序》这本教材(缩写为 HTDP),2018年又出版了该书的第二版,使用 Racket 语言的图形界面 DrRacket 作为示例,内容全部开源。

工具

1、

命令行查看图片的工具。

2、

???????中国程序员容易发音错误的单词。(@* *投稿)

3、

???????一个用 Markdown 语法做笔记的桌面软件。

4、

???????终于有人觉得 Babel 太慢,用 Rust 语言重写了一遍,速度提高了十几倍,一分钟的编译可以缩短为5秒。下一步该是重写 Webpack 吧。

5、

该网站提供 SVG 格式的各种 Logo 文件下载,目前一共有 1,080个公司/项目的3,458个 Logo。

6、

这个网站通过请求速度,比较各大 CDN 的性能表现。

7、

???????谷歌官方推出的一个兼容库,将 Flutter 框架的 API 用于开发跨平台的桌面应用。文档说,只要把手机应用的源码改改,就能编译成桌面应用。如果可行的话,Flutter 就是全平台框架了。

8、

???????一个开源的网站统计后端,带有 Web 界面,可以作为 Google Analytics 的替代。

9、

???????Webpack 的一个封装,目的是简化 Webpack 的配置,让大多数情况下可以不写配置文件,直接使用 Webpack。

10、

???????一个 SQLite 数据库的命令行客户端,借鉴了和项目。

文摘

1、

有人提出制造超音速的电动飞机。我认为,这是不太可能的。

波音737的起飞重量为72吨,需要96600兆焦耳(MJ)的能量才能达到35,000英尺的巡航高度,这还只是爬升阶段需要的能量。(注:焦耳是能量单位,表示1牛顿的力移动1米所需要的能量。)

现在,假设72吨的重量里面,有25吨是电池,那就意味着在爬升阶段,电池需要提供每公斤3.8MJ的能量。巡航阶段还需要额外的 2~4MJ/kg 的能量。因此,电池至少必须含有 8MJ/kg 的能量。你知道 TNT 炸药的能量是多少吗?4.2MJ/kg。也就是说,这个电池包含的能量是 TNT 的两倍,是现有最好的化学电池的6倍。

如果想要实现超音速飞行,那么还需要更多的能量。最乐观的情况下,电池必须提供 15-20MJ/kg 的能量。这时,电池跟炸弹已经没什么两样了,一旦出现任何问题,就会导致非常强大的爆炸。

2、

???????电路板其实就是一块铜片,外面包裹一层树脂。这层树脂外壳又称为阻焊膜,是一种带有彩色颜料的硬化树脂,以丝网印刷的方式涂在铜板上。它的目的是保护下面的电子线路免受潮湿和灰尘的影响,并控制焊料的流动。

这层树脂可以有多种颜色,如蓝色,红色,黄色,黑色和白色,但是通常情况下,它是绿色,这是为什么?

早期,电路板是否印刷正确,依赖于工人的裸眼检查。整天看这么微小的电路,非常伤视力。绿色最有利于减轻视觉疲劳。此外,人眼或视锥细胞对绿光非常敏感。绿色电路板的视觉对比度更大,而对比度越高,错误越容易发现。绿色电路板往往肉眼就可以轻松识别电路的缺陷。

???????另外,绿色电路板也有更好的性能。目前,绿色阻焊膜是唯一可以可靠地生产 0.1毫米(4mil)焊接掩模坝的颜色。接下来是红色,黄色和蓝色,可以产生0.12毫米,然后才是黑色和白色,只能达到0.15毫米。焊接掩模坝在防止焊桥形成方面很有价值。

最后,绿色是历史上默认的电路板颜色,所有厂家都支持,所以批量生产绿色电路板的成本是最低的。

本周图片

1、

???????Youtube 有一个频道,专门介绍电影特效是怎么制作的。这一期介绍《神奇动物在哪里》,可以看到完全是绿幕合成,比如下面这个从箱子里钻出来的效果。

再比如,抚摸大鸟的效果。

2、

索尼和松下的合资企业 JOLED 在日本 Finetech 展览上,展出了一款柱状 4K 显示器的概念产品。

???????还有专为地铁/火车的车厢设计的可弯曲显示器。

3、

世界最长的步行路线,是从俄罗斯的马加丹一直到南非的开普敦,全长22387公里。你必须经过16个国家,包括南苏丹,叙利亚和格鲁吉亚,所以这条路线不是很安全。

新奇

1、

???????荷兰一家公司运输自行车时,总是发生很大的损耗。该公司忍无可忍,在包装纸箱上印上了电视机。初看之下都会以为,箱子里面是一台平板电视,运输耗损率因此降低了80%。

本周金句

1、

???????在创业公司工作,很可能报酬更少(公司失败的话),工作更多。如果钱少活多对你没有吸引力,那么我建议你不要加入创业公司。

--

2、

苹果公司的策略是,不要让第三方应用赚到大钱,便宜的第三方应用会让人们更愿意购买 iPhone。所以,它通过应用商店,让第三方开发者的收入减少 30%。

???????--。使用某种产品时,需要配合使用的其他东西,叫做补充品,比如,果酱是面包的补充品。如果补充品非常便宜,将促进所补充的那种产品的销售。因此,聪明的公司会让补充品变得便宜,并形成一个专门的商品市场。

3、

当市场出现大的热潮时,最好的策略通常不是参与这个热潮,而是成为工具提供者。

--

4、

???????统计发现,城市越大,人们的步行速度越快。

--

欢迎订阅

???????这个专栏每周五发布,同步更新在我的、和。

微信搜索"阮一峰的网络日志 "或者扫描二维码,即可订阅。

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名()
  • 发表日期:2019年2月 1日
  • 更多内容:»
  • 文集:,
  • 社交媒体:,
0     0

fwnace.com 聚合 | 评论: 0 |

]]>
0