PHPer 快速熟悉 Golang

创作人 Leo


编辑时间 Thu May 21,2020 at 14:21


解释型 vs 编译型

php 是解释型语言,通过运行时实时将文字码转换成字节码执行,所以,php 源文件修改后直接执行 php xxx 或者刷新网页即可看到效果

go 是编译型语言,在运行前需要转换成2进制可执行文件才能执行,所以,go 源文件改动后,必须重新执行 go run xx 或者 go build xxx 才能看到效果

弱类型 vs 强类型

php

弱类型语言,在使用变量或者常量时不需要声明数据类型

go

强类型语言,需要明确指定数据类型

var a int = 1

或者也可以自动发现类型

a := 1

自动发现类型不同于 php 的弱类型,类型还是存在的,只是编译器帮你做了这部分
自动发现类型在特定情况下会有问题,比如用 a 和一个明确声明 int64 类型的变量做运算,就会报错:类型不匹配(因为a会被自动声明为 int )

类 vs 结构体

php 通过 class 声明一个类,实现数据结构,定义成员,和成员对应的操作

go 通过 struct 定义数据结构

package main

import (
	"fmt"
)

type Ca struct {
	Name string 
}

func (c *Ca) SetName(n string) {
	c.Name = n
}

func (c *Ca) GetName() string {
	return c.Name
}

继承 vs 组合

php 通过继承来扩展一个超类

go 通过组合将一个或多个超类的功能集成进来并扩展

package main

import (
	"fmt"
)

type Ca struct {
	Name string 
}

func (c *Ca) SetName(n string) {
	c.Name = n
}

func (c *Ca) GetName() string {
	return c.Name
}

type Cb struct {
	Ca
}

func (c *Cb) PrintName() {
	fmt.Println(c.Ca.Name)
}

func main() {
	c := new(Cb)
	c.SetName("lx")
	c.PrintName() 
	fmt.Println(c.GetName())
}

显示实现 vs 隐式实现

php 通过 implement 关键字,明确指定实现哪一个 interface

go 通过完全实现对应 interface 的所有函数声明,来实现一个 interface

package main

import (
	"fmt"
)

type Cinterface interface {
	SetName(string) 
	GetName() string 
}

type Ca struct {
	Name string 
}

func (c *Ca) SetName(n string) {
	c.Name = n
}

func (c *Ca) GetName() string {
	return c.Name
}

// Ca 实现了 Cinterface 接口
// Cb 组合了 Ca,变相实现了 Cinterface 接口

type Cb struct {
	Ca
}

func (c *Cb) PrintName() {
	fmt.Println(c.Ca.Name)
}

func itest(c Cinterface) {
	fmt.Println(c.GetName())
}

func main() {
	c := new(Cb)
	c.SetName("lx")
	
	c2 := new(Ca)
	c2.SetName("lx-a")
	
	itest(c)
	itest(c2)
}

顺序执行 vs 支持并发

php 是从上到下顺序执行,后面的代码需要等待前面代码执行完成才会执行

go 可以通过关键字 go 让一段代码异步执行或者并行执行多段程序

package main

import (
	"fmt"
	"sync"
	"net/http"
	"io/ioutil"
)

func main() {
	var wg sync.WaitGroup // 可以批量等待一批异步任务返回

	url := "http://kong.localstudy.com/" 

	for i:=0; i<10; i++ {
		wg.Add(1)

		go func() {
			defer wg.Done()
			res,err := http.Get(url)
			if err!=nil {
				fmt.Println(err)
				return
			}

			defer res.Body.Close()

			b, err := ioutil.ReadAll(res.Body)
			if err != nil {
				fmt.Println(err)
				return
			}

			fmt.Println(string(b))
		}()
	}

	wg.Wait()
}

Exception vs panic

php 通过 Exception 触发运行时异常,并可以通过 try catch 从异常中恢复并继续执行

go 通过 panic 触发运行时异常,并可以通过 defer recover 从异常中恢复并继续执行

package main

import (
	"fmt"
)

func main() {
	test()

	fmt.Println("it can not be print if test() didn't recover from panic")
}

func test() {
	defer func () {
		e := recover()

		fmt.Println(e)
	}()

	test2()

	fmt.Println("it can not be print because test2() triggerd off a panic")
}

func test2() {
	panic("panic test")
}

依赖web容器 vs 自给自足

php 一般用来提供web服务,通过 sapi 接口或者 fastcgi 对接到 Apache 或者 nginx 服务器

go 也可以提供网页服务,可以直接通过内置的 http 模块对外提供web服务

package main

import (
	"fmt"
	"net/http"
	"html"
	"log"
)

func main() {
	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
	})

	log.Fatal(http.ListenAndServe(":10020", nil))
}

命名空间 vs 包

php 通过 namespace 将功能整合到一个命名空间

go 通过 package 将功能整合到一个包

主文件 vs 主函数

php 执行时通过执行一个 php 文件,然后 include 或者 require 需要的功能文件,来运行整个程序

go 执行时通过在 main 包声明 main 函数,在 main 函数中对整个程序进行初始化来运行整个程序

作用域

php 函数外的变量作用域是文件,函数内不能调用函数外的变量,include/require 之后,相当于是把目标文件代码复制到该文件

go 在函数外声明的变量属于全局变量,整个包的任何位置都可以访问,如果该变量首字母大写,其他包可以通过 包名.变量名 的形式调用

语法细节

语句
php 通过分号 ; 代表语句结束
go 不需要使用分号,通过换行即可标识语句结束

字符串声明
php 使用单引号或者双引号声明字符串
go 只能使用双引号声明字符串,因为单引号是字符类型

成员导出
php include 或者 require 后即可使用上文的全部内容(变量,常量,函数,类)
go 语言使用 import 的包中的成员,该成员必须首字母大写

返回值
php 只能返回一个值
go 可以返回多个值,如果部分返回值不需要,可以使用下划线 _ 忽略(注意,忽略需要 Close 的资源类型,可能会造成内存溢出)

对比

x php go
运行方式 所见即所得 需要编译
抗攻击 可以上传php脚本文件攻击服务器 编译成2进制代码,运行中无法插入代码
发布 批量同步后,替换文件夹可以做到用户无感 需要停服,可以通过灰度发布做到用户无感,相对麻烦
代码安全 弱类型,容易造成运行时异常 强类型,编译过程中会暴露出大部分异常

更多细节

避免循环引用

A 明确 import B,B 又明确 import A,会造成循环引用错误
编写 go 时需要明确代码包层级,避免循环引用,每个包明确所处位置是负责底层逻辑还是负责表现层
一般情况下由表现层引入多个底层包,通过数据传递让各个底层包协作

协程注意事项

协程中的 panic 会引发整个程序挂掉,所以一般在协程函数的开头建议通过 defer + recover 处理可能出现的 panic 防止一个点出问题,全局崩溃的尴尬局面

区别于 php,go 语言启动后,很多变量可以整个程序共享,所以 go 语言在连接数据库时一般都是使用连接池

轮子

go 有很多优秀的社区,而且国内外很多大厂在用 go ,所以有很多优秀的包,这里推荐几个发现好工具的网站
官方包 第一位当然是 go 官方包,作为定位为网络编程的语言,大部分 web 相关功能官方都已经实现了,甚至包括 http 模板,常用的字符串处理、加密、编解码都有,不要忽略页面下方的 x 系列包
Gorilla 提供 web 相关技术模块,包含 rpc、ws 等
pkg.go 一个 go 语言包管理网站,可以通过搜索找到很多好用的包,而且可以查看各个版本,非常适合配合 go mod 使用


阅读:19
搜索
  • Linux 高性能网络编程库 Libevent 简介和示例 1899
  • Mac系统编译PHP7【20190929更新】 1765
  • Windows 安装Swoole 1546
  • Hadoop 高可用集群搭建 (Hadoop HA) 1438
  • Hadoop 高可用YARN 配置 1362
  • 小白鼠问题 1290
  • Hadoop Map Reduce 案例:好友推荐 1241
  • 自动化测试工具 Selenium 1103
  • GIT 分支管理 1022
  • Golang 使用 Grpc 968
简介
不定期分享软件开发经验,生活经验