http协议
HTTP(Hypertext Transfer Protocol,超文本传输协议)是一种用于在计算机之间传输超文本的应用层协议。它是Web应用中最常用的协议,用于在客户端和服务器之间传输数据。
HTTP超文本传输协议教程
http由定义可以看出是传输超文本文件的,这里的范围很广,html文件,xml文件,字符串,数字,视频,图片等等,总之一切以流形式存在的文件都可以通过http协议传输。总的来说http传输的主体是流,字节流(文件,视频),字符流(各种数据),字符串常常以字符串表现,通过统一的序列化与反序列化规则恢复与压缩。
http协议存在与B/S或者C/S架构,为客户端(request)和服务端(respose)的新式,因此在计算器通讯时必须存在一个机器作为服务端一直监听请求,等待客户端请求数据。客户端与服务端通过http规定的解码与编码规则保证了传输数据的一致性。
对于服务端监听请求的叫web服务器,对于客户端发送请求的叫web浏览器。浏览器是指能够发送http请求的一类应用,现在有很多封装了http库可以直接使用如Ajax,Axios等,对http协议十分了解的也可以自行封装工具库。
一个http请求一般包含如下部分:
浏览器的开发者模式,对http请求的命名为xhr,如下
总结来说,http协议主要用来传输不同计算机上的产生的数据的。
在网络通讯的发展中仅仅用于传输数据的http协议已不再满足需求,如需要某个计算机的运行结果,使用http协议还需要等待其运行完成,在对数据封装,再发送;那么有没有可能,在其他计算机上直接调用需求计算机的线程,使其运行结果直接返回目标计算机而不必在对数据封装。于是rpc协议就诞生了。
rpc
RPC(Remote Procedure Call,远程过程调用)是一种计算机通信协议,用于在不同的计算机或进程之间进行远程调用。它允许一个计算机程序通过网络请求另一个计算机上的服务或函数,就像调用本地函数一样。
在通讯的不断发展中rpc协议已经实现了进程的调用,在其他计算机上调用目标计算机的线程,实现内存共享。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。
rpc相关问题
在RPC中,客户端程序通过发送请求消息给服务器端程序,服务器端程序接收请求消息并执行相应的操作,然后将结果返回给客户端。这个过程对于客户端来说是透明的,就像调用本地函数一样,不需要关心底层的网络通信细节。
RPC协议通常包括以下几个关键组件:
-
通信协议:定义了客户端和服务器之间的通信规则,如数据传输格式、编码方式、网络传输协议等。常见的通信协议有HTTP、TCP、UDP等。
-
接口定义语言(IDL):用于定义客户端和服务器之间的接口和数据结构。IDL提供了一种统一的语法和语义,使得不同编程语言的程序可以相互调用。
-
序列化和反序列化:将数据结构转换为字节流进行传输,或将接收到的字节流转换为数据结构。序列化和反序列化是RPC协议中的重要环节,用于在网络中传输数据。
-
代理和存根:客户端和服务器端分别使用代理和存根来隐藏底层的网络通信细节。客户端的代理负责将函数调用转换为网络请求,发送给服务器端;服务器端的存根负责接收请求,执行相应的操作,并将结果返回给客户端。
RPC协议可以用于构建分布式系统,将不同的计算机或进程连接起来,实现跨网络的函数调用。常见的RPC框架有gRPC、Apache Thrift、Apache Dubbo等。
rpc教程
rpcx
rpcx官方文档
rpcx是和grpc一样流行的rpc框架,更加轻量级,也更贴合原生的rpc使用。
rpcx时国人开发的文档丰富就不再赘述了,可以移步开发手册rpcx快速起步
大致分三步:
- 需调用线程及方法
- 服务端程序
- 客户端程序
案例如下server为服务端程序,client为客户端程序,service为服务线程。
service
服务线程方法
package service
import (
"context"
)
// 主程序的方法及数据
type Param struct {
A, B int
C string
}
type Calculate int
// 返回数据
type Result struct {
D int
}
// 计算方法
func (t *Calculate) Compute(cxt context.Context, args *Param, reply *Result) error {
if args.C == "+" {
reply.D = args.A + args.B
} else if args.C == "-" {
reply.D = args.A - args.B
} else if args.C == "*" {
reply.D = args.A * args.B
} else if args.C == "/" {
reply.D = args.A / args.B
} else {
reply.D = 0
}
return nil
}
服务相对简单,定义一个类编写方法,Param是参数,Result是返回数据。
这里需要注意的是,rpc调用的方法的参数和返回值必须来源于服务的方法类,不能通过远程调用接收时自定义。就行在使用grpc中参数和返回值都只能来源于pb文件,即使是自定义的完全一致的也不可以。
定义了Calculate 类有Compute方法传入参数和返回值,一般情况下,第一个参数是context.Context
,第二个参数是参数,第三个参数是返回值。方法体就是根据表示进行加减乘除计算了。
server
服务端程序
package main
import (
"rpctest/service"
"github.com/smallnest/rpcx/server"
)
// rpc服务器注册
func main() {
s := server.NewServer()
s.RegisterName("xiaoxu", new(service.Calculate), "")
s.Serve("tcp", ":8972")
}
创建服务端将服务注册到服务端并起别名。
client
客户端
package main
import (
"context"
"flag"
"fmt"
"log"
"rpctest/service"
"github.com/smallnest/rpcx/client"
)
var (
addr = flag.String("addr", "localhost:8972", "server address")
)
func main() {
flag.Parse()
// 直接连接rpc服务器,连接会话
d, err := client.NewPeer2PeerDiscovery("tcp@"+*addr, "")
if err != nil {
log.Fatalf("failed to call: %v", err)
}
// 获取客户端接口
xclient := client.NewXClient("xiaoxu", client.Failtry, client.RandomSelect, d, client.DefaultOption)
defer xclient.Close()
//接口方法参数
args := &service.Param{A: 10, B: 8, C: "+"}
var reply service.Result
//客户端接口调用方法
err = xclient.Call(context.Background(), "Compute", args, &reply)
if err != nil {
log.Fatalf("failed to call: %v", err)
}
fmt.Printf("调用rpc的结果为:%d", reply.D)
}
创建rpc连接会话,通过别名获取连接到服务端的客户端,通过客户端创建接口,一般调用方法使用Call
方法,第一个参数是context.Context
,第一个参数是要调用的方法名称,第三个参数是服务参数,第四哥参数是服务返回值。
启动服务端
调用客户端
在此情况下,服务中定义一的任意类,参数与返回值均可以在客户端被调用,如下,在service
再定义一个方法:
type ResultStr struct {
Str string
}
func (t *Calculate) Hello(cxt context.Context, args *Param, reply *ResultStr) error {
reply.Str = fmt.Sprintf("连接的三个参数分别为%d,%d,%s", args.A, args.B, args.C)
return nil
}
由于属于同一个类连接会话一样,接口一样,在客户端可直接调用。
server
// hello函数
var replay1 service.ResultStr
xclient.Call(context.Background(), "Hello", args, &replay1)
fmt.Println(replay1.Str)
除了Call
方法同步获取返回结果外,也提供了Go
异步获取返回结果,此处不再赘述。
注册中心
在使用rpc时常常使用注册中心功能,对于分布式部署来说,各个主机的ip时不一样的,但是方法名都是一样的,因此可以通过方法名调用,这就需要使用注册中心功能。
在之前的服务器会话是点对点连接,未使用注册中心,点对点是最简单的一种注册中心的方式,事实上没有注册中心,客户端直接得到唯一的服务器的地址,连接服务。也就是使用NewPeer2PeerDiscovery
方法。
d := client.NewPeer2PeerDiscovery("tcp@"+*addr, "")
xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, client.DefaultOption)
defer xclient.Close()
但是有若干台服务器,且是分布式的就不再实用了,需要实用注册中心,常见的注册中心有ZooKeeper
,Etcd
,Consul
等。
rpcx默认实用配置方式完成注册中心功能,只要开启相关功能配置插件即可。
实用etcd注册中心的服务端就变成了
// go run -tags etcd server.go
func main() {
flag.Parse()
s := server.NewServer()
addRegistryPlugin(s)
s.RegisterName("Arith", new(example.Arith), "")
s.Serve("tcp", *addr)
}
func addRegistryPlugin(s *server.Server) {
r := &serverplugin.EtcdRegisterPlugin{
ServiceAddress: "tcp@" + *addr,
EtcdServers: []string{*etcdAddr},
BasePath: *basePath,
Metrics: metrics.NewRegistry(),
UpdateInterval: time.Minute,
}
err := r.Start()
if err != nil {
log.Fatal(err)
}
s.Plugins.Add(r)
}
客户端连接的方法换成了注册中心的连接,客户端需要设置EtcdDiscovery插件,设置basepath和etcd集群的地址。
// go run -tags etcd client.go
d := client.NewEtcdDiscovery(*basePath, "Arith",[]string{*etcdAddr}, nil)
xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, client.DefaultOption)
defer xclient.Close()
go rpc开发指南
Go RPC 开发指南
地鼠文档