第一章:Go语言入门与开发环境搭建
Go语言是由Google开发的一种静态类型、编译型语言,以其简洁、高效和并发支持良好而广受开发者喜爱。要开始编写Go程序,首先需要在系统中安装Go运行环境。
安装Go运行环境
前往 Go官方网站 下载适合你操作系统的安装包。以Linux系统为例,安装步骤如下:
# 下载Go二进制包
wget https://dl.google.com/go/go1.21.3.linux-amd64.tar.gz
# 解压到 /usr/local 目录
sudo tar -C /usr/local -xzf go1.21.3.linux-amd64.tar.gz
然后将Go的bin目录添加到系统环境变量中:
# 编辑用户环境变量配置文件
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
# 使配置立即生效
source ~/.bashrc
执行 go version
命令,如果输出类似 go version go1.21.3 linux/amd64
,则表示安装成功。
编写第一个Go程序
创建一个文件 hello.go
,内容如下:
package main
import "fmt"
func main() {
fmt.Println("Hello, Go language!")
}
执行以下命令运行程序:
go run hello.go
输出结果为:
Hello, Go language!
通过以上步骤,完成了Go语言开发环境的搭建,并成功运行了一个简单的程序。后续章节将深入讲解语言特性与项目开发技巧。
第二章:Go语言基础语法详解
2.1 变量、常量与数据类型
在编程语言中,变量是存储数据的基本单元,它具有名称、类型和值。变量的值在程序运行过程中可以改变,例如:
age = 25 # 变量 age 被赋值为整数 25
age
是变量名=
是赋值运算符25
是值,类型为整数(int)
与变量不同,常量的值一旦定义,通常不应被修改。虽然部分语言(如 Python)没有原生常量支持,但可以通过命名约定(如全大写)表示其不变性:
MAX_USERS = 100 # 约定为常量,表示最大用户数
在编程中,数据类型决定了变量可以存储哪种类型的数据,以及可以执行哪些操作。常见的基本数据类型包括:
数据类型 | 示例值 | 说明 |
---|---|---|
int | 10, -3 | 整数类型 |
float | 3.14, -0.001 | 浮点数(带小数点的数) |
str | “hello” | 字符串类型 |
bool | True, False | 布尔类型(逻辑值) |
不同数据类型之间可以进行转换,例如将字符串转为整数:
num_str = "123"
num_int = int(num_str) # 将字符串 "123" 转换为整数 123
在程序设计中,合理使用变量和常量,并选择合适的数据类型,有助于提高代码的可读性与执行效率。
2.2 运算符与表达式实战
在实际编程中,运算符与表达式的灵活运用是构建逻辑的核心基础。我们不仅需要理解其语法,更要掌握其在不同上下文中的行为。
逻辑表达式的短路特性
在使用逻辑与 &&
和逻辑或 ||
时,JavaScript 会进行短路求值:
let a = 0;
let b = a || 10;
console.log(b); // 输出 10
上述代码中,由于 a
为假值(falsy),表达式继续求值并返回 10
。这种模式常用于变量默认值赋值。
算术运算与类型转换
JavaScript 在执行算术运算时会自动进行类型转换,这在实际开发中尤其需要注意:
let result = '5' + 3; // 输出 '53'
let result2 = '5' - 3; // 输出 2
+
运算符在操作数中存在字符串时会执行拼接操作,而 -
则会强制转换为数字进行运算。这种差异要求开发者在编写表达式时格外小心。
2.3 控制结构:条件与循环
在程序设计中,控制结构是决定程序执行流程的核心机制。其中,条件语句和循环语句构成了逻辑控制的两大支柱。
条件语句:分支的抉择
条件语句通过判断布尔表达式决定程序走向。以 Python 为例:
if temperature > 30:
print("天气炎热,建议开空调") # 当温度高于30度时执行
elif temperature > 20:
print("天气宜人,适合户外活动") # 温度介于20~30之间时执行
else:
print("注意保暖") # 其他情况执行
上述代码中,程序根据 temperature
的值选择执行不同的代码块,实现逻辑分支。
循环语句:重复的优雅
循环用于重复执行某段代码。常见结构如 for
循环:
for i in range(5):
print(f"当前计数为:{i}")
该循环将执行 5 次,变量 i
从 0 到 4 递增。这种结构适用于已知迭代次数的场景。
条件与循环的融合
在实际开发中,条件和循环常结合使用,形成更复杂的逻辑控制。例如:
count = 0
while True:
user_input = input("请输入一个数字(输入 q 退出):")
if user_input == 'q':
break
try:
number = int(user_input)
count += 1
print(f"第 {count} 次输入,数值为 {number}")
except ValueError:
print("无效输入,请重新输入")
该代码实现一个持续输入的交互流程,直到用户输入 q
才退出循环。在每次循环中,程序判断输入是否合法,并进行相应的处理。
控制结构的流程示意
以下是上述 while
循环的流程图示意:
graph TD
A[开始循环] --> B{用户输入是否为 q}
B -- 是 --> C[退出循环]
B -- 否 --> D{输入是否为数字}
D -- 是 --> E[累加计数并输出]
D -- 否 --> F[提示无效输入]
E --> A
F --> A
通过这样的流程设计,程序能够灵活地处理各种输入情况,体现出控制结构在实际应用中的强大能力。
2.4 函数定义与参数传递
在 Python 中,函数是组织代码和实现复用的核心结构。使用 def
关键字可以定义一个函数,其基本形式如下:
def greet(name):
print(f"Hello, {name}!")
参数传递机制
Python 的参数传递采用“对象引用传递”的方式。当参数为不可变对象(如整数、字符串)时,函数内部修改不会影响外部;若为可变对象(如列表、字典),则可能被修改。
传参方式对比
参数类型 | 是否可变 | 是否影响外部 |
---|---|---|
不可变类型 | 否 | 否 |
可变类型 | 是 | 是 |
2.5 错误处理与defer机制
在系统编程中,错误处理是保障程序健壮性的关键环节。Go语言通过error
接口和defer
语句提供了一种清晰且高效的错误处理机制。
defer 的执行机制
defer
语句用于延迟执行某个函数调用,通常用于资源释放、文件关闭等操作。其执行顺序为后进先出(LIFO),即最后声明的 defer 函数最先执行。
示例代码如下:
func readFile() {
file, err := os.Open("data.txt")
if err != nil {
log.Fatal("无法打开文件:", err)
}
defer file.Close() // 延迟关闭文件
// 读取文件内容...
}
逻辑分析:
os.Open
尝试打开文件,若失败则通过log.Fatal
输出错误并终止程序;defer file.Close()
确保无论后续代码是否发生错误,文件都会在函数退出时被关闭;- 这种机制提高了代码的可读性与安全性,避免资源泄露。
错误处理与 defer 的结合使用
将错误处理与 defer 结合,可以在发生错误时统一进行清理操作,确保程序状态的一致性。例如:
func process() error {
conn, err := connectDB()
if err != nil {
return err
}
defer conn.Close()
// 执行数据库操作
if err := doSomething(conn); err != nil {
return err
}
return nil
}
逻辑分析:
connectDB
用于建立数据库连接;- 若连接失败,直接返回错误;
- 使用
defer conn.Close()
确保连接在函数返回前被释放; - 数据库操作过程中若出错,仍能保证资源正确释放。
defer 的性能考量
虽然 defer
提高了代码的可维护性,但其背后也存在一定的性能开销。每次 defer 调用都会将函数压入栈中,函数返回时依次执行。因此,在性能敏感的路径中应谨慎使用 defer。
使用场景 | 是否推荐使用 defer |
---|---|
资源释放(如文件、连接) | 是 |
高频循环或性能关键路径 | 否 |
错误恢复与状态清理 | 是 |
总结
Go语言通过简洁的 defer
和 error
机制,为开发者提供了一种结构清晰、易于维护的错误处理方式。合理使用 defer
不仅能提升代码质量,还能有效减少资源泄漏的风险,是构建健壮系统的重要工具。
第三章:Go语言复合数据类型与结构体
3.1 数组与切片操作实战
在 Go 语言中,数组是固定长度的序列,而切片是对数组的动态抽象。我们可以通过以下代码创建并操作切片:
arr := [5]int{1, 2, 3, 4, 5} // 定义一个数组
slice := arr[1:4] // 从数组创建切片,包含索引1到3的元素
逻辑分析:arr
是长度为5的数组,slice
是基于 arr
的切片,其起始索引为1,结束索引为4(不包含4),因此切片内容为 [2, 3, 4]
。
切片扩容机制
切片底层自动扩容机制如下:
graph TD
A[初始化切片] --> B{添加元素}
B --> C[容量足够]
C --> D[直接追加]
B --> E[容量不足]
E --> F[申请新内存]
F --> G[复制旧数据]
G --> H[追加新元素]
当使用 append
函数添加元素时,若当前容量不足,运行时会分配新的更大内存块,并将原有数据复制过去。
3.2 映射(map)的使用与优化
在 Go 语言中,map
是一种高效且灵活的键值对数据结构,广泛用于缓存、配置管理及数据索引等场景。
基本使用
// 声明并初始化一个 map
userAges := map[string]int{
"Alice": 30,
"Bob": 25,
}
// 添加或更新键值对
userAges["Charlie"] = 28
// 删除键值对
delete(userAges, "Bob")
上述代码展示了 map
的基本操作,包括初始化、插入、更新和删除。其底层基于哈希表实现,查找、插入和删除的平均时间复杂度为 O(1)。
优化策略
为提升性能,建议在初始化时预分配容量:
// 预分配容量为10的 map
m := make(map[string]int, 10)
预分配可减少动态扩容带来的性能波动,尤其适用于数据量较大的场景。
3.3 结构体定义与方法绑定
在 Go 语言中,结构体(struct)是构建复杂数据类型的基础。通过结构体,可以将多个不同类型的字段组合成一个自定义类型。
定义一个结构体
type User struct {
ID int
Name string
Age int
}
上述代码定义了一个 User
结构体,包含三个字段:ID、Name 和 Age。每个字段都有明确的数据类型。
方法绑定
Go 语言允许将方法绑定到结构体上,实现面向对象的编程风格:
func (u User) Greet() string {
return "Hello, " + u.Name
}
该方法使用 User
类型作为接收者,调用时会绑定到具体的实例上,例如:user.Greet()
。通过这种方式,可实现对结构体行为的封装与扩展。
第四章:Go语言并发与网络编程
4.1 Goroutine与并发模型实践
Go 语言的并发模型基于 CSP(Communicating Sequential Processes)理论,通过 Goroutine 和 Channel 实现高效的并发编程。
Goroutine 的轻量特性
Goroutine 是由 Go 运行时管理的轻量级线程,启动成本极低,一个程序可轻松运行数十万个 Goroutine。
示例代码如下:
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello from Goroutine")
}
func main() {
go sayHello() // 启动一个 Goroutine
time.Sleep(time.Second) // 主 Goroutine 等待
}
逻辑分析:
go sayHello()
启动一个并发执行的 Goroutine,函数sayHello
将在后台运行。主 Goroutine 通过time.Sleep
等待,确保程序不会提前退出。
使用 Channel 进行数据同步
Channel 是 Goroutine 之间通信的标准方式,提供类型安全的队列机制。
ch := make(chan string)
go func() {
ch <- "data" // 向 Channel 发送数据
}()
fmt.Println(<-ch) // 从 Channel 接收数据
参数说明:
make(chan string)
创建一个字符串类型的无缓冲 Channel。ch <- "data"
表示发送操作,<-ch
表示接收操作,二者会同步完成数据传递。
并发模型的演进优势
特性 | 线程模型 | Goroutine 模型 |
---|---|---|
资源消耗 | 高 | 极低 |
切换开销 | 较大 | 极小 |
通信机制 | 共享内存 | 通道通信 |
并发规模 | 几百 ~ 几千 | 上百万 |
通过上述对比可以看出,Goroutine 模型更适合高并发场景,能显著提升系统吞吐能力和资源利用率。
4.2 Channel通信机制详解
Channel 是 Go 语言中实现 Goroutine 之间通信的核心机制,它提供了一种类型安全的、同步的数据传递方式。
数据传递模型
Channel 可以看作是一个带有缓冲的队列,支持 发送(send)
和 接收(receive)
操作。其定义方式如下:
ch := make(chan int) // 无缓冲通道
ch := make(chan int, 5) // 有缓冲通道,容量为5
无缓冲 Channel 要求发送和接收操作必须同时就绪,否则会阻塞;而缓冲 Channel 则允许在缓冲区未满时异步发送。
同步机制
Channel 的同步行为可通过如下流程图表示:
graph TD
A[发送方写入] --> B{缓冲区满?}
B -->|是| C[阻塞等待]
B -->|否| D[数据入队]
E[接收方读取] --> F{缓冲区空?}
F -->|是| G[阻塞等待]
F -->|否| H[数据出队]
这种机制确保了多个 Goroutine 在并发执行时的数据一致性与协作调度。
4.3 使用sync包实现同步控制
在并发编程中,多个goroutine访问共享资源时,需要进行同步控制以避免竞态条件。Go语言的sync
包提供了基础的同步机制,如Mutex
、WaitGroup
等。
sync.Mutex 的使用
var mu sync.Mutex
var count = 0
func increment() {
mu.Lock() // 加锁,防止多个goroutine同时修改count
count++
mu.Unlock() // 解锁,允许其他goroutine访问
}
上述代码中,sync.Mutex
用于保护共享变量count
,确保每次只有一个goroutine可以修改它。
sync.WaitGroup 等待任务完成
var wg sync.WaitGroup
func task() {
defer wg.Done() // 每次task执行完减少WaitGroup计数器
fmt.Println("Task done")
}
func main() {
for i := 0; i < 5; i++ {
wg.Add(1) // 每启动一个goroutine就增加计数器
go task()
}
wg.Wait() // 等待所有任务完成
}
在该示例中,sync.WaitGroup
用于协调多个goroutine的执行,确保主函数在所有任务完成后才退出。
小结
通过sync.Mutex
可以保护共享资源,而sync.WaitGroup
则用于等待一组操作完成。二者结合,可以实现基本的并发控制逻辑。
4.4 TCP/HTTP网络服务构建实战
在实际开发中,构建稳定高效的网络服务是后端系统的核心能力。本章将围绕 TCP 与 HTTP 协议,通过实战方式演示如何构建基础服务。
基于 TCP 的回声服务器
以下是一个使用 Python 构建的简单 TCP 回声服务器示例:
import socket
# 创建 TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 8888))
server_socket.listen(5)
print("Server listening on port 8888...")
while True:
client_socket, addr = server_socket.accept()
print(f"Connection from {addr}")
data = client_socket.recv(1024)
client_socket.sendall(data) # 将接收到的数据原样返回
client_socket.close()
该服务监听本地 8888 端口,接收客户端连接并返回接收到的数据,适用于测试网络连通性和数据传输完整性。
HTTP 服务基础实现
借助 Python 的 http.server
模块,可快速搭建一个支持 GET 请求的 Web 服务:
from http.server import BaseHTTPRequestHandler, HTTPServer
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b"Hello, World!")
HTTPServer(('0.0.0.0', 8000), MyHandler).serve_forever()
该服务监听 8000 端口,接收 HTTP GET 请求并返回 “Hello, World!” 文本,适合用于构建基础 API 或静态资源服务。
第五章:Go语言项目构建与工具链
Go语言自诞生之初就注重开发效率与构建工具的集成,其自带的工具链极大简化了项目的构建、依赖管理和测试流程。在实际项目中,熟练掌握Go的构建工具与流程,是保障项目稳定交付和持续集成的关键。
项目构建基础
Go项目通常使用 go build
命令进行构建,其背后会自动处理源码编译、链接等步骤。例如,以下命令会在当前目录下编译生成可执行文件:
go build -o myapp
通过 -o
参数可以指定输出路径和文件名。构建时还可以通过 -ldflags
注入构建信息,例如版本号和构建时间:
go build -o myapp -ldflags "-X main.version=1.0.0 -X main.buildTime=$(date +%Y%m%d%H%M)"
这种方式常用于生产环境部署时记录构建元数据。
依赖管理与模块化
从 Go 1.11 开始,官方引入了模块(Go Module)机制来管理依赖。开发者可以通过以下命令初始化一个模块:
go mod init example.com/myproject
构建过程中,Go会自动下载并缓存依赖到 go.mod
和 go.sum
文件中。例如,引入一个第三方库:
import "github.com/gin-gonic/gin"
执行 go build
时,工具链会自动下载 gin 框架及其依赖,确保项目构建的一致性。
工具链实战:CI/CD 中的集成
在持续集成流程中,Go的工具链可以很好地与CI系统集成。例如,在 GitHub Actions 中定义一个构建任务:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: '1.20'
- name: Build
run: |
go mod download
go build -o myapp
上述配置展示了如何在CI环境中自动下载依赖并构建项目,确保每次提交都能快速验证构建可行性。
构建优化与交叉编译
Go支持跨平台交叉编译,开发者可以在一个平台上构建多个目标平台的二进制文件。例如,在Mac上构建Linux版本:
GOOS=linux GOARCH=amd64 go build -o myapp
这一特性在微服务部署中尤为实用,可避免为每个平台单独配置构建环境。
构建产物与发布管理
构建完成后,通常需要将产物打包并发布。可以结合 go install
和 goreleaser
工具自动化版本打包和发布流程。例如使用 goreleaser 定义 .goreleaser.yml
:
builds:
- id: myapp
main: .
goos:
- linux
- darwin
goarch:
- amd64
- arm64
运行 goreleaser
后,会自动生成适用于不同平台的压缩包,并可集成到GitHub Release中,实现一键发布。
Go语言的构建与工具链设计,不仅简化了日常开发流程,也极大提升了项目在不同阶段的可维护性与可扩展性。通过合理利用这些工具,可以构建出高效、稳定、易发布的工程化项目。
第六章:Go语言实战案例解析
6.1 构建RESTful API服务
构建RESTful API 是现代 Web 开发的核心技能之一。通过统一的资源表述和标准的 HTTP 方法,开发者可以设计出清晰、可维护的接口。
设计原则
RESTful API 强调无状态交互和资源导向设计,常见 HTTP 方法包括:
GET
:获取资源POST
:创建资源PUT/PATCH
:更新资源DELETE
:删除资源
示例代码
以下是一个使用 Express.js 构建的简单 API 示例:
const express = require('express');
const app = express();
// 获取用户列表
app.get('/users', (req, res) => {
res.json([{ id: 1, name: 'Alice' }]);
});
app.get()
:监听 GET 请求/users
:资源路径req
:请求对象,包含查询参数等res
:响应对象,用于返回数据
请求流程图
graph TD
A[Client 发送 HTTP 请求] --> B[服务器接收请求]
B --> C{路由匹配?}
C -->|是| D[执行对应控制器逻辑]
D --> E[返回 JSON 响应]
C -->|否| F[返回 404 错误]
6.2 实现一个简易区块链系统
要理解区块链的核心原理,最有效的方式之一是动手实现一个简易版本。该系统将包含区块结构定义、链式连接机制以及基础的共识规则。
区块结构设计
每个区块通常包含时间戳、数据、前一个区块的哈希值和当前区块的哈希值。
import hashlib
import time
class Block:
def __init__(self, data, previous_hash):
self.timestamp = time.time()
self.data = data
self.previous_hash = previous_hash
self.nonce = 0
self.hash = self.mine()
def mine(self):
while True:
candidate_hash = self.calculate_hash()
if candidate_hash[:4] == "0000": # 简单的工作量证明机制
return candidate_hash
self.nonce += 1
def calculate_hash(self):
block_string = f"{self.timestamp}{self.data}{self.previous_hash}{self.nonce}"
return hashlib.sha256(block_string.encode()).hexdigest()
逻辑分析:
timestamp
:记录区块创建时间;data
:用于存储交易或其他信息;previous_hash
:指向前一个区块的哈希,实现链式结构;mine()
方法模拟了工作量证明(PoW),确保区块生成具有一定计算成本;calculate_hash()
负责生成当前区块的唯一标识(哈希值);- 通过限制哈希值前四位为 “0000” 来模拟挖矿难度。
区块链连接机制
将多个区块串联起来形成区块链,确保数据不可篡改。
class Blockchain:
def __init__(self):
self.chain = [self.create_genesis_block()]
def create_genesis_block(self):
return Block("Genesis Block", "0")
def add_block(self, data):
latest_block = self.chain[-1]
new_block = Block(data, latest_block.hash)
self.chain.append(new_block)
逻辑分析:
create_genesis_block()
创建创世区块,是链的起点;add_block()
接收新数据,基于最新区块哈希构建新区块并加入链中;- 区块通过哈希形成链式结构,任意区块被修改都会导致后续区块哈希失效,从而保障安全性。
数据验证机制
为确保链的完整性,可以添加验证方法。
def is_chain_valid(self):
for i in range(1, len(self.chain)):
current = self.chain[i]
previous = self.chain[i - 1]
if current.hash != current.calculate_hash():
return False
if current.previous_hash != previous.hash:
return False
return True
逻辑分析:
- 遍历整个链,验证每个区块的哈希是否一致;
- 若发现哈希不匹配或前一个区块哈希不对应,说明链被篡改。
总结
通过上述实现,我们构建了一个具备基本功能的区块链原型,包括区块生成、链式连接和数据验证机制。虽然功能简单,但已体现出区块链不可篡改与去中心化的核心思想。
6.3 开发并发爬虫应用
在构建高性能网络爬虫时,并发机制是提升数据采集效率的关键。通过合理利用多线程、异步IO或协程,可以显著提高爬虫的响应速度和吞吐量。
异步爬虫的核心结构
使用 Python 的 aiohttp
与 asyncio
可实现高效的异步爬虫框架:
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
return await asyncio.gather(*tasks)
逻辑说明:
fetch
函数负责发起异步 HTTP 请求并等待响应;main
函数创建多个任务(tasks
),通过asyncio.gather
并发执行;- 使用
ClientSession
实现连接复用,减少握手开销;
并发策略对比
并发方式 | 适用场景 | 性能优势 | 资源消耗 |
---|---|---|---|
多线程 | I/O 密集型 | 中 | 高 |
协程 | 高并发网络请求 | 高 | 低 |
多进程 | CPU 密集型 | 高 | 极高 |
根据任务特性选择合适的并发模型,是构建高效爬虫系统的关键步骤。
6.4 构建CLI命令行工具
在现代软件开发中,命令行工具(CLI)因其高效、可组合和易于自动化的特点而广泛使用。构建一个专业的CLI工具,通常需要选择合适的框架,并遵循良好的设计原则。
以Python为例,click
是一个非常流行的CLI开发库。下面是一个简单的示例:
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name', help='The person to greet.')
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for _ in range(count):
click.echo(f"Hello, {name}!")
if __name__ == '__main__':
hello()
逻辑分析:
@click.command()
将函数hello
标记为CLI命令;@click.option()
定义了两个可选参数:--count
和--name
;prompt
参数在未输入时会提示用户输入;click.echo()
是跨平台兼容的输出方式。
CLI工具的设计应遵循以下原则:
- 简洁性:命令和参数命名清晰、简洁;
- 可组合性:支持管道、输入输出重定向;
- 可扩展性:结构清晰,便于后续功能扩展。
6.5 使用Go进行微服务开发
Go语言凭借其简洁的语法、高效的并发模型和内置的网络支持,已成为微服务开发的理想选择。
微服务架构优势
- 高度解耦,便于独立部署与扩展
- 技术栈灵活,支持多语言混合架构
- 快速迭代,提升开发效率
示例:基础HTTP服务
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from Go Microservice!")
}
func main() {
http.HandleFunc("/hello", helloHandler)
fmt.Println("Server started at :8080")
http.ListenAndServe(":8080", nil)
}
逻辑说明:
- 使用标准库
net/http
快速搭建HTTP服务 helloHandler
处理/hello
路径请求,返回文本响应main
函数启动服务监听8080端口
服务通信方式
Go支持多种服务间通信机制,包括REST、gRPC、消息队列等,可根据业务需求灵活选择。