40分钟学 Go 语言高并发:Go程序性能优化方法论
Go程序性能优化方法论
一、性能指标概述
指标类型 | 关键指标 | 重要程度 | 优化目标 |
---|---|---|---|
CPU相关 | CPU使用率、线程数、上下文切换 | ⭐⭐⭐⭐⭐ | 降低CPU使用率,减少上下文切换 |
内存相关 | 内存使用量、GC频率、对象分配 | ⭐⭐⭐⭐⭐ | 减少内存分配,优化GC |
延迟指标 | 响应时间、处理延迟、等待时间 | ⭐⭐⭐⭐ | 降低延迟,提高响应速度 |
吞吐量 | QPS、TPS、并发数 | ⭐⭐⭐⭐ | 提高系统吞吐量 |
让我们通过代码示例来展示如何进行性能优化:
package mainimport ("fmt""runtime""sync""testing""time"
)// 性能基准测试示例
func BenchmarkSliceAppend(b *testing.B) {for i := 0; i < b.N; i++ {var s []intfor j := 0; j < 1000; j++ {s = append(s, j)}}
}// 优化后的版本
func BenchmarkSliceAppendOptimized(b *testing.B) {for i := 0; i < b.N; i++ {s := make([]int, 0, 1000)for j := 0; j < 1000; j++ {s = append(s, j)}}
}// 内存优化示例
type DataBlock struct {mu sync.Mutexitems map[string][]byte
}// 未优化版本
func (db *DataBlock) ProcessDataUnoptimized(key string, data []byte) {db.mu.Lock()defer db.mu.Unlock()// 创建一个新的切片并复制数据dataCopy := make([]byte, len(data))copy(dataCopy, data)db.items[key] = dataCopy
}// 优化后的版本 - 使用对象池
var dataBlockPool = sync.Pool{New: func() interface{} {return make([]byte, 0, 1024)},
}func (db *DataBlock) ProcessDataOptimized(key string, data []byte) {// 从对象池获取缓冲区buf := dataBlockPool.Get().([]byte)buf = buf[:len(data)]// 复制数据copy(buf, data)db.mu.Lock()db.items[key] = bufdb.mu.Unlock()
}// CPU优化示例
func CalculateSum(numbers []int) int64 {var sum int64for _, n := range numbers {sum += int64(n)}return sum
}// 优化后的并行版本
func CalculateSumParallel(numbers []int) int64 {if len(numbers) < 1000 {return CalculateSum(numbers)}numGoroutines := runtime.NumCPU()var wg sync.WaitGroupch := make(chan int64, numGoroutines)// 计算每个goroutine处理的数量batchSize := len(numbers) / numGoroutinesfor i := 0; i < numGoroutines; i++ {wg.Add(1)start := i * batchSizeend := start + batchSizeif i == numGoroutines-1 {end = len(numbers)}go func(start, end int) {defer wg.Done()var sum int64for _, n := range numbers[start:end] {sum += int64(n)}ch <- sum}(start, end)}// 等待所有goroutine完成go func() {wg.Wait()close(ch)}()// 汇总结果var totalSum int64for sum := range ch {totalSum += sum}return totalSum
}// 性能测试工具
type PerformanceMetrics struct {StartTime time.TimeEndTime time.TimeMemStats runtime.MemStatsNumGoroutine int
}func NewPerformanceMetrics() *PerformanceMetrics {return &PerformanceMetrics{StartTime: time.Now(),}
}func (pm *PerformanceMetrics) Stop() {pm.EndTime = time.Now()runtime.ReadMemStats(&pm.MemStats)pm.NumGoroutine = runtime.NumGoroutine()
}func (pm *PerformanceMetrics) Report() string {duration := pm.EndTime.Sub(pm.StartTime)return fmt.Sprintf("Performance Report:\n"+"Duration: %v\n"+"Memory Allocated: %v MB\n"+"Number of GC Cycles: %v\n"+"Number of Goroutines: %v\n",duration,pm.MemStats.Alloc/1024/1024,pm.MemStats.NumGC,pm.NumGoroutine,)
}func main() {// 创建测试数据data := make([]int, 1000000)for i := range data {data[i] = i}// 测试未优化版本metrics := NewPerformanceMetrics()sum1 := CalculateSum(data)metrics.Stop()fmt.Printf("Unoptimized version result: %d\n", sum1)fmt.Println("Unoptimized version metrics:")fmt.Println(metrics.Report())// 测试优化版本metrics = NewPerformanceMetrics()sum2 := CalculateSumParallel(data)metrics.Stop()fmt.Printf("Optimized version result: %d\n", sum2)fmt.Println("Optimized version metrics:")fmt.Println(metrics.Report())
}
二、性能优化方法
1. CPU优化
主要优化方向:
-
算法优化
- 降低时间复杂度
- 减少不必要的计算
- 使用更高效的算法
-
并行处理
- 合理使用goroutine
- 避免过度并行
- 控制并发数量
-
缓存利用
- 使用本地缓存
- 避免频繁GC
- 减少内存分配
2. 内存优化
主要优化方向:
-
内存分配
- 预分配内存
- 使用对象池
- 减少临时对象
-
GC优化
- 控制GC触发频率
- 减少GC压力
- 使用合适的GC参数
-
数据结构
- 选择合适的数据结构
- 控制切片容量
- 减少指针使用
3. 并发优化
-
goroutine管理
- 控制goroutine数量
- 避免goroutine泄露
- 使用合适的并发模型
-
锁优化
- 减少锁竞争
- 使用细粒度锁
- 采用无锁算法
三、基准测试
1. 编写基准测试
package mainimport ("sync""testing"
)// 字符串连接基准测试
func BenchmarkStringConcat(b *testing.B) {b.ResetTimer()for i := 0; i < b.N; i++ {var s stringfor j := 0; j < 100; j++ {s += "a"}}
}// 使用 strings.Builder 的优化版本
func BenchmarkStringBuilder(b *testing.B) {b.ResetTimer()for i := 0; i < b.N; i++ {var builder strings.Builderfor j := 0; j < 100; j++ {builder.WriteString("a")}_ = builder.String()}
}// 内存分配基准测试
func BenchmarkSliceAllocation(b *testing.B) {b.ResetTimer()for i := 0; i < b.N; i++ {data := make([]int, 1000)for j := range data {data[j] = j}}
}// 使用对象池的优化版本
var slicePool = sync.Pool{New: func() interface{} {return make([]int, 1000)},
}func BenchmarkSlicePool(b *testing.B) {b.ResetTimer()for i := 0; i < b.N; i++ {data := slicePool.Get().([]int)for j := range data {data[j] = j}slicePool.Put(data)}
}// 并发基准测试
func BenchmarkConcurrentMap(b *testing.B) {m := make(map[int]int)var mu sync.Mutexb.RunParallel(func(pb *testing.PB) {for pb.Next() {mu.Lock()m[1] = 1mu.Unlock()}})
}// 使用sync.Map的优化版本
func BenchmarkSyncMap(b *testing.B) {var m sync.Mapb.RunParallel(func(pb *testing.PB) {for pb.Next() {m.Store(1, 1)}})
}// 子测试基准测试
func BenchmarkCalculation(b *testing.B) {nums := make([]int, 1000000)for i := range nums {nums[i] = i}b.Run("Sequential", func(b *testing.B) {for i := 0; i < b.N; i++ {_ = CalculateSum(nums)}})b.Run("Parallel", func(b *testing.B) {for i := 0; i < b.N; i++ {_ = CalculateSumParallel(nums)}})
}
2. 运行基准测试
# 运行所有基准测试
go test -bench=.# 运行特定基准测试
go test -bench=BenchmarkStringConcat# 包含内存统计
go test -bench=. -benchmem# 指定运行时间
go test -bench=. -benchtime=10s
3. 分析测试结果
基准测试输出解释:
BenchmarkStringConcat-8 1000000 1234 ns/op 2048 B/op 3 allocs/op
- 8: 使用的CPU核心数
- 1000000: 执行的迭代次数
- 1234 ns/op: 每次操作的平均时间
- 2048 B/op: 每次操作分配的内存
- 3 allocs/op: 每次操作的内存分配次数
继续完成性能采样部分的内容。
四、性能采样
1. CPU Profiling
package mainimport ("fmt""log""os""runtime/pprof""time"
)// CPU密集型操作示例
func cpuIntensiveTask() {// 创建CPU profile文件f, err := os.Create("cpu.prof")if err != nil {log.Fatal(err)}defer f.Close()// 启动CPU profilingif err := pprof.StartCPUProfile(f); err != nil {log.Fatal(err)}defer pprof.StopCPUProfile()// 执行CPU密集型操作start := time.Now()result := 0for i := 0; i < 10000000; i++ {result += fibonacci(20)}duration := time.Since(start)fmt.Printf("计算完成,耗时: %v, 结果: %d\n", duration, result)
}func fibonacci(n int) int {if n <= 1 {return n}return fibonacci(n-1) + fibonacci(n-2)
}func main() {fmt.Println("开始CPU profiling...")cpuIntensiveTask()fmt.Println("CPU profiling完成,使用以下命令查看结果:")fmt.Println("go tool pprof cpu.prof")
}
2. 内存 Profiling
package mainimport ("fmt""log""os""runtime""runtime/pprof"
)// 内存分配示例
type BigStruct struct {data []bytestr string
}func memoryIntensiveTask() {// 创建内存profile文件f, err := os.Create("mem.prof")if err != nil {log.Fatal(err)}defer f.Close()// 分配大量内存var structs []*BigStructfor i := 0; i < 1000; i++ {s := &BigStruct{data: make([]byte, 1024*1024), // 1MBstr: fmt.Sprintf("large string %d", i),}structs = append(structs, s)}// 触发GCruntime.GC()// 写入内存profileif err := pprof.WriteHeapProfile(f); err != nil {log.Fatal(err)}// 打印内存统计信息var m runtime.MemStatsruntime.ReadMemStats(&m)fmt.Printf("Alloc = %v MiB\n", m.Alloc/1024/1024)fmt.Printf("TotalAlloc = %v MiB\n", m.TotalAlloc/1024/1024)fmt.Printf("Sys = %v MiB\n", m.Sys/1024/1024)fmt.Printf("NumGC = %v\n", m.NumGC)
}func main() {fmt.Println("开始内存profiling...")memoryIntensiveTask()fmt.Println("内存profiling完成,使用以下命令查看结果:")fmt.Println("go tool pprof mem.prof")
}
3. 协程 Profiling
package mainimport ("fmt""log""net/http"_ "net/http/pprof""runtime""sync""time"
)// 模拟协程泄露
func leakyGoroutine() {// 永远阻塞的通道ch := make(chan struct{})go func() {<-ch // 永远不会收到数据}()
}// 模拟协程阻塞
func blockingGoroutine(wg *sync.WaitGroup) {defer wg.Done()var mu sync.Mutexmu.Lock()go func() {time.Sleep(time.Second)mu.Unlock()}()mu.Lock() // 会阻塞mu.Unlock()
}func startProfileServer() {go func() {log.Println(http.ListenAndServe("localhost:6060", nil))}()
}func goroutineIntensiveTask() {var wg sync.WaitGroup// 创建一些泄露的协程for i := 0; i < 100; i++ {leakyGoroutine()}// 创建一些阻塞的协程for i := 0; i < 10; i++ {wg.Add(1)go blockingGoroutine(&wg)}// 等待一段时间time.Sleep(2 * time.Second)// 打印协程数量fmt.Printf("当前协程数量: %d\n", runtime.NumGoroutine())
}func main() {// 启动profile serverstartProfileServer()fmt.Println("Profile server started at http://localhost:6060/debug/pprof")// 记录初始协程数量fmt.Printf("初始协程数量: %d\n", runtime.NumGoroutine())// 执行协程密集型任务goroutineIntensiveTask()fmt.Println("使用以下命令查看协程profile:")fmt.Println("go tool pprof http://localhost:6060/debug/pprof/goroutine")// 保持程序运行select {}
}
4. 性能分析工具使用流程
- 收集性能数据
# 收集CPU profile
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30# 收集内存profile
go tool pprof http://localhost:6060/debug/pprof/heap# 收集协程profile
go tool pprof http://localhost:6060/debug/pprof/goroutine
- 分析性能数据
# 查看top N的耗时函数
(pprof) top 10# 查看特定函数的详细信息
(pprof) list functionName# 生成可视化报告
(pprof) web
- 优化建议
问题类型 | 现象 | 优化方向 |
---|---|---|
CPU瓶颈 | CPU使用率高,响应慢 | 优化算法、减少计算、并行处理 |
内存问题 | 内存使用高,GC频繁 | 减少分配、使用对象池、控制对象大小 |
并发问题 | 协程数量多,竞争严重 | 控制并发数、减少锁竞争、优化通信 |
5. 性能优化实践建议
-
制定优化目标
- 明确性能指标
- 设定具体目标
- 评估优化成本
-
选择优化方向
- 找到性能瓶颈
- 分析收益成本比
- 制定优化策略
-
实施优化方案
- 循序渐进
- 及时验证效果
- 保证代码质量
-
长期维护
- 持续监控
- 定期评估
- 及时调整
6. 注意事项
-
优化原则
- 先性能分析,后优化
- 优化最有价值的部分
- 保持代码可维护性
-
避免过早优化
- 确认真实瓶颈
- 评估优化收益
- 权衡开发成本
-
注意测试
- 完整的测试覆盖
- 验证优化效果
- 确保功能正确
怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!
相关文章:
40分钟学 Go 语言高并发:Go程序性能优化方法论
Go程序性能优化方法论 一、性能指标概述 指标类型关键指标重要程度优化目标CPU相关CPU使用率、线程数、上下文切换⭐⭐⭐⭐⭐降低CPU使用率,减少上下文切换内存相关内存使用量、GC频率、对象分配⭐⭐⭐⭐⭐减少内存分配,优化GC延迟指标响应时间、处理延…...
一文解析Kettle开源ETL工具!
ETL(Extract, Transform, Load)工具是用于数据抽取、转换和加载的软件工具,用于支持数据仓库和数据集成过程。Kettle作为传统的ETL工具备受用户推崇。本文就来详细说下Kettle。 一、Kettle是什么? Kettle 是一款开源的 ETL&#x…...
Tomcat新手成长之路:安装部署优化全解析(上)
文章目录 1.Tomcat简介2.Tomcat原理架构2.1.总体架构2.2.连接器2.2.1.具体功能2.2.2.IO模型2.2.3.逻辑处理流程2.2.4.内部处理流程 2.3.容器2.4.启动过程2.5.请求过程 3.Tomcat适用场景4.Tomcat与其他Web容器对比5.Tomcat安装和启动5.1.Java环境变量5.2.系统服务5.3.启动关闭 6…...
跟我学C++中级篇——通信的数据的传递形式
一、通信的数据传递 在开发程序中,无可避免的会进行数据的传递。这种传递方式有很多种,字节流、消息、Json、参数以及对象甚至可能的方法。那么在传递这些数据时,如何正确的采用更合适的方法,就成为了一个设计的首选的问题。 二…...
C语言 qsort及应用
qsort及应用 qsort:快速排序函数,需要引用stdlib.h文件. void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *, const void *) ); 参数: base:需要排序的数组 num:数据个数(数组长度) width:每个数据的字节数(sizeof(数据类型)) compa…...
【C语言】连接陷阱探秘(4):检查外部类型
目录 一、外部类型概述 1.1. 外部类型的重要性 1.2. 外部类型在C语言中的使用 1.3. 注意事项 二、常见的外部类型陷阱 2.1. 结构体和联合体的大小不匹配 2.1.1. 示例代码 2.1.2. 正确的做法 2.2. 枚举类型的值不匹配 2.3. 函数签名不一致 2.3.1. 函数签名不一致的问…...
打造双层环形图:基础与高级渐变效果的应用
在数据可视化领域,环形图因其独特的展示方式而广受欢迎。今天,我们将通过ECharts库来创建一个具有双层渐变效果的高级环形图。本文将详细介绍如何实现这种视觉效果。 1. 环形图基础 首先,我们需要了解环形图的基本构成。环形图由内外两个圆…...
【Git】Git 完全指南:从入门到精通
Git 完全指南:从入门到精通 Git 是现代软件开发中最重要的版本控制工具之一,它帮助开发者高效地管理项目,支持分布式协作和版本控制。无论是个人项目还是团队开发,Git 都能提供强大的功能来跟踪、管理代码变更,并保障…...
【mac】mac自动定时开关机和其他常用命令,管理电源设置的工具pmset
一、操作步骤 1、打开终端 2、pmset 是用于管理电源设置的强大工具,我们将使用这个命令 (1)查询当前任务 pmset -g sched查看到我当前的设置是 唤醒电源开启在 工作日的每天早上8点半 上班时不用手动开机了 (2)删…...
【Leecode】Leecode刷题之路第62天之不同路径
题目出处 62-不同路径-题目出处 题目描述 个人解法 思路: todo代码示例:(Java) todo复杂度分析 todo官方解法 62-不同路径-官方解法 方法1:动态规划 思路: 代码示例:(Java&…...
基于深度学习的手势识别算法
基于深度学习的手势识别算法 概述算法原理核心逻辑效果演示使用方式参考文献 概述 本文基于论文 [Simple Baselines for Human Pose Estimation and Tracking[1]](ECCV 2018 Open Access Repository (thecvf.com)) 实现手部姿态估计。 手部姿态估计是从图像或视频帧集中找到手…...
helm部署golang服务
Helm 是 Kubernetes 的一个包管理工具,类似于 Linux 中的 apt 或 yum。它使得在 Kubernetes 上部署和管理应用程序变得更加简单和高效。 安装 https://get.helm.sh/helm-v3.16.3-linux-amd64.tar.gz具体版本号可以在github上看到最新的版本号,然后替换上面链接来获取。gith…...
DreamCamera2相机预览变形的处理
最近遇到一个问题,相机更换了摄像头后,发现人像角度顺时针旋转了90度,待人像角度正常后,发现 预览时图像有挤压变形,最终解决。在此记录 一人像角度的修改 先放示意图 设备预览人像角度如图1所示,顺时针旋…...
Mysql误删表中数据与误删表的恢复方法
查看数据库是否开启binlog日志 mysql> show variables like %log_bin%; ------------------------------------------------------------------------ | Variable_name | Value | ------------------------------------…...
lapack、blas、solver库的区别和联系
LAPACK、BLAS、Solver 库 是数值计算领域的重要组成部分,它们各自的功能和设计目标有所不同,但也存在密切的联系。 1. 概述 库主要功能设计目标BLAS提供基础的线性代数操作,如向量运算、矩阵-向量乘法、矩阵-矩阵乘法等。提供高度优化的基础线性代数操作,作为更高级库的底层…...
deepin 安装 chrome 浏览器
deepin 安装 chrome 浏览器 最近好多小伙伴儿和我说 deepin 无法安装最新的谷歌浏览器 其实是因为最新的 谷歌浏览器 其中的一个依赖需要提前安装 提前安装依赖然后再安装谷歌浏览器就可以了 安装 fonts-liberationsudo apt -y install fonts-liberation安装 chrome 浏览器sudo…...
永久免费的PDF万能水印删除工具
永久免费的PDF万能水印删除工具 1.简介 PDF万能水印删除工具,可以去除99.9%的PDF水印。例如:XObject水印(含图片水印)、文本水印、绘图水印/曲线水印、注释水印、工件水印、剪切路径水印等等。本软件是永久免费,无有…...
Linux网络——NAT/代理服务器
一.NAT技术 1.NAT IP转换 之前我们讨论了, IPv4 协议中, IP 地址数量不充足的问题,NAT 技术就是当前解决 IP 地址不够用的主要手段, 是路由器的一个重要功能。 NAT 能够将私有 IP 对外通信时转为全局 IP. 也就是一种将私有 IP 和全局IP 相互转化的技术方法: 很…...
大米中的虫子检测-检测储藏的大米中是否有虫子 支持YOLO,VOC,COCO格式标注,4070张图片的数据集
大米中的虫子检测-检测储藏的大米中是否有虫子 支持YOLO,VOC,COCO格式标注,4070张图片的数据集 数据集分割 4070总图像数 训练组 87% 3551图片 有效集 9% 362图片 测试集 4% 157图片 预处理 自动定向…...
基于Java的小程序电商商城开源设计源码
近年来电商模式的发展越来越成熟,基于 Java 开发的小程序电商商城开源源码,为众多开发者和企业提供了构建个性化电商平台的有力工具。 基于Java的电子商城购物平台小程序的设计在手机上运行,可以实现管理员;首页、个人中心、用户…...
node.js基础学习-fs模块-文件操作(六)
一、前言 fs模块是 Node.js 内置的文件系统(File System)模块,它提供了一系列用于与文件系统进行交互的方法。通过fs模块,可以对文件或目录进行读取、写入、删除、重命名、查询状态等操作,这使得 Node.js 能够很好地处…...
设计模式:11、迭代器模式(游标)
目录 0、定义 1、迭代器模式的四种角色 2、迭代器模式的UML类图 3、示例代码 4、迭代器的next()方法与集合的get(int index)方法的效率对比(LinkedList为例) 0、定义 提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象…...
Oracle SCN与时间戳的映射关系
目录 一、基本概述 二、相关操作 三、参考文档 一、基本概述 Oracle 数据库中的 SYS.SMON_SCN_TIME 表是一个关键的内部表,主要用于记录过去时间段中SCN与具体的时间戳之间的映射关系。这种映射关系可以帮助用户将 SCN 值转换为可读性更强的时间戳,从而…...
【广告投放系统】头条可视化投放平台vue3+element-plus+vite落地历程和心得体会
前言 hallo,又是许久未见,昨天也是正式把公司内部的广告投放平台暂时落地,我也即将离开待了两年多的地方。言归正传,由于头条广告后台的升级改版,因此为了满足内部投放需求,做了一个可视化的投放平台&…...
Gazebo插件相机传感器(可订阅/camera/image_raw话题)
在仿真环境中使用相机传感器,通常需要结合Gazebo插件来实现。Gazebo是一个功能强大的机器人仿真工具,支持多种传感器模型,包括相机。下面是如何在Gazebo中使用相机传感器的详细步骤。 1. 修改Xacro文件以包含Gazebo插件 首先,修…...
华三(HCL)和华为(eNSP)模拟器共存安装手册
接上章叙述,解决同一台PC上同时部署华三(HCL)和华为(eNSP)模拟器。原因就是华三HCL 的老版本如v2及以下使用VirtualBox v5版本,可以直接和eNSP兼容Oracle VirtualBox,而其他版本均使用Oracle VirtualBox v6以上的版本,…...
信息学奥赛一本通 1448:【例题1】电路维修 | 洛谷 P4667 [BalticOI 2011 Day1] Switch the Lamp On 电路维修
【题目链接】 ybt 1448:【例题1】电路维修 洛谷 P4667 [BalticOI 2011 Day1] Switch the Lamp On 电路维修 【题目考点】 1. 双端队列广搜(0-1BFS) 【解题思路】 整个电路是由一个个的正方形的电路元件组成,每个正方形有四个…...
k8s删除网络组件错误
k8s集群删除calico网络组件重新部署flannel网络组件,再部署pod后出现报错不能分配ip地址 plugin type"calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized 出现该问题是因为删除网络组件后,网…...
MySQL之JDBC
我们在学习完了数据库的基本操作后,希望和我们的Java程序建立连接,那么我们今天就来一探究竟JDBC是如何让Java程序与数据库建立连接的 1. 什么是JDBC JDBC(Java Data Base Connectivity, Java数据库连接) 是Java程序和数据库之间…...
音视频入门基础:MPEG2-TS专题(10)——PAT简介
一、引言 当某个transport packet的TS Header中的PID属性的值为0x0000时,该transport packet的payload为Program association table ,即 PAT表。PAT表包含所有PMT表的目录列表,将program_number和PMT表的PID相关联,获取数据的起始…...
中山网站建设gdyouzi/网站seo应用
数据库使用锁是为了支持更好的并发,提供数据的完整性和一致性。InnoDB是一个支持行锁的存储引擎,锁的类型有:共享锁(S)、排他锁(X)、意向共享(IS)、意向排他(…...
如何做一个商城类型的网站/电脑优化软件
参考:函数参数 Note 1.Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者…...
抚州建设网站/如何利用seo赚钱
文章目录文件上传和下载准备工作使用类介绍代码编写文件上传和下载 在Web应用中,文件上传和下载功能是非常常用的功能,这篇博客就来讲一下JavaWeb中的文件上传和下载功能的实现。 准备工作 对于文件上传,浏览器在上传的过程中是将文件以流…...
福永镇网站建设/百度网站推广价格查询
1 首先说下字符集。gb18030字符集兼容了gbk字符集,以两个字节表示一个文字。windows系统可能使用的就是这两种的一种。unicode字符集以2个或以上的字节表示一个汉字。通用字符集(Universal Character Set, UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义…...
php网站后台管理模板/网络营销的优势有哪些
在Java中我们常使用Comparable接口来实现排序,其中compareTo是实现该接口方法。我们知道compareTo返回0表示两个对象相等,返回正数表示大于,返回负数表示小于。同时我们也知道equals也可以判断两个对象是否相等,那么他们两者之间是…...
富蕴县建设局网站/cms系统
打电话时听不到对方声音什么原因?日常我们有些事情都不需要去见面,通过手机打个电话就沟通了。这样大大方便了我们的生活,但是如果你给朋友打电话,听不到对方声音怎么办?小编忍不住自行脑补了一下对方那尴尬的场面。那么iPhone通话听不到对方声音怎么解决呢?看看小编给大家整…...