为什么我粘贴出来的文件 出来以后全是截图?(我的文档不能复制粘贴了)
1902
2022-05-30
【引言】
gRPC(gRPC Remote Procedure Calls)是一个开源的远程过程调用(RPC)系统,由谷歌公司最先在2015年开发。它使用HTTP/2作为传输方式,协议缓冲器作为接口描述语言,提供了认证、双向流和流控制、阻塞或非阻塞绑定、取消和超时等功能。它可以为许多编程语言生成跨平台的客户端和服务器绑定。最常见的使用场景包括在微服务架构中的服务连接,以及移动设备、网页客户端连接到后端服务等。
【概述】
在gRPC中,客户端应用程序可以直接调用不同服务器应用程序上的方法,就像调用本地对象接口一样,这让你更容易创建分布式应用和服务。和许多RPC系统一样,gRPC也是围绕着定义一个服务的思想,指定可以远程调用的方法,参数和返回类型。在服务器端,服务程序实现这个接口,并运行gRPC服务器来处理客户端的调用。在客户端上有一个存根,这个存根提供与服务器相同的方法。
gRPC客户端和服务器可以在各种环境下运行和对话,并且可以用gRPC支持的任何一种语言编写。比如,你可以轻松地用Java创建一个gRPC服务器,用Go、Python或Ruby的客户端来调用。此外,最新的Google API都会有gRPC版本的接口,让你可以轻松地将Google功能构建到你的应用程序中。
【gRPC的优点】
性能
gRPC消息使用Protobuf进行序列化,Protobuf是一种高效的二进制消息格式。Protobuf在服务器和客户端上的序列化速度非常快。Protobuf序列化的结果是消息的有效载荷小,这对于移动应用等有限带宽的场景来说非常重要。
gRPC是为HTTP/2设计的,这是HTTP的一个重要修订版,它比HTTP 1.x提供了显著的性能优势。
l 二进制框架和压缩技术的使用使得HTTP/2协议在发送和接收方面都非常紧凑和高效。
l 在单一TCP连接上实现多个HTTP/2调用的复用。多路复用消除了线头阻塞。
代码通过工具生成
所有的gRPC框架都提供了一流的代码生成支持。gRPC开发的一个核心文件是.proto文件,它定义了gRPC服务和消息的契约。从这个文件中,gRPC框架将代码生成一个服务基类、消息和一个完整的客户端。
通过在服务器和客户端之间共享.proto文件,消息和客户端的代码可以从端到端的生成。客户端的代码生成消除了客户端和服务器上的消息重复,为你创建一个强类型化的客户端。在有很多服务的应用程序中,不需要编写客户端,这可以节省大量的开发时间。
严格的规范
目前还没有一个针对使用JSON的HTTP API的正式规范。开发者们一直对URL、HTTP动词和响应代码的最佳格式争论不休。
gRPC规范对gRPC服务必须遵循的格式是有规定的。gRPC消除了争论,从而节省了开发者的时间,因为gRPC在不同平台和实现上是一致的。
流处理
HTTP/2为长效实时通信流提供了基础,gRPC通过HTTP/2提供了一流的流处理支持。
一个gRPC服务支持所有的流处理组合:
l 单一式(无流处理)
l 服务器到客户端的流处理
l 客户端到服务器的流处理
l 双向流处理
超时取消机制
gRPC允许客户端指定他们愿意等待多长时间完成一个RPC。把这个时间决定的最后期限被发送至服务器,服务器可以决定超过最后期限时采取什么行动。例如,服务器可以在超时后取消正在进行中的gRPC或HTTP或数据库请求。
通过对gRPC调用设置超时取消机制,有助于实现对资源的限制使用。
【gRPC的缓冲器与认证】
【使用协议缓冲器】
gRPC使用协议缓冲区对数据进行编码。与使用JSON的HTTP API不同,它们有一个更严格的规范。
协议缓冲区是Google成熟的开源机制,用于序列化结构化数据(尽管它可以使用其他数据格式,如JSON)。以下是对其工作原理的简单介绍。
在使用协议缓冲区时,第一步是在proto文件中定义要序列化的数据结构:是一个普通的文本文件,扩展名为.proto。协议缓冲区的数据是以消息的形式结构化,每个消息都是一个小的逻辑信息记录,其中包含一系列称为字段的名-值对。下面是一个简单的例子:
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
接着,一旦你指定了你的数据结构,你就可以使用协议缓冲区编译器 protoc 从你的 proto 定义中生成你喜欢的编程语言的数据访问类。这些类为每个字段提供了简单的访问器,如 name()和 set_name(),以及将整个结构序列化/解析为原始字节的方法。因此,例如,如果你选择的语言是C++,用上面的例子中运行,编译器会生成一个名为Person的类。然后你可以在你的应用程序中使用这个类来填充、序列化和检索Person协议缓冲区消息。
你在普通的proto文件中定义gRPC服务,RPC方法参数和返回类型指定为协议缓冲区消息:
// greeter 服务定义.
service Greeter {
// 发出问候
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 请求信息包含用户名字
message HelloRequest {
string name = 1;
}
// 响应消息包含问候消息
message HelloReply {
string message = 1;
}
gRPC 使用 protoc 和一个特殊的 gRPC 插件来从你的 proto 文件中生成代码:你会得到生成的 gRPC 客户端和服务器代码,以及用于补足、序列化和检索消息类型的常规协议缓冲区代码。
【协议缓冲区版本】
虽然协议缓冲区已经向开源用户提供了一段时间,目前推荐使用协议缓冲区3版(proto3),它的语法稍有简化,并添加了一些有用的新功能,而且支持了更多的语言。
Proto3目前支持Java、C++、Dart、Python、Objective-C、C#、Android Java、Ruby、Golang和JavaScript,,还有更多语言正在开发中。
一般来说,虽然可以使用proto2(当前默认的协议缓冲区版本),但建议你使用gRPC时使用proto3,因为它支持gRPC目前支持的所有语言,同时也避免了proto2客户端与proto3服务器的兼容性问题,反之亦然。
【认证】
gRPC支持使用TLS和基于令牌的认证。与Google服务的连接必须使用TLS。
gRPC设计的初衷是可以与各种认证机制一起工作,从而可以很容易地通过gRPC与其他系统对话。
gRPC还提供了一个简单的认证API,让您在创建通道或调用时提供所有必要的认证信息作为凭证。
凭证类型
凭证可以分为两种类型:
l 通道凭证,附加在通道上,如SSL凭证。
l 调用凭证,它附加到一个调用(或C++中的ClientContext)上。
你也可以在CompositeChannelCredentials中结合这些内容,例如,你可以为通道指定SSL的详细信息,并为通道上的每个呼叫指定呼叫凭证。CompositeChannelCredentials 将通道凭证和呼叫凭证关联起来,以创建一个新的通道凭证。其结果将在信道上的每一次呼叫中发送与组成的CallCredentials相关联的认证数据。
比如说,你可以从一个SSLCredentials和一个AccessTokenCredentials创建一个ChannelCredentials。当应用到一个Channel时,其结果将为这个通道上的每个呼叫发送相应的访问令牌。
单个CallCredentials也可以用CompositeCallCredentials组成。在呼叫中使用的CallCredentials将触发与两个CallCredentials相关联的认证数据的发送。
gRPC具有SSL/TLS集成功能,提倡使用SSL/TLS来验证服务器,并对客户端和服务器之间交换的所有数据进行加密。客户端可选择提供相互认证的证书机制。
现在我们来看看Credentials是如何与我们支持的一种认证机制一起工作的。我们假定最简单的验证场景,客户端只想验证服务器并加密所有数据。这个例子是用C++语言编写的,但所有语言的API都是类似的。
// 创建一个默认的SSL ChannelCredentials对象。
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
// 使用上一步中创建的凭证创建一个通道。
auto channel = grpc::CreateChannel(server_name, channel_creds);
// 在通道上创建一个存根。
std::unique_ptr
// 在存根上进行实际的RPC调用。
grpc::Status s = stub->sayHello(&context, *request, response);
对于高级用例,如修改根CA或使用客户端证书,可以在传递给工厂方法的 SslCredentialsOptions 参数中设置相应的选项。
基于令牌的身份验证
gRPC提供了一个通用的机制,可以将基于元数据的凭证附加到请求和响应中。这种机制专门用于访问Google 的API服务。 一般来说,Google不允许没有SSL/TLS的连接,而且大多数gRPC语言实现也不会让你在未加密的通道上发送凭证。
gRPC应用程序可以使用一个简单的API来创建一个在各种部署场景中与Google进行认证的凭证。例子:
auto creds = grpc::GoogleDefaultCredentials();
// 创建一个通道,存根并进行RPC调用(与上例的功能一样)
auto channel = grpc::CreateChannel(server_name, creds);
std::unique_ptr
grpc::Status s = stub->sayHello(&context, *request, response);
这个通道凭证对象适用于使用服务账户的应用程序和在 Google Compute Engine (GCE) 中运行的应用程序。在前一种情况下,服务账户的私钥是从环境变量 GOOGLE_APPLICATION_CREDENTIALS 中命名的文件中加载的。这些密钥用于生成附加到相应通道上的每个出站RPC的承载令牌。
对于在 GCE 中运行的应用程序,可以在 VM 设置期间配置一个默认服务帐户和相应的 OAuth2 作用域。在运行时,该凭证处理与认证系统的通信,以获取OAuth2访问令牌,并将其附加到相应通道上的每个出站RPC上。
扩展gRPC以支持其他认证机制
凭证插件API允许开发人员插入自己的凭证类型。这些插件有:
l MetadataCredentialsPlugin抽象类,它包含纯虚拟的GetMetadata方法,需要由开发者创建的子类来实现。
l MetadataCredentialsFromPlugin函数,它从
MetadataCredentialsPlugin中创建一个CallCredentials。
下面是一个简单的凭证插件的例子,它可以在自定义头中设置一个认证依据。
类实现:
class MyCustomAuthenticator : public grpc::MetadataCredentialsPlugin {
public:
MyCustomAuthenticator(const grpc::string& ticket) : ticket_(ticket) {}
grpc::Status GetMetadata(
grpc::string_ref service_url, grpc::string_ref method_name,
const grpc::AuthContext& channel_auth_context,
std::multimap
metadata->insert(std::make_pair("x-custom-auth-ticket", ticket_));
return grpc::Status::OK;
}
private:
grpc::string ticket_;
};
类调用:
auto call_creds = grpc::MetadataCredentialsFromPlugin(
std::unique_ptr
new MyCustomAuthenticator("super-secret-ticket")));
通过在核心层插入gRPC凭证实现,可以实现更深层次的集成。
更多例子
这些认证机制将适用于所有gRPC支持的语言。
conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
// 此处需要添加错误处理
client := pb.NewGreeterClient(conn)
// ...
s := grpc.NewServer()
lis, _ := net.Listen("tcp", "localhost:50051")
// 此处需要添加错误处理
s.Serve(lis)
creds, _ := credentials.NewClientTLSFromFile(certFile, "")
conn, _ := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
// 此处需要添加错误处理
client := pb.NewGreeterClient(conn)
// ...
creds, _ := credentials.NewServerTLSFromFile(certFile, keyFile)
s := grpc.NewServer(grpc.Creds(creds))
lis, _ := net.Listen("tcp", "localhost:50051")
// 此处需要添加错误处理
s.Serve(lis)
pool, _ := x509.SystemCertPool()
// 此处需要添加错误处理
creds := credentials.NewClientTLSFromCert(pool, "")
perRPC, _ := oauth.NewServiceAccountFromFile("service-account.json", scope)
conn, _ := grpc.Dial(
"greeter.googleapis.com",
grpc.WithTransportCredentials(creds),
grpc.WithPerRPCCredentials(perRPC),
)
// 此处需要添加错误处理
client := pb.NewGreeterClient(conn)
// ...
stub = Helloworld::Greeter::Stub.new('localhost:50051', :this_channel_is_insecure)
creds = GRPC::Core::ChannelCredentials.new(load_certs) # load_certs通常加载一个CA根文件
stub = Helloworld::Greeter::Stub.new('myservice.example.com', creds)
require 'googleauth' # from http://www.rubydoc.info/gems/googleauth/0.1.0
...
ssl_creds = GRPC::Core::ChannelCredentials.new(load_certs) # load_certs通常加载一个CA根文件
authentication = Google::Auth.get_application_default()
call_creds = GRPC::Core::CallCredentials.new(authentication.updater_proc)
combined_creds = ssl_creds.compose(call_creds)
stub = Helloworld::Greeter::Stub.new('greeter.googleapis.com', combined_creds)
auto channel = grpc::CreateChannel("localhost:50051", InsecureChannelCredentials());
std::unique_ptr
...
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
auto channel = grpc::CreateChannel("myservice.example.com", channel_creds);
std::unique_ptr
...
auto creds = grpc::GoogleDefaultCredentials();
auto channel = grpc::CreateChannel("greeter.googleapis.com", creds);
std::unique_ptr
...
var channel = new Channel("localhost:50051", ChannelCredentials.Insecure);
var client = new Greeter.GreeterClient(channel);
...
var channelCredentials = new SslCredentials(File.ReadAllText("roots.pem")); // Load a custom roots file.
var channel = new Channel("myservice.example.com", channelCredentials);
var client = new Greeter.GreeterClient(channel);
using Grpc.Auth; // 从Grpc.Auth NuGet包中获取
...
// 加载Google应用的默认凭证与公开信任的根。
var channelCredentials = await GoogleGrpcCredentials.GetApplicationDefaultAsync();
var channel = new Channel("greeter.googleapis.com", channelCredentials);
var client = new Greeter.GreeterClient(channel);
...
var channel = new Channel("greeter.googleapis.com", new SslCredentials()); // Use publicly trusted roots.
var client = new Greeter.GreeterClient(channel);
...
var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
var result = client.SayHello(request, new CallOptions(credentials: googleCredential.ToCallCredentials()));
...
import grpc
import helloworld_pb2
channel = grpc.insecure_channel('localhost:50051')
stub = helloworld_pb2.GreeterStub(channel)
import grpc
import helloworld_pb2
with open('roots.pem', 'rb') as f:
creds = grpc.ssl_channel_credentials(f.read())
channel = grpc.secure_channel('myservice.example.com:443', creds)
stub = helloworld_pb2.GreeterStub(channel)
import grpc
import helloworld_pb2
from concurrent import futures
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
with open('key.pem', 'rb') as f:
private_key = f.read()
with open('chain.pem', 'rb') as f:
certificate_chain = f.read()
server_credentials = grpc.ssl_server_credentials( ( (private_key, certificate_chain), ) )
# 此处要将GreeterServicer添加到服务器
server.add_secure_port('myservice.example.com:443', server_credentials)
server.start()
import grpc
import helloworld_pb2
from google import auth as google_auth
from google.auth import jwt as google_auth_jwt
from google.auth.transport import grpc as google_auth_transport_grpc
credentials, _ = google_auth.default()
jwt_creds = google_auth_jwt.OnDemandCredentials.from_signing_credentials(
credentials)
channel = google_auth_transport_grpc.secure_authorized_channel(
jwt_creds, None, 'greeter.googleapis.com:443')
stub = helloworld_pb2.GreeterStub(channel)
import grpc
import helloworld_pb2
from google import auth as google_auth
from google.auth.transport import grpc as google_auth_transport_grpc
from google.auth.transport import requests as google_auth_transport_requests
credentials, _ = google_auth.default(scopes=(scope,))
request = google_auth_transport_requests.Request()
channel = google_auth_transport_grpc.secure_authorized_channel(
credentials, request, 'greeter.googleapis.com:443')
stub = helloworld_pb2.GreeterStub(channel)
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext(true).build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
在Java中,建议使用OpenSSL。
要在服务器上启用TLS,需要以PEM格式指定证书链和私钥。这样的私钥不应该使用密码。证书链中证书的顺序很重要:更具体地说,最上面的证书必须是主机CA,而最下面的证书必须是根CA。标准的TLS端口是443,但为了避免申请操作系统的额外权限,下面用的是8443。
Server server = ServerBuilder.forPort(8443)
// 启用TLS
.useTransportSecurity(certChainFile, privateKeyFile)
.addService(TestServiceGrpc.bindService(serviceImplementation))
.build();
server.start();
如果客户端不知道发证机构,那么应该分别向NettyChannelBuilder或OkHttpChannelBuilder提供一个正确配置的SSLContext或SSLSocketFactory。
在客户端,使用SSL/TLS的服务器认证大体是这样的:
// 通过服务器认证 SSL/TLS
ManagedChannel channel = ManagedChannelBuilder.forAddress("myservice.example.com", 443).build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
// 具有服务器认证SSL/TLS;自定义CA根证书;不适用于Android
ManagedChannel channel = NettyChannelBuilder.forAddress("myservice.example.com", 443)
.sslContext(GrpcSslContexts.forClient().trustManager(new File("roots.pem")).build()).build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
下面的代码片段显示了如何使用服务账户使用gRPC调用Google Cloud PubSub API。凭证是从存储在一个已知位置的密钥中加载的,或者通过检测应用程序运行在一个可以自动提供密钥的环境中,例如Google Compute Engine等,来加载。虽然这个例子是针对Google及其服务的,但类似的模式也可以适用于其他服务提供商。
GoogleCredentials creds = GoogleCredentials.getApplicationDefault();
ManagedChannel channel = ManagedChannelBuilder.forTarget("greeter.googleapis.com")
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel)
.withCallCredentials(MoreCallCredentials.from(creds));
var stub = new helloworld.Greeter('localhost:50051', grpc.credentials.createInsecure());
var ssl_creds = grpc.credentials.createSsl(root_certs);
var stub = new helloworld.Greeter('myservice.example.com', ssl_creds);
// 使用谷歌认证
var GoogleAuth = require('google-auth-library'); // from https://www.npmjs.com/package/google-auth-library
...
var ssl_creds = grpc.credentials.createSsl(root_certs);
(new GoogleAuth()).getApplicationDefault(function(err, auth) {
var call_creds = grpc.credentials.createFromGoogleCredential(auth);
var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
var stub = new helloworld.Greeter('greeter.googleapis.com', combined_credentials);
});
var GoogleAuth = require('google-auth-library'); // from https://www.npmjs.com/package/google-auth-library
...
var ssl_creds = grpc.Credentials.createSsl(root_certs); // load_certs通常加载一个CA根文件
var scope = 'https://www.googleapis.com/auth/grpc-testing';
(new GoogleAuth()).getApplicationDefault(function(err, auth) {
if (auth.createScopeRequired()) {
auth = auth.createScoped(scope);
}
var call_creds = grpc.credentials.createFromGoogleCredential(auth);
var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
var stub = new helloworld.Greeter('greeter.googleapis.com', combined_credentials);
});
$client = new helloworld\GreeterClient('localhost:50051', [
'credentials' => Grpc\ChannelCredentials::createInsecure(),
]);
function updateAuthMetadataCallback($context)
{
$auth_credentials = ApplicationDefaultCredentials::getCredentials();
return $auth_credentials->updateMetadata($metadata = [], $context->service_url);
}
$channel_credentials = Grpc\ChannelCredentials::createComposite(
Grpc\ChannelCredentials::createSsl(file_get_contents('roots.pem')),
Grpc\CallCredentials::createFromPlugin('updateAuthMetadataCallback')
);
$opts = [
'credentials' => $channel_credentials
];
$client = new helloworld\GreeterClient('greeter.googleapis.com', $opts);
// 需要设置环境变量 "GOOGLE_APPLICATION_CREDENTIALS "
$scope = "https://www.googleapis.com/auth/grpc-testing";
$auth = Google\Auth\ApplicationDefaultCredentials::getCredentials($scope);
$opts = [
'credentials' => Grpc\Credentials::createSsl(file_get_contents('roots.pem'));
'update_metadata' => $auth->getUpdateMetadataFunc(),
];
$client = new helloworld\GreeterClient('greeter.googleapis.com', $opts);
final channel = new ClientChannel('localhost',
port: 50051,
options: const ChannelOptions(
credentials: const ChannelCredentials.insecure()));
final stub = new GreeterClient(channel);
// 加载一个自定义的根文件。
final trustedRoot = new File('roots.pem').readAsBytesSync();
final channelCredentials =
new ChannelCredentials.secure(certificates: trustedRoot);
final channelOptions = new ChannelOptions(credentials: channelCredentials);
final channel = new ClientChannel('myservice.example.com',
options: channelOptions);
final client = new GreeterClient(channel);
// 默认使用公开的信任根。
final channel = new ClientChannel('greeter.googleapis.com');
final serviceAccountJson =
new File('service-account.json').readAsStringSync();
final credentials = new JwtServiceAccountAuthenticator(serviceAccountJson);
final client =
new GreeterClient(channel, options: credentials.toCallOptions);
// 默认使用公开的信任根。
final channel = new ClientChannel('greeter.googleapis.com');
final client = new GreeterClient(channel);
...
final serviceAccountJson =
new File('service-account.json').readAsStringSync();
final credentials = new JwtServiceAccountAuthenticator(serviceAccountJson);
final response =
await client.sayHello(request, options: credentials.toCallOptions);
【gRPC的应用场景】
gRPC非常适用于以下情况:
l 微服务--gRPC是为低延迟和高吞吐量通信而设计的。
l 点对点实时通信--gRPC对双向流的支持非常好,gRPC服务可以实时推送消息,无需轮询。
l 多语言环境--gRPC工具支持所有流行的开发语言,使gRPC成为多语言环境的最佳选择。
l 网络约束环境--gRPC消息采用Protobuf(一种轻量级消息格式)进行序列化。一个gRPC消息总是比同等的JSON消息小。
【应用现状】
许多公司都采用了gRPC,如Square、Netflix、CoreOS、Docker、Cocker、CockroachDB、Cisco、Juniper Networks等。
开源项目u-bmc使用gRPC来取代IPMI。2019年1月8日,Dropbox宣布,其SOA架构核心的RPC框架 "Courier "的下一个版本将迁移到基于gRPC的新架构上,主要原因是该框架与他们现有的定制的RPC框架可以很好地接轨。
【小结】
在我们的开发过程中,对于API技术的选取是一个重要的环节,相比HTTP API, 本文介绍了gRPC具有的独特优势,并从缓冲机制和认证两个重要方面对这门技术进行了学习,下面来看看它的缺点和对应的策略:
浏览器支持有限
在目前情况下,从浏览器中直接调用gRPC服务是不可能的,gRPC大量使用HTTP/2的功能,目前的浏览器不支持gRPC客户端所需要的网络请求控制能力。例如,浏览器不允许调用者使用HTTP/2,也不提供对底层HTTP/2帧的访问。
应对策略
gRPC-Web是gRPC团队的一项附加支持技术,它在浏览器中提供有限的gRPC支持。
gRPC-Web由两部分组成:支持所有现代浏览器的JavaScript客户端和服务器上的gRPC-Web代理。通过客户端调用代理,代理再把gRPC请求转发到gRPC服务器上。
gRPC-Web并不支持gRPC的所有功能,比如不支持客户端流处理和双向流处理,对服务器流处理的支持也很有限。
传输内容人类不可读
HTTP API请求以文本形式发送,人类可以读取。
gRPC消息默认使用Protobuf进行编码。虽然Protobuf的发送和接收效率很高,但它的二进制格式并不是人类可以读取的。
Protobuf需要在.proto文件中指定消息的接口描述来正确地解序列化。需要额外的工具来分析Protobuf的线上有效载荷和手工编写请求。
应对策略
诸如服务器反射和gRPC命令行工具等功能可以帮助处理二进制Protobuf消息。此外,Protobuf消息支持与JSON格式的转换。内置的JSON转换,可以将Protobuf消息转换为人类可读的形式,这在调试时非常有用。
RPC
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。