【go语言之thrift协议二之server端分析】

在上一篇文章分析了在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")
    
    // 编码方式 默认是compact
	protocol := flag.String("P", "compact", "Specify the protocol (binary, compact, json, simplejson)")
 
    // 是否使用基于frame格式的编码方式
   	framed := flag.Bool("framed", false, "Use framed transport")
   	
   	// 是否使用带有buffer的transport
	buffered := flag.Bool("buffered", false, "Use buffered transport")
	
	// server或者client的地址
	addr := flag.String("addr", "localhost:9090", "Address to listen to")
 
    // 是否使用https
	secure := flag.Bool("secure", false, "Use tls secure transport")
  
    // 解析参数
	flag.Parse()
 
    // 根据不同的protocol初始化不同的thrift.TProtocolFactory
	var protocolFactory thrift.TProtocolFactory
	switch *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)
	}
 
    // 根据参数生成不同的transportFactory
	var transportFactory thrift.TTransportFactory
	cfg := &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或者client
	if *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)
		}
	}
}

这里可以看出来这里的主要就是有两个动作

  1. 初始化thrift.TProtocolFactory 这里因为使用了默认的protocol也就是compact。所以这里是 protocolFactory = thrift.NewTCompactProtocolFactoryConf(nil)
  2. 初始化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.ReadWriteCloser
	ContextFlusher
	ReadSizeProvider

	// Opens the transport for communication
	Open() error

	// Returns true if the transport is open
	IsOpen() bool
}

然后看一下继承的结构体的属性

ReadWriteCloser
// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
	Reader
	Writer
	Closer
}
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) error
	WriteMessageEnd(ctx context.Context) error
	WriteStructBegin(ctx context.Context, name string) error
	WriteStructEnd(ctx context.Context) error
	WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error
	WriteFieldEnd(ctx context.Context) error
	WriteFieldStop(ctx context.Context) error
	WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error
	WriteMapEnd(ctx context.Context) error
	WriteListBegin(ctx context.Context, elemType TType, size int) error
	WriteListEnd(ctx context.Context) error
	WriteSetBegin(ctx context.Context, elemType TType, size int) error
	WriteSetEnd(ctx context.Context) error
	WriteBool(ctx context.Context, value bool) error
	WriteByte(ctx context.Context, value int8) error
	WriteI16(ctx context.Context, value int16) error
	WriteI32(ctx context.Context, value int32) error
	WriteI64(ctx context.Context, value int64) error
	WriteDouble(ctx context.Context, value float64) error
	WriteString(ctx context.Context, value string) error
	WriteBinary(ctx context.Context, value []byte) error
	WriteUUID(ctx context.Context, value Tuuid) error

	ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error)
	ReadMessageEnd(ctx context.Context) error
	ReadStructBegin(ctx context.Context) (name string, err error)
	ReadStructEnd(ctx context.Context) error
	ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error)
	ReadFieldEnd(ctx context.Context) error
	ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error)
	ReadMapEnd(ctx context.Context) error
	ReadListBegin(ctx context.Context) (elemType TType, size int, err error)
	ReadListEnd(ctx context.Context) error
	ReadSetBegin(ctx context.Context) (elemType TType, size int, err error)
	ReadSetEnd(ctx context.Context) error
	ReadBool(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.TServerTransport
	var err error
	// 判断是否使用https
	if 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)
	// 初始化handler
	handler := NewCalculatorHandler()
	// 初始化process
	processor := 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.Listener
	addr          net.Addr
	clientTimeout time.Duration

	// Protects the interrupted value to make it thread safe.
	mu          sync.RWMutex
	interrupted 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.TProcessorFunction
	handler      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 = l
	return 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 nothing
			case <-p.stopChan:
				// TSimpleServer.Close called, close the client connection
				client.Close()
			}
		}()
	}
	return 0, nil
}

这里的p.serverTransport.Accept() 其实就是Accept到一个新的连接,然后封装一下返回了,看一下实现

func (p *TServerSocket) Accept() (TTransport, error) {
	p.mu.RLock()
	interrupted := p.interrupted
	p.mu.RUnlock()

	if interrupted {
		return nil, errTransportInterrupted
	}

	p.mu.Lock()
	listener := p.listener
	p.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就是返回的client
	inputTransport, err := p.inputTransportFactory.GetTransport(client)
	if err != nil {
		return err
	}
	// 这里的GetProtocol就是调用的TCompactProtocolFactory的GetProtocol方法,返回了TCompactProtocol结构体
	inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)
	var outputTransport TTransport
	var 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 {
	    // 其实就是返回了client
		oTrans, err := p.outputTransportFactory.GetTransport(client)
		if err != nil {
			return err
		}
		outputTransport = oTrans
		outputProtocol = 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 TApplicationException
		if errors.As(err, &tae) && tae.TypeId() == UNKNOWN_METHOD {
			continue
		}
		if !ok {
			break
		}
	}
	return nil
}

上面的注释就是根据默认的请求去做的逻辑处理,如果参数不同,比如protocol是simplejson,或者是json等,那么上面可能不一样,这里是走的默认compact。然后process的处理逻辑就是和选择的protocol相关。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spark SQL是一个用于处理结构化数据的模块,它提供了一种基于SQL的编程接口,可以让用户使用SQL语句来查询数据。ThriftServer是Spark SQL的一个组件,它提供了一个基于Thrift协议的服务,可以让用户通过网络连接到Spark SQL,并使用SQL语句来查询数据。Beeline是一个用于连接到ThriftServer的命令行工具,它可以让用户通过命令行界面来执行SQL语句。 使用ThriftServer和Beeline可以让用户通过网络连接到Spark SQL,并使用SQL语句来查询数据。首先需要启动ThriftServer,可以使用以下命令: ``` ./sbin/start-thriftserver.sh ``` 启动成功后,可以使用Beeline连接到ThriftServer,可以使用以下命令: ``` ./bin/beeline -u jdbc:hive2://localhost:10000 ``` 连接成功后,就可以使用SQL语句来查询数据了。例如,可以使用以下命令查询表格: ``` SELECT * FROM table_name; ``` 使用ThriftServer和Beeline可以方便地查询Spark SQL中的数据,特别是在需要远程访问数据时非常有用。 ### 回答2: ThriftServer(又称HiveServer2)和Beeline都是Spark SQL中常用的工具,用于连接和操作Spark SQL。 ThriftServer是一个支持Hive/Spark SQL的服务,它允许用户通过多种编程语言(如Java、Python等)来访问和查询数据。ThriftServer通过Thrift协议提供了基于网络的服务,允许远程客户连接到Spark集群并执行Spark SQL查询。ThriftServer可以通过配置来启用或禁用Kerberos身份验证,以实现安全连接。 Beeline是一个基于命令行的工具,它是Hive和Spark SQL的原生客户。可以使用Beeline连接到ThriftServer,并通过命令行界面执行Spark SQL查询。Beeline支持多种连接方式,例如通过JDBC连接到ThriftServer、通过Kerberos进行身份验证等。用户可以通过Beeline执行SQL语句、管理数据库、查看查询结果等。 使用ThriftServer和Beeline的步骤如下: 1. 首先,确保Spark集群已经启动,并且ThriftServer已经启动。可以通过spark-sql或spark-sql2启动ThriftServer,默认情况下会监听口10000。 2. 使用Beeline连接到ThriftServer。可以通过命令beeline -u jdbc:hive2://hostname:port进行连接,其中hostname是ThriftServer所在的主机名或IP地址,port是ThriftServer监听的口号。此外,还需要提供用户名和密码进行身份验证。 3. 连接成功后,可以在Beeline中执行SQL语句。输入SQL语句后,按下回车键即可执行。查询结果会显示在命令行界面上。还可以使用Beeline提供的命令来管理数据库、查看表、导入导出数据等操作。 ThriftServer和Beeline的使用简单而方便,能够有效地连接和操作Spark SQL。它们为用户提供了一种灵活的方式来查询和管理数据,便于开发人员和数据分析师使用Spark SQL进行数据处理和分析

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值