月盾的博客

beego注解路由404

月盾

beego注解路由匹配不到,返回404页面 router.go使用了两种方式注册路由:

ns := beego.NewNamespace("/admin",
	beego.NSRouter("/", &controllers.UserController{}, "get:Welcome"),
	beego.NSInclude(
		&controllers.UserController{},
	),

controller中的路由注解设置:

// @router /admin/user/get-all-user [get]
func (c *UserGroupController) GetAllUser() {
	user := new(User)
	users, err := user.GetUserList()
	if nil != err {
		c.Data["json"] = ErrorMsg(err)
	}
	c.Data["json"] = users
	c.ServeJSON()
}

使用上面的方式注册路由后结果是nomatch

最终结果显示上面的注解路由时错误的,下面是正确的注册方式: 问题在于controller的注解写法,如果该路由在namespace下,则不能在注解中拼接命名空间前缀,框架会自动拼接。 即/admin为命名空间,注解中只需写/user/get-all-user,不能这样写/admin/user/get-all-user

// @router /user/get-all-user [get]
func (c *UserGroupController) GetAllUser() {
	user := new(User)
	users, err := user.GetUserList()
	if nil != err {
		c.Data["json"] = ErrorMsg(err)
	}
	c.Data["json"] = SuccessData(users)
	c.ServeJSON()
}

当然,两种路由注册的方式可以同时

完整项目:https://github.com/yuedun/metal

go并发获取数据

月盾

go语言可以很轻松的实现并发获取数据,就算是新手也可以按部就班的套用现成的并发模式来实现并发。以下是一个简单的测试程序,其中有串行,并行。

package main

import (
	"sync"
	"time"
	"fmt"
)
func main() {
	syncFunc()
	fmt.Println(">>>>>>>>>>>>>>>")
	asyncFunc()
	fmt.Println(">>>>>>>>>>>>>>>")
	asyncChanFunc()
}
// 串行执行
func syncFunc() {
	var n,m,x int
	start := time.Now()
	fmt.Println("syncFunc start:",start)
	func () {
		time.Sleep(time.Second*1)
		n = 1
	}()
	func () {
		time.Sleep(time.Second*2)
		m = 2
	}()
	func () {
		time.Sleep(time.Second*3)
		x  =3
	}()
	t := time.Now()
	fmt.Println(t)
	elapsed := t.Sub(start)
	fmt.Println("syncFunc end:", elapsed, n, m, x)
}
// 并行执行
func asyncFunc() {
	var n,m,x int
	var wg sync.WaitGroup
	wg.Add(3)
	start := time.Now()
	fmt.Println("asyncFunc start:", start)
	go func () {
		defer wg.Done()
		time.Sleep(time.Second*1)
		n = 1
	}()
	go func () {
		defer wg.Done()
		time.Sleep(time.Second*2)
		m = 2
	}()
	go func () {
		defer wg.Done()
		time.Sleep(time.Second*3)
		x = 3
	}()
	wg.Wait()
	t := time.Now()
	fmt.Println(t)
	elapsed := t.Sub(start)
	fmt.Println("asyncFunc end:", elapsed, n, m, x)
}

// 并行执行
func asyncChanFunc() {
	var n, m, x =make(chan int),make(chan int),make(chan int)
	start := time.Now()
	fmt.Println("asyncChanFunc start:",start)
	go func () {
		time.Sleep(time.Second*1)
		n <- 1
	}()
	go func () {
		time.Sleep(time.Second*2)
		m <- 2
	}()
	go func () {
		time.Sleep(time.Second*3)
		x <- 3
	}()

	fmt.Printf("n:%d, m:%d, x:%d\n",<-n, <-m, <-x)
	t := time.Now()
	fmt.Println(t)
	elapsed := t.Sub(start)
	fmt.Println("asyncChanFunc end:", elapsed)
}

测试结果:

go测试函数的编写及运行

月盾

go test命令是一个按照一定的约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源文件并不是go build构建包的一部分,它们是go test测试的一部分。 在\*_test.go文件中,有三种类型的函数:测试函数、基准测试函数、示例函数。一个测试函数是以Test为函数名前缀的函数,用于测试程序的一些逻辑行为是否正确; go test命令会调用这些测试函数并报告测试结果是PASS或FAIL。基准测试函数是以Benchmark为函数名前缀的函数,它们用于衡量一些函数的性能;go test命令会多次运行基准函数以计算一个平均的执行时间。示例函数是以Example为函数名前缀的函数,提供一个由编译器保证正确性的示例文档。

测试函数

每个测试函数必须导入testing包。测试函数有如下的签名:

func TestName(t *testing.T) {
// ...
}

测试函数的名字必须以Test开头,可选的后缀名必须以大写字母开头:

func TestSin(t *testing.T) { /* ... */ }
func TestCos(t *testing.T) { /* ... */ }
func TestLog(t *testing.T) { /* ... */ }

其中t参数用于报告测试失败和附加的日志信息。让我们定义一个实例包gopl.io/ch11/word1,其中只有一个函数IsPalindrome用于检查一个字符串是否从前向后和从后向前读都是一样的。(下面这个实现对于一个字符串是否是回文字符串前后重复测试了两次;我们稍后会再讨论这个问题。)

// gopl.io/ch11/word1
// Package word provides utilities for word games.
package word

// IsPalindrome reports whether s reads the same forward and backward.
// (Our first attempt.)
func IsPalindrome(s string) bool {
	for i := range s {
		if s[i] != s[len(s)-1-i] {
			return false
		}
	}
	return true
}

在相同的目录下,word_test.go测试文件中包含了TestPalindrome和TestNonPalindrome两个测试函数。每一个都是测试IsPalindrome是否给出正确的结果,并使用t.Error报告失败信息

Golang- import 导入包的语法

月盾

一、 包的导入语法 在写Go代码的时候经常用到import这个命令用来导入包文件,看到的方式参考如下:

import(
	"fmt"
)

然后在代码里面可以通过如下的方式调用

fmt.Println("hello world")

上面这个fmt是Go语言的标准库,他其实是去GOROOT下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:

相对路径

import   "./model"  //当前文件同一目录的model目录,但是不建议这种方式import`

绝对路径

import   "shorturl/model"  //加载GOPATH/src/shorturl/model模块`

上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面是三种导入包的使用方法。

  1. 点操作

有时候会看到如下的方式导入包

import( 
    . "fmt" 
)

这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的

fmt.Println("hello world")

可以省略的写成

Println("hello world")
  1. 别名操作

别名操作顾名思义可以把包命名成另一个用起来容易记忆的名字

import(
  f "fmt"
)

别名操作调用包函数时前缀变成了重命名的前缀,即

f.Println("hello world")
  1. _操作

这个操作经常是让很多人费解的一个操作符,请看下面这个import

import ( 
  "database/sql" 
  _ "github.com/ziutek/mymysql/godrv" 
)

_操作其实只是引入该包。当导入一个包时,它所有的init()函数就会被执行,但有些时候并非真的需要使用这些包,仅仅是希望它的init()函数被执行而已。这个时候就可以使用_操作引用该包了。即使用_操作引用包是无法通过包名来调用包中的导出函数,而是只是为了简单的调用其init函数()。

二、 包的导入过程说明 程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。下图详细地解释了整个执行过程:

go包导入过程

通过上面的介绍我们了解了import的时候其实是执行了该包里面的init函数,初始化了里面的变量,_操作只是说该包引入了,只初始化里面的init函数和一些变量,不能通过包名来调用其它的函数,这有什么用呢?往往这些init函数里面是注册自己包里面的引擎,让外部可以方便的使用,就很多实现database/sql的引起,在init函数里面都是调用了sql.Register(name string, driver driver.Driver)注册自己,然后外部就可以使用了。

原文地址:http://blog.csdn.net/zhangzhebjut/article/details/25564457

github提交不记录Contributions

月盾

github上提交了很多commit但是没有Contributions绿色方块,原因是提交的email和github不匹配,使用git log查看记录中使用的邮箱是否是github的邮箱,如果不是也不需要做什么修改,只需要在github上添加对应的邮箱地址即可。

右上角头像-settings-emails-Add email address

验证邮箱后就会立马重新统计

pm2设置NODE_ENV环境变量

月盾

nodejs中经常使用到环境变量,最常见的如:process.env.NODE_ENV。那么在生产环境中使用pm2如何设置环境变量?

设置方式一:shell命令设置

linuxexport NODE_ENV=development&& node app.js

winset NODE_ENV=development&& node app.js

一般是作临时变量在系统启动时设置,不影响其他系统,也可同时运行开发环境和生产环境,只需要根据process.env.NODE_ENV来运行不同逻辑即可。

设置方式二:配置文件设置

要在pm2设置环境变量也很简单。

pm2 start pm2.json –env production

--env production参数是为了设置环境变量,由pm2.json中的配置决定设置什么样的环境变量。

pm2.json

{
  "apps" : [{
    "name": "issue",
    "cwd": "dest",
    "script"    : "bin/www.js",
    "instances" : "2",
    "exec_mode" : "cluster",
    "env": {
      "NODE_ENV": "development",
      "PORT": 3002
    },
    "env_production" : {
       "NODE_ENV": "production",
       "PORT": 3003
    },
    "log_date_format": "YYYY-MM-DD_HH:mm Z",
    "merge_logs": true
  }]
}

如果不加参数则默认使用

"env": {
    "NODE_ENV": "development",
    "PORT": 3002
}

结果:NODE_ENV=development,PORT=3002

--env production则使用的是

"env_production" : {
    "NODE_ENV": "production",
    "PORT": 3003
}

结果:NODE_ENV=production,PORT=3003

webpack构建vue项目警告

月盾
bundle.js:935 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

(found in <Root>)

解决方案:与entry属性平级添加

resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
			//该路径为node_modules下的vue目录
        }
    }
// 需要编译器
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 不需要编译器
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

出现警告的原因是使用了template属性。 文文点到为止,详情请直接访问中文官方文档https://cn.vuejs.org/v2/guide/installation.html#对不同构建版本的解释

再聊docker和nodejs

月盾

上一篇写到了如何在docker中运行nodejs,运行方式是在docker中安装了pm2来保证node服务宕机重启,这种方式更像是把docker当做虚拟机来使用。其实,既然使用了docker的话就可以不使用pm2来管理进程,因为docker自身可以充当守护进程,在node进程退出时进行重启。只要在启动docker容器时加上–restart=always参数即可。例如:docker run -d --restart=always -p 3000:3000 mynode:1

没有pm2如何开启多进程

使用pm2可以开启多node进程,并且自带负载均衡,但是有个限制,pm2可以开启的进程数是CPU最大核心数。而使用docker的话就不会受限于此了,开启几十个上百个node服务都可以,然后通过nginx实现负载均衡。不过要手动开启几十上百个docker容器那怎么行?让我手动开启3个都很烦了,这时候就需要用到docker编排工具了,比如:Docker Swarm、Kubernetes、docker compose等,可以一键开启多个容器。但是使用编排工具启动docker端口就不确定了,是由编排工具随机开启服务端口的,这又要做到服务注册发现,所以这些工具结合起来使用。

哪一种部署方式支持并发高?

使用jmeter在本机上进行了简单的并发测试,服务端进行简单的10万次hash计算,使用pm2开启4个实例,docker开启5个实例。docker使用Nginx做负载均衡,单次访问响应时间在1.2s~1.4s之间不等,在200个并发的情况下,两种模式响应时间相差不大,docker模式响应时间略占优势,大概快了0.1s。当并发数在300以上时两者的响应时间都有增加,此时docker部署方式出现了响应失败的情况,pm2就比较稳定了,虽然响应时间增加,但是并未出现过响应失败。 所以在单机上低并发docker还是有点优势,如果在高并发情况下还是pm2更稳定一些。(以上测试是单机上进行,准确性并不高)

pm2日志记录和日志分割

月盾

pm2介绍

pm2是nodejs进程管理工具,现在基本是node生产服务器的标准选择,可以帮助我们实现node多进程服务,开启的多个实例自动实现负载均衡。 最重要的是保证node单进程不会因为错误退出,作为守护进程保证nodejs服务不宕机。 总体来说就是有性能监控、自动重启、负载均衡的作用。

pm2-logrotate介绍

pm2本身是可以输出日志文件的,默认的文件路径:

error log path    │ /home/username/.pm2/logs/app-error-0.log
out log path      │ /home/username/.pm2/logs/app-out-0.log 

但是pm2的日志文件不能自动分割,这会导致一个文件不断变大,不但影响性能,查看这些日志也会带来麻烦。所以需要pm2-logrotate来实现自动分割日志。

安装pm2-logrotate

pm2 install pm2-logrotate,是用pm2命令,不是npm命令

pm2-logrotate配置

  • max_size (默认 10M): 最大为多少时进行分割,例如: 10G, 10M, 10K
  • retain (Defaults to all): This number is the number of rotated logs that are keep at any one time, it means that if you have retain = 7 you will have at most 7 rotated logs and your current one.
  • compress (默认 false): 是否压缩日志
  • dateFormat (默认 YYYY-MM-DD_HH-mm-ss) : 日志格式
  • rotateModule (Defaults to true) : Rotate the log of pm2’s module like other apps
  • workerInterval (Defaults to 30 in secs) : You can control at which interval the worker is checking the log’s size (minimum is 1)
  • rotateInterval (Defaults to 0 0 * * * everyday at midnight): This cron is used to a force rotate when executed. We are using node-schedule to schedule cron, so all valid cron for node-schedule is valid cron for this option. Cron style :
  • TZ (Defaults to system time): This is the standard tz database timezone used to offset the log file saved. For instance, a value of Etc/GMT-1, with an hourly log, will save a file at hour 14 GMT with hour 13 GMT-1 in the log name.
*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    |
│    │    │    │    │    └ day of week (0 - 7) (0 or 7 is Sun)
│    │    │    │    └───── month (1 - 12)
│    │    │    └────────── day of month (1 - 31)
│    │    └─────────────── hour (0 - 23)
│    └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, OPTIONAL)

可以尝试使用pm2 set pm2-logrotate:max_size 1K (1KB)设置日志文件最大为1KB 代码中console.log的内容会输出到 /home/username/.pm2/logs/app-out-0.log, 大小达到1KB就会自动生成 app-out-0__2018-01-25_16-58-16.log这种格式的日志文件。 在/home/username/.pm2/logs/(默认路径,可以配置)路径下还有