【go语言之thrift协议二之server端分析】
go语言之thrift协议二
- server
- thrift.TProtocolFactory
- TTransport
- ReadWriteCloser
- ContextFlusher
- ReadSizeProvider
- TProtocol
- runServer
- NewTServerSocket
- NewCalculatorHandler
- NewCalculatorProcessor
- NewTSimpleServer4
- server.Serve
- Listen
- AcceptLoop
- processRequests
在上一篇文章分析了在thrift协议下面,使用thrift生成了go对应的文件,以及thrift不同的语法在go中生成的模块。接下来结合官方的示例分析一下thrift的基本使用。
实现看一下在server端的实现。
server
package main
import ("crypto/tls""flag""fmt""os""github.com/apache/thrift/lib/go/thrift"
)func Usage() {fmt.Fprint(os.Stderr, "Usage of ", os.Args[0], ":\n")flag.PrintDefaults()fmt.Fprint(os.Stderr, "\n")
}func main() {flag.Usage = Usage// 是server端还是client端server := flag.Bool("server", false, "Run server")// 编码方式 默认是compactprotocol := flag.String("P", "compact", "Specify the protocol (binary, compact, json, simplejson)")// 是否使用基于frame格式的编码方式framed := flag.Bool("framed", false, "Use framed transport")// 是否使用带有buffer的transportbuffered := flag.Bool("buffered", false, "Use buffered transport")// server或者client的地址addr := flag.String("addr", "localhost:9090", "Address to listen to")// 是否使用httpssecure := flag.Bool("secure", false, "Use tls secure transport")// 解析参数flag.Parse()// 根据不同的protocol初始化不同的thrift.TProtocolFactoryvar protocolFactory thrift.TProtocolFactoryswitch *protocol {case "compact":protocolFactory = thrift.NewTCompactProtocolFactoryConf(nil)case "simplejson":protocolFactory = thrift.NewTSimpleJSONProtocolFactoryConf(nil)case "json":protocolFactory = thrift.NewTJSONProtocolFactory()case "binary", "":protocolFactory = thrift.NewTBinaryProtocolFactoryConf(nil)default:fmt.Fprint(os.Stderr, "Invalid protocol specified", protocol, "\n")Usage()os.Exit(1)}// 根据参数生成不同的transportFactoryvar transportFactory thrift.TTransportFactorycfg := &thrift.TConfiguration{TLSConfig: &tls.Config{InsecureSkipVerify: true,},}if *buffered {transportFactory = thrift.NewTBufferedTransportFactory(8192)} else {transportFactory = thrift.NewTTransportFactory()}if *framed {transportFactory = thrift.NewTFramedTransportFactoryConf(transportFactory, cfg)}// 初始化server或者clientif *server {if err := runServer(transportFactory, protocolFactory, *addr, *secure); err != nil {fmt.Println("error running server:", err)}} else {if err := runClient(transportFactory, protocolFactory, *addr, *secure, cfg); err != nil {fmt.Println("error running client:", err)}}
}
这里可以看出来这里的主要就是有两个动作
- 初始化thrift.TProtocolFactory 这里因为使用了默认的protocol也就是compact。所以这里是 protocolFactory = thrift.NewTCompactProtocolFactoryConf(nil)
- 初始化thrift.TTransportFactory。因为这里是初始化的默认的.所以transportFactory = thrift.NewTTransportFactory()
这里看一下这两个方法
thrift.TProtocolFactory
从名字上来看这个应该是一个工厂模式。然后先看一下这个定义
type TProtocolFactory interface {GetProtocol(trans TTransport) TProtocol
}
所以看出来这个具体的作用就是根据TTransport进行封装,然后生成符合TProtocol的结构体
然后看一下TTransport和TProtocol这两个方法
TTransport
// Encapsulates the I/O layer
type TTransport interface {io.ReadWriteCloserContextFlusherReadSizeProvider// Opens the transport for communicationOpen() error// Returns true if the transport is openIsOpen() bool
}
然后看一下继承的结构体的属性
ReadWriteCloser
// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {ReaderWriterCloser
}
type Reader interface {Read(p []byte) (n int, err error)
}
type Writer interface {Write(p []byte) (n int, err error)
}
type Closer interface {Close() error
}
ContextFlusher
type ContextFlusher interface {Flush(ctx context.Context) (err error)
}
ReadSizeProvider
type ReadSizeProvider interface {RemainingBytes() (num_bytes uint64)
}
从后面可以看出来这就是服务端接收到连接后,拿到的连接,所以这个里面有read,write等方法
TProtocol
这个就是具体的编码方式对应的interface,具体如下
type TProtocol interface {WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) errorWriteMessageEnd(ctx context.Context) errorWriteStructBegin(ctx context.Context, name string) errorWriteStructEnd(ctx context.Context) errorWriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) errorWriteFieldEnd(ctx context.Context) errorWriteFieldStop(ctx context.Context) errorWriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) errorWriteMapEnd(ctx context.Context) errorWriteListBegin(ctx context.Context, elemType TType, size int) errorWriteListEnd(ctx context.Context) errorWriteSetBegin(ctx context.Context, elemType TType, size int) errorWriteSetEnd(ctx context.Context) errorWriteBool(ctx context.Context, value bool) errorWriteByte(ctx context.Context, value int8) errorWriteI16(ctx context.Context, value int16) errorWriteI32(ctx context.Context, value int32) errorWriteI64(ctx context.Context, value int64) errorWriteDouble(ctx context.Context, value float64) errorWriteString(ctx context.Context, value string) errorWriteBinary(ctx context.Context, value []byte) errorWriteUUID(ctx context.Context, value Tuuid) errorReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error)ReadMessageEnd(ctx context.Context) errorReadStructBegin(ctx context.Context) (name string, err error)ReadStructEnd(ctx context.Context) errorReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error)ReadFieldEnd(ctx context.Context) errorReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error)ReadMapEnd(ctx context.Context) errorReadListBegin(ctx context.Context) (elemType TType, size int, err error)ReadListEnd(ctx context.Context) errorReadSetBegin(ctx context.Context) (elemType TType, size int, err error)ReadSetEnd(ctx context.Context) errorReadBool(ctx context.Context) (value bool, err error)ReadByte(ctx context.Context) (value int8, err error)ReadI16(ctx context.Context) (value int16, err error)ReadI32(ctx context.Context) (value int32, err error)ReadI64(ctx context.Context) (value int64, err error)ReadDouble(ctx context.Context) (value float64, err error)ReadString(ctx context.Context) (value string, err error)ReadBinary(ctx context.Context) (value []byte, err error)ReadUUID(ctx context.Context) (value Tuuid, err error)Skip(ctx context.Context, fieldType TType) (err error)Flush(ctx context.Context) (err error)Transport() TTransport
}
然后这个就是不同编码方式,在针对go中不同的类型,读和写的具体实现方式,然后不同的编码方式这个后面在用到的时候进行细说。
因为都是使用的默认的初始化方式,也就是 protocol是compact。所以这里的protocolFactory是thrift.NewTCompactProtocolFactoryConf(nil).也就是
func NewTCompactProtocolFactoryConf(conf *TConfiguration) *TCompactProtocolFactory {return &TCompactProtocolFactory{cfg: conf,}
}
这个是初始化了TCompactProtocolFactory,然后看一下实现和对应的方法
type TCompactProtocolFactory struct {cfg *TConfiguration
}func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol {return NewTCompactProtocolConf(trans, p.cfg)
}func (p *TCompactProtocolFactory) SetTConfiguration(conf *TConfiguration) {p.cfg = conf
}
这里的GetProtocol其实是调用了NewTCompactProtocolConf方法
func NewTCompactProtocolConf(trans TTransport, conf *TConfiguration) *TCompactProtocol {PropagateTConfiguration(trans, conf)p := &TCompactProtocol{origTransport: trans,cfg: conf,}if et, ok := trans.(TRichTransport); ok {p.trans = et} else {p.trans = NewTRichTransport(trans)}return p
}
然后就是初始化。NewTRichTransport这个方法
type RichTransport struct {TTransport
}// Wraps Transport to provide TRichTransport interface
func NewTRichTransport(trans TTransport) *RichTransport {return &RichTransport{trans}
}
其实这个RichTransport 就是 就是在trans的基础上面包装了一下trans方法,其实和并没有做多大的改变。
然后就是示例化 thrift.TTransportFactory这个方法,这里在默认的情况下是调用thrift.NewTTransportFactory(),也就是
type TTransportFactory interface {GetTransport(trans TTransport) (TTransport, error)
}type tTransportFactory struct{}// Return a wrapped instance of the base Transport.
func (p *tTransportFactory) GetTransport(trans TTransport) (TTransport, error) {return trans, nil
}func NewTTransportFactory() TTransportFactory {return &tTransportFactory{}
}
可以看出来这个GetTransport在传入TTransport后其实返回的就是trans,没有多大区别。
然后接下来看一下runServer
runServer
还是一样先看一下对应的实现
func runServer(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string, secure bool) error {var transport thrift.TServerTransportvar err error// 判断是否使用httpsif secure {cfg := new(tls.Config)if cert, err := tls.LoadX509KeyPair("server.crt", "server.key"); err == nil {cfg.Certificates = append(cfg.Certificates, cert)} else {return err}transport, err = thrift.NewTSSLServerSocket(addr, cfg)} else {// 初始化地址transport, err = thrift.NewTServerSocket(addr)}if err != nil {return err}fmt.Printf("%T\n", transport)// 初始化handlerhandler := NewCalculatorHandler()// 初始化processprocessor := tutorial.NewCalculatorProcessor(handler)// 初始化服务server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory)// 启动服务fmt.Println("Starting the simple server... on ", addr)return server.Serve()
}
接下来看几个重要的方法。
NewTServerSocket
因为这里不走https,所以调用了thrift.NewTServerSocket(addr)这个方法,然后来看一下具体的实现
func NewTServerSocket(listenAddr string) (*TServerSocket, error) {return NewTServerSocketTimeout(listenAddr, 0)
}
func NewTServerSocketTimeout(listenAddr string, clientTimeout time.Duration) (*TServerSocket, error) {addr, err := net.ResolveTCPAddr("tcp", listenAddr)if err != nil {return nil, err}return &TServerSocket{addr: addr, clientTimeout: clientTimeout}, nil
}
type TServerSocket struct {listener net.Listeneraddr net.AddrclientTimeout time.Duration// Protects the interrupted value to make it thread safe.mu sync.RWMutexinterrupted bool
}
可以看出来NewTServerSocket 这个方法就是初始化了TServerSocket 这个方法,然后将地址根据tcp协议给解析进去,因为这里可能是“tcp://localhost:8080”等等。
NewCalculatorHandler
接下来就是初始化Calculator的Handler,然后这里的实现是
type CalculatorHandler struct {log map[int]*shared.SharedStruct
}func NewCalculatorHandler() *CalculatorHandler {return &CalculatorHandler{log: make(map[int]*shared.SharedStruct)}
}
然后需要注意的是这里的CalculatorHandler实现了Ping,Add,Calculate,GetStruct,Zip方法,这里是因为在thirft里面的结构体是需要这些方法,然后GetStruct是shared.SharedServic中的方法,然后Calculator继承了。thrift的定义如下
/*** Ahh, now onto the cool part, defining a service. Services just need a name* and can optionally inherit from another service using the extends keyword.*/
service Calculator extends shared.SharedService {/*** A method definition looks like C code. It has a return type, arguments,* and optionally a list of exceptions that it may throw. Note that argument* lists and exception lists are specified using the exact same syntax as* field lists in struct or exception definitions.*/void ping(),i32 add(1:i32 num1, 2:i32 num2),i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),/*** This method has a oneway modifier. That means the client only makes* a request and does not listen for any response at all. Oneway methods* must be void.*/oneway void zip()}
NewCalculatorProcessor
这里初始化Calculator的Processor,注意这里的方法是通过thrift文件生成的。
type CalculatorProcessor struct {*shared.SharedServiceProcessor
}func NewCalculatorProcessor(handler Calculator) *CalculatorProcessor {self10 := &CalculatorProcessor{shared.NewSharedServiceProcessor(handler)}self10.AddToProcessorMap("ping", &calculatorProcessorPing{handler: handler})self10.AddToProcessorMap("add", &calculatorProcessorAdd{handler: handler})self10.AddToProcessorMap("calculate", &calculatorProcessorCalculate{handler: handler})self10.AddToProcessorMap("zip", &calculatorProcessorZip{handler: handler})return self10
}
type SharedServiceProcessor struct {processorMap map[string]thrift.TProcessorFunctionhandler SharedService
}func (p *SharedServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {p.processorMap[key] = processor
}
可以看出来这个方法主要就是把方法都写到了processorMap中了,key就是这四个方法名字。
需要这四个方法,对应着不用的结构体。详细后面用到了再细说。
NewTSimpleServer4
这里就是初始化简单的服务结构体
func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {return NewTSimpleServerFactory4(NewTProcessorFactory(processor),serverTransport,transportFactory,protocolFactory,)
}
func NewTProcessorFactory(p TProcessor) TProcessorFactory {return &tProcessorFactory{processor: p}
}func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor {return p.processor
}
这里还把process这个给放进了tProcessorFactory中,从GetProcessor可以看出来就是给返回了原来的。
func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {return NewTSimpleServerFactory6(processorFactory,serverTransport,transportFactory,transportFactory,protocolFactory,protocolFactory,)
}
func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {return &TSimpleServer{processorFactory: processorFactory,serverTransport: serverTransport,inputTransportFactory: inputTransportFactory,outputTransportFactory: outputTransportFactory,inputProtocolFactory: inputProtocolFactory,outputProtocolFactory: outputProtocolFactory,stopChan: make(chan struct{}),}
}
这里 最后是初始化了TSimpleServer结构体,然后初始化了对应的结构体的成员。
server.Serve
然后就是启动服务了,这里看一下实现,
func (p *TSimpleServer) Serve() error {p.logger = fallbackLogger(p.logger)err := p.Listen()if err != nil {return err}p.AcceptLoop()return nil
}
Listen
然后看一下实现
func (p *TSimpleServer) Listen() error {return p.serverTransport.Listen()
}
然后这里其实调用的serverTransport的Listen方法,然后这里的serverTransport就是之前TServerSocket。看一下TServerSocket的Listen方法
func (p *TServerSocket) Listen() error {p.mu.Lock()defer p.mu.Unlock()if p.IsListening() {return nil}l, err := net.Listen(p.addr.Network(), p.addr.String())if err != nil {return err}p.listener = lreturn nil
}
可以看出来这里调用了官方的 net.Listen方法,或者到了一个Listener结构体。
AcceptLoop
到这里就开始accept连接,然后进行处理
func (p *TSimpleServer) AcceptLoop() error {for {closed, err := p.innerAccept()if err != nil {return err}if closed != 0 {return nil}}
}
然后看一下innerAccept的实现
func (p *TSimpleServer) innerAccept() (int32, error) {// 获取创建好的连接client, err := p.serverTransport.Accept()p.mu.Lock()defer p.mu.Unlock()closed := p.closed.Load()if closed != 0 {return closed, nil}if err != nil {return 0, err}if client != nil {ctx, cancel := context.WithCancel(context.Background())p.wg.Add(2)// 新开一个链接处理请求go func() {defer p.wg.Done()defer cancel()if err := p.processRequests(client); err != nil {p.logger(fmt.Sprintf("error processing request: %v", err))}}()// 判断超时等操作 go func() {defer p.wg.Done()select {case <-ctx.Done():// client exited, do nothingcase <-p.stopChan:// TSimpleServer.Close called, close the client connectionclient.Close()}}()}return 0, nil
}
这里的p.serverTransport.Accept() 其实就是Accept到一个新的连接,然后封装一下返回了,看一下实现
func (p *TServerSocket) Accept() (TTransport, error) {p.mu.RLock()interrupted := p.interruptedp.mu.RUnlock()if interrupted {return nil, errTransportInterrupted}p.mu.Lock()listener := p.listenerp.mu.Unlock()if listener == nil {return nil, NewTTransportException(NOT_OPEN, "No underlying server socket")}conn, err := listener.Accept()if err != nil {return nil, NewTTransportExceptionFromError(err)}return NewTSocketFromConnTimeout(conn, p.clientTimeout), nil
}
所以这里就是在Accept到一个原始的conn后,然后在调用NewTSocketFromConnTimeout方法,
// Deprecated: Use NewTSocketFromConnConf instead.
func NewTSocketFromConnTimeout(conn net.Conn, socketTimeout time.Duration) *TSocket {return NewTSocketFromConnConf(conn, &TConfiguration{SocketTimeout: socketTimeout,noPropagation: true,})
}
// NewTSocketFromConnConf creates a TSocket from an existing net.Conn.
func NewTSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSocket {return &TSocket{conn: wrapSocketConn(conn),addr: conn.RemoteAddr(),cfg: conf,}
}
所以这里是初始化了TSocket这个结构体,然后返回了回去。从上面的注释可以看出来,看一下processRequests方法
processRequests
这里因为很多方法都是interface,因此往往需要需要带着上下文去分析。
func (p *TSimpleServer) processRequests(client TTransport) (err error) {defer func() {err = treatEOFErrorsAsNil(err)}()// 获取process 这里的processorFactory就是tProcessorFactory 然后GetProcessor 就是返回的CalculatorProcessor 这个结构体 // client 没有发挥作用processor := p.processorFactory.GetProcessor(client)// 这里的inputTransportFactory就是tTransportFactory GetTransport就是返回的clientinputTransport, err := p.inputTransportFactory.GetTransport(client)if err != nil {return err}// 这里的GetProtocol就是调用的TCompactProtocolFactory的GetProtocol方法,返回了TCompactProtocol结构体inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)var outputTransport TTransportvar outputProtocol TProtocol// for THeaderProtocol, we must use the same protocol instance for// input and output so that the response is in the same dialect that// the server detected the request was in.headerProtocol, ok := inputProtocol.(*THeaderProtocol)if ok {outputProtocol = inputProtocol} else {// 其实就是返回了clientoTrans, err := p.outputTransportFactory.GetTransport(client)if err != nil {return err}outputTransport = oTransoutputProtocol = p.outputProtocolFactory.GetProtocol(outputTransport)}if inputTransport != nil {defer inputTransport.Close()}if outputTransport != nil {defer outputTransport.Close()}for {if p.closed.Load() != 0 {return nil}// 这里忽略ctx := SetResponseHelper(defaultCtx,TResponseHelper{THeaderResponseHelper: NewTHeaderResponseHelper(outputProtocol),},)if headerProtocol != nil {// We need to call ReadFrame here, otherwise we won't// get any headers on the AddReadTHeaderToContext call.//// ReadFrame is safe to be called multiple times so it// won't break when it's called again later when we// actually start to read the message.if err := headerProtocol.ReadFrame(ctx); err != nil {return err}ctx = AddReadTHeaderToContext(ctx, headerProtocol.GetReadHeaders())ctx = SetWriteHeaderList(ctx, p.forwardHeaders)}// 调用processor的Process方法.这里的processor就是CalculatorProcessor这个结构体ok, err := processor.Process(ctx, inputProtocol, outputProtocol)if errors.Is(err, ErrAbandonRequest) {return client.Close()}if errors.As(err, new(TTransportException)) && err != nil {return err}var tae TApplicationExceptionif errors.As(err, &tae) && tae.TypeId() == UNKNOWN_METHOD {continue}if !ok {break}}return nil
}
上面的注释就是根据默认的请求去做的逻辑处理,如果参数不同,比如protocol是simplejson,或者是json等,那么上面可能不一样,这里是走的默认compact。然后process的处理逻辑就是和选择的protocol相关。
相关文章:
【go语言之thrift协议二之server端分析】
go语言之thrift协议二serverthrift.TProtocolFactoryTTransportReadWriteCloserContextFlusherReadSizeProviderTProtocolrunServerNewTServerSocketNewCalculatorHandlerNewCalculatorProcessorNewTSimpleServer4server.ServeListenAcceptLoopprocessRequests在上一篇文章分析…...
【办公类05-03】Python批量修改文件名前面的序号(已有的序号错了,需要改成正确的号码)
背景需求下载教程,手动输入编号,有一个编号错误,导致后面所有编号都错了。30实际是29,以此类推怎样才能快速修改编号数字?前期考虑到可能要改编号,所以在每个编号后面加“ ”(空格)&…...
定向模糊测试工具Beacon基本用法
Beacon是一个定向模糊测试工具,给定行号,能够定向探索行号附近的代码区域。主要思想是采用静态分析的方法获取到与目标有关的变量的最弱前置条件(weakest precondition)的信息,并在相关位置插入断言,来提前…...
《程序员面试金典(第6版)》面试题 02.01. 移除重复节点
题目描述 编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。 示例1: 输入:[1, 2, 3, 3, 2, 1] 输出:[1, 2, 3] -示例2: 输入:[1, 1, 1, 1, 2] 输出:[1, 2] 提示: 链表长度在[0, 20000]范…...
如何对web系统开展无障碍测试
Accessibility test(无障碍测试)是一种测试方法,旨在评估软件、网站或其他数字产品的可访问性,以确保它们能够被身体残障或其他特殊需求的用户使用。这些测试通常包括使用辅助技术,如屏幕阅读器和放大器,以…...
使用vite+vue3.0 创建一个cesium基础应用 ----01 项目搭建
使用vitevue3.0 创建一个cesium基础应用 ----01 项目搭建 1.使用yarn创建一个vite项目 我们可以在vite官网找到vite创建项目的命令 https://cn.vitejs.dev/ 可以使用yarn创建项目选择使用vue3.0框架,语言使用js 创建完成后结构如下: 2.找到vite社区中的…...
【Python学习笔记】第二十七节 Python 多线程
一、进程和线程进程:是程序的一次执行,每个进程都有自己的地址空间、内存、数据栈及其他记录运行轨迹的辅助数据。线程:所有的线程都运行在同一个进程当中,共享相同的运行环境。线程有开始、顺序执行和结束三个部分, …...
【id:18】【20分】B. DS顺序表--连续操作
题目描述建立顺序表的类,属性包括:数组、实际长度、最大长度(设定为1000)该类具有以下成员函数:构造函数:实现顺序表的初始化。插入多个数据的multiinsert(int i, int n, int item[])函数,实现在…...
vi编辑器操作指令分享
vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法和一小部分指令。由于对Unix及Linux系统的任何版本,vi编辑器是完全相同的,因此您可以在其他任何介绍vi的地方…...
OSPF与BFD联动配置
13.1.1BFD概念 BFD提供了一个通用的、标准化的、介质无关的、协议无关的快速故障检测机制,有以下两大优点: 对相邻转发引擎之间的通道提供轻负荷、快速故障检测。 用单一的机制对任何介质、任何协议层进行实时检测。 BFD是一个简单的“Hello”协议。两个系统之间建立BFD会…...
jQuery基础
> 🥲 🥸 🤌 🫀 🫁 🥷 🐻❄️🦤 🪶 🦭 🪲 🪳 🪰 🪱 🪴 🫐 🫒 …...
day39|139.单词拆分 背包问题ending
139.单词拆分 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 示例 1: 输入: s "leetcode",…...
Shell脚本编程
Shell编程 视频地址https://www.bilibili.com/video/BV1hW41167NW/?p1&vd_source977d52a6b92ce8b6ae67c16fc61f0428 第一章 Shell概述 大数据程序员为什么要学习Shell呢? 需要看懂运维人员编写的Shell程序偶尔会编写一些简单的Shell程序来管理集群…...
ChatGPT解答:JavaScript保存当前网页页面图片为pdf文件或者word文件,前端用vue2,给出详细的方案和代码
ChatGPT解答:JavaScript保存当前网页页面图片为pdf文件或者word文件,前端用vue2,给出详细的方案和代码 ChatGPTDemo Based on OpenAI API (gpt-3.5-turbo). JavaScript保存当前网页页面图片为pdf文件或者word文件,前端用vue2&am…...
Python基础学习11——文件
我们可以利用python对本电脑文件夹里的文件进行处理,python中提供了一系列相关的方法和函数供我们使用。 读取文件 我们现在在本python文件中有一个txt文件名为Lego,那么我们就可以利用python打开该文件 with open(Lego.txt) as file_text:contents …...
外网用户打不开公司的网站?web服务器端口映射到公网
我们经常会遇到这样的情景,在公司内部可以打开公司的网站,在家里或者外网却打不开,按照网上的做法,重新启动了服务器和iis,还是不行。许多用户设置了路由器端口映射功能,但是端口映射不成功怎么办ÿ…...
【CS224W】(task9)图神经网络的表示能力(更新中!!)
note 基于图同构网络(GIN)的图表征网络。为了得到图表征首先需要做节点表征,然后做图读出。GIN中节点表征的计算遵循WL Test算法中节点标签的更新方法,因此它的上界是WL Test算法。 在图读出中,我们对所有的节点表征&…...
binlog找回误删数据
1、检查当前是否开启binlog存储 输入命令show variables like %log_bin%;,结果如下 可以看到log_bin的值是ON,说明binlog开启了。 2、查找binlog的存储位置 这个去到数据库的my.cnf配置文件中寻找,有一个log_bin的配置 切换到log_bin的目…...
《程序员面试金典(第6版)》面试题 02.03. 删除中间节点
题目描述 若链表中的某个节点,既不是链表头节点,也不是链表尾节点,则称其为该链表的「中间节点」。 假定已知链表的某一个中间节点,请实现一种算法,将该节点从链表中删除。 例如: 传入节点 c(…...
Spring Boot
目录 SpringBoot SpringBoot创建和使用 什么是Spring Boot Spring Boot优点 Spring Boot项目的创建 项目目录介绍和运行 目录介绍 项目运行 SpringBoot核心设计思想 SpringBoot的配置文件 配置文件的作用 配置文件的格式 注意事项 properties配置文件 propertie…...
图论初入门
目录 一、前言 二、图的概念 三、例题及相关概念 1、全球变暖(2018年省赛,lanqiao0J题号178) 2、欧拉路径 3、小例题 4、例题(洛谷P7771) 一、前言 本文主要讲了树与图的基本概念,图的存储、DFS遍历…...
02-Oracle数据库的启动与关闭
本文章主要讲解Oracle数据库的启动与关闭方法,详细讲解启动Oracle的命令,三种启动数据库的方法及区别;关闭数据库的4种方法及他们的区别。 启动和关闭数据库 •数据库没启动前,只有拥有DBA权限或者以sysoper或sysdba身份才能连接到…...
网络营销培训完能达到什么水平?学完能创业吗?
网络营销本身就是一门创业的技术,很多人学习网络营销,往往担心学完以后技术达不到,再工作几年才可以创业,实际这是错误的理解,那么,网络营销培训完能达到什么水平?新手学员参加网络营销培训&…...
大数据技术之——zeppelin数据清洗
一、zeppelin的安装zeppelin解压后进入到conf配置文件界面。修改zeppelin-site.xml[roothadoop02 conf]# cp zeppelin-site.xml.template zeppelin-site.xml[roothadoop02 conf]# vim zeppelin-site.xml将IP地址和端口号设置成自己的修改 zeppelin-env.shexport JAVA HOME/opt/…...
Barra模型因子的构建及应用系列五之NonLinear Size因子
一、摘要 在前期的Barra模型系列文章中,我们构建了Size因子、Beta因子、Momentum因子和Residual Volatility因子,并分别创建了对应的单因子策略,本节文章在该系列下进一步构建NonLinear Size因子。从回测结果看,自2022年以来&…...
C++ 常用命令行开发工具(Linux)
文章目录1、简介2、gcc / g2.1 system(执行shell 命令)2.2 popen(建立管道I/O)2.3 vforkexec(新建子进程)3、clang3.1 下载和安装clang3.2 clang和gcc比较3.2.1 gcc3.2.2 clang3.2.3 LLVM4、make4.1 例子14…...
java基础学习 day47(抽象类,抽象方法)
1. 抽象方法 将共性的行为(方法)抽取到父类之后,由于每一个子类执行的内容是不一样的,所以,在父类中不能确定具体的方法体,该方法就可以定义为抽象方法。抽象方法定义格式: public abstract 返…...
Java代码弱点与修复之——Open redirect(开放重定向)
弱点描述 Open redirect , 开放重定向,是一种常见的安全漏洞,也被称为“重定向漏洞”。该漏洞通常出现在 Web 应用程序中,攻击者可以利用它将用户重定向到恶意站点,从而进行钓鱼攻击、恶意软件传播、诱骗等活动。 在 Java 中,通过重定向 HTTP 请求来实现应用程序中的跳转…...
Go 指针
指针在编程中,一个内存地址用来定位一段内存。通常地,一个内存地址用一个操作系统原生字(native word)来存储。 一个原生字在32位操作系统上占4个字节,在64位操作系统上占8个字节。 所以,32位操作系统上的理…...
shardingsphere5.1.1分表分库yaml配置 自定义策略
前言通过阅读官方稳定给出示例 https://shardingsphere.apache.org/document一、基本配置示例spring:sharding:datasource:names: ds0, ds1ds0:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/db0username: rootpassword: rootds1:driver-class-na…...
佛山美容网站建设/女教师遭网课入侵直播录屏曝光视频
VS 报错: 警告”不能像使用方法那样使用不可调用的成员”System.Data.DataSet.Tables”. 今天用VS敲机房重构时,有一个获取表中信息的语句。 return adataset.Tables(0); 然后报警:不能像使用方法那样使用不可调用的成员”System.Data.Da…...
wordpress 修改表前缀/网站seo推广优化教程
Saavedro再论TOEFL-iBT阅读做题方法论 (附上TPO19第一篇阅读题目详细分析)最近看见版上的考生们在对TPO19阅读部分喊难不止。我私下调查和询问了下各位现任 版主和正在准备考试的考生们,大家对TPO和真实托福考试的描述都是越来越难。尤其 是现阶段的托福考试几乎难度呈现指数型…...
怎么开发销售网站/中国网络营销公司
a 100def test(num): num num print(num)test(a)print(a) 200100 这里 num num 与 num num num 不能等价 num num 这里有两层意思 1。 看num指向的值是否能够修改 如果能修改 就直接修改(列表和字典类型可以修改) 2 如果不能修改 这里num想当于…...
微信网站开发平台/一个产品营销策划方案
解决vscode出现两个光标的问题参考文章: (1)解决vscode出现两个光标的问题 (2)https://www.cnblogs.com/huahuayu/p/12235244.html 备忘一下。...
外贸做网站要多久做好/俄罗斯搜索引擎yandex推广
原文:redis 系列26 Cluster高可用 (1)一.概述 Redis集群提供了分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能。在大数据量方面的高可用方案,cluster集群比Sentinel有优势。但Redis集群并不支持处理多个keys的命令,因…...
深圳网站建设公司 概况/平面设计主要做什么
说的 捱三顶四 地方转载于:https://blog.51cto.com/1369879/284697...