当前位置: 首页 > news >正文

网站后台的验证码百度蜘蛛

网站后台的验证码,百度蜘蛛,儿童网页设计,兰州网络推广范文原文#xff1a;https://scene-si.org/2018/07/24/writing-great-go-code/我写了多年的 Go 微服务#xff0c;并在写完两本关于 (API Foundations in Go 和 12 Factor Applications with Docker and Go) 主题的书之后#xff0c;有了一些关于如何写好 Go 代码的想法但首先https://scene-si.org/2018/07/24/writing-great-go-code/我写了多年的 Go 微服务并在写完两本关于 (API Foundations in Go 和 12 Factor Applications with Docker and Go) 主题的书之后有了一些关于如何写好 Go 代码的想法但首先我想给阅读这篇文章的读者解释一点。好代码是主观的。你可能对于好代码这一点有完全不同的想法而我们可能只对其中一部分意见一致。另一方面我们可能都没有错只是我们从两个角度出发从而选择了不同的方式解决工程问题并不意味着意见不一致的不是好代码。包包很重要你可能会反对 - 但是如果你在用 Go 写微服务你可以将所有代码放在一个包中。当然下面也有一些反对的观点将定义的类型放入单独的包中维护与传输无关的服务层在服务层之外维护一个数据存储(repository)层我们可以计算一下一个微服务包的最小数量是 1。如果你有一个大型的微服务它拥有 websocket 和 http 网关你最终可能需要 5 个包(类型数据存储服务websocket 和 http 包)。简单的微服务实际上并不关心从数据存储层(repository)或者从传输层(websockethttp)抽离业务逻辑。你可以写简单的代码转换数据然后响应也是可以运行的。但是添加更多的包可以解决一些问题。例如如果你熟悉  SOLID 原则S 代表单一职责。如果我们拆分成包这些包就可以是单一职责的。types - 声明一些结构可能还有一些结构的别名等repository - 数据存储层用来处理存储和读取结构service - 服务层包装存储层的具体业务逻辑实现http, websocket, … - 传输层用来调用服务层当然根据你使用的情况还可以进一步细分例如可以使用types/request 和 types/response 来更好的分隔一些结构。这样就可以拥有 request.Message 和response.Message 而不是 MessageRequest和 MessageResponse。如果一开始就像这样拆分开可能会更有意义。但是为了强调最初的观点 - 如果你只用了这些声明包中的一部分也没什么影响。像 Docker 这样的大型项目在 server 包下只使用了 types  包这是它真正需要的。它使用的其他包(像 errors 包)可能是第三方包。同样需要注意的是在一个包中共享正在处理的结构和函数会很容易。如果你有相互依赖的结构将它们拆分为两个或多个不同的包可能会导致钻石依赖问题。解决方案也很显然 - 将代码放到一块儿或者将所有代码放在一个包中。到底选哪一个呢两种方法都行。如果我非要按规则来的话将其拆分更多的包可能会使添加新代码变得麻烦。因为你可能要修改这些包才能添加单个 API 调用。如果不是很清楚如何布局那么在包之间跳转可能会带来一些认知上的开销。在很多情况下如果项目只有一两个包阅读代码会更容易。你肯定也不想要太多的小包。错误如果是描述性的 Errors 可能是开发人员检查生产问题的唯一工具。这就是为什么我们要优雅地处理错误要么将它们一直传递到应有程序的某一层如果错误无法处理该层就接收错误并记录下来这一点非常重要。以下是标准库错误类型缺少的一些特性错误信息不含堆栈跟踪不能堆积错误errors 是预实例化的但是通过使用第三方错误包(我最喜欢的是pkg/Errors.))可以帮助解决这些问题。也有其他的第三方错误包但是这个是 Dave Cheney (Go 语言大神)编写的它在错误处理的方式在一定程度上是一种标准。他的文章 Don’t just check errors, handle them gracefully 是推荐必读的。错误的堆栈跟踪pkg/errors 包在调用 errors.New 时会将上下文(堆栈跟踪)添加到新建的错误中。users_test.go:34: testing error Hello world        github.com/crusttech/crust/rbac_test.TestUsers                /go/src/github.com/crusttech/crust/rbac/users_test.go:34        testing.tRunner                /usr/local/go/src/testing/testing.go:777        runtime.goexit                /usr/local/go/src/runtime/asm_amd64.s:2361考虑到完整的错误信息是 Hello world使用 fmt.Printf 带有%v 的参数或者类似的方式来打印少量的上下文 - 对于查找错误的而言是一件很棒的事。你可以确切知道是哪里创建了错误(关键字)。当然当涉及到标准库时errors 包和本地 error 类型 - 不提供堆栈跟踪。但是使用 pkg/errors 可以很容易地添加一个。例如resp, err : u.Client.Post(fmt.Sprintf(resourcesCreate, resourceID), body)if err ! nil {        return errors.Wrap(err, request failed)}在上面这个例子中pkg/errors包将上下文添加 err 中加的错误消息(request failed) 和堆栈跟踪都会抛出来。通过调用 errors.Wrap 来添加堆栈跟踪所以你可以精准追踪到此行的错误。堆积错误你的文件系统数据库或者其他可能抛出相对不太好描述的错误。例如Mysql 可能会抛出这种强制错误ERROR 1146 (42S02): Table test.no_such_table doesnt exist这不是很好处理。然而你可以使用 errors.Wrap(errdatabase aseError) 在上面堆积新的错误。这样就可以更好地处理 databaseError 等。pkg/errors 包将在 causer 接口后面保留实际的错误信息。type causer interface {       Cause() error}这样错误堆积在一起不会丢失任何上下文。附带说一下mysql 错误是一个类型错误其背后包含的不仅仅是错误字符串的信息。这意味着它有可能被处理的更好if driverErr, ok : err.(*mysql.MySQLError); ok {    if driverErr.Number  mysqlerr.ER_ACCESS_DENIED_ERROR {        // Handle the permission-denied error    }}此例子来自于 this stackoverflow thread。错误预实例化究竟什么是错误(error)呢非常简单错误需要实现下面的接口type error interface {    Error() string}在 net/http 的例子中这个包将几种错误类型暴露为变量如文档所示。在这里添加堆栈跟踪是不可能的(Go不允许对全局 var 声明可执行代码只能进行类型声明)。其次如果标准库将堆栈跟踪添加到错误中 - 它不会指向返回错误的位置而是指向声明变量(全局变量)的位置。这意味着你仍然需要在后面的代码中强制调用类似于  return errors.WithStack(ErrNotSupported) 的代码。这也不是很痛苦但不幸的是你不能只导入 pkg/errors 就让所有现有的错误都带有堆栈跟踪。如果你还没有使用 errors.New 来实例化你的错误那么它需要一些手动调用。日志接下来是日志或者更恰当的说结构化日志。这里提供了许多软件包类似于 sirupsen/logrus 或我最喜欢的APEX/LOG。这些包也支持将日志发送到远程的机器或者服务我们可以用工具来监控这些日志。当谈到标准日志包时我不常看到的一个选项是创建一个自定义 logger并将 log.LShorfile 或 log.LUTC 等标志传递给它以再次获得一点上下文这能让你的工作变轻松 - 尤其在处理不同时区的服务器时。const (        Ldate          1 iota     // the date in the local time zone: 2009/01/23        Ltime                         // the time in the local time zone: 01:23:23        Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.        Llongfile                     // full file name and line number: /a/b/c/d.go:23        Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile        LUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone        LstdFlags      Ldate | Ltime // initial values for the standard logger)即使你没有创建自定义 logger你也可以使用 SetFlags 来修改默认 logger。(playground link)package mainimport (    log)func main() {    log.SetFlags(log.LstdFlags | log.Lshortfile)    log.Println(Hello, playground)}结果如下2009/11/10 23:00:00 main.go:9: Hello, playground你不想知道你在哪里打印了日志吗这会让跟踪代码变得更容易。接口如果你正在写接口并命名接口中的参数请考虑以下的代码片段type Mover interface {    Move(context.Context, string, string) error}你知道这里的参数代表什么吗只需要在接口中使用命名参数就可以让它很清晰。type Mover interface {    Move(context.Context, source string, destination string)}我还经常看到一些使用一个具体类型作为返回值的接口。一种未得到充分利用的做法是根据一些已知的结构体或接口参数以某种方式声明接口然后在接收器中填充结果。这可能是 Go 中最强大的接口之一。type Filler interface {    Fill(r *http.Request) error}func (s *YourStruct) Fill(r *http.Request) error {    // here you write your code...}更可能的是一个或多个结构体可以实现该接口。如下type RequestParser interface {    Parse(r *http.Request) (*types.ServiceRequest, error)}此接口返回具体类型(而不是接口)。通常这样的代码会使你代码库中的接口变得杂乱无章因为每个接口只有一个实现并且在你的应用包结构之外会变得不可用。小帖士如果你希望在编译时确保你的结构体符合并完全实现一个接口(或多个接口)你可以这么做var _ io.Reader  YourStruct{}var _ fmt.Stringer  YourStruct{}如果你缺少这些接口所需的某些函数编译器就会报错。字符 _ 表示丢弃变量所以没有副作用编译器完全优化了这些代码会忽视这些被丢弃的行。空接口与上面的观点相比这可能是更有争议的观点 - 但是我觉得使用 interface{} 有时非常有效。在 HTTP API 响应的例子中最后一步通常是 json 编码它接收一个接口参数func (enc *Encoder) Encode(v interface{}) error因此完全可以避免将 API 响应设置成具体类型。我并不建议对所有情况都这么处理但是在某些情况下可以在 API 中完全忽略响应的具体类型或者至少说明具体类型声明的意义。脑海中浮现的一个例子是使用匿名结构体。body : struct {    Username string   json:username    Roles    []string json:roles,omitempty}{username, roles}首先不使用 interface{} 的话无法从函数里返回这种结构体。显然json 编码器可以接受任何类型的内容因此按传递空接口(对我来说)是完全有意义的。虽然趋势是声明具体类型但有时候你可能不需要一层中间层。对于包含某些逻辑并可能返回各种形式的匿名结构体的函数空接口也很合适。更正匿名结构体不是不可能返回只是做起来很麻烦playground感谢 Ikearens at Discord Gophers #golang channel第二个用例是数据库驱动的 API 设计我之前写过一些有关内容我想指出的是实现一个完全由数据库驱动的 API 是非常可能的。这也意味着添加和修改字段是仅仅在数据库中完成的而不会以 ORM 的形式添加额外的间接层。显然你仍然需要声明类型才能在数据库中插入数据但是从数据库中读取数据可以省略声明。// getThread fetches comments by data, order by IDfunc (api *API) getThread(params *CommentListThread) (comments []interface{}, err error) {    // calculate pagination parameters    start : params.PageNumber * params.PageSize    length : params.PageSize    query : fmt.Sprintf(select * from comments where news_id? and self_id? and visible1 and deleted0 order by id %s limit %d, %d, params.Order, start, length)    err  api.db.Select(comments, query, params.NewsID, params.SelfID)    return}同样你的应用程序可能充当反向代理或者只使用无模式(schema-less)的数据库存储。在这些情况下目的只是传递数据。一个大警告(这是你需要输入结构体的地方)是修改 Go 中的接口值并不是一件容易的事。你必须将它们强制转换为各种内容如 map、slice 或结构体以便可以在访问这些返回的数据。如果你不能保持结构体一成不变而只是将它从 DB(或其他后端服务)传递到 JSON 编码器(会涉及到断言成具体类型)那么显然这个模式不适合你。这种情况下不应该存在这样的空接口代码。也就是说当你不想了解任何关于载荷的信息时空接口就是你需要的。代码生成尽可能使用代码生成。如果你想生成用于测试的 mock如果你想生成 proc/GRPC 代码或者你可能拥有的任何类型的代码生成可以直接生成代码并提交。在发生冲突的情况下可以随时将其丢弃然后重新生成。唯一可能的例外是提交类似于 public_html 文件夹的内容其中包含你将使用 rakyll/statik 打包的内容。如果有人想告诉我由 gomock 生成的代码在每次提交时都会以兆字节的数据污染 GIT 历史记录不会的。结束语关于 Go 的最佳实践和最差实践的另一本值得注意的好书应该是Idiomatic Go。如果你不熟悉的话可以阅读一下 - 它是与本文很好的搭配。我想在这里引用Jeff Atwood post - The Best Code is No Code At All文章的一句话这是一句令人难忘的结束语如果你真的喜欢写代码你会非常喜欢尽可能少地写代码。但是一定要编写那些单元测试。完结。
http://www.zqtcl.cn/news/752424/

相关文章:

  • 网站建设公司 跨界鱼科技专业简述网站的制作步骤
  • 手机网站cms网站优化推广哪家好
  • 网站被k换域名qq空间 wordpress
  • 网站模板种类昆明cms建站模板
  • wordpress em企业网站做seo的优势
  • 设计师做私单网站全球最大的电商平台
  • 外贸联系网站wordpress 优惠券 插件
  • 公司网站开发费用兴田德润官方网站深圳百度快照优化
  • 做网站需要备案么行业网站策划
  • 去年做啥网站能致富周口seo推广
  • 主体负责人电话修改 网站备案什么样算网站需要备案
  • 网站建站免费空间外贸网站建设与优化
  • 网站极简设计建立网站基本知识
  • 网站建设管理标准wordpress rss采集
  • 乐清网站建设费用装修房子的app软件哪个好
  • 专业网站搭建运营工业网站素材
  • 建网站要会什么wordpress电影下载站
  • 济南设计网站的公司西安模板网站建设
  • 网站搜索功能如何实现网络培训学习心得体会
  • 网站设计方案书ppt网站展示型推广
  • 中国建设注册管理中心网站首页大连地区建设网站
  • 广州致峰网站建设藁城网络推广
  • 怎么做免费个人网站wordpress dux 5.3
  • 手机触屏版网站网站功能介绍
  • 商场设计案例青岛百度快速排名优化
  • 制作网站要步骤湖北省建设厅网站上岗证查询
  • 网站建设制作公司都选万维科技制作网站需要注意什么
  • jsp小型网站开发wordpress微博插件
  • app充值网站开发怎么去做网站
  • 合肥建站网站模板word上下页纸张方向