Go问题集萃

语言相关

谈谈 进程、线程、协程,以及它们出现所要解决的问题,及相关技术的演进历程?

c10k主要指单机并发连接在1万的情况下,硬件性能足够,但仍然无法正常提供服务的问题
c10k是计算机从PC时代到互联网时代过程中由于并发连接量剧增做产生的问题;
c10k解决的关键在于,降低cpu等核心资源的消耗

IO多路复用的种类:

  • select: 存在句柄上线、重复初始化、轮询低效问题
  • poll:解决了句柄上线、重复初始化问题,但依然需要轮询IO句柄
  • epoll: 采用仅排查当前状态变化的句柄,无需逐个轮询句柄的方式,效率提升, 但不能跨平台,不同平台的库不同 epoll\kqueque\IOCP;

nginx, libevent, libev, nodejs底层的libuv都是基于epoll;

Epoll两种触发方式:

  • 水平触发:当被监控的文件描述符(IO文件句柄)有可读写事件发生,epoll_wait()会通知处理程序去读写,若本次未读写完,则在下次epoll_wait()时会接着上次没读写完的位置继续读写;与select\poll类似;(可能会有部分不需要读写的文件描述符被epoll_wait(),效率要低些)
  • 边缘触发:当被监控的文件描述符(IO文件句柄)有可读写事件发生,epoll_wait()会通知处理程序去读写,若本次未读写完,则在下次epoll_wait(),并不会通知继续处理,只会在文件描述符(IO文件句柄)下次再有可读写事件时才会通知执行后续读写操作;

发展三阶段:
1). 一个进程/线程处理一个连接
2). 一个进程/线程处理多条连接(IO多路复用)
3). 用户态异步协程处理连接

进程:
是系统进行资源分配和调度的独立单元,拥有独立的内存空间,不同进程通过IPC进行通信,进程较重,上下文进程间切换(栈、寄存器、虚拟存储、文件句柄)开销大,但稳定性好;

线程:
是进程内部的实体,是CPU调度和分派的基本执行单元,它与同属一个进程内的其他线程共享进程所拥有的资源,线程间通信主要通过共享内存,相比进程资源开销小,上下文切换快,但较不稳定;

协程:
是一种用户态的轻量级线程,调度完全由用户程序控制,没有内核开销,效率快;

进程与线程:

  • 地址空间:线程是进程内独立的执行单元,一个进程至少有一个线程,进程有独立的地址空间, 且进程内的线程共享进程的地址空间;
  • 资源共享:进程内的资源与其内部的线程共享;
  • 调度处理:线程是处理器CPU调度的基本单位,进程不是;
  • 都可以并发执行;

线程与协程:

  • 一个线程或进程可以拥有多个协程;
  • 线程进程是同步机制,协程是异步机制;

https://www.cnblogs.com/lxmhhy/p/6041001.html

channel 要记得 close,但 close 时候需要注意些什么?

  • channel 不能被重复close,否则会panic;
  • 已经关闭的 channel 不能向其发送数据,否则painc;
  • 从已关闭的 channel 中读取数据,如果是无缓冲的,读出的是零值,如果有缓冲且有数据,可以继续读;
  • 通过i, ok := <- ch的 ok 获取channel是否已关闭;
  • 应该只在唯一或最后唯一剩下的生产者发送端协程中关闭channel;

channel 是通过注册相关 goroutine id 实现消息通知,具体阐述?

// todo

slice 相关问题, slice 与数组的区别,slice的底层结构?

array 属于值类型,长度固定;
slice 属于引用类型,长度不定,slice的底层引用了一个数组对象;

声明时,array要指明长度,slice则不需要;
作为函数参数时,数组传递的时其副本,而slice传递的是指针;

谈谈 互斥锁,读写锁,死锁,及其数据竞争的概念?

互斥锁: 是并发程序对共享资源进行唯一性访问控制的主要手段;
读写锁: 即是并发时针对于读写操作的互斥锁;多个写操作之间是互斥的;写操作与读操作之间是互斥的;多个读操作之间不存在互斥;
死锁: 并发时所有协程都彼此等待的状态(goroutine间存在无缓冲channel只读不写或只写不读的情况、加锁但没解锁的情况)
数据竞争:并发情况下存在的多个协程读写相同数据的情况,至少有一个协程是写操作;若多个协程都是读操作,则不存在数据竞争;

什么是channel,为什么它可以做到线程安全?

是golang协程间通信的方式,channel本质是通过内存队列,一次只处理一个数据,从而实现了访问的序列化,避免了数据竞争,从而并发安全;

如何用channel实现一个令牌桶

// todo

如何写单元测试和基准测试

  • 使用 gotest 包,文件名格式 *_test.go
  • 导入 import testing,单元测试函数命名格式:func Test*(t *testing.T){}
  • 导入 import testing,基准测试函数命名格式:func Benchmark*(b *testing.B){}
  • go test执行单元测试,go test -test.bench=.*执行所有基准测试;

go 及 goroutine 的调度是怎样的(为什么并发性能好),抢占式goroutine调用什么意思?

1). 线程池的缺陷
线程池中的woker线程获取任务队列中的任务并执行,但若任务中发生系统调用,该worker进程将处于阻塞状态,从而任务队列任务堆积,
解决方法是增加线程数,但会使得多线程争抢CPU,从而使得处理能力下降;

2). G-P-M调度模型

  • G: Goroutine Go协程
  • M: Machine 系统工作线程
  • P: Processor 上下文调度器
    调度策列:
  • 队列轮询:P维护着一个G的队列,P周期性将G队列中的G调度到M中执行,执行一会儿,保存上下文放入队尾,再执行队列中下一个G;
  • 系统调用: 当M在执行G1的过程中遇到系统调用时,M会释放P以及P所维护的G队列,此时空闲的M1获取到P及剩余的G队列继续执行,M1接替M的剩余工作
  • 工作量窃取:通常当两个P中的G队列不均衡时,空闲的P会从忙碌的P的G队列中窃取部分G来执行;

抢占式goroutine调用:如果一个goroutine运行时间过长或则长时间阻塞于系统调用,它就会被剥夺运行权;

https://segmentfault.com/a/1190000015352983
https://my.oschina.net/renhc/blog/2221426

golang 的内存回收是如何做到的?

go 的 GC 采用了mark and sweep 标记清除模式,从根变量扫描遍历所有被引用的对象并进行标记,将没有标记的对象回收;
缺点:每次执行垃圾回收时会暂停所有正常运行的代码,系统响应能力降低,但后期加入了三色标记法与写屏障,又使得性能缓解;

三色标记法:

  • 起初所有对象都是白色。
  • 从根出发扫描所有可达对象,标记为灰色,放入待处理队列。
  • 从队列取出灰色对象,将其引用对象标记为灰色放入队列,自身标记为黑色。
  • 重复上一步,直到灰色对象队列为空。此时白色对象即为垃圾,进行回收。

http://legendtkl.com/2017/04/28/golang-gc/

cap和len分别获取的是什么, cap函数适用的类型有哪些?

len:
array: 指数组成员的数量
slice: 指切片成员的数量
channel: 指通道缓冲区未读的成员数量

cap:
array: 指数组成员的数量
slice: 指切片当前最大容量
channel: 指通道缓冲区的容量

https://studygolang.com/articles/8519

go build时,tag 中 netgo,netcgo有什么区别?

netgo 使用 /opt/go/pkg/tool/linux_amd64/compile 进行编译;
netcgo 使用 gcc 编译;

什么是interface?

interface是一种抽象类型,是一组方法(method)的集合,
是鸭式(duck-type)编程的一种体现;

但凡不相关的对象实现了interface中的所有方法,
那这些不相关的对象都可以给interface变量赋值,从而完成一些共性的行为操作;

使用go语言,编写并行计算的快速排序算法

// todo

数据竞争(Data Race)问题怎么解决?能不能不加锁解决这个问题?

数据竞争指并发多个协程对同一共享资源进行读写操作时,产生的数据错乱问题;
解决的关键在于同一时间仅允许一个协程对共享资源进行读写操作;

通过 go build -race 命令,生成可执行文件并运行,即可检测资源竞争并输出检测报告;

加锁的方式处理:sync/atomicsync.Mutex

https://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong

常用的 golang 库有哪些?用到其中哪些方法?

  • viper 配置管理
  • zerolog 可配置日志管理
  • redigo redis客户端操作
  • gin web服务搭建
  • cron 定时任务设置
  • jwt-go 请求认证相关
  • swaggo restful api 文档化

  • crypto 常用的加解密算法库

  • fmt 信息格式化及打印信息
  • errors 设置自定义错误信息
  • net/http 服务相关
  • strings|strconv 字符串处理
  • time 时间相关

go for range 的坑?

for range 遍历的出的是slice/array中每个元素的副本,并非引用
而将这些副本值传给新变量时,我们最终结果的每一项都存的时新变量的指针,
那么所有新变量的指针都同时指向遍历到的最后一个值,故多个结果值一样;

https://studygolang.com/articles/9701

Golang 的 GC 触发时机是什么

  • 内存阈值触发:当前分配内存是否大于上次GC后内存的2倍
  • 每两分钟定时主动触发:没2min触发一次

golang中的引用类型与值类型分别有哪些?

值类型:int、float、bool、string;

引用类型:slice、map、channel;

两者区别:拷贝操作和函数传参

go vet 的用途

用于检查golang源码中的静态错误;

select 的用途

select是一种通信开关,用来监听协程通过channel发送的信息

  • 每次执行select,都会只执行其中1个case或者执行default语句。
  • 当没有case或者default可以执行时,select则阻塞,等待直到有1个case可以执行。
  • 当有多个case可以执行时,则随机选择1个case执行。
  • case后面跟的必须是读或者写通道的操作,否则编译出错。

go 的 new 与 make 的区别?

new 和 make 都是用于分配内存;
make 仅适用于slice、map、channel引用类型的非零值初始化,且会返回类型本身;
new 用于类型的零值初始化,且会返回类型的指针;

怎么实现协程完美退出

使用 sync.WaitGroup;

  • 创建一个 waitgroup 实例wg
  • 在每个协程启动时调用 wg.Add(1),或者创建n个协程前调用wg.Add(n)
  • 当协程任务完成后,调用wg.Done()
  • 在等待所有协程的地方调用wg.Wait()

gRPC是什么?RPC的优点?

gRPC是谷歌开源的远程过程调用(RPC)框架,它采用 protocol buffers 作为接口描述语言(Interface Description Language),
protobuf 也可以看做 gRPC 底层消息交换的格式;

通过编写 .proto 扩展名的protobuf文件,再使用 protoc 及相关插件、选择指定语言、编译该文件,就可以生成的对应的客户端、服务端代码,
我们只需要实现C/S两端代码中定义的方法,即可实现通信;

相较于REST基于http1.1,RPC基于 tcp 或 http2 较多,头部信息较少,传输效率更高,适合后台服务间通信;
但较于REST学习开发成本略大;

设计模式:

  • 单例模式:
    定义了一个单一的类,并且该类负责创建自己的对象,同时保证只有一个对象被创建
  • 工厂模式:
    定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
  • 观察者模式:
    定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
  • 模板方法模式:
    定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
  • 桥接模式:
    将抽象部分与实现部分分离,使它们都可以独立的变化

容器相关问题

如何给容器添加映射端口?

提交一个运行中的容器为镜像:

1
docker commit containerid foo/live

运行镜像并添加端口:

1
docker run -d -p 8000:80  foo/live /bin/bash

如何查看 docker 镜像的环境变量?

1
docker run <image id> env

Linux 操作相关

如何后台启动一个服务?

  • 命令:conmmand &,通过增加一个(&)符号,将应用程序在后台启动,但此操作在关闭终端时服务会停止;
  • 命令:nohup conmmand &, 该命令可以在你退出帐户之后继续运行相应的进程,nohup( no hang up);
  • bg: 将一个在后台暂停的命令,变成继续执行; fg: 将后台中的命令调至前台继续运行; jobs: 查看当前有多少在后台运行的命令; ctrl+z: 将一个正在前台执行的命令放到后台,并且暂停;

说说孤儿进程、僵尸进程

孤儿进程与僵尸进程都是指子进程的状态;

孤儿进程:
当父进程退出时,并未通知子进程结束,使得子进程仍然在运行,这样的子进程就是孤儿进程,
它将被系统init进程接管,当孤儿进程生命周期结束,init进程将调用wait()处理孤儿进程;

僵尸进程:
当子进程退出时,父进程并未调用wait()释放子进程的进程描述符(进程号、退出状态、运行时间等),此时的子进程状态为僵尸状态,进程即僵尸进程;

任何子进程在exit()后,并非立即释放所有该子进程的资源信息,而是会留下一个僵尸状态的数据结构,等待父进程处理;
由于系统所能使用的进程号是有限的,如果大量产生僵尸进程占用所有进程号资源,将使得新进程无法创建,危害极大;
故孤儿进程无危害,僵尸进程存在危害;

解决僵尸进程的方法:
即 kill 掉僵尸子进程的父进程,使得僵尸进程被系统init进程接管,这样init进程会使用wait()释放掉进程号资源;

https://blog.csdn.net/morgerton/article/details/69388694

数据库相关

如何做 SQL 优化

使用 explain select ... 分析:
id执行顺序、key用到的索引、extra额外改进信息

优化建议:

  • 尽量不要在复杂的查询中使用模糊匹配 LIKE %param%
  • 避免在索引字段上进行计算操作、使用not、<>、!=、IS NULL、NOT NULL判断、进行数据类型转换、使用函数、使用空值,这些都会使得索引失效;
  • 避免SELECT * FROM t
  • 使用 JOIN 代替子查询
  • 适当添加冗余字段来减少连表查询
  • 在 WHERE 中,尽量避免对索引字段进行计算操作

数据库事务相关

ACID 特性
  • 原子性 automatic:事务是原子工作单位,要么全部执行、要么全部不执行
  • 一致性 consistency:数据库通过事务完成状态转变
  • 隔离性 isolation:事务提交前,对数据的影响是不可见的
  • 持久性 duration:事务完成后,对数据的影响是持久的
4 种隔离级别

隔离级别由低到高:

  • read uncommitted(读未提交),本事务可读取其他事务未提交的数据,隐患:脏读:即可以随意读取其他事务未提交的数据;
  • read committed(读已提交),本事务可读取其他事务已提交的数据,隐患:非重复读:即在事务中两次相同读取操作,结果不同;
  • repeatable read(可重读),本事务查询多次相同查询,结果必须相同,mysql默认级别,隐患:幻读:即A事务提交的新增数据,在B事务中查不到,此时B事务新增同样数据会报数据已存在;
  • serializeable(串行化),事务间按串行方式执行,隐患:写挂起:即事务执行过程,若有其他写操作访问相同数据,则会挂起等待;

https://www.jianshu.com/p/4e3edbedb9a8

3 种实现隔离的锁
  • 共享锁 S 锁:只读 SELECT 操作,锁定共享资源,阻止其他用户写数据
  • 更新锁 U 锁:阻止其他用户更新数据
  • 独占锁 X 锁:一次只能有一个独占锁占用一个资源,阻止添加其他所有锁。有效防止脏读

SQL查询成绩表的各个科目中成绩最高与最低的学生

首先查询成绩表中各个科目最高与最低分
1
2
> select subject, max(score) from grade group by subject;
> select subject, min(score) from grade group by subject;
然后查询最高及最低分对应学生
1
2
> select b.* from (select subject, max(score) m from grade group by subject) t, grade b where t.subject=b.subject and t.m=b.score;
> select b.* from (select subject, min(score) m from grade group by subject) t, grade b where t.subject=b.subject and t.m=b.score;

最后使用 UNION 合并结果集

1
2
> select b.*, "最高分" from (select subject, max(score) m from grade group by subject) t, grade b where t.subject=b.subject and t.m=b.score union
select b.*, "最低分" from (select subject, min(score) m from grade group by subject) t, grade b where t.subject=b.subject and t.m=b.score;

WEB 通信相关

当打开浏览器输入url到打开网页,这当中发生了什么?

  • URL输入
  • DNS解析:浏览器缓存->操作系统缓存->本地host文件->路由器缓存->ISP DNS缓存->顶级DNS服务器/根DNS服务器,获取到IP地址
  • TCP连接:通过ip与服务器进行三次握手建立连接
  • 发送HTTP请求:请求包括请求方法、请求头、请求体
  • 服务器处理请求:服务器通过请求路径找到对应路由,获取请求中的参数
  • 服务器响应请求:根据请求参数进行业务逻辑处理,可能包含对数据库、第三方服务接口、缓存数据的访问,处理完后返回响应数据;
  • 浏览器解析渲染页面:返回的数据经过前端HTML CSS 经行可视化渲染;
  • 连接结束

https://juejin.im/post/5b148a2ce51d4506965908d2

什么是 restful api, 有哪些方法,分别代表什么?

REST 是一种接口定义的规范,它要求服务提供标准可见的接口;
1). 通过 URI 标识资源
2). 统一使用 POST GET DELTE PATCH PUT 来定义操作资源的方式;
3). 数据组织形式采用标准通用格式 JSON 或 XML
4). 使用 HTTP 作为组件间交流协议

TCP 和 UDP 有什么区别? 描述一下 TCP 三次握手、四次挥手的过程?

OSI七层模型:

  • 应用层:FTP\HTTP\SMTP\DNS\Telnet …
  • 表现层: 数据格式、代码转换、数据加密
  • 会话层:建立和解除与别的节点的连接
  • 传输层:提供端到端接口 TCP\UDP
  • 网络层: 为数据包选择路由 IP\ICMP …
  • 数据链路层: 传输有地址的帧及错误检测
  • 物理层:以二进制形式在物理媒体上传输数据

TCP传输控制协议,UDP用户数据报协议

  • 基于连接与无连接;
  • 对系统资源的要求(TCP较多,UDP少);
  • UDP程序结构较简单;
  • 字节流模式与数据报模式 ;
  • TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。
  • TCP仅支持点对点通信,UDP可以支持一对一、一对多、多对一、多对多;

seq: 字节流序列号(Sequence Number),表示报文中的第一个数据字节在发送的数据流中的序号,防止网络报乱序;

ack: 确认序列号(Acknowledgment Number),通常是上一次收到的字节流序列号加一(seqNum + 1),防止网络丢包;

三次握手:

  • 第一次,客户端发送SYN连接请求报文,设置SYN为1,seqNum为x,此时客户端处于 SYN_SEND(同步发送) 状态;
  • 第二次,服务端收到SYN报文,需要对其确认, 设置SYN为1,ackNum为x+1、seqNum为y 的 SYN-ACK 报文给客户端,此时服务端处于 SYN_RECV(同步接收) 状态
  • 第三次,客户端接收到 SYN-ACK 报文,向服务端发送 ackNum为y+1 的ACK报文,此时客户端/服务端都进入 ESTABLISHED(确认)状态;

为什么握手需要三次?
在信道不可靠的情况下要保证数据传输可靠,理论上需要三次通信;

  • 第一次和第二次确保了 Server 能收到 Client 信息且 Server 能做出正确应答;
  • 第二次和第三次确保了 Client 能收到 Server 信息且 Client 能做出正确应答;

四次挥手:

  • 第一次,客户端发送 FIN 报文,设置 seqNum 为 x ,ackNum 为 y , 此时客户端进入 FIN_WAIT_1 (终止等待1) 状态;
  • 第二次, 服务端收到 FIN 报文,向客户端发送 ackNum 为 x+1 的 ACK 报文,此时服务端进入 CLOSE_WAIT(关闭等待)状态,客户端收到ACK报文将进入FIN_WAIT_2状态;
  • 第三次,服务端判断是否还有数据需要发送,若有,则先将剩余数据发送完毕,然后向客户端发送 FIN 报文(seqNum为y),请求关闭连接,同时服务端进入 LAST_ACK(最终确认)状态;
  • 第四次,客户端收到 FIN 报文,向服务端发送 ackNum 为 y+1 的 ACK 报文,然后客户端进入 TIME_WAIT 状态,服务端此时收到FIN报文直接关闭连接,客户端等待一会儿依然没有收到服务端回复,则客户端也关闭连接;

https://juejin.im/post/5b189ca0f265da6e1e1adcbf

HTTPS 和 HTTP 有什么区别? 状态码有哪些?301和302有什么区别? 401和403有什么区别? 502和504有什么区别?

HTTP 传输过程是明文,HTTPS 在 HTTP 基础上增加了 SSL 安全层,使得传输的数据被加密成密文,保证传输数据安全;

SSL 握手协议:

  • 第一, 客户端向服务端发送协议版本号一个客户端生成的随机数A客户端支持的加密方式三个信息;
  • 第二, 服务端收到客户端信息后,将确认的双方使用的加密方式数字证书一个服务端生成的随机数B发送给客户端;
  • 第三, 客户端确认数字证书有效,然后生成新的随机数C,并使用数字证书中公钥加密该随机数C,发送给服务器;
  • 第四, 服务端使用私钥对随机数C密文进行解密,至此客户端/服务端都拥有了三个随机数(clientRandom、serverRandom、premasterSecret);
  • 第五, 客户端根据约定的加密方式,使用之前三个随机数生成 session 密钥,用于加密之后的通信数据;

状态码:

  • 1XX 信息提示
  • 2XX 请求成功
  • 3XX 重定向相关
  • 4XX 客户端请求错误
  • 5XX/6XX 服务端错误

301:是永久重定向(表示旧地址资源被永久移除),302:是临时重定向(表示旧地址仍在,只是临时跳转);

401:是在认证过程中,认证信息缺失或认证有误而导致的未被授权,403:则是在认证完成以后,在访问某个特定资源时,由于没有权限而被禁止;

502:通过网关或代理服务执行请求时,应用服务返回了一个无效的响应;504:通过网关或代理服务执行请求时,应用服务响应超时;

http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html

CSRF 跨站请求伪造:攻击者知道所有参数、构造合法请求

伪造成其他用户,发起请求:如拼接恶意链接给其他用户点击。
防范:

  • 关键操作限制 POST
  • 合适的使用验证码
  • 添加 token:发起请求时添加附加的 token 参数,值随机。服务端做验证
  • header refer:外部来源可拒绝请求

XSS 跨站脚本攻击:在客户端不知情的情况下运行 js 脚本。

防范:

  • 过滤和转义用户的输入内容:htmlspecialchars()
  • 限制参数类型
  • 文件上传做大小、类型限制

如何实现一个URL短链接服务?

将长短链接映射关系存入数据库,访问短链时,查询获取到对应数据库中的长链,然后转发请求;

https://www.zhihu.com/question/29270034/answer/46446911

由于http协议是无状态的,服务器需要记录用户的状态,所以cookie和session都是用来保持状态的方案;

cookie/session 验证过程:
1). 用户在登录页面向服务器发起用户名/密码认证请求;
2). 服务端认证成功,将该认证用户相关信息保存session中(存储于服务端的文件、数据库、redis中任其一),并为该session生成一个唯一的sessionID;
3). 在认证请求的响应头中,会将该sessionID及有效期保存于set-cookie键值中;
4). 客户端收到响应,从set-cookie中获取cookie并保存浏览器本地,而在接下来的每次请求中,都会将cookie带入请求头一并发出;
5). 服务端从请求头的cookie中获取到sessionID,并查找是否存在相关session,若存在则通过验证;

如果浏览器禁用cookie,则可以在URL添加querystring参数?sid=<seesion id>

token 验证过程:
1). 用户在登录页面向服务器发起用户名/密码认证请求;
2). 服务端认证成功,将用户的少量非重要信息生成token(服务端不存储),并在响应数据中带入token返回给客户端;
4). 客户端收到响应,获取token并保存在浏览器本地,之后在每次请求中,都会将token设置到请求头的Authorization中;
5). 服务端从请求头的Authorization中获取到token,通过jwt验证token是否有效,若有效则通过验证;

json web token机制不能取代session机制:
由于jwt的Token只能被动等待过期,不能直接设置失效,不能延续有效期;
且jwt在服务端无状态存储的特性,使得在像如下场景无法替代session机制:

  • 比如多端登录情况下,只允许最后一个token有效
  • 修改密码后,希望当前token立即失效
  • 页面访问刷新时,token有效期延长

https://www.jianshu.com/p/bd1be47a16c1
https://segmentfault.com/a/1190000016236765
https://stayhungrystayfoolish.github.io/Token/

使用golang net/http 实现一个简易的http服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

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

// w表示response对象,返回给客户端的内容都在对象里处理
// r表示客户端请求对象,包含了请求头,请求参数等等
func index(w http.ResponseWriter, r *http.Request) {
// 往w里写入内容,就会在浏览器里输出
fmt.Fprintf(w, "Hello golang http!")
}

func main() {
// 设置路由,如果访问/,则调用index方法
http.HandleFunc("/", index)

// 启动web服务,监听9090端口
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

使用 golang request/response 常用的方法?

// todo


编程题

题目1

有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推…
现在有四个文件ABCD。初始都为空。现要让四个文件呈如下格式:
A:1 2 3 4 1 2….
B:2 3 4 1 2 3….
C:3 4 1 2 3 4….
D:4 1 2 3 4 1….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"bytes"
"fmt"
)

func main() {
chs := make([]chan int, 4)
for i := 0; i < len(chs); i++ {
chs[i] = make(chan int)
go func(i int) {
for {
chs[i] <- i + 1
}
}(i)
}
f := make([]bytes.Buffer, len(chs))

for i := 0; i < 10; i++ {
for j := 0; j < len(f); j++ {
fmt.Fprintf(&f[j], "%d ", <-chs[(i+j)%len(chs)])
}
}
for i := 0; i < len(f); i++ {
fmt.Printf("%d: %s\n", i, f[i].String())
}
}

题目2

实现strconv库中的atoi与itoa方法

1). golang版本 atoi 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func MyAtoi(s string) int {
s0 := s
if s[0] == '-' || s[0] == '+' {
s = s[1:]
fmt.Printf("string: %s\n", s)
if len(s) < 1 {
fmt.Printf("err: param isn't number format")
}
}
n := 0
for _, ch := range []byte(s) {
ch -= '0'
if ch > 9 {
fmt.Printf("err: param isn't number format")
}
fmt.Printf("ch item: %d\n", ch)
n = n*10 + int(ch)
}
if s0[0] == '-' {
n = -n
}
return n
}

2). c 版本 atoi 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

#include <stdio.h>
#include <ctype.h>

/* atoi : convert s to integer; version 2*/
int atoi(char s[]){
int i, n, sign;

for (i = 0; isspace(s[i]); i ++) {
// do nothing
}

sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '-' || s[i] == '+') {
i ++;
}
for ( n = 0; isdigit(s[i]); i ++) {
n = 10 * n + (s[i] - '0');
}

return sign * n;
}

int main(){
char a[] = "1132";
printf("%d\n", atoi(a));
return 0;
}

3). golang 版本 atoi 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func MyItoa(n int) string {
b := []byte{}
for {
b = append(b, uint8(n%10)+'0')
n = int(n / 10)
if n == 0 {
break
}
}
reverse(b)
return string(b)
}

func reverse(b []byte) {
for i, j := 0, len(b)-1; i<j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
}

4). c 语言版本 itoa 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <ctype.h>
#include <string.h>

void reverse(char s[]);
/*itoa : convert n to characters in s*/
void itoa(int n, char s[]){
int i, sign;

if((sign = n) < 0){//记录符号
n = -n; //使n变为正数
}

i = 0;
do {//生成的字符是反序的
s[i ++] = n % 10 + '0';
} while((n /= 10) > 0);
if (sign < 0) {
s[i ++] = '-';
}
s[i] = '\0';

reverse(s);
}

void reverse(char s[]){
int c, i, j;

for (i = 0, j = strlen(s) - 1; i < j ;i ++, j --) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}

int main(){
int n = 235;
char s[] = "000";
itoa(n, s);
printf("%s\n", s);

return 0;
}

题目3: 两数之和算法

题目一

题目二

题目4: 无重复字符的最长子串

https://blog.csdn.net/heart66_A/article/details/83714168

题目5:如何用channel实现一个令牌桶

题目6:编写并行计算的快速排序算法

https://studygolang.com/articles/5618

题目5

SQL: 查出用户表中每个省区钱最多的用户

说说对分布式系统原理的理解、容器技术的原理、网络相关知识、kubernetes用途、CI/CD相关工具