本文整理汇总了Golang中github.com/streadway/amqp.DialTLS函数的典型用法代码示例。如果您正苦于以下问题:Golang DialTLS函数的具体用法?Golang DialTLS怎么用?Golang DialTLS使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。
在下文中一共展示了DialTLS函数的16个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的Golang代码示例。
示例1: Connect
func (q *AMQP) Connect() error {
q.Lock()
defer q.Unlock()
q.headers = amqp.Table{
"precision": q.Precision,
"database": q.Database,
"retention_policy": q.RetentionPolicy,
}
var connection *amqp.Connection
// make new tls config
tls, err := internal.GetTLSConfig(
q.SSLCert, q.SSLKey, q.SSLCA, q.InsecureSkipVerify)
if err != nil {
return err
}
if tls != nil {
connection, err = amqp.DialTLS(q.URL, tls)
} else {
connection, err = amqp.Dial(q.URL)
}
if err != nil {
return err
}
channel, err := connection.Channel()
if err != nil {
return fmt.Errorf("Failed to open a channel: %s", err)
}
err = channel.ExchangeDeclare(
q.Exchange, // name
"topic", // type
true, // durable
false, // delete when unused
false, // internal
false, // no-wait
nil, // arguments
)
if err != nil {
return fmt.Errorf("Failed to declare an exchange: %s", err)
}
q.channel = channel
go func() {
log.Printf("Closing: %s", <-connection.NotifyClose(make(chan *amqp.Error)))
log.Printf("Trying to reconnect")
for err := q.Connect(); err != nil; err = q.Connect() {
log.Println(err)
time.Sleep(10 * time.Second)
}
}()
return nil
}
开发者ID:cwegener,项目名称:telegraf,代码行数:55,代码来源:amqp.go
示例2: connectToAMQP
func connectToAMQP(uri string) (*amqp.Connection, error) {
var conn *amqp.Connection
var err error
if strings.Contains(uri, "amqps") {
cfg := new(tls.Config)
if len(os.Getenv("PMB_SSL_INSECURE_SKIP_VERIFY")) > 0 {
cfg.InsecureSkipVerify = true
}
logrus.Debugf("calling DialTLS")
conn, err = amqp.DialTLS(uri, cfg)
logrus.Debugf("Connection obtained")
} else {
conn, err = amqp.Dial(uri)
}
if err != nil {
return nil, err
}
//logrus.Debugf("Conn: ", conn)
return conn, nil
}
开发者ID:justone,项目名称:pmb,代码行数:26,代码来源:amqp.go
示例3: TestTLSHandshake
// Tests that the server has handshaked the connection and seen the client
// protocol announcement. Does not nest that the connection.open is successful.
func TestTLSHandshake(t *testing.T) {
srv := startTlsServer()
defer srv.Close()
cfg := new(tls.Config)
cfg.RootCAs = x509.NewCertPool()
cfg.RootCAs.AppendCertsFromPEM([]byte(caCert))
cert, _ := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
cfg.Certificates = append(cfg.Certificates, cert)
c, err := amqp.DialTLS(srv.URL, cfg)
select {
case <-time.After(10 * time.Millisecond):
t.Fatalf("did not succeed to handshake the TLS connection after 10ms")
case header := <-srv.Header:
if string(header) != "AMQP" {
t.Fatalf("expected to handshake a TLS connection, got err: %v", err)
}
}
if st := c.ConnectionState(); !st.HandshakeComplete {
t.Errorf("TLS handshake failed, TLS connection state: %+v", st)
}
}
开发者ID:streadway,项目名称:amqp,代码行数:28,代码来源:tls_test.go
示例4: init
func init() {
//defer debug.PrintStack()
var err error
cert, err = tls.LoadX509KeyPair("certs/client0-ca.crt", "certs/client0.key")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
tmpurl := os.Getenv("CLOUDAMQP_URL")
if tmpurl != "" {
url = tmpurl
}
connection, err := amqp.DialTLS(url, &tls.Config{Certificates: []tls.Certificate{cert}, PreferServerCipherSuites: true, InsecureSkipVerify: true})
//connection, err := amqp.Dial(url)
channel, _ = connection.Channel()
durable := true
autoDelete, noWait := false, false
internal := false
err = channel.ExchangeDeclarePassive(exchangeName, "topic", durable, autoDelete, internal, noWait, nil)
if err != nil {
log.Printf("Exchange does not exist: %s", err)
channel, _ = connection.Channel()
err = channel.ExchangeDeclare(exchangeName, "topic", durable, autoDelete, internal, noWait, nil)
if err != nil {
log.Fatalf("Could not create Exchange: %s", err)
}
}
}
开发者ID:mikechack,项目名称:wsrouter,代码行数:28,代码来源:mfmanager1.go
示例5: tryToConnect
func (r *rabbitMQConn) tryToConnect(secure bool, config *tls.Config) error {
var err error
if secure || config != nil {
if config == nil {
config = &tls.Config{
InsecureSkipVerify: true,
}
}
url := strings.Replace(r.url, "amqp://", "amqps://", 1)
r.Connection, err = amqp.DialTLS(url, config)
} else {
r.Connection, err = amqp.Dial(r.url)
}
if err != nil {
return err
}
r.Channel, err = newRabbitChannel(r.Connection)
if err != nil {
return err
}
r.Channel.DeclareExchange(r.exchange)
return nil
}
开发者ID:micro,项目名称:go-plugins,代码行数:26,代码来源:connection.go
示例6: Dial
// If additional TLS settings were specified, the dialer uses amqp.DialTLS
// instead of amqp.Dial.
func (dialer *AMQPDialer) Dial(url string) (conn AMQPConnection, err error) {
if dialer.tlsConfig != nil {
conn, err = amqp.DialTLS(url, dialer.tlsConfig)
} else {
conn, err = amqp.Dial(url)
}
return
}
开发者ID:salekseev,项目名称:heka,代码行数:10,代码来源:types.go
示例7: run
func run(config Config) {
listener := pq.NewListener(config.PostgresURL, 10*time.Second, time.Minute, errorReporter)
err := listener.Listen("urlwork")
if err != nil {
log.Fatal(err)
}
rabbitchannel := make(chan string, 100)
go func() {
cfg := new(tls.Config)
cfg.InsecureSkipVerify = true
conn, err := amqp.DialTLS(config.RabbitMQURL, cfg)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
ch, err := conn.Channel()
if err != nil {
log.Fatal(err)
}
defer ch.Close()
for {
payload := <-rabbitchannel
log.Println(payload)
err := ch.Publish("urlwork", "todo", false, false, amqp.Publishing{
ContentType: "text/plain",
Body: []byte(payload),
})
if err != nil {
log.Fatal(err)
}
}
}()
for {
select {
case notification := <-listener.Notify:
rabbitchannel <- notification.Extra
case <-time.After(90 * time.Second):
go func() {
err := listener.Ping()
if err != nil {
log.Fatal(err)
}
}()
}
}
}
开发者ID:compose-ex,项目名称:postrabbit,代码行数:51,代码来源:postrabbitrun.go
示例8: init
func (ac *amqpClient) init() error {
var err error
ac.conn, err = amqp.DialTLS(ac.uri, ac.tlsConfig)
if err != nil {
return fmt.Errorf("DialTLS: %v", err)
}
ac.channel, err = ac.conn.Channel()
if err != nil {
return fmt.Errorf("Channel: %v", err)
}
if err := ac.channel.Confirm(false); err != nil {
return fmt.Errorf("Confirm: %v", err)
}
ac.acks, ac.nacks = ac.channel.NotifyConfirm(make(chan uint64), make(chan uint64))
go ac.handleMsgs()
return nil
}
开发者ID:jvgutierrez,项目名称:rpcmq,代码行数:20,代码来源:amqp.go
示例9: SetupAmqpConnection
// This function will return a amqp channel as well
// as setup the reconnect and retry logic in case
// the server its connected to becomes unavailable.
func (t *Tasks) SetupAmqpConnection() *amqp.Connection {
config := t.GetConfig()
conn, err := amqp.DialTLS(config.Uri, config.TlsConfig)
//if err retry until connected.
if err != nil {
log.Printf("Error Connecting to amqp: %s\n", err)
time.Sleep(1 * time.Second)
conn = t.SetupAmqpConnection()
} else {
log.Printf("Connected to Rabbit")
//Setup Reconnect logic
go func() {
log.Printf("closing: %s \n", <-conn.NotifyClose(make(chan *amqp.Error)))
time.Sleep(1 * time.Second)
conn = t.SetupAmqpConnection()
}()
}
t.Connection = conn
// return the connection
return conn
}
开发者ID:divideandconquer,项目名称:go-celery-api,代码行数:26,代码来源:go-celery-api.go
示例10: ExampleDialTLS
func ExampleDialTLS() {
// To get started with SSL/TLS follow the instructions for adding SSL/TLS
// support in RabbitMQ with a private certificate authority here:
//
// http://www.rabbitmq.com/ssl.html
//
// Then in your rabbitmq.config, disable the plain AMQP port, verify clients
// and fail if no certificate is presented with the following:
//
// [
// {rabbit, [
// {tcp_listeners, []}, % listens on 127.0.0.1:5672
// {ssl_listeners, [5671]}, % listens on 0.0.0.0:5671
// {ssl_options, [{cacertfile,"/path/to/your/testca/cacert.pem"},
// {certfile,"/path/to/your/server/cert.pem"},
// {keyfile,"/path/to/your/server/key.pem"},
// {verify,verify_peer},
// {fail_if_no_peer_cert,true}]}
// ]}
// ].
cfg := new(tls.Config)
// The self-signing certificate authority's certificate must be included in
// the RootCAs to be trusted so that the server certificate can be verified.
//
// Alternatively to adding it to the tls.Config you can add the CA's cert to
// your system's root CAs. The tls package will use the system roots
// specific to each support OS. Under OS X, add (drag/drop) your cacert.pem
// file to the 'Certificates' section of KeyChain.app to add and always
// trust.
//
// Or with the command line add and trust the DER encoded certificate:
//
// security add-certificate testca/cacert.cer
// security add-trusted-cert testca/cacert.cer
//
// If you depend on the system root CAs, then use nil for the RootCAs field
// so the system roots will be loaded.
cfg.RootCAs = x509.NewCertPool()
if ca, err := ioutil.ReadFile("testca/cacert.pem"); err == nil {
cfg.RootCAs.AppendCertsFromPEM(ca)
}
// Move the client cert and key to a location specific to your application
// and load them here.
if cert, err := tls.LoadX509KeyPair("client/cert.pem", "client/key.pem"); err == nil {
cfg.Certificates = append(cfg.Certificates, cert)
}
// Server names are validated by the crypto/tls package, so the server
// certificate must be made for the hostname in the URL. Find the commonName
// (CN) and make sure the hostname in the URL matches this common name. Per
// the RabbitMQ instructions for a self-signed cert, this defautls to the
// current hostname.
//
// openssl x509 -noout -in server/cert.pem -subject
//
// If your server name in your certificate is different than the host you are
// connecting to, set the hostname used for verification in
// ServerName field of the tls.Config struct.
conn, err := amqp.DialTLS("amqps://server-name-from-certificate/", cfg)
log.Printf("conn: %v, err: %v", conn, err)
}
开发者ID:gcnonato,项目名称:amqp,代码行数:69,代码来源:examples_test.go
示例11: open
// Connects to the message queue, opens a channel, declares a queue
func (amqpBackend *AMQPBackend) open(taskUUID string) (*amqp.Connection, *amqp.Channel, amqp.Queue, <-chan amqp.Confirmation, error) {
var (
conn *amqp.Connection
channel *amqp.Channel
queue amqp.Queue
err error
)
// Connect
// From amqp docs: DialTLS will use the provided tls.Config when it encounters an amqps:// scheme
// and will dial a plain connection when it encounters an amqp:// scheme.
conn, err = amqp.DialTLS(amqpBackend.config.Broker, amqpBackend.config.TLSConfig)
if err != nil {
return conn, channel, queue, nil, fmt.Errorf("Dial: %s", err)
}
// Open a channel
channel, err = conn.Channel()
if err != nil {
return conn, channel, queue, nil, fmt.Errorf("Channel: %s", err)
}
// Declare an exchange
err = channel.ExchangeDeclare(
amqpBackend.config.Exchange, // name of the exchange
amqpBackend.config.ExchangeType, // type
true, // durable
false, // delete when complete
false, // internal
false, // noWait
nil, // arguments
)
if err != nil {
return conn, channel, queue, nil, fmt.Errorf("Exchange Declare: %s", err)
}
// Declare a queue
arguments := amqp.Table{
"x-message-ttl": int32(amqpBackend.getExpiresIn()),
}
queue, err = channel.QueueDeclare(
taskUUID, // name
false, // durable
true, // delete when unused
false, // exclusive
false, // no-wait
arguments,
)
if err != nil {
return conn, channel, queue, nil, fmt.Errorf("Queue Declare: %s", err)
}
// Bind the queue
if err := channel.QueueBind(
queue.Name, // name of the queue
taskUUID, // binding key
amqpBackend.config.Exchange, // source exchange
false, // noWait
nil, // arguments
); err != nil {
return conn, channel, queue, nil, fmt.Errorf("Queue Bind: %s", err)
}
// Enable publish confirmations
if err := channel.Confirm(false); err != nil {
return conn, channel, queue, nil, fmt.Errorf("Channel could not be put into confirm mode: %s", err)
}
return conn, channel, queue, channel.NotifyPublish(make(chan amqp.Confirmation, 1)), nil
}
开发者ID:gooops,项目名称:machinery,代码行数:71,代码来源:amqp.go
示例12: run
func run(config Config) {
session, err := r.Connect(r.ConnectOpts{
Address: config.RethinkDBAddress,
Database: config.RethinkDBDatabase,
AuthKey: config.RethinkDBAuthkey,
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
})
if err != nil {
log.Fatal(session, err)
}
rabbitchannel := make(chan []byte, 100)
go func() {
cfg := new(tls.Config)
cfg.InsecureSkipVerify = true
conn, err := amqp.DialTLS(config.RabbitMQURL, cfg)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
ch, err := conn.Channel()
if err != nil {
log.Fatal(err)
}
defer ch.Close()
for {
payload := <-rabbitchannel
log.Println(string(payload))
err := ch.Publish("urlwork", "todo", false, false, amqp.Publishing{
ContentType: "text/plain",
Body: []byte(payload),
})
if err != nil {
log.Fatal(err)
}
}
}()
res, err := r.Table("urls").Changes().Run(session)
if err != nil {
log.Fatalln(err)
}
var value interface{}
for res.Next(&value) {
mapval := value.(map[string]interface{})
if mapval["new_val"] != nil && mapval["old_val"] == nil {
jsonbytes, err := json.Marshal(mapval["new_val"])
if err != nil {
log.Fatal(err)
}
rabbitchannel <- jsonbytes
}
}
}
开发者ID:compose-ex,项目名称:rethinkrabbit,代码行数:61,代码来源:rethinkrabbitrun.go
示例13: makeAmqpChannel
// makeAmqpChannel sets an AMQP connection up using SSL if configuration is provided
func makeAmqpChannel(conf *cmd.AMQPConfig) (*amqp.Channel, error) {
var conn *amqp.Connection
var err error
log := blog.Get()
serverURL, err := conf.ServerURL()
if err != nil {
return nil, err
}
if conf.Insecure == true {
// If the Insecure flag is true, then just go ahead and connect
conn, err = amqp.Dial(serverURL)
} else {
// The insecure flag is false or not set, so we need to load up the options
log.Info("AMQPS: Loading TLS Options.")
if strings.HasPrefix(serverURL, "amqps") == false {
err = fmt.Errorf("AMQPS: Not using an AMQPS URL. To use AMQP instead of AMQPS, set insecure=true")
return nil, err
}
if conf.TLS == nil {
err = fmt.Errorf("AMQPS: No TLS configuration provided. To use AMQP instead of AMQPS, set insecure=true")
return nil, err
}
cfg := new(tls.Config)
// If the configuration specified a certificate (or key), load them
if conf.TLS.CertFile != nil || conf.TLS.KeyFile != nil {
// But they have to give both.
if conf.TLS.CertFile == nil || conf.TLS.KeyFile == nil {
err = fmt.Errorf("AMQPS: You must set both of the configuration values AMQP.TLS.KeyFile and AMQP.TLS.CertFile")
return nil, err
}
cert, err := tls.LoadX509KeyPair(*conf.TLS.CertFile, *conf.TLS.KeyFile)
if err != nil {
err = fmt.Errorf("AMQPS: Could not load Client Certificate or Key: %s", err)
return nil, err
}
log.Info("AMQPS: Configured client certificate for AMQPS.")
cfg.Certificates = append(cfg.Certificates, cert)
}
// If the configuration specified a CA certificate, make it the only
// available root.
if conf.TLS.CACertFile != nil {
cfg.RootCAs = x509.NewCertPool()
ca, err := ioutil.ReadFile(*conf.TLS.CACertFile)
if err != nil {
err = fmt.Errorf("AMQPS: Could not load CA Certificate: %s", err)
return nil, err
}
cfg.RootCAs.AppendCertsFromPEM(ca)
log.Info("AMQPS: Configured CA certificate for AMQPS.")
}
conn, err = amqp.DialTLS(serverURL, cfg)
}
if err != nil {
return nil, err
}
return conn.Channel()
}
开发者ID:jfrazelle,项目名称:boulder,代码行数:72,代码来源:amqp-rpc.go
示例14: setupJobQueueAndCanceller
func (i *CLI) setupJobQueueAndCanceller() error {
switch i.Config.QueueType {
case "amqp":
var amqpConn *amqp.Connection
var err error
if i.Config.AmqpInsecure {
amqpConn, err = amqp.DialTLS(
i.Config.AmqpURI,
&tls.Config{InsecureSkipVerify: true},
)
} else {
amqpConn, err = amqp.Dial(i.Config.AmqpURI)
}
if err != nil {
i.logger.WithField("err", err).Error("couldn't connect to AMQP")
return err
}
go i.amqpErrorWatcher(amqpConn)
i.logger.Debug("connected to AMQP")
canceller := NewAMQPCanceller(i.ctx, amqpConn)
i.logger.WithFields(logrus.Fields{
"canceller": fmt.Sprintf("%#v", canceller),
}).Debug("built")
i.Canceller = canceller
go canceller.Run()
jobQueue, err := NewAMQPJobQueue(amqpConn, i.Config.QueueName)
if err != nil {
return err
}
jobQueue.DefaultLanguage = i.Config.DefaultLanguage
jobQueue.DefaultDist = i.Config.DefaultDist
jobQueue.DefaultGroup = i.Config.DefaultGroup
jobQueue.DefaultOS = i.Config.DefaultOS
i.JobQueue = jobQueue
return nil
case "file":
canceller := NewFileCanceller(i.ctx, i.Config.BaseDir)
go canceller.Run()
i.Canceller = canceller
jobQueue, err := NewFileJobQueue(i.Config.BaseDir, i.Config.QueueName, i.Config.FilePollingInterval)
if err != nil {
return err
}
jobQueue.DefaultLanguage = i.Config.DefaultLanguage
jobQueue.DefaultDist = i.Config.DefaultDist
jobQueue.DefaultGroup = i.Config.DefaultGroup
jobQueue.DefaultOS = i.Config.DefaultOS
i.JobQueue = jobQueue
return nil
}
return fmt.Errorf("unknown queue type %q", i.Config.QueueType)
}
开发者ID:apjanke,项目名称:worker,代码行数:65,代码来源:cli.go
示例15: Connect
func (q *AMQP) Connect() error {
q.Lock()
defer q.Unlock()
q.headers = amqp.Table{
"precision": q.Precision,
"database": q.Database,
"retention_policy": q.RetentionPolicy,
}
var connection *amqp.Connection
var err error
if q.SslCert != "" && q.SslKey != "" {
// make new tls config
cfg := new(tls.Config)
if q.SslCa != "" {
// create ca pool
cfg.RootCAs = x509.NewCertPool()
// add self-signed cert
if ca, err := ioutil.ReadFile(q.SslCa); err == nil {
cfg.RootCAs.AppendCertsFromPEM(ca)
} else {
log.Println(err)
}
}
if cert, err := tls.LoadX509KeyPair(q.SslCert, q.SslKey); err == nil {
cfg.Certificates = append(cfg.Certificates, cert)
} else {
log.Println(err)
}
connection, err = amqp.DialTLS(q.URL, cfg)
} else {
connection, err = amqp.Dial(q.URL)
}
if err != nil {
return err
}
channel, err := connection.Channel()
if err != nil {
return fmt.Errorf("Failed to open a channel: %s", err)
}
err = channel.ExchangeDeclare(
q.Exchange, // name
"topic", // type
true, // durable
false, // delete when unused
false, // internal
false, // no-wait
nil, // arguments
)
if err != nil {
return fmt.Errorf("Failed to declare an exchange: %s", err)
}
q.channel = channel
go func() {
log.Printf("Closing: %s", <-connection.NotifyClose(make(chan *amqp.Error)))
log.Printf("Trying to reconnect")
for err := q.Connect(); err != nil; err = q.Connect() {
log.Println(err)
time.Sleep(10 * time.Second)
}
}()
return nil
}
开发者ID:zooplus,项目名称:telegraf,代码行数:68,代码来源:amqp.go
示例16: ExampleDialTLS
func ExampleDialTLS() {
// This example assume you have a RabbitMQ node running on localhost
// with TLS enabled.
//
// The easiest way to create the CA, certificates and keys required for these
// examples is by using tls-gen: https://github.com/michaelklishin/tls-gen
//
// A comprehensive RabbitMQ TLS guide can be found at
// http://www.rabbitmq.com/ssl.html
//
// Once you have the required TLS files in place, use the following
// rabbitmq.config example for the RabbitMQ node that you will run on
// localhost:
//
// [
// {rabbit, [
// {tcp_listeners, []}, % listens on 127.0.0.1:5672
// {ssl_listeners, [5671]}, % listens on 0.0.0.0:5671
// {ssl_options, [{cacertfile,"/path/to/your/testca/cacert.pem"},
// {certfile,"/path/to/your/server/cert.pem"},
// {keyfile,"/path/to/your/server/key.pem"},
// {verify,verify_peer},
// {fail_if_no_peer_cert,true}]}
// ]}
// ].
//
//
// In the above rabbitmq.config example, we are disabling the plain AMQP port
// and verifying that clients and fail if no certificate is presented.
//
// The self-signing certificate authority's certificate (cacert.pem) must be
// included in the RootCAs to be trusted, otherwise the server certificate
// will fail certificate verification.
//
// Alternatively to adding it to the tls.Config. you can add the CA's cert to
// your system's root CAs. The tls package will use the system roots
// specific to each support OS. Under OS X, add (drag/drop) cacert.pem
// file to the 'Certificates' section of KeyChain.app to add and always
// trust. You can also add it via the command line:
//
// security add-certificate testca/cacert.pem
// security add-trusted-cert testca/cacert.pem
//
// If you depend on the system root CAs, then use nil for the RootCAs field
// so the system roots will be loaded instead.
//
// Server names are validated by the crypto/tls package, so the server
// certificate must be made for the hostname in the URL. Find the commonName
// (CN) and make sure the hostname in the URL matches this common name. Per
// the RabbitMQ instructions (or tls-gen) for a self-signed cert, this defaults to the
// current hostname.
//
// openssl x509 -noout -in /path/to/certificate.pem -subject
//
// If your server name in your certificate is different than the host you are
// connecting to, set the hostname used for verification in
// ServerName field of the tls.Config struct.
cfg := new(tls.Config)
// see at the top
cfg.RootCAs = x509.NewCertPool()
if ca, err := ioutil.ReadFile("testca/cacert.pem"); err == nil {
cfg.RootCAs.AppendCertsFromPEM(ca)
}
// Move the client cert and key to a location specific to your application
// and load them here.
if cert, err := tls.LoadX509KeyPair("client/cert.pem", "client/key.pem"); err == nil {
cfg.Certificates = append(cfg.Certificates, cert)
}
// see a note about Common Name (CN) at the top
conn, err := amqp.DialTLS("amqps://server-name-from-certificate/", cfg)
log.Printf("conn: %v, err: %v", conn, err)
}
开发者ID:streadway,项目名称:amqp,代码行数:78,代码来源:examples_test.go
注:本文中的github.com/streadway/amqp.DialTLS函数示例由纯净天空整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。 |
请发表评论