东风草堂博客

公众号:开发者来风

thrift编解码过程

Thrift是一种用于定义RPC服务和数据交换格式的框架,其中编解码过程是其重要的组成部分。下面简单介绍一下Thrift的编解码过程:

  1. 定义IDL接口:使用Thrift的IDL语言进行服务接口定义,包括服务方法、参数和返回值的类型及名称等信息。
  2. 生成代码:使用Thrift提供的代码生成器根据IDL文件生成各语言环境下可用的客户端和服务端代码。
  3. 编写客户端和服务端:使用生成的代码进行客户端和服务端的开发。
  4. 序列化请求:客户端将请求参数序列化为二进制格式,以便在网络上传输。
  5. 发送请求:客户端通过网络发送请求消息。
  6. 接收请求:服务端接收请求消息。
  7. 反序列化请求:服务端将接收到的二进制数据反序列化为具体的参数类型。
  8. 处理请求:服务端根据请求消息调用对应的服务方法,并返回处理结果。
  9. 序列化响应:服务端将返回结果序列化为二进制格式,以便在网络上传输。
  10. 发送响应:服务端通过网络发送响应消息。
  11. 接收响应:客户端接收响应消息。
  12. 反序列化响应:客户端将接收到的二进制数据反序列化为具体的返回值类型。

序列化过程

Thrift的序列化过程可以分为两个步骤:写入(Write)和读取(Read)。对于一个需要序列化的对象,Thrift会按照其定义的类型和顺序依次将其转换为二进制数据。
Thrift支持多种序列化协议,常用的有: Binary、Compact、JSON。binary序列化是一种二进制的序列化方式。不可读,但传输效率高。

阅读全文 »

概述

简介

在函数内部,可以省略var关键字,使用更简单的定义模式。

1
2
3
4
fun main() {
x := 100
fmt.Println(x)
}

流程控制可以省略条件判断:

阅读全文 »

Go语言的反射实现了反射的大部分功能,但没有像Java语言那样内置类型工厂,故而无法做到像Java那样通过类型字符串创建对象实例。反射是把双刃剑,功能强大但代码可读性并不理想。若非必要,我们并不推荐使用反射。

Type为io.Reader,Value为MyReader{“a.txt”}。顾名思义,Type主要表达的是被反射的这个变量本身的类型信息,而Value则为该变量实例本身的信息。

1
2
3
4
5
6
7
8
type MyReader struct {
Name string
}
func (r MyReader)Read(p []byte) (n int, err error) {
// 实现自己的Read方法
}
var reader io.Reader
reader = &MyReader{"a.txt"}

直接传递一个float到函数时,函数不能对外部的这个float变量有任何影响,要想有影响的话,可以传入该float变量的指针.

1
2
3
4
5
6
7
8
9
var x float64 = 3.4
p := reflect.ValueOf(&x) // 注意:得到X的地址
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:" , p.CanSet())
v := p.Elem()
fmt.Println("settability of v:" , v.CanSet())
v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)
阅读全文 »

Go语言明确宣告了拥护骆驼命名法而排斥下划线法。骆驼命名法在Java和C#中得到官方的支持和推荐,而下划线命名法则主要用在C语言的世界里,比如Linux内核和驱动开发上。

不带任何参数直接运行go fmt的话,可以直接格式化当前目录下的所有*.go文件,或者也可以指定一个GOPATH中可以找到的包名。

Go语言不仅允许我们导入本地包,还支持在语言级别调用远程的包。然后,在执行go build或者go install之前,只需要加这么一句: go get github.com/myteam/exp/crc32

1
2
3
4
import (
"fmt"
"github.com/myteam/exp/crc32"
)

当我们执行完go get之后,我们会在src目录中看到github.com目录,其中包含myteam/exp/crc32目录。在crc32中,就是该包的所有源代码。也就是说,go工具会自动帮你获取位于远程的包源码,在随后的编译中,也会在pkg目录中生成对应的.a文件。
对于Go语言本身来讲,远程包github.com/myteam/ exp/crc32只是一个与fmt无异的本地路径而已。

阅读全文 »

采用单密钥的加密算法,我们称为对称加密。整个系统由如下几部分构成:需要加密的明文、加密算法和密钥。在加密和解密中,使用的密钥只有一个。常见的单密钥加密算法有DES、AES、RC4等。

采用双密钥的加密算法,我们称为非对称加密。整个系统由如下几个部分构成:需要加密的明文、加密算法、私钥和公钥。在该系统中,私钥和公钥都可以被用作加密或者解密,但是用私钥加密的明文,必须要用对应的公钥解密,用公钥加密的明文,必须用对应的私钥解密。常见的双密钥加密算法有RSA等。

哈希算法是一种从任意数据中创建固定长度摘要信息的办法。一般我们要求,对于不同的数据,要求产生的摘要信息也是唯一的。常见的哈希算法包括MD5、SHA-1等。

数字签名,是指用于标记数字文件拥有者、创造者、分发者身份的字符串。数字签名拥有标记文件身份、分发的不可抵赖性等作用。
A公司发布了一个可执行文件,称为AProduct.exe,A在AProduct.exe中加入了A公司的数字签名。A公司的数字签名是用A公司的私钥加密了AProduct.exe文件的哈希值,我们得到打过数字签名的AProduct.exe后,可以查看数字签名。这个过程实际上是用A公司的公钥解密了文件哈希值,从而可以验证两个问题:AProduct.exe是否由A公司发布,AProduct.exe是否被篡改。

我们登录某银行的网站,这时候网站会提示我们下载数字证书,否则将无法正常使用网银等功能。在我们首次使用U盾的时候,初始化过程即是向U盾中下载数字证书。那么,数字证书中包含什么呢?数字证书中包含了银行的公钥,有了公钥之后,网银就可以用公钥加密我们提供给银行的信息,这样只有银行才能用对应的私钥得到我们的信息,确保安全

阅读全文 »

TCP链接:
conn, err := net.Dial(“tcp”, “192.168.0.10:2100”)
UDP链接:
conn, err := net.Dial(“udp”, “192.168.0.12:975”)
ICMP链接(使用协议名称):
conn, err := net.Dial(“ip4:icmp”, “www.baidu.com“)
ICMP链接(使用协议编号):
conn, err := net.Dial(“ip4:1”, “10.0.0.3”)

在成功建立连接后,我们就可以进行数据的发送和接收。发送数据时,使用conn的Write()成员方法,接收数据时使用Read()方法。

Dial()函数是对DialTCP()、DialUDP()、DialIP()和DialUnix()的封装.
验证IP地址有效性的代码如下: func net.ParseIP()
创建子网掩码的代码如下:
func IPv4Mask(a, b, c, d byte) IPMask
获取默认子网掩码的代码如下:
func (ip IP) DefaultMask() IPMask 根据域名查找IP的代码如下:
func ResolveIPAddr(net, addr string) (*IPAddr, error)
func LookupHost(name string) (cname string, addrs []string, err error);

net/http包的Client类型提供了如下几个方法,让我们可以用最简洁的方式实现HTTP请求:
func (c *Client) Get(url string) (r *Response, err error)
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err
error)
func (c *Client) PostForm(url string, data url.Values) (r *Response, err error) func (c *Client) Head(url string) (r *Response, err error)
func (c *Client) Do(req *Request) (resp *Response, err error)

http.Get()方法,等价于http.DefaultClient.Get()。

阅读全文 »

在工程上,有两种最常见的并发通信模型:共享数据和消息。共享数据是指多个并发单元分别保持对同一个数据的引用,实现对该数据的共享。被共享的数据可能有多种形式,比如内存数据块、磁盘文件、网络数据等。在实际工程应用中最常见的无疑是内存了,也就是常说的共享内存。Go语言提供的是另一种通信模型,即以消息机制而非共享内存作为通信方式。

消息机制认为每个并发单元是自包含的、独立的个体,并且都有自己的变量,但在不同并发单元间这些变量不共享。每个并发单元的输入和输出只有一种,那就是消息。这有点类似于进程的概念,每个进程不会被其他进程打扰,它只做好自己的工作就可以了。不同进程间靠消息来通信,它们不会共享内存。

channel是Go语言在语言级别提供的goroutine间的通信方式。如果对Unix管道有所了解的话,就不难理解channel,可以将其认为是一种类型安全的管道。

我们通过ch <- 1语句向对应的channel中写入一个数据。在这个channel被读取前,这个操作是阻塞的。在所有的goroutine启动完成后,我们通过<-ch语句从10个channel中依次读取数据。在对应的channel写入数据前,这个操作也是阻塞的。这样,我们就用channel实现了类似锁的功能,进而保证了所有goroutine完成后主函数才返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"
func Count(ch chan int) {
ch <- 1
fmt.Println("Counting")
}
func main() {
chs := make([]chan int10)
for i := 0; i < 10; i++ {
chs[i] = make(chan int)
go Count(chs[i]) }
for _, ch := range(chs) {
<-ch // 阻塞等待,不然退出了
}
}
阅读全文 »

在Go语言中,你可以给任意类型(包括内置类型,但不包括指针类型)添加相应的方法,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}

func main() {
var a Integer = 1
if a.Less(2) {
fmt.Println(a, "Less 2")
}
}

func (a Integer) Less(b Integer) bool { // 面向对象 return a < b
}
func Integer_Less(a Integer, b Integer) bool { // 面向过程 return a < b
}
a.Less(2) // 面向对象的用法
Integer_Less(a, 2) // 面向过程的用法

C++语言的面向对象之所以让有些人迷惑的一大原因就在于其隐藏的this指针。一旦把隐藏的this指针显露出来,大家看到的就是一个面向过程编程。如果读者了解Python语法,就会知道Python的成员方法中会有一个self参数,它和this指针的作用是完全一样的。

1
2
3
4
5
struct Integer { int val;
};
bool Integer_Less(Integer* this, Integer* b) { // 不隐藏的写法
return this->val < b->val;
}

go传递指针而不是值的写法:

阅读全文 »

Go语言中提供了C/C++程序员期盼多年的多重赋值功能,比如下面这个交换i和j变量的语句:
i, j = j, i
在不支持多重赋值的语言中,交互两个变量的内容需要引入一个中间变量:
t = i; i = j; j = t;

Go的常量定义const Pi float64 = 3.14159265358979323846可以限定常量类型,但不是必需的。如果定义常量时没有指定类型,那么它 与字面常量一样,是无类型常量const zero = 0.0。
由于常量的赋值是一个编译期行为,所以右值不能出现任何需要运行期才能得出结果的表达式,比如试图以如下方式定义常量就会导致编译错误:const Home = os.GetEnv(“HOME”)

iota比较特殊,可以被认为是一个可被编译器修改的常量,在每一个const关键字出现时被重置为0,然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增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
const (
a = 1 << iota // a == 1 (iota在每个const开头被重设为0)
b = 1 << iota // b == 2
c = 1 << iota // c == 4
)

// 如果两个const的赋值语句的表达式是一样的,那么可以省略后一个赋值表达式
const ( // iota被重设为0
c0 = iota // c0 == 0
c1 // c1 == 1
c2 // c2 == 2
)

// 枚举的写法
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 这个常量没有导出
)

布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换。

阅读全文 »

所谓垃圾回收,即所有的内存分配动作都会被在运行时记录,同时任何对该内存的使用也都会被记录,然后垃圾回收器会对所有已经分配的内存进行跟踪监测,一旦发现有些内存已经不再被任何人使用,就阶段性地回收这些没人用的内存。

1
2
3
4
5
6
// c语言内存回收的考验
int* p = new int;
p += 10; // 对指针进行了偏移,因此那块内存不再被引用
// ...... 这里可能会发生针对这块int内存的垃圾收集 ......
p -= 10; // 咦,居然又偏移到原来的位置
*p = 10; // 如果有垃圾收集,这里就无法保证可以正常运行了

Go语言还内置了一个对于其他静态类型语言通常用库方式支持的字典类型(map)。可以认为数组切片是一种可动态增长的数组,数组切片的功能与C++标准库中的vector非常类似。

1
2
3
4
5
6
7
8
9
10
11
12
// 多返回值
func getName()(firstName, middleName, lastName, nickName string){ return "May", "M", "Chen", "Babe"
}
// 可以返回空值
func getName()(firstName, middleName, lastName, nickName string){ firstName = "May"
middleName = "M"
lastName = "Chen"
nickName = "Babe"
return
// 调用过程
fn, mn, ln, nn := getName()
}

c语言在实现一个接口之前必须先定义该接口,并且将类型和接口紧密绑定,即接口的修改会影响到所有实现了该接口的类型,而Go语言的接口体系则避免了这类问题。

阅读全文 »
0%