东风草堂博客

公众号:开发者来风

网页重定向

网页迟迟没有被google收录,进入https://search.google.com/发现:

在 Nuxt.js 中,您可以通过设置 generate.subFolders 选项来控制生成的文件是否应该包含子目录。默认情况下,该选项的值为 true,这意味着生成的文件将被放置在相应的路由目录中。

如果您不想要生成的文件包含子目录,并直接生成 HTML 文件,您可以将 generate.subFolders 设置为 false。您可以在 nuxt.config.js 文件中添加以下配置:

1
2
3
4
5
export default {
generate: {
subFolders: false
}
}
阅读全文 »

服务器TNonblockingServer

rpc请求生成task放入到任务队列中,这个任务是Runnable的,但没有依附于某个thread,等于是封装了个run函数,供线程池工作线程调用时运行的。

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
void addTask(stdcxx::shared_ptr<Runnable> task) {
threadManager_->add(task, 0LL, taskExpireTime_);
}

stdcxx::shared_ptr<Runnable> task = stdcxx::shared_ptr<Runnable>(
new Task(processor_, inputProtocol_, outputProtocol_, this));
server_->addTask(task);

// 真正的任务,其实也是一个run函数,这个Runnable是没有thread实体的
class TNonblockingServer::TConnection::Task : public Runnable {
public:
Task(stdcxx::shared_ptr<TProcessor> processor,
stdcxx::shared_ptr<TProtocol> input,
stdcxx::shared_ptr<TProtocol> output,
TConnection* connection)
: processor_(processor),
input_(input),
output_(output),
connection_(connection),
serverEventHandler_(connection_->getServerEventHandler()),
connectionContext_(connection_->getConnectionContext()) {}

void run() {
try {
for (;;) {
if (serverEventHandler_) {
serverEventHandler_->processContext(connectionContext_, connection_->getTSocket());
}
if (!processor_->process(input_, output_, connectionContext_)
|| !input_->getTransport()->peek()) {
break;
}
}
} catch (const TTransportException& ttx) {
GlobalOutput.printf("TNonblockingServer: client died: %s", ttx.what());
} catch (const std::bad_alloc&) {
GlobalOutput("TNonblockingServer: caught bad_alloc exception.");
exit(1);
} catch (const std::exception& x) {
GlobalOutput.printf("TNonblockingServer: process() exception: %s: %s",
typeid(x).name(),
x.what());
} catch (...) {
GlobalOutput.printf("TNonblockingServer: unknown exception while processing.");
}

// Signal completion back to the libevent thread via a pipe
if (!connection_->notifyIOThread()) {
GlobalOutput.printf("TNonblockingServer: failed to notifyIOThread, closing.");
connection_->server_->decrementActiveProcessors();
connection_->close();
throw TException("TNonblockingServer::Task::run: failed write on notify pipe");
}
}

TConnection* getTConnection() { return connection_; }

private:
stdcxx::shared_ptr<TProcessor> processor_;
stdcxx::shared_ptr<TProtocol> input_;
stdcxx::shared_ptr<TProtocol> output_;
TConnection* connection_;
stdcxx::shared_ptr<TServerEventHandler> serverEventHandler_;
void* connectionContext_;
};

线程池管理ThreadManager

线程池的管理,包括增加工作线程worker,这个worker线程是继承自Runnable的,依附于具体的线程thread,还有就是增加任务,上面说到了server是怎么调用这个增加任务的接口的。注意worker和task都是继承自runnable,一个有依附的thread,一个没有,都要实现run函数。

阅读全文 »

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传递指针而不是值的写法:

阅读全文 »
0%