男男床做视频网站,微信推广员,全网络品牌推广,网站标题怎么修改Go管理工具
早期 Go 语言不使用 go module 进行包管理#xff0c;而是使用 go path 进行包管理#xff0c;这种管理方式十分老旧#xff0c;两者最显著的区别就是#xff1a;Go Path 创建之后没有 go.mod 文件被创建出来#xff0c;而 go module 模式会创建出一个 go.mod…Go管理工具
早期 Go 语言不使用 go module 进行包管理而是使用 go path 进行包管理这种管理方式十分老旧两者最显著的区别就是Go Path 创建之后没有 go.mod 文件被创建出来而 go module 模式会创建出一个 go.mod 文件用于管理包信息
现在就是尽量使用 Go Modules 模式
另外我们在引入包的时候可以先进行 import 再通过编译器来下载内容这样能让我们更简便的处理包关系
Go 编码规范
命名规范
命名首字母大写 被视为 Public小写被视为 Private
包名和目录名应为小写不带有下划线与驼峰
文件名应为全小写其中可能带有的多个单词以下划线分隔
对于接口的命名我们应该在末尾加上 ‘er’ 来标注
常量使用全大写命名中间使用下划线隔开
Go 语言中的包分为三种Go 语言自带的标准库中的包、第三方包、自己写的包
RPC
rpc 是指 remote procedure call 也就是远程节点调用其实就是一个节点调用另一个节点
这之中最关键的三个问题是Call的id映射、序列化与反序列化、网络传输
Call id 映射问题解决的是系统A的程序想要远程调用系统B的程序时B中有许多个程序到底调用哪个程序的问题也就是说B系统中的每个程序都具有一个唯一 id只要其他系统在发起远程调用时携带自己要调用程序的 Call id系统B就能成功识别他想要运行的程序
我们的调用逻辑是
将传递的参数使用 json 协议进行传输类似的协议还有 xml、protobuf、msgpack另外现在网络调用有两个端客户端用于发送数据服务器端用于接收数据
另外一个问题就是JSON 不是一个高性能的编码协议我们在追求极致性能的时候可能不会优先考虑 json
另外json 的优势在于其通用性可扩展性几乎所有的系统都支持 json但其另外的问题就是其过于灵活不能将其作为程序的对象存储来代替struct
而我们的网络协议是看不懂 struct 的其只能识别二进制的流故而我们必须将我们转换的数据转换成二进制的流才可以进行传输
在一次 RPC 过程中服务器端和客户端分别要做的事情
客户端
1. 建立连接tcp \ http
2. 将我们要发送的数据序列化为 json 字符串 - 序列化
3. 发送实际上发送的是二进制流
4. 等待服务器结果
5. 服务器返回结果客户端将结果反序列化为可识别数据服务器端
1. 监听网络端口80
2. 读取客户端发来的二进制数据并将其转换成Employee对象反序列化
3. 处理数据生成一个带有更完成信息的对象例如R其中封装了404、201等信息
4. 将处理的数据结果转换成 json 二进制 序列化 发送给客户端我们的序列化技术不一定非要使用 json、我们还可以选择xml、protobuf、msgpack 等
我们只要解决了 序列化问题其实就解决了数据互通问题其实也就屏蔽了我们相互调用时的语言不同的问题java、python、go
网络问题
我们使用 http 与 tcp 最大的区别就是
http是一次性的建立连接之后一旦收到数据的返回tcp 连接就断开而 tcp 连接是可以复用的解决了 tcp 连接要重新建立的问题
另外我们也可以使用 http2.0 来解决这个问题http2.0 支持长连接可以解决连接的建立问题
一个简单的例子服务器端
func main() {http.HandleFunc(/add, func(w http.ResponseWriter, r *http.Request) {// 这里面写逻辑_ r.ParseForm() // 解析参数可能会报错fmt.Println(path: , r.URL.Path)a, _ : strconv.Atoi(r.Form[a][0])b, _ : strconv.Atoi(r.Form[a][0])// 进行返回w.Header().Set(Content-Type, application/json)// 构建返回体jData, _ : json.Marshal(map[string]int{data: a b,})// 真正写入w.Write(jData)})// 设置监听的端口_ http.ListenAndServe(:8000, nil)
}上面这个例子就典型的解决了
Call ID问题使用URL路径指明要调用的方法
数据传输协议Http 的参数传递协议
但这里的问题是使用的是 http1.0的协议性能低手写http数据需要自己解析效率低
客户端举例
我们也可以不写客户端直接使用浏览器来发送请求解决问题
我们访问127.0.0.1:8000/add?a1b4
RPC 技术原理
客户端发送请求由客户端存根处理客户端存根将请求整理成协议对应的格式进行发送将其发送到服务器端由服务器端接收这之后发送给服务器端存根进行解码再返回给服务器处理
RPC 开发Hello World级别
创建目录结构 Project server server.go client client.go server.go
type HelloService struct {
}// 给这个类绑定这个方法
func (s *HelloService) Hello(request string, reply *string) error {// 通过修改 reply 值来进行返回*reply hello, requestreturn nil
}/*
*
1. 实例化一个server
2. 注册逻辑
3. 开启服务
*/
func main() {// 第一步实例化 serverlistener, _ : net.Listen(tcp, :1234)// 第二步将我们的 struct 注册进 RPC// 我们如果把形参列表中的参数定义为 interface{} 就代表这个参数我们可以任意传入_ rpc.RegisterName(HelloService, HelloService{})// 第三部启动服务绑定rpcconn, _ : listener.Accept()rpc.ServeConn(conn)
}
client.go
func main() {// Dial() 意思是拨号也就是尝试进行连接同时进行Gob进行编码client, err : rpc.Dial(tcp, localhost:1234)if err ! nil {panic(链接失败)}// 注意这种方式会直接开辟一片空间并且给这片空间的 string 赋予初值 //var reply string// 如果以下面这种方式就复杂一点var replyy *string new(string)// 发送请求请求对应的方法其后面的参数是传入的参数根据我们方法的编写我们最后一个参数用来接收数据err client.Call(HelloService.Hello, Chen, replyy)if err ! nil {panic(方法调用出错)}fmt.Println(*replyy)}hello, Chen注意在上面的代码中实例化 server、启动服务都是由 net 包完成的但单独 net 包是不能完成一整个流程的这是因为还有 call id 的匹配以及序列化机制是由 rpc 来完成的
另外的是GRPC在此时还不够简洁使用效率不够高这体现在包括客户端在调用时不能直接调用方法而是需要明确方法名等
同时上面这种最基本的rpc使用的编码解码协议是 Gob这只能在 go 语言中进行通信其不支持跨语言
使用JSON协议的RPC
建立目录结构 json_rpc_test server server.go client client.go server.go
type HelloService struct {
}func (s *HelloService) Hello(request string, reply *string) error {// 通过修改 reply 值来进行返回*reply hello, requestreturn nil
}
func main() {listener, _ : net.Listen(tcp, :1234)_ rpc.RegisterName(HelloService, HelloService{})// 允许服务器处理多次请求for {conn, _ : listener.Accept()// 使用自定义协议进行修改go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) // 这里不加 go 协程会出现多个请求的并发问题}
}client.go
func main() {// 使用基础的拨号不进行编码编码在后面进行conn, err : net.Dial(tcp, localhost:1234)if err ! nil {panic(链接失败)}// 进行 json 编码client : rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))var replyy *string new(string)err client.Call(HelloService.Hello, Chen, replyy)if err ! nil {panic(方法调用出错)}fmt.Println(*replyy)}测试 RPC 的跨语言特性
使用 python 的socket 编程发送一个json 不使用http因为我们服务器没有使用http无法进行解析
import json
import socketrequest {id:0,params:[Zhang],method:HelloService.Hello
}client socket.create_connection((localhost, 1234))
client.sendall(json.dumps(request).encode())# 获取服务器返回的数据
rsp client.recv(1024)
rsp json.loads(rsp.decode())
print(rsp[result])hello, Zhang使用 java public static void main(String[] args) {try {// Connect to the serverSocket socket new Socket(localhost, 1234);// Create a JSON requestString jsonRequest {\id\: 0, \method\: \HelloService.Hello\, \params\: [\Yang\]};// Send the JSON request to the serverOutputStream outputStream socket.getOutputStream();outputStream.write(jsonRequest.getBytes());outputStream.flush();// Receive the response from the serverBufferedReader reader new BufferedReader(new InputStreamReader(socket.getInputStream()));String response reader.readLine();System.out.println(Response from server: response);// Close the socketsocket.close();} catch (IOException e) {e.printStackTrace();}}Response from server: {id:0,result:hello, Yang,error:null}使用 HTTP 协议 JSON 的RPC
其实在这一步已经有成熟框架可以使用这里为了学习我们使用rpc搭建一个自己的框架
构建目录 http_rpc_test server server.go client client.go server.go
type HelloService struct {
}func (s *HelloService) Hello(request string, reply *string) error {// 通过修改 reply 值来进行返回*reply hello, requestreturn nil
}
func main() {_ rpc.RegisterName(HelloService, HelloService{})http.HandleFunc(/jsonrpc, func(w http.ResponseWriter, r *http.Request) {var conn io.ReadWriteCloser struct {io.Writerio.ReadCloser}{ReadCloser: r.Body,Writer: w,}rpc.ServeRequest(jsonrpc.NewServerCodec(conn))})http.ListenAndServe(:1234, nil)
}在这里就可以使用其他语言发送 http 请求获取结果了
将方法改造成调用式的方法
以基础的 HelloWorld 级别代码为例构建目录 new_helloworld server server.go client client.go handler handler.go server_proxy server_proxy.go client_proxy client_proxy.go 我们通过定义一个公共文件来实现
handler.go
const HelloServiceName handler/HelloServicetype HelloService struct{}// 给这个类绑定这个方法
func (s *HelloService) Hello(request string, reply *string) error {// 通过修改 reply 值来进行返回*reply hello, requestreturn nil
}
server.go
func main() {// 第一步实例化 serverlistener, _ : net.Listen(tcp, :1234)// 第二步将我们的 struct 注册进 RPC// 我们如果把形参列表中的参数定义为 interface{} 就代表这个参数我们可以任意传入_ rpc.RegisterName(handler.HelloServiceName, handler.HelloService{})// 第三部启动服务绑定rpcfor {conn, _ : listener.Accept()go rpc.ServeConn(conn)}client.go
func main() {// Dial() 意思是拨号也就是尝试进行连接client : client_proxy.NewHelloServiceClient(tcp, 127.0.0.1:1234)// 注意这种方式会直接开辟一片空间并且给这片空间的 string 赋予初值 //var reply string// 如果以下面这种方式就复杂一点var replyy *string new(string)// 发送请求请求对应的方法其后面的参数是传入的参数根据我们方法的编写我们最后一个参数用来接收数据_ client.Hello(Chen, replyy)fmt.Println(*replyy)
}server_proxy.go
type HellosSrvicer interface {Hello(request string, reply *string) error
}func RegisterHelloService(srv HellosSrvicer) error {return rpc.RegisterName(handler.HelloServiceName, srv)
}client_proxy.go
type HelloServiceStub struct {*rpc.Client
}// 初始化在Go中使用 Newxxx进行初始化
func NewHelloServiceClient(protcol, address string) HelloServiceStub {conn, err : rpc.Dial(protcol, address)if err ! nil {panic(connect error)}return HelloServiceStub{conn}
}func (c *HelloServiceStub) Hello(request string, reply *string) error {err : c.Call(handler.HelloServiceName.Hello, request, reply)return err
}