项目基于 .Net Standard 2.0 构建。
其中 DotNetZip 无直接支持 .Net Standard 2.0 的依赖包,暂用基于 .Net Framework 4.6.1 的包,经过验证在 MacOS X 环境下可用。
.
├── Aliyun.Api.LogService/.............SDK源码主项目
│ ├── Domain/...........................主要领域模型
│ ├── Infrastructure/...................SDK基础设施
│ │ ├── Authentication/...................验证相关设施
│ │ ├── Protocol/.........................底层协议相关设施
│ │ │ └── Http/.............................HTTP协议实现
│ │ └── Serialization/....................序列化相关设施
│ │ └── Protobuf/.........................Protobuf序列化实现
│ ├── Properties/.......................程序集自身相关信息
│ │ └── AssemblyInfo.cs
│ └── Utils/.............................内部工具类
├── Aliyun.Api.LogService.Benchmark/...SDK性能指标项目
├── Aliyun.Api.LogService.Examples/....SDK示例项目
├── Aliyun.Api.LogService.Tests/.......SDK单元测试项目
└── docs/..............................文档存放处
-
Log.proto
编译$ cd Aliyun.Api.LogService/Infrastructure/Serialization/Protobuf $ protoc --csharp_out=. --csharp_opt=file_extension=.Generated.cs Log.proto
-
proto2兼容性
在阿里云服务中使用的是proto2,由于在proto2官方没有对应的C# Stub生成支持,只能使用proto3协议生成stub。
在proto2中,
string
类型字段在为空(非null
)时回写入字段的标头;但在proto3中会直接跳过整个字段,从而导致服务器无法解释报文。 在处理可空的字符串类型时需要使用修改生成的stub文件,并手动在代码中过滤null
值,如:// Log.Generated.cs public static partial class Types { public sealed partial class Content : pb::IMessage<Content> { public void WriteTo(pb::CodedOutputStream output) { ... // if (Value.Length != 0) { if (value_ != null) { output.WriteRawTag(18); output.WriteString(Value); } ... } public int CalculateSize() { ... // if (Value.Length != 0) { if (value_ != null) { size += 1 + pb::CodedOutputStream.ComputeStringSize(Value); } ... } public void MergeFrom(Content other) { ... // if (other.Value.Length != 0) { if (other.value_ != null) { Value = other.Value; } ... }
// proto.Value = value; proto.Value = value ?? String.Empty;
项目中对外API相关的类型中禁止出现依赖库相关的外部类型在类型声明的层面出现,实现层面不限制。如:
class Foo
{
// 错误,返回值为外部类型
JObject get(String key);
// 正确,隐藏外部类型
IDictionary<String, Object> get(String key);
// 推荐,提供访问便利性同时隐藏外部类型
dynamic get(String key)
{
// 实现的代码中直接依赖外部类型是可以的
return JObject.Parse("{......}");
}
}
对于部分外部类型(如:HttpResponseMessage
)可以通过在扩展方法中声明对应的外部类型,但依旧不能直接在原始的接口或类中声明。
public interface IFoo
{
// 错误,不要在基础接口中声明外部类型
HttpResponseMessage Response { get; }
}
internal class HttpFoo : IFoo
{
// 实现层面可依赖外部类型
internal HttpResponseMessage Response { get; }
}
public static class FooExtensions
{
// 正确,通过扩展方法返回外部类型
public static HttpResponseMessage GetResponse(this IFoo source)
{
return ((HttpFoo)souce).Response;
}
}
在Aliyun.Api.LogService
项目中,出于便利性对外提供了dynamic
返回的方法(如:LogHeaderExtensions.GetQueryInfoAsDynamic()
)。
但Aliyun.Api.LogService
项目中并不支持使用dynamic对象,如需使用请按下面步骤添加依赖:
- 添加依赖
Microsoft.CSharp
- 可执行的项目中添加
System.Dynamic.Runtime
,如果已是.NetFramework或.NetCoreApp项目可以忽略。
单元测试使用生产环境,配置文件:Aliyun.Api.LogService.Tests/TestUtils/TestContextFixture.cs,其中构建 Client 时候的 Credential
配置已被隐藏,运行单元测试前务必配置有效的服务凭据。
如需要需要测试投递功能,由于对外 API 不支持动态创建 Shipper,需要先创建 LogStore 后按照投递流程的指引创建独立的 Shipper 再把 Shipper 的名称配置到 TestContextFixture
.ShipperName
才可测试。
TestContextFixture
中有如下开关:
-
ShouldInitProject
是否执行
CreateProject
-
ShouldCleanProject
是否执行
DeleteProject
-
ShouldInit
是否执行
CreateLogStore
、CreateConfig
、CreateIndex
、CreateMachineGroup
-
ShouldClean
是否执行
DeleteLogStore
、DeleteConfig
、DeleteMachineGroup
-
ShouldTestShipper
是否执行
GetShipperStatus
、RetryShipperTask