提交
This commit is contained in:
parent
b86343c308
commit
e3385e1464
@ -1334,4 +1334,390 @@ var jobDetail = JobBuilder.Create<SayHelloJob>()
|
||||
|
||||
## 介绍
|
||||
|
||||
## 使用
|
||||
NLog 是一个跨平台的 .Net 日志组件。
|
||||
|
||||
## 安装
|
||||
|
||||
### Package Manager
|
||||
|
||||
```shell
|
||||
Install-Package NLog
|
||||
```
|
||||
|
||||
### .Net CLI
|
||||
|
||||
```shell
|
||||
dotnet add package NLog
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
### 配置方法
|
||||
|
||||
NLog 可以通过两种方式进行配置:
|
||||
|
||||
- 配置文件
|
||||
- 程序代码
|
||||
|
||||
### 通过配置文件配置
|
||||
|
||||
#### 配置文件路径
|
||||
|
||||
NLog 启动时会在某些标准路径查找配置文件,并进行自动配置:
|
||||
|
||||
- 对于 **ASP.NET** 应用程序,以下文件会被查询:
|
||||
- 标准 web 应用程序文件 web.config
|
||||
- web.config 所在目录下的 web.nlog 文件
|
||||
- 应用程序目录下的 NLog.config 文件
|
||||
- NLog.dll 所在目录下的 NLog.dll.nlog 文件(only if NLog isn't installed in the GAC)
|
||||
|
||||
#### 配置文件格式
|
||||
|
||||
NLog 支持两种文件格式:
|
||||
|
||||
1. 在标准的 *.exe.config 或 web.config 中嵌入配置
|
||||
2. 在单独的文件中进行配置
|
||||
|
||||
个人觉得单独的配置文件便于在项目中切换配置或日志库。
|
||||
|
||||
NLog 配置文件是一个以`nlog`为根节点的 XML 文件。`nlog`节点可以添加命名空间,以开启 Visual Studio 的 Intellisense 功能。
|
||||
|
||||
```xml
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
</nlog>
|
||||
```
|
||||
|
||||
以下元素可以作为`nlog`节点的子节点,前两种元素在所有 NLog 配置文件中都必须存在,剩余元数是可选的。
|
||||
|
||||
- `targets` - 定义日志目标/输出
|
||||
- `rules` - 定义日志路由规则
|
||||
- `extensions` - 定义要加载的 NLog 扩展性 *.dll 文件
|
||||
- `includes` - 定义要包含的外部配置文件
|
||||
- `variables` - 设置配置变量的值
|
||||
|
||||
在`nlog`节点中设置属性`autoReload="true"`, Nlog会监视配置文件,并在配置文件发送更改时自动载入配置文件而不需要重启应用程序。该功能支持通过`include`包含的子配置文件。
|
||||
|
||||
示例如下
|
||||
|
||||
```xml
|
||||
<nlog autoReload="true">
|
||||
...
|
||||
</nlog>
|
||||
```
|
||||
|
||||
##### targets
|
||||
|
||||
`targets`节点中定义了一系列日志输出目标,每一个输出目标是一个`target`元素。对于每一个`target`元素,`name`属性和`type`属性是必须要指定的:
|
||||
|
||||
- `name` - `target`的名字。路由规则根据该属性将日志信息路由到当前目标。
|
||||
- `type` - `target`的类型。当使用了 xsi 命名空间时,该属性被命名为 xsi:type。目前 NLog 支持的输出目标列表:Targets
|
||||
|
||||
不同类型的`target`节点可以接受不同的属性。例如对于`File`目标,`fileName`参数指定了日志文件的文件名;对于`Console`目标,`error`参数指定日志信息是写到标准错误流还是标准输出流。NLog 内置了许多预定义类型,也可以自定义输出目标类型,详见如何自定义输出目标。
|
||||
|
||||
以下是 `targets`节点的例子:
|
||||
|
||||
```xml
|
||||
<targets>
|
||||
<target name="f1" xsi:type="File" fileName="file1.txt"/>
|
||||
<target name="f2" xsi:type="File" fileName="file2.txt"/>
|
||||
<target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>
|
||||
<target name="ds" xsi:type="OutputDebugString"/>
|
||||
</targets>
|
||||
```
|
||||
|
||||
##### rules
|
||||
|
||||
`rules`节点是日志路由规则的集合,由一个或多个`logger`元素组成。每个`logger`元素记录了logger的名字、目标输出以及要处理的日志等级。NLog 从路由规则表的第一个`logger`开始处理,如果当前`logger`有效,则日志信息将被输出到指定的`target`。如果某个`logger`被标记为`final`,那么其后的`logger`都会被忽略。
|
||||
|
||||
`logger`包含下列属性:
|
||||
|
||||
- `name` - logger的名字(可以使用通配符*)
|
||||
- `minLevel` - 最小日志等级
|
||||
- `maxLevel` - 最大日志等级
|
||||
- `level` - 单一的日志等级
|
||||
- `levels` - 以逗号分割的日志等级列表
|
||||
- `writeTo` - 以逗号分割的输出目标列表
|
||||
- `final` - 标记当前规则为最后一条规则
|
||||
- `enabled` - 使能当前规则
|
||||
|
||||
如果在一条规则中定义了多个日志等级相关的属性(`level`, `levels`, `minLevel`和`maxLevel`),
|
||||
|
||||
按照优先级只生效当前优先级最高的属性。等级相关属性优先级如下
|
||||
|
||||
1. `level`
|
||||
2. `levels`
|
||||
3. `minLevel`和`maxLevel`
|
||||
4. 没有设置(所有等级的日志都会被记录)
|
||||
|
||||
以下是`rules`节点的例子:
|
||||
|
||||
```xml
|
||||
<rules>
|
||||
<logger name="Name.Space.Class1" minlevel="Debug" writeTo="f1" />
|
||||
<logger name="Name.Space.Class1" levels="Debug,Error" writeTo="f1" />
|
||||
<logger name="Name.Space.*" writeTo="f3,f4" />
|
||||
<logger name="Name.Space.*" minlevel="Debug" maxlevel="Error" final="true" />
|
||||
</rules>
|
||||
```
|
||||
|
||||
1. 命名空间 `Name.Space` 下类 `Class1` 中高于 `Debug` 级别的日志信息将被写入输出目标 `f1`
|
||||
2. 命名空间`Name.Space` 下类 `Class1` 中级别为 `Debug` 和 `Error` 的日志信息将被写入 `target:f1`
|
||||
3. 命名空间 `Name.Space` 下所有类中的日志信息将被写入 `target:f3, f4`
|
||||
4. 命名空间 `Name.Space` 下所有类中级别在`Debug` 和 `Error` 之间 (`Debug`,`Info`,`Warn` 和 `Error`) 的日志信息不被记录(因为没有指定属性`writeTo`)。由于标记了属性 `final`,之后的 `logger` 都会被忽略。
|
||||
|
||||
##### extensions
|
||||
|
||||
`extensions`节点可以添加额外的NLog元包或自定义功能。`assembly`属性指定的被包含程序集不带后缀`.dll`。示例如下
|
||||
|
||||
```xml
|
||||
<nlog>
|
||||
<extensions>
|
||||
<add assembly="MyAssembly" />
|
||||
</extensions>
|
||||
<targets>
|
||||
<target name="a1" type="MyFirst" host="localhost" />
|
||||
</targets>
|
||||
<rules>
|
||||
<logger name="*" minLevel="Info" appendTo="a1" />
|
||||
</rules>
|
||||
</nlog>
|
||||
```
|
||||
|
||||
NLog 4.0 之后,与`NLog.dll`同目录下名如`NLog*.dll`的程序集(如`NLog.CustomTarget.dll`)会被自动加载。
|
||||
|
||||
##### Includes
|
||||
|
||||
`include`节点指定当前配置文件包含多个子配置文件。通过`${}`语法可以使用环境变量,下例展示包含一个名为当前机器名的配置文件。
|
||||
|
||||
```xml
|
||||
<nlog>
|
||||
...
|
||||
<include file="${machinename}.config" />
|
||||
...
|
||||
</nlog>
|
||||
```
|
||||
|
||||
NLog 4.4.2 之后可以使用通配符`*`指定多个文件。例如,`<include file="nlog-*.config" />`
|
||||
|
||||
##### variables
|
||||
|
||||
`variable`元素定义了配置文件中需要用到的变量,一般用来表示复杂或者重复的表达式(例如文件名)。变量需要先定义后使用,否则配置文件将初始化失败。定义变量的语法如下:
|
||||
|
||||
```xml
|
||||
<variable name="var" value="xxx" />
|
||||
```
|
||||
|
||||
定义变量之后,可以通过`${var}`语法来使用:
|
||||
|
||||
```xml
|
||||
<nlog>
|
||||
<variable name="logDirectory" value="logs/${shortdate}" />
|
||||
<targets>
|
||||
<target name="file1" xsi:type="File" fileName="${logDirectory}/file1.txt" />
|
||||
<target name="file2" xsi:type="File" fileName="${logDirectory}/file2.txt" />
|
||||
</targets>
|
||||
</nlog>
|
||||
```
|
||||
|
||||
## 记录日志
|
||||
|
||||
### 获取`NLog.Logger`实例
|
||||
|
||||
通过`NLog.LogManager.GetCurrentClassLogger`方法或`NLog.LogManager.GetLogger`方法可以获得`NLog.Logger`实例。
|
||||
|
||||
1. `NLog.LogManager.GetCurrentClassLogger`方法
|
||||
通过`NLog.LogManager.GetCurrentClassLogger`方法可以创建一个与所在类同名(包括namespace)的`NLog.Logger`的实例。
|
||||
|
||||
```c#
|
||||
var logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
```
|
||||
|
||||
|
||||
|
||||
2. `NLog.LogManager.GetLogger`方法
|
||||
通过`NLog.LogManager.GetLogger("MyLogger")`方法可以显示地指定`NLog.Logger`的名称为MyLogger。
|
||||
|
||||
```c#
|
||||
var logger = NLog.LogManager.GetLogger("MyLogger");
|
||||
```
|
||||
|
||||
### NLog 支持的日志级别
|
||||
|
||||
- `Trace` - very detailed logs,包含大量的信息,例如 protocol payloads。该级别一般仅在开发环境中启用。
|
||||
- `Debug` - debugging information, 比 `Trance` 级别稍微粗略,一般在生产环境中不启用。
|
||||
- `Info` - information messages,一般在生产环境中启用。
|
||||
- `Warn` - warning messages,一般用于可恢复或临时性错误的非关键问题。
|
||||
- `Error` - error messages,一般是异常信息。
|
||||
- `Fatal` - 非常严重的错误!
|
||||
|
||||
### NLog 日志记录函数方法
|
||||
|
||||
在代码中通过以下方法分别进行不同等级的日志记录,
|
||||
|
||||
```csharp
|
||||
NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
logger.Trace("Sample trace message");
|
||||
logger.Debug("Sample debug message");
|
||||
logger.Info("Sample informational message");
|
||||
logger.Warn("Sample warning message");
|
||||
logger.Error("Sample error message");
|
||||
logger.Fatal("Sample fatal error message");
|
||||
```
|
||||
|
||||
除此之外,`NLog.Logger` 还可以通过调用 `Log` 方法记录日志,
|
||||
|
||||
```csharp
|
||||
NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
// log level is passed as the parameter.
|
||||
logger.Log(LogLevel.Trace, "Sample trace message");
|
||||
logger.Log(LogLevel.Debug, "Sample debug message");
|
||||
logger.Log(LogLevel.Info, "Sample informational message");
|
||||
logger.Log(LogLevel.Warn, "Sample warning message");
|
||||
logger.Log(LogLevel.Error, "Sample error message");
|
||||
logger.Log(LogLevel.Fatal, "Sample fatal message");
|
||||
```
|
||||
|
||||
### 预定义和模板宏 Layouts and Layout Renderers
|
||||
|
||||
#### 模板宏 `Layout Renderers`
|
||||
|
||||
Layout 使用模板宏 Layout Renders 自定义日志输出的内容和格式。例如,大多数`target`使用的默认`SimpleLayout`如下
|
||||
|
||||
```xml
|
||||
${longdate}|${level:uppercase=true}|${logger}|${message}
|
||||
```
|
||||
|
||||
通过 `${}` 语法可以使用预定义的模版宏 Layout Renders。
|
||||
Layout Renders 的列表:[Layout Renders](https://github.com/NLog/NLog/wiki/Layout-Renderers)。
|
||||
|
||||
#### **预定义 `Layout`**
|
||||
|
||||
\- [CsvLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/CsvLayout) - A specialized layout that renders CSV-formatted events.
|
||||
\- [JsonLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/JsonLayout) - A specialized layout that renders to JSON.
|
||||
\- [LayoutWithHeaderAndFooter](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/LayoutWithHeaderAndFooter) - A specialized layout that supports header and footer.
|
||||
\- [Log4JXmlEventLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/Log4JXmlEventLayout) - A specialized layout that renders Log4j-compatible XML events.
|
||||
\- [SimpleLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/SimpleLayout) - Represents a string with embedded placeholders that can render contextual information.
|
||||
\- [CompoundLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/CompoundLayout) - A layout containing one or more nested layouts.
|
||||
|
||||
## 最佳实践
|
||||
|
||||
#### **`NLog.Logger` 应为类的静态变量**
|
||||
|
||||
新建 `NLog.Logger` 对象消耗一定的开销,例如需要获取锁或者分配对象。因此推荐以下方式创建`NLog.Logger`
|
||||
|
||||
```csharp
|
||||
namespace MyNamespace
|
||||
{
|
||||
public class MyClass
|
||||
{
|
||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **应由 `NLog.Logger` 格式化日志**
|
||||
|
||||
尽量避免直接使用字符串进行格式化。例如,
|
||||
|
||||
```csharp
|
||||
var message = "Hello" + "Earch";
|
||||
logger.Info(message);
|
||||
```
|
||||
|
||||
推荐使用 `NLog.Logger` 对象进行格式化。例如,
|
||||
|
||||
```csharp
|
||||
logger.Info("Hello {0}", "Earth");
|
||||
```
|
||||
|
||||
`NLog.Logger` 对象可以推迟执行格式化操作的时机,从而减少开销。
|
||||
|
||||
#### **应将异常对象传递给 `NLog.Logger`**
|
||||
|
||||
避免将异常作为字符串格式化的参数,而是显示的将异常作为参数传递给函数。例如,
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, "Something bad happened");
|
||||
}
|
||||
```
|
||||
|
||||
#### **开启配置文件的有效性验证**
|
||||
|
||||
默认情况下,NLog 屏蔽了自身的所有异常,因此 NLog 出错时不会使应用程序崩溃。对于大多数应用程序,建议在 `nlog` 元素增加 `throwConfigExceptions="true"` 属性开启初始化配置时的异常捕获功能,以验证配置文件的有效性。
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
throwConfigExceptions="true">
|
||||
</nlog>
|
||||
```
|
||||
|
||||
> ⚠️注意:还有一个名为 `throwExceptions` 的属性,不应在生产环境中使用。它是为了单元测试和本地调试设计的。
|
||||
|
||||
#### **别忘了 Flush 日志信息**
|
||||
|
||||
默认情况下,NLog 在程序关闭时会自动 flush。 Windows 限定 .NET 应用程序在程序终止前一定时间内进行关闭操作(一般是 2 秒)。如果 NLog 的 `target` 依赖于网络传输(例如 Http, Mail, Tcp),那么建议手动执行 Flush/Shutdown 操作。
|
||||
Mono/Linux 上运行的 .NET 应用程序在关闭前需要停止 Thread/Timer。如果未能完成这些操作,则会引发未处理异常、段错误以及其他难以预料的行为,同样建议手动执行 Flush/Shutdown 操作。
|
||||
|
||||
```csharp
|
||||
NLog.LogManager.Shutdown(); // Flush and close down internal threads and timers
|
||||
```
|
||||
|
||||
# 远程过程调用应用
|
||||
|
||||
## 调用 gRPC 服务
|
||||
|
||||
### 客户端工厂集成
|
||||
|
||||
gRPC 与`HttpClientFactory`的集成提供了一种创建 gRPC 客户端的集中方式。它可用作配置独立 gRPC 客户端实例的替代方法。
|
||||
|
||||
工厂具有以下优势:
|
||||
|
||||
- 提供了用于配置逻辑 gRPC 客户端实例的中心位置。
|
||||
- 可管理基础`HttpClientMessageHandler`的生存期。
|
||||
- 在 ASP.NET Core gRPC 服务中自动传播截止时间和取消。
|
||||
|
||||
#### 注册 gRPC 客户端
|
||||
|
||||
若要注册 gRPC 客户端,可在`Program.cs`中的应用入口点处的WebApplicationBuilder的实例中使用通用的`AddGrpcClient`扩展方法,并指定 gRPC 类型化客户端类和服务地址:
|
||||
|
||||
```c#
|
||||
builder.Services.AddGrpcClient<Greeter.GreeterClient>(o =>
|
||||
{
|
||||
o.Address = new Uri("https://localhost:5001");
|
||||
})
|
||||
```
|
||||
|
||||
gRPC 客户端类型通过依赖性注入(DI)注册为暂时性。现在可以在由 DI 创建的类型中直接注入和使用客户端。ASP.NET Core MVC 控制器、SignalR 中心和 gRPC 服务是可以自动注入 gRPC 客户端的位置:
|
||||
|
||||
```c#
|
||||
public class AggregatorService : Aggregator.AggregatorBase
|
||||
{
|
||||
private readonly Greeter.GreeterClient _client;
|
||||
|
||||
public AggregatorService(Greeter.GreeterClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public override async Task SayHellos(HelloRequest request,
|
||||
IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
|
||||
{
|
||||
using (var call = _client.SayHellos(request))
|
||||
{
|
||||
await foreach (var response in call.ResponseStream.ReadAllAsync())
|
||||
{
|
||||
await responseStream.WriteAsync(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
34
source/_posts/Spring.md
Normal file
34
source/_posts/Spring.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Spring
|
||||
date: 2023-10-09 14:17:02
|
||||
tags:
|
||||
---
|
||||
|
||||
# Spring Cloud
|
||||
|
||||
## OpenFeign
|
||||
|
||||
### 引入pom.xml
|
||||
|
||||
```xml
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<spring-cloud.version>2022.0.1</spring-cloud.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@ -39,10 +39,6 @@ protobuf 采用 `varint` 和 处理负数的 `ZigZag` 两种编码方式使得
|
||||
|
||||
第 2 个问题呢,其实需要的就是[每个平台]一套代码生成工具。生成的代码需要覆盖类的定义、对象的序列化/反序列化、服务接口的暴露和远程调用等等必要的模板代码,如此,开发人员只需要负责接口文档的维护和业务代码的实现(很自然的面向接口编程:))。此时,采用 protobuf 的`gRPC`自然而然的映入眼帘,因为对于目前所有主要的编程语言和平台,都有 gRPC 工具和库,包括 .NET、Java、Python、Go、C++、Node.js、Swift、Dart、Ruby 以及 PHP。可以说,这些工具和库的提供,使得 gRPC 可以跨多种语言和平台一致地工作,成为一个全面的 RPC 解决方案。
|
||||
|
||||
## gRPC 在 .NET 中的使用
|
||||
|
||||
`gRPC`作为 .NET 平台中的“一等公民”。
|
||||
|
||||
## proto文件
|
||||
|
||||
```protobuf
|
||||
@ -190,5 +186,126 @@ message Person {
|
||||
| `string` | `google.protobuf.StringValue` |
|
||||
| `ByteString` | `google.protobuf.BytesValue` |
|
||||
|
||||
### 小数
|
||||
|
||||
Protobuf 本身不支持 .NET `decimal` 类型,只支持 `double` 和 `float`。在 Protobuf 项目中,我们正在探讨这样一种可能性:将标准 decimal 类型添加到已知类型,并为支持它的语言和框架添加平台支持。尚未实现任何内容。
|
||||
|
||||
可以创建消息定义来表示 `decmial` 类型,以便在 .NET 客户端和服务器之间实现安全序列化。但其他平台上的开发人员必须了解所使用的格式,并能够实现自己对其的处理。
|
||||
|
||||
### 为 Protobuf 创建自定义 decimal 类型
|
||||
|
||||
```protobuf
|
||||
package CustomTypes;
|
||||
|
||||
message DecimalValue {
|
||||
int64 units = 1;
|
||||
|
||||
sfixed32 nanos = 2;
|
||||
}
|
||||
```
|
||||
|
||||
`nanos`字段表示从`0.999_999_999`到`-0.999_999_999`的值。例如,`decimal`值`1.5m`将表示`{ units = 1, nanos = 500_000_000 }`。这就是此示例中的 `nanos` 字段使用 `sfixed32` 类型的原因:对于较大的值,其编码效率比 `int32` 更高。 如果 `units` 字段为负,则 `nanos` 字段也应为负。
|
||||
|
||||
### 集合
|
||||
|
||||
#### 列表
|
||||
|
||||
Protobuf 中,在字段上使用`repeated`前缀关键字指定列表。以下示例演示如何创建列表:
|
||||
|
||||
```protobuf
|
||||
message Person {
|
||||
// ...
|
||||
repeated string roles = 8;
|
||||
}
|
||||
```
|
||||
|
||||
在生产的代码中,`repeated`字段由`Google.Protobuf.Collections.RepeatedField<T>`泛型类型表示。
|
||||
|
||||
```c#
|
||||
public class Person
|
||||
{
|
||||
// ...
|
||||
public RepeatedField<string> Roles { get; }
|
||||
}
|
||||
```
|
||||
|
||||
`RepeatedField<T>`可实现 IList<T>。因此你可使用 LINQ 查询,或者将其转换为数组或列表。`RepeatedField<T>`属性没有公共 setter。项应添加到现有集合中。
|
||||
|
||||
```c#
|
||||
var person = new Person();
|
||||
|
||||
person.Roles.Add("user");
|
||||
|
||||
var roles = new [] { "admin", "manager" };
|
||||
person.Roles.Add(roles);
|
||||
```
|
||||
|
||||
#### 字典
|
||||
|
||||
.NET IDictionary<TKey, TValue> 类型在 Protobuf 中使用 `map<key_type, value_type>`表示。
|
||||
|
||||
```protobuf
|
||||
message Person {
|
||||
// ...
|
||||
map<string, string> attributes = 9;
|
||||
}
|
||||
```
|
||||
|
||||
在生成的 .NET 代码中,`map`字段由`Google.Protobuf.Collections.MapField<TKey, TValue>`泛型类型表示。
|
||||
|
||||
`MapField<TKey, TValue>`可实现 IDictionary<TKey, TValue>。与`repeated`属性一样,`map`属性没有公共 setter。项应添加到现有集合中。
|
||||
|
||||
```c#
|
||||
var person = new Person();
|
||||
|
||||
person.Attributes["create_by"] = "James";
|
||||
|
||||
var attributes = new Dictionary<string, string>
|
||||
{
|
||||
["last_modified"] = DateTime.UtcNow.ToString()
|
||||
};
|
||||
person.Attributes.Add(attributes);
|
||||
```
|
||||
|
||||
### 无结构的条件消息
|
||||
|
||||
Protobuf 是一种协定优先的消息传递格式。 构建应用时,必须在 `.proto` 文件中指定应用的消息,包括其字段和类型。 Protobuf 的协定优先设计非常适合强制执行消息内容,但可能会限制不需要严格协定的情况:
|
||||
|
||||
- 包含未知有效负载的消息。 例如,具有可以包含任何消息的字段的消息。
|
||||
- 条件消息。 例如,从 gRPC 服务返回的消息可能是成功结果或错误结果。
|
||||
- 动态值。 例如,具有包含非结构化值集合的字段的消息,类似于 JSON。
|
||||
|
||||
Protobuf 提供语言功能和类型来支持这些情况。
|
||||
|
||||
#### 任意
|
||||
|
||||
利用`Any`类型,可以将消息作为嵌入类型使用,而无需`.proto`定义。若使用`Any`类型,请导入`any.proto`。
|
||||
|
||||
```protobuf
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
message Status {
|
||||
string message = 1;
|
||||
google.protobuf.Any detail = 2;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
```c#
|
||||
// Create a status with a Person message set to detail.
|
||||
var status = new ErrorStatus();
|
||||
status.Detail = Any.Pack(new Person { FirstName = "James" });
|
||||
|
||||
// Read Person message from detail.
|
||||
if (status.Detail.Is(Person.Desciptor))
|
||||
{
|
||||
var person = status.Detail.Unpack<Person>();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Dubbo 3
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user