背景
本文目的旨在,大家看完文章就能上手进行Golang的业务开发实现需求,属于一篇Golang速成,如果有相关基础,后边会再出一篇高级篇,用来给大家做一下比较深入的讨论。
技术架构
Golang的设计目标之一是提高开发效率和代码可维护性,其技术架构也反映了这一点。
标准库
标准库的数量和质量常常决定了我们在开发过程中的上线,和其他主流开发语言一样,Golang拥有丰富的标准库,涵盖了从输入/输出、字符串处理、网络编程到数据序列化等各个方面。这使得开发者可以快速构建功能齐全的应用程序,而无需依赖第三方库。
并发模型
Golang的并发模型基于goroutine和channel。Goroutine是轻量级的线程,创建和销毁的开销很小。Channel则用于在goroutine之间传递消息和数据,实现高效的并发编程。在使用channel来进行并发处理的时候,大家会发现实现极其简单,不同水平的开发人员写出的代码几乎相差无几。
静态类型
Golang是静态类型语言,这意味着所有变量的类型在编译时确定。
编译和工具链
Golang的编译器非常高效,可以快速编译代码生成可执行文件。Golang的工具链还包括自动化测试、代码格式化、静态分析等工具,进一步提高了开发效率。同时还包含了RPC这种框架下的十分简洁的上下游调用链,对于开发来说会是一个比较舒服的调用过程
基础知识
ok,热身完毕,现在我们来聊一下Golang的一些基础知识,保证大家看完后可以立马上手开发。
安装和配置环境
要开始使用Golang,首先需要安装Golang的开发环境。你可以从Golang官方网站下载并安装相应版本。安装完成后,配置环境变量:
下面展示一些 内联代码片
。
export PATH=$PATH:/usr/local/go/bin
第一个程序
一个典型的Golang程序如下:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
上述代码定义了一个简单的main包,并在main函数中输出"Hello, World!"。
包管理
Golang的包管理是Golang极具特色的一个能力,相比于Java中多模块下多pom的管理方式,Golang的包管理更为简洁
每个包代表一个独立的功能模块,可以被其他包导入和使用。包的定义如下:
package math
func Add(a int, b int) int {
return a + b
}
编译和运行
使用go run命令可以直接运行Golang程序,当然如果用开发工具的话直接点点就可以了:
go run main.go
使用go build命令可以编译程序生成可执行文件:
go build -o myapp main.go
数据类型
Golang提供了丰富的数据类型,包括基本类型和复合类型。
整数类型:int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64
浮点类型:float32, float64
布尔类型:bool
字符串类型:string
复合类型(非常重要,非常重要,非常重要,因为Golang中没有太花里胡哨的东西,下面三个复合类型就囊括了99.99%的开发中使用的数据类型)
数组:固定长度的同类型元素的集合
切片:动态数组,可以改变长度
映射(map):键值对集合
结构体(struct):自定义类型,将多个字段组合在一起
示例代码
package main
import "fmt"
func main() {
// 基本类型
var i int = 10
var f float64 = 3.14
var b bool = true
var s string = "Golang"
fmt.Println(i, f, b, s)
// 数组
var arr [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Println(arr)
// 切片
var slice []int = []int{1, 2, 3}
fmt.Println(slice)
// 映射
var m map[string]int = map[string]int{"one": 1, "two": 2}
fmt.Println(m)
// 结构体
type Person struct {
Name string
Age int
}
var p Person = Person{Name: "Alice", Age: 25}
fmt.Println(p)
}```
数组
数组是固定长度的同类型元素的集合。声明数组时需要指定长度。
var arr [5]int
arr[0] = 1
fmt.Println(arr)
切片
切片是动态数组,可以改变长度。切片比数组更灵活,使用更频繁。
slice := []int{1, 2, 3}
slice = append(slice, 4)
fmt.Println(slice)
映射
映射(map)是一种键值对集合。键和值可以是任意类型。
m := make(map[string]int)
m["one"] = 1
fmt.Println(m)
结构体
结构体(struct)是一种聚合数据类型,将多个字段组合在一起。注意一下,struct还可以当作一个类的专属名称来使用
type Person struct {
Name string
Age int
}
p := Person{Name: "Bob", Age: 30}
fmt.Println(p)
//除此之外还可以这么用
func (Person *h)insert(int id){
//逻辑放在这里
//上面的参数就可以当作公有变量在这个类中使用
}
错误处理
Golang使用内置的error接口来处理错误。函数返回值通常包括结果和错误信息。这里错误处理还有一种defer的用法,可以来做一些兜底操作和处理,不过这篇文章主要是让大家快速上手和熟悉Golang这里就不做讲解了,感兴趣的大家可以自行搜索。
示例代码
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(4, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
开发常用
基础知识了解完毕就来聊一下开发中几乎必用的一些能力吧。
thrift
Thrift是一个用于跨语言服务开发的框架。Golang支持使用Thrift生成客户端和服务器代码。
安装Thrift
安装Thrift编译器:
brew install thrift
使用Thrift
定义Thrift接口文件,相当于Java中的swagger,但是这里更为直观例如service.thrift:
namespace go tutorial
service Calculator {
i32 add(1: i32 num1, 2: i32 num2)
}
生成Golang代码:
thrift -r --gen go service.thrift
并发
Golang以其强大的并发模型而著称。Golang中的并发编程基于goroutine和channel。
Goroutine
Goroutine是轻量级的线程,使用go关键字启动。
go func() {
fmt.Println("Hello from a goroutine")
}()
Channel
Channel用于在goroutine之间传递消息和数据。
ch := make(chan int)
go func() {
ch <- 42
}()
fmt.Println(<-ch)
然后可以以此堆叠,因为Golang的特性,你堆叠几十个乃至上百个都不会影响性能,所以在开发的过程中就要求下游只提供最基础的查询,然后全用并发来拼接,最后在上游完成处理。
数据库
Golang提供了多种数据库驱动,可以连接和操作不同类型的数据库。常见的数据库驱动有database/sql和gorm。
连接数据库
使用database/sql连接和操作数据库:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
log.Fatal(err)
}
defer db.Close()
查询数据
执行SQL查询:
rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
log.Fatal(err)
}
fmt.Println(id, name)
}
常见问题
在实际的Golang开发过程中,可能会遇到各种各样的问题。以下是一些常见问题及其解决方案。
Thrift版本问题
问题描述:
在使用Thrift进行跨语言服务开发时,可能会遇到Thrift版本不兼容的问题。这可能会导致生成的代码无法正常工作。
解决方案:
确保所有使用Thrift的项目使用相同的Thrift编译器版本。
使用go.mod文件来管理Thrift库的版本,确保所有依赖的一致性。
如果遇到版本不兼容的问题,可以尝试升级或降级Thrift编译器版本,或者手动修改生成的代码以兼容当前版本。
示例:
在项目的go.mod文件中指定Thrift库的版本:
module your_module_name
go 1.18
require (
github.com/apache/thrift v0.13.0
)
新引入依赖不生效
问题描述:
在项目中引入新的依赖包后,发现依赖包无法生效,导致编译错误或运行时错误。
解决方案:
确保已经正确添加新的依赖包,并运行了go mod tidy或go mod vendor命令来更新依赖。
检查项目的go.mod文件,确保新引入的依赖包已经被正确添加。
如果使用了IDE,尝试重启IDE以刷新依赖缓存。
确保没有使用旧版本的依赖包,可以尝试删除go.sum文件并重新运行go mod tidy。
示例:
在项目中引入新的依赖包并更新依赖:
go get github.com/gin-gonic/gin
go mod tidy(这个命令有事没事就可以敲一下,能解决7成左右的问题)
总结
本文介绍了Golang的基础知识,涵盖了从数据类型、数据结构、错误处理到并发编程和数据库操作的各个方面。通过阅读本文,读者应当能够掌握Golang的基本概念和编程技巧,并能够上手进行实际的开发。
package main
import (
"database/sql"
"errors"
"fmt"
"log"
"os"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// 基本数据类型
var i int = 10
var f float64 = 3.14
var b bool = true
var s string = "Golang"
fmt.Println(i, f, b, s)
// 数组
var arr [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Println(arr)
// 切片
var slice []int = []int{1, 2, 3}
slice = append(slice, 4)
fmt.Println(slice)
// 映射
var m map[string]int = map[string]int{"one": 1, "two": 2}
fmt.Println(m)
// 结构体
type Person struct {
Name string
Age int
}
var p Person = Person{Name: "Alice", Age: 25}
fmt.Println(p)
// 错误处理
result, err := divide(4, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
// 日志
log.Println("This is a log message")
// 配置管理
dbHost := os.Getenv("DB_HOST")
fmt.Println("DB Host:", dbHost)
// 并发
ch := make(chan int)
go func() {
ch <- 42
}()
fmt.Println(<-ch)
// 数据库连接
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 数据库查询
rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
log.Fatal(err)
}
fmt.Println(id, name)
}
}
// divide 函数用于演示错误处理
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}