东风草堂博客

公众号:开发者来风

https://docs.midjourney.com/docs/invite-the-bot,可以将机器人拉入到自己的群中,这样消息不会被淹没。

api下载mj生成的图片:https://medium.com/@neonforge/how-to-create-a-discord-bot-to-download-midjourney-images-automatically-python-step-by-step-guide-3e76d3282871
api自动发送mj消息:https://medium.com/@neonforge/how-to-automate-midjourney-image-generation-with-python-and-gui-automation-ac9ca5f747ae

discord生成应用:https://discord.com/developers/applications
discord开发手册:https://discord.com/developers/docs/interactions/application-commands

1
2
3
4
5
Action shot.An Asian little boy sitting on a flying pterosaur .Close-Up.surreal photography. 

A 10 year old Chinese girl wearing a fiery red dress and a large fiery phoenix, 16k, ultra detailed, ultra realistic, captured by a Fuji camera,, --iw 2 --ar 3:4 --v 5.2

A lively and cute Chinese girl interacts with a huge flower in a surreal setting, 6 year old girl, looks like Angelababy, the art of Hitoko Kawauchi, natural poses, dadcore for vacation, energy and pressure of youth, body extension, simulation film in the sky, super detail, dreamlike high photography. A riot of colour, shot on fujifilm XT4,3d, c4d,hd, oc renderer, cinematic lighting, super detail, 8k, super detail --s 350 --ar 3:4

网页重定向

网页迟迟没有被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 // 阻塞等待,不然退出了
}
}
阅读全文 »
0%