NATS 消息系统的 AC 客户端。
请转到此处获取在线文档,并查看常见问题。
此 NATS 客户端实现很大程度上基于 NATS GO 客户端。支持 Mac OS/X、Linux 和 Windows(尽管我们没有特定的平台支持矩阵)。
有多个带有 NATS C 客户端库的包管理器可用。如果您知道此列表中未包含的内容,请提交 PR 来添加它!
首先,下载源码:
git clone [email protected]:nats-io/nats.c.git .
要构建库,请使用 CMake。请注意,默认情况下,将构建 NATS Streaming API 并将其包含在 NATS 库中。如果您不想构建与 Streaming 相关的 API,请参阅下文。
确保 CMake 已添加到您的路径中。如果在 Windows 上构建,请从 Visual Studio 工具菜单中打开命令 shell,然后选择适当的命令 shell(分别用于 64 或 32 位构建的 x64 或 x86)。您可能还需要以管理员权限运行它。
从根源树创建一个build
目录(任何名称都可以),然后cd
进入该目录。然后第一次发出此命令:
cmake ..
在某些体系结构中,您可能会遇到mutex.co
的编译错误,因为不支持我们在旋转尝试获取锁时用于产生的汇编指令。
您可能会遇到此类构建错误:
/tmp/cc1Yp7sD.s: Assembler messages:
/tmp/cc1Yp7sD.s:302: Error: selected processor does not support ARM mode `yield'
src/CMakeFiles/nats_static.dir/build.make:542: recipe for target 'src/CMakeFiles/nats_static.dir/unix/mutex.c.o' failed
如果是这种情况,您可以通过启用NATS_BUILD_NO_SPIN
标志来解决此问题(如果不使用 CMake 进行编译,则使用-DNATS_NO_SPIN
):
cmake .. -DNATS_BUILD_NO_SPIN=ON
如果您之前构建过该库,则可能需要执行make clean
,或者在执行 cmake 命令之前删除并重新创建构建目录。
要在 Windows 上构建,您需要选择构建生成器。例如,要选择nmake
,您可以运行:
cmake .. -G "NMake Makefiles"
运行cmake -h
将为您提供可能的选项列表和所有生成器名称。
或者,您可以运行 GUI 版本。从同一个构建命令 shell 启动 GUI:
c:program files (x86)CMakebincmake-gui.exe
如果您从空的构建目录开始,则需要选择源和构建目录,然后单击Configure
。在这里,您将能够从下拉框中选择构建生成器的名称。完成后,单击Generate
。然后您可以返回命令 shell 或 Visual Studio 并进行构建。
要修改某些构建选项,您需要编辑缓存并重建。
make edit_cache
请注意,如果您在 Windows 上构建并选择了“NMake Makefiles”,请将以下所有对make
引用替换为nmake
。
编辑缓存允许您选择构建类型(调试、发布等)、体系结构(64 或 32 位)等。
默认目标将构建所有内容,即静态和共享 NATS 库以及示例和测试程序。每个都位于构建目录下各自的目录中: src
、 examples
和test
。
make install
将复制文件夹install/lib
中的静态库和共享库以及install/include
中的公共标头。
默认情况下,该库是使用 TLS 支持构建的。您可以从 cmake gui make edit_cache
禁用此功能,并将NATS_BUILD_WITH_TLS
选项切换为OFF
,或直接将该选项传递给cmake
命令:
cmake .. -DNATS_BUILD_WITH_TLS=OFF
从2.0.0
开始,在使用 TLS/SSL 支持进行构建时,始终会验证服务器证书的预期主机名。这意味着 URL 中或通过选项natsOptions_SetExpectedHostname()
提供的主机名将用于检查证书中存在的主机名。在2.0.0
之前,仅当调用选项natsOptions_SetExpectedHostname()
时才会验证主机名。
尽管我们建议保留新的默认行为,但您可以通过关闭此选项构建库来恢复以前的行为:
cmake .. -DNATS_BUILD_TLS_FORCE_HOST_VERIFY=OFF
NATS C 客户端是使用 OpenSSL 库中的 API 构建的。默认情况下,我们使用3.0+
API。由于不再支持 OpenSSL 1.0.2
,从 NATS C Client v3.6.0
版本开始,CMake 变量NATS_BUILD_TLS_USE_OPENSSL_1_1_API
现在默认设置为ON
(如果您要设置新环境),并将使用1.1+
版本的 OpenSSL API / 3.0+
API。通过将此 CMake 选项设置为OFF
,您仍然可以使用 OpenSSL 1.0.2
库进行编译:
cmake .. -DNATS_BUILD_TLS_USE_OPENSSL_1_1_API=OFF
变量NATS_BUILD_TLS_USE_OPENSSL_1_1_API
已弃用,这意味着将来该选项将被简单地删除,并且仅使用 OpenSSL 3.0+
API。库中使用旧版 OpenSSL API 的代码也将被删除。
请注意, v2.0.0
中已弃用的变量NATS_BUILD_WITH_TLS_CLIENT_METHOD
现已删除。
由于 NATS C 客户端动态链接到 OpenSSL 库,因此您需要确保随后针对 OpenSSL 1.1+/3.0+ 库运行应用程序。
如果要链接到静态 OpenSSL 库,则需要删除CMakeCache.txt
并使用附加选项重新生成它:
rm CMakeCache.txt
cmake .. -DNATS_BUILD_OPENSSL_STATIC_LIBS=ON
然后调用make
(或等效函数,具体取决于您的平台),这应该确保库(以及示例和/或测试套件可执行文件)链接到 OpenSSL 库(如果 CMake 找到了它)。
在构建具有流支持的库时,NATS 库使用 libprotobuf-c 库。当 cmake 第一次运行时(或删除CMakeCache.txt
并再次调用cmake ..
后),它正在寻找 libprotobuf-c 库。如果没有找到,则会打印一条消息并且构建过程失败。 CMake 在通常找到库的目录中搜索库。但是,如果您想指定库所在的特定目录,则需要这样做:
cmake .. -DNATS_PROTOBUF_DIR=
默认情况下将使用静态库。如果您想更改它,或者库没有预期的名称,您需要执行以下操作:
# Use the library named mylibproto.so located at /my/location
cmake .. -DNATS_PROTOBUF_LIBRARY=/my/location/mylibproto.so
如果包含头位于不同的目录中,则可以将两者组合起来
# Use the library named mylibproto.so located at /my/location and the directory protobuf-c/ containing protobuf-c.h located at /my/other/location
cmake .. -DNATS_PROTOBUF_LIBRARY=/my/location/mylibproto.so -DNATS_PROTOBUF_DIR=/my/other/location
如果您不想构建要包含在 NATS 库中的 NATS Streaming API:
cmake .. -DNATS_BUILD_STREAMING=OFF
当使用新的 NATS 2.0 安全功能时,库需要在连接或重新连接期间对服务器发送的一些“随机数”进行签名。我们使用 Ed25519 公钥签名。该库附带了一些执行签名的代码。在大多数情况下,这会没问题,但如果性能是一个问题(特别是如果您计划大量使用natsConnection_Sign()
函数),您将可以选择使用 Libsodium 库进行构建。
请按照此处有关如何安装 libsodium 库的说明进行操作。
在macOS上,您可以使用brew
:
brew install libsodium
在 Linux 上,您可以使用apt-get
apt-get install libsodium-dev
安装后,您可以通过首先启用 libsodium 库来重建 NATS C 客户端:
cmake .. -DNATS_BUILD_USE_SODIUM=ON
如果您将 libsodium 库安装在 CMake 无法找到的非标准位置,则可以指定此目录的位置:
cmake .. -DNATS_BUILD_USE_SODIUM=ON -DNATS_SODIUM_DIR=/my/path/to/libsodium
在valgrind
可用的平台上,您可以通过内存检查来运行测试。这是一个例子:
make test ARGS="-T memcheck"
或者,您可以直接调用ctest
程序:
ctest -T memcheck -V -I 1,4
上面的命令将使用valgrind
( -T memcheck
) 运行测试,并带有详细输出 ( -V
),并运行从 1 到 4 的测试 ( -I 1,4
)。
如果将测试添加到test/test.c
,则需要将其添加到list_test.txt
文件中。每个条目仅包含测试名称,函数的名称必须相同,并带有test_
前缀。该列表按字母顺序排列,但不一定如此,您可以在任何地方添加。
如果您要添加基准测试,则应将其添加到list_bench.txt
中。这些测试有不同的标签( -L 'bench'
)并在 CI 上单独执行。
您需要重新运行cmake
以使更改生效:
cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ivan/nats.c/build
您可以使用以下环境变量来影响测试套件的行为。
当使用内存检查运行时,时序会发生变化,整体性能会变慢。以下变量允许测试套件调整测试期间使用的一些值:
export NATS_TEST_VALGRIND=yes
在 Windows 上,将set
它而不是export
。
在详细模式下运行测试时,以下环境变量允许您从测试本身查看服务器输出。如果没有此选项,服务器输出将被静音:
export NATS_TEST_KEEP_SERVER_OUTPUT=yes
如果要更改默认服务器可执行文件名称 ( nats-server.exe
) 或指定特定位置,请使用以下环境变量:
set NATS_TEST_SERVER_EXE=c:testnats-server.exe
公共 API 已使用 Doxygen 进行记录。
要生成文档,请转到doc
目录并键入以下命令:
doxygen DoxyFile.NATS.Client
如果您切换 Streaming API 的构建,并且文档不再匹配正在构建的内容,您可以通过切换NATS_UPDATE_DOC
构建标志来更新文档并重建文档。
从构建目录:
cmake .. -DNATS_UPDATE_DOC=ON
make
cd /doc
doxygen DoxyFile.NATS.Client
生成的文档将位于html
目录中。要查看文档,请将浏览器指向该目录中的文件index.html
。
请转到此处获取在线文档。
源代码也有很好的文档记录。
本节列出了重要的更改,例如弃用通知等......
2.0.0
此版本引入了 NATS Server 2.0.0
使用的安全概念,因此与服务器版本保持一致。引入了新的 API,但最重要的变化是 TLS 连接的新默认行为:
建立安全连接时,现在始终验证服务器证书的主机名,无论用户是否调用natsOptions_SetExpectedHostname()
。例如,这可能会破坏使用 IP 连接到证书中仅包含主机名的服务器的应用程序。这可以通过更改应用程序以使用 URL 中的主机名或使用natsOptions_SetExpectedHostname()
来解决。如果这是不可能的,您可以通过构建禁用新行为的库来恢复旧行为。请参阅#tls-support 了解更多信息。
该存储库用于包含适用于 macOS、Linux 和 Windows 的 libprotobuf-c 预编译库以及头文件(位于/pbuf
目录中)。我们现在已经删除了这个目录,并要求用户单独安装 libprotobuf-c 库。如果 CMake 无法直接找到库,请参阅构建说明来指定库位置。
1.8.0
natsConnStatus
枚举值已添加前缀NATS_CONN_STATUS_
。如果您的应用程序未使用引用任何原始值,例如CONNECTED
或CLOSED
等,那么您无需执行任何操作。如果您这样做,您有两个选择:NATS_BUILD_NO_PREFIX_CONNSTS
。这可以通过构建目录来完成: cmake .. -DNATS_BUILD_NO_PREFIX_CONNSTS=ON
examples/getstarted
目录有一组简单的示例,它们功能齐全,但非常简单。目标是展示 API 的使用是多么容易。
examples/
目录中有一组更复杂的示例,也可用于对客户端库进行基准测试。
请注意,为简单起见,此处不执行错误检查。
natsConnection * nc = NULL ;
natsSubscription * sub = NULL ;
natsMsg * msg = NULL ;
// Connects to the default NATS Server running locally
natsConnection_ConnectTo ( & nc , NATS_DEFAULT_URL );
// Connects to a server with username and password
natsConnection_ConnectTo ( & nc , "nats://ivan:secret@localhost:4222" );
// Connects to a server with token authentication
natsConnection_ConnectTo ( & nc , "nats://myTopSecretAuthenticationToken@localhost:4222" );
// Simple publisher, sending the given string to subject "foo"
natsConnection_PublishString ( nc , "foo" , "hello world" );
// Publish binary data. Content is not interpreted as a string.
char data [] = { 1 , 2 , 0 , 4 , 5 };
natsConnection_Publish ( nc , "foo" , ( const void * ) data , 5 );
// Simple asynchronous subscriber on subject foo, invoking message
// handler 'onMsg' when messages are received, and not providing a closure.
natsConnection_Subscribe ( & sub , nc , "foo" , onMsg , NULL );
// Simple synchronous subscriber
natsConnection_SubscribeSync ( & sub , nc , "foo" );
// Using a synchronous subscriber, gets the first message available, waiting
// up to 1000 milliseconds (1 second)
natsSubscription_NextMsg ( & msg , sub , 1000 );
// Destroy any message received (asynchronously or synchronously) or created
// by your application. Note that if 'msg' is NULL, the call has no effect.
natsMsg_Destroy ( msg );
// Unsubscribing
natsSubscription_Unsubscribe ( sub );
// Destroying the subscription (this will release the object, which may
// result in freeing the memory). After this call, the object must no
// longer be used.
natsSubscription_Destroy ( sub );
// Publish requests to the given reply subject:
natsConnection_PublishRequestString ( nc , "foo" , "bar" , "help!" );
// Sends a request (internally creates an inbox) and Auto-Unsubscribe the
// internal subscriber, which means that the subscriber is unsubscribed
// when receiving the first response from potentially many repliers.
// This call will wait for the reply for up to 1000 milliseconds (1 second).
natsConnection_RequestString ( & reply , nc , "foo" , "help" , 1000 );
// Closing a connection (but not releasing the connection object)
natsConnection_Close ( nc );
// When done with the object, free the memory. Note that this call
// closes the connection first, in other words, you could have simply
// this call instead of natsConnection_Close() followed by the destroy
// call.
natsConnection_Destroy ( nc );
// Message handler
void
onMsg ( natsConnection * nc , natsSubscription * sub , natsMsg * msg , void * closure )
{
// Prints the message, using the message getters:
printf ( "Received msg: %s - %.*sn" ,
natsMsg_GetSubject ( msg ),
natsMsg_GetDataLength ( msg ),
natsMsg_GetData ( msg ));
// Don't forget to destroy the message!
natsMsg_Destroy ( msg );
}
对 JetStream 的支持从库的v3.0.0
版本和 NATS Server v2.2.0+
开始,尽管获取 JetStream 特定错误代码需要v2.3.0+
版本的服务器。某些配置选项仅从v2.3.3
开始可用,因此我们建议您使用最新的 NATS Server 版本以获得更好的体验。
查看examples
目录中名为js-xxx.c
的示例,了解如何使用 API 的示例。新对象和 API 已完整记录在在线文档中。
// Connect to NATS
natsConnection_Connect ( & conn , opts );
// Initialize and set some JetStream options
jsOptions jsOpts ;
jsOptions_Init ( & jsOpts );
jsOpts . PublishAsync . MaxPending = 256 ;
// Create JetStream Context
natsConnection_JetStream ( & js , conn , & jsOpts );
// Simple Stream Publisher
js_Publish ( & pa , js , "ORDERS.scratch" , ( const void * ) "hello" , 5 , NULL , & jerr );
// Simple Async Stream Publisher
for ( i = 0 ; i < 500 ; i ++ )
{
js_PublishAsync ( js , "ORDERS.scratch" , ( const void * ) "hello" , 5 , NULL );
}
// Wait for up to 5 seconds to receive all publish acknowledgments.
jsPubOptions_Init ( & jsPubOpts );
jsPubOpts . MaxWait = 5000 ;
js_PublishAsyncComplete ( js , & jsPubOpts );
// One can get the list of all pending publish async messages,
// to either resend them or simply destroy them.
natsMsgList pending ;
s = js_PublishAsyncGetPendingList ( & pending , js );
if ( s == NATS_OK )
{
int i ;
for ( i = 0 ; i < pending . Count ; i ++ )
{
// There could be a decision to resend these messages or not.
if ( your_decision_to_resend ( pending . Msgs [ i ]))
{
// If the call is successful, pending.Msgs[i] will be set
// to NULL so destroying the pending list will not destroy
// this message since the library has taken ownership back.
js_PublishMsgAsync ( js , & ( pending . Msgs [ i ]), NULL );
}
}
// Destroy the pending list object and all messages still in that list.
natsMsgList_Destroy ( & pending );
}
// To create an asynchronous ephemeral consumer
js_Subscribe ( & sub , js , "foo" , myMsgHandler , myClosure , & jsOpts , NULL , & jerr );
// Same but use a subscription option to ask the callback to not do auto-ack.
jsSubOptions so ;
jsSubOptions_Init ( & so );
so . ManualAck = true;
js_Subscribe ( & sub , js , "foo" , myMsgHandler , myClosure , & jsOpts , & so , & jerr );
// Or to bind to an existing specific stream/durable:
jsSubOptions_Init ( & so );
so . Stream = "MY_STREAM" ;
so . Consumer = "my_durable" ;
js_Subscribe ( & sub , js , "foo" , myMsgHandler , myClosure , & jsOpts , & so , & jerr );
// Synchronous subscription:
js_SubscribeSync ( & sub , js , "foo" , & jsOpts , & so , & jerr );
jsStreamConfig cfg ;
// Connect to NATS
natsConnection_Connect ( & conn , opts );
// Create JetStream Context
natsConnection_JetStream ( & js , conn , NULL );
// Initialize the configuration structure.
jsStreamConfig_Init ( & cfg );
// Provide a name
cfg . Name = "ORDERS" ;
// Array of subjects and its size
cfg . Subjects = ( const char * [ 1 ]){ "ORDERS.*" };
cfg . SubjectsLen = 1 ;
// Create a Stream. If you are not interested in the returned jsStreamInfo object,
// you can pass NULL.
js_AddStream ( NULL , js , & cfg , NULL , & jerr );
// Update a Stream
cfg . MaxBytes = 8 ;
js_UpdateStream ( NULL , js , & cfg , NULL , & jerr );
// Delete a Stream
js_DeleteStream ( js , "ORDERS" , NULL , & jerr );
实验功能!我们保留更改 API 的权利,而不必更改库的主要版本。
KeyValue 存储是基于 JetStream 的物化视图。存储桶是一个流,键是该流中的主题。
部分功能需要 NATS Server v2.6.2
,因此我们建议您使用最新的 NATS Server 版本以获得更好的体验。
新对象和 API 已完整记录在在线文档中。
如何创建 KeyValue 存储的示例:
jsCtx * js = NULL ;
kvStore * kv = NULL ;
kvConfig kvc ;
// Assume we got a JetStream context in `js`...
kvConfig_Init ( & kvc );
kvc . Bucket = "KVS" ;
kvc . History = 10 ;
s = js_CreateKeyValue ( & kv , js , & kvc );
// Do some stuff...
// This is to free the memory used by `kv` object,
// not delete the KeyValue store in the server
kvStore_Destroy ( kv );
这展示了如何“绑定”到现有的:
jsCtx * js = NULL ;
kvStore * kv = NULL ;
// Assume we got a JetStream context in `js`...
s = js_KeyValue ( & kv , ks , "KVS" );
// Do some stuff...
// This is to free the memory used by `kv` object,
// not delete the KeyValue store in the server
kvStore_Destroy ( kv );
这是删除服务器中的 KeyValue 存储的方法:
jsCtx * js = NULL ;
// Assume we got a JetStream context in `js`...
s = js_DeleteKeyValue ( js , "KVS" );
这是为给定键赋值的方法:
kvStore * kv = NULL ;
uint64_t rev = 0 ;
// Assume we got a kvStore...
s = kvStore_PutString ( & rev , kv , "MY_KEY" , "my value" );
// If the one does not care about getting the revision, pass NULL:
s = kvStore_PutString ( NULL , kv , "MY_KEY" , "my value" );
上面为给定的键放置了一个值,但如果想确保仅当该键以前从未存在过时才为该键放置该值,则可以调用:
// Same note than before: if "rev" is not needed, pass NULL:
s = kvStore_CreateString ( & rev , kv , "MY_KEY" , "my value" );
当且仅当服务器中的最后一个修订版与传递给此 API 的修订版相匹配时,才可以更新密钥:
// This would update the key "MY_KEY" with the value "my updated value" only if the current revision (sequence number) for this key is 10.
s = kvStore_UpdateString ( & rev , kv , "MY_KEY" , "my updated value" , 10 );
这是获取密钥的方法:
kvStore * kv = NULL ;
kvEntry * e = NULL ;
// Assume we got a kvStore...
s = kvStore_Get ( & e , kv , "MY_KEY" );
// Then we can get some fields from the entry:
printf ( "Key value: %sn" , kvEntry_ValueString ( e ));
// Once done with the entry, we need to destroy it to release memory.
// This is NOT deleting the key from the server.
kvEntry_Destroy ( e );
这是清除密钥的方法:
kvStore * kv = NULL ;
// Assume we got a kvStore...
s = kvStore_Purge ( kv , "MY_KEY" );
这将删除服务器中的密钥:
kvStore * kv = NULL ;
// Assume we got a kvStore...
s = kvStore_Delete ( kv , "MY_KEY" );
为给定键创建“观察者”:
kvWatcher * w = NULL ;
kvWatchOptions o ;
// Assume we got a kvStore...
// Say that we are not interested in getting the
// delete markers...
// Initialize a kvWatchOptions object:
kvWatchOptions_Init ( & o );
o . IgnoreDeletes = true;
// Create the watcher
s = kvStore_Watch ( & w , kv , "foo.*" , & o );
// Check for error..
// Now get updates:
while ( some_condition )
{
kvEntry * e = NULL ;
// Wait for the next update for up to 5 seconds
s = kvWatcher_Next ( & e , w , 5000 );
// Do something with the entry...
// Destroy to release memory
kvEntry_Destroy ( e );
}
// When done with the watcher, it needs to be destroyed to release memory:
kvWatcher_Destroy ( w );
要获取密钥的历史记录:
kvEntryList l ;
int i ;
// The list is defined on the stack and will be initilized/updated by this call:
s = kvStore_History ( & l , kv , "MY_KEY" , NULL );
for ( i = 0 ; i < l . Count ; i ++ )
{
kvEntry * e = l . Entries [ i ];
// Do something with the entry...
}
// When done with the list, call this to free entries and the content of the list.
kvEntryList_Destroy ( & l );
// In order to set a timeout to get the history, we need to do so through options:
kvWatchOptions o ;
kvWatchOptions_Init ( & o );
o . Timeout = 5000 ; // 5 seconds.
s = kvStore_History ( & l , kv , "MY_KEY" , & o );
这是获取存储桶密钥的方式:
kvKeysList l ;
int i ;
// If no option is required, pass NULL as the last argument.
s = kvStore_Keys ( & l , kv , NULL );
// Check error..
// Go over all keys:
for ( i = 0 ; i < l . Count ; i ++ )
printf ( "Key: %sn" , l . Keys [ i ]);
// When done, list need to be destroyed.
kvKeysList_Destroy ( & l );
// If option need to be specified:
kvWatchOptions o ;
kvWatchOptions_Init ( & o );
o . Timeout = 5000 ; // 5 seconds.
s = kvStore_Keys ( & l , kv , & o );
连接到 2.2.0+ 版本的服务器时,标头可用。
它们与 http 标头非常相似。它们是键/值对的映射,值是字符串数组。
标头允许用户添加有关消息的元信息,而不会干扰消息负载。
请注意,如果应用程序在连接到不理解它们的服务器时尝试发送带有标头的消息,则发布调用将返回错误NATS_NO_SERVER_SUPPORT
。
有一个 API 可以了解当前连接的服务器是否支持标头:
natsStatus s = natsConnection_HasHeaderSupport ( conn );
if ( s == NATS_NO_SERVER_SUPPORT )
// deal with server not supporting this feature.
如果服务器能够理解标头,但要将消息传递给不理解标头的客户端,则标头将被剥离,以便较旧的客户端仍然可以接收消息。如果应用程序依赖标头,那么让所有客户端和服务器都使用支持标头的版本非常重要。
有关 headers API 的更多详细信息,请获取示例: examples/getstarted/headers.c
。
*
通配符匹配主题的任何级别的任何标记:
natsConnection_Subscribe ( & sub , nc , "foo.*.baz" , onMsg , NULL );
该订阅者将收到发送至以下地址的消息:
但是,它不会收到以下消息:
>
通配符匹配主题失败的任意长度,并且只能是最后一个标记。
natsConnection_Subscribe ( & sub , nc , "foo.>" , onMsg , NULL );
该订阅者将收到发送至以下地址的任何消息:
但是,它不会接收发送到以下位置的消息:
发布此主题将导致上述两个订阅者收到消息:
natsConnection_PublishString ( nc , "foo.bar.baz" , "got it?" );
所有具有相同队列名称的订阅将形成一个队列组。使用队列语义,每条消息将仅传递给每个队列组的一个订阅者。您可以根据需要拥有任意数量的队列组。普通订阅者将继续按预期工作。
natsConnection_QueueSubscribe ( & sub , nc , "foo" , "job_workers" , onMsg , NULL );
(请注意,该库需要在默认情况下使用 TLS 支持进行构建,才能使这些 API 正常工作。有关如何使用或不使用 TLS 进行构建的更多详细信息,请参阅“构建”一章)。
SSL/TLS 连接是通过使用natsOptions
配置的。根据您想要的安全级别,它可以像在natsOptions_SetSecure()
调用中将 secure 布尔值设置为 true 一样简单。
即使具有完全的安全性(客户端验证服务器证书,服务器需要客户端证书),设置也只涉及几次调用。
// Here is the minimum to create a TLS/SSL connection:
// Create an options object.
natsOptions_Create ( & opts );
// Set the secure flag.
natsOptions_SetSecure ( opts , true);
// You may not need this, but suppose that the server certificate
// is self-signed and you would normally provide the root CA, but
// don't want to. You can disable the server certificate verification
// like this:
natsOptions_SkipServerVerification ( opts , true);
// Connect now...
natsConnection_Connect ( & nc , opts );
// That's it! On success you will have a secure connection with the server!
(...)
// This example shows what it takes to have a full SSL configuration,
// including server expected's hostname, root CA, client certificates
// and specific ciphers to use.
// Create an options object.
natsOptions_Create ( & opts );
// Set the secure flag.
natsOptions_SetSecure ( opts , true);
// For a server with a trusted chain built into the client host,
// simply designate the server name that is expected. Without this
// call, the server certificate is still verified, but not the
// hostname.
natsOptions_SetExpectedHostname ( opts , "localhost" );
// Instead, if you are using a self-signed cert and need to load in the CA.
natsOptions_LoadCATrustedCertificates ( opts , caCertFileName );
// If the server requires client certificates, provide them along with the
// private key, all in one call.
natsOptions_LoadCertificatesChain ( opts , certChainFileName , privateKeyFileName );
// You can also specify preferred ciphers if you want.
natsOptions_SetCiphers ( opts , "-ALL:HIGH" );
// Then simply pass the options object to the connect call:
natsConnection_Connect ( & nc , opts );
// That's it! On success you will have a secure connection with the server!