<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>نوشته های Farhad Nosrati فرهاد نصرتی</title><link>https://virgool.ir/feed/@farhadnosrati</link><description>https://github.com/nosratifarhad</description><language>fa</language><pubDate>2026-11-12 04:49:53</pubDate><image><url>https://files.virgool.ir/upload/users/788498/avatar/czjeNp.jpeg</url><title>Farhad Nosrati فرهاد نصرتی</title><link>https://virgool.ir/@farhadnosrati</link></image><item><title>Kafka Producer : The Definitive Guide</title><link>https://virgool.ir/@farhadnosrati/kafka-producer-acqv1mlzauwj</link><description>Producer Overview To produce messages to Kafka, the application first creates a ProducerRecord containing at least the target topic and the message value. Optionally, it can also include a key, partition, timestamp, and headers. Before transmission, the Producer serializes the key and value into byte arrays. If no partition is specified, the partitioner selects one, typically based on the message key. The record is then added to a batch of messages for the same topic and partition, which is sent to the appropriate broker by a background thread. After processing the request, the broker returns either a RecordMetadata object containing the topic, partition, and offset if the write succeeds, or an error if it fails. In the event of a failure, the Producer may automatically retry sending the message before ultimately reporting an exception if all retry attempts are exhausted.The Kafka Producer is the component responsible for producing and sending messages to a Kafka Cluster. Before sending any messages, a Producer must be created with a set of configuration properties that enable it to connect to Kafka brokers and publish data to the desired topics. The most essential configuration properties required to create a Producer are bootstrap.servers, key.serializer, and value.serializer.The bootstrap.servers property specifies a list of brokers that the Producer uses to establish its initial connection to the Kafka cluster. This list does not need to include every broker in the cluster because, after the initial connection is established, the Producer retrieves complete cluster metadata, including information about all available brokers. However, it is recommended to specify at least two brokers so that if one broker becomes unavailable, the Producer can still connect to the cluster through another broker.Since Kafka accepts messages only as byte arrays, the Producer must serialize both the key and the value of each message before sending it. This is accomplished through the key.serializer and value.serializer configuration properties. The values of these properties must be the names of classes that implement the org.apache.kafka.common.serialization.Serializer interface. Kafka provides several built-in serializers, such as StringSerializer, IntegerSerializer, LongSerializer, and ByteArraySerializer, which are sufficient for most use cases, eliminating the need for custom serializer implementations. An important point to note is that even if the Producer sends only a value and does not use a key, the key.serializer configuration is still mandatory.Once the configuration is complete, a Producer is created using the KafkaProducer class. The generic types specified for the key and value when creating the Producer must match the selected serializers. For example, if StringSerializer is used, both the key and value types should be String.To send a message, a ProducerRecord object must first be created. This object contains information such as the topic name, the message key, and the message value. The record is then passed to the Producer using the send() method. Contrary to a common misconception, the send() method is inherently asynchronous. This means that after it is invoked, the message is first placed into the Producer&#39;s internal buffer and is subsequently transmitted to the broker by a separate thread. Therefore, calling send() alone does not guarantee that the message has already been delivered; it merely initiates the sending process.The simplest message delivery approach is the Fire-and-Forget pattern. In this approach, the application simply invokes the send() method without checking the outcome of the operation. Under normal circumstances, thanks to Kafka&#39;s high reliability and the Producer&#39;s built-in retry mechanism, most messages are successfully delivered. However, if a non-retriable error or a timeout occurs, the message may be lost without the application being aware of the failure. Therefore, this approach is suitable only for scenarios where occasional message loss is acceptable.The second approach is Synchronous Send. Since the send() method returns a Future&lt;RecordMetadata&gt; object, calling the get() method on this object causes the sending thread to block until a response is received from Kafka. If the operation succeeds, the returned RecordMetadata object provides information such as the topic, partition, and offset of the message. If an error occurs, an appropriate exception is thrown. Although this approach guarantees that the application knows whether the message was successfully delivered, waiting for the broker&#39;s response significantly reduces throughput and is therefore rarely used in production environments.The third approach is Asynchronous Send with Callback, which is the most commonly used method in production systems. In this approach, a Callback object is passed along with the send() method. Once Kafka responds, the callback&#39;s onCompletion() method is invoked. If the message is successfully delivered, the Exception parameter is null; otherwise, it contains information about the failure, allowing the application to perform actions such as logging the error, implementing custom retry logic, or storing failed messages in monitoring systems.An important aspect of callbacks is that they execute on the Producer&#39;s internal I/O thread. As a result, callback execution order is preserved for messages sent to the same partition. However, performing heavy or blocking operations inside a callback—such as database access, HTTP requests, or file I/O—can negatively impact the Producer&#39;s performance and delay the delivery of subsequent messages. Therefore, time-consuming tasks should be delegated to a separate thread or executor.KafkaProducer classifies errors into two categories. The first category consists of Retriable Errors, which can be resolved by automatically resending the message. Examples include temporary broker connection failures or leader changes for a partition. In such cases, the Producer retries the operation automatically. Only after all configured retry attempts have been exhausted does the exception propagate back to the application. The second category consists of Non-Retriable Errors, which cannot be resolved through retries. An example is the Message size too large error. In these situations, the Producer immediately returns the exception without attempting any retries.In addition to broker-related failures, the Producer may encounter exceptions before the message is even transmitted. Common examples include SerializationException when the message cannot be serialized, BufferExhaustedException or TimeoutException when the Producer&#39;s internal buffer is full, and InterruptException when the sending thread is interrupted.Finally, one of the key characteristics of KafkaProducer is that it is thread-safe. A single Producer instance can be safely shared by multiple threads concurrently, eliminating the need to create a separate Producer for each thread. This design reduces resource consumption, improves performance, and enables more efficient utilization of the connections established with the Kafka cluster.Configuring ProducersSo far we’ve seen very few configuration parameters for the producers—just the mandatory bootstrap.servers URI and serializers.The producer has a large number of configuration parameters documented in Apache Kafka documentation, and many have reasonable defaults, so there is no need to tune everything. However, some parameters significantly impact memory usage, performance, and reliability, and these are the most important ones.client.idclient.id is a logical identifier for the client and the application it is used in. It is used by brokers for logging, metrics, and quota tracking.A meaningful client.id greatly improves debugging and operational visibility, making it easier to identify which service is generating traffic or errors instead of relying on raw IP addresses.acksThe acks parameter controls how many replicas must acknowledge a write before it is considered successful. It directly affects durability and reliability.acks=0The producer does not wait for any response. This gives maximum throughput but no guarantee of delivery. Messages can be lost without detection.acks=1The producer waits for the leader to acknowledge the message. This provides a balance between performance and reliability, but data can still be lost if the leader fails before replication.acks=allThe producer waits until all in-sync replicas acknowledge the message. This provides the highest durability guarantee but increases latency.TIP:Lower ack values improve throughput but reduce reliability. However, end-to-end latency remains the same across all modes because Kafka only exposes messages to consumers after replication to all in-sync replicas is complete. Therefore, for end-to-end latency, there is no real trade-off—only for producer latency and durability.Message Delivery TimeMessage delivery time refers to how long a send() call takes to either succeed or fail. This includes waiting for buffer space, sending data, and receiving broker response.Since Kafka 2.1, delivery time is split into two parts:Time spent inside send() before it returns (blocking time)Time from message batching until callback execution (success or failure)If send() is synchronous, both phases are combined into one blocking period.max.block.msThis parameter defines how long the producer can block when calling send() or requesting metadata.It applies when:The send buffer is fullMetadata is unavailableIf exceeded, a timeout exception is thrown.delivery.timeout.msThis is the total time allowed for a message from the moment it is ready to be sent until final success or failure, including retries.Must be greater than linger.ms and request.timeout.msIf exceeded during retries, callback returns the last broker errorIf exceeded before sending, callback returns a timeout errorTIP:A practical approach is to set delivery.timeout.ms to a fixed upper bound (e.g., a few minutes) and allow retries to continue within that window. This avoids complex retry tuning and ensures Kafka retries automatically until success or timeout.request.timeout.msThis parameter controls how long the producer waits for a response from the broker for each individual request. It applies per request only and does not include retries or time spent waiting before sending.If the broker does not respond within this time, the request is considered failed. At that point, the producer may either retry the request (if retries are enabled) or fail the operation and trigger a TimeoutException in the callback.retry.backoff.msThe retries parameter defines how many times the producer will retry sending a message when it receives a retryable (transient) error from the broker, such as missing leader or temporary unavailability of a partition.Between each retry attempt, the producer waits for the duration defined by retry.backoff.ms (default is 100 ms). This prevents tight retry loops and gives the cluster time to recover.However, modern Kafka usage generally discourages fine-tuning retries manually. Instead, it is recommended to rely on delivery.timeout.ms as the overall control mechanism for retry duration. The idea is to configure a total time window large enough to survive typical broker failures (for example, leader election delays), and let Kafka handle retries automatically within that window.Not all errors are retriable. Some errors, such as “message too large,” are permanent and will immediately fail without retries. Because retries are handled internally by the producer, application-level retry logic is usually unnecessary and should only focus on non-retriable or exhausted-retry cases.TIP:Setting retries=0 completely disables retry behavior.linger.mslinger.ms controls how long the producer waits to accumulate additional messages before sending a batch.By default, Kafka sends a batch immediately when a sender thread is available, even if the batch contains only one message. However, setting linger.ms to a value greater than zero allows the producer to wait briefly to collect more messages into the same batch before sending.This slightly increases latency but significantly improves throughput because:batching reduces per-message overheadcompression becomes more efficientbuffer.memoryThis parameter defines the total memory available for buffering records waiting to be sent to the broker.If the application produces messages faster than Kafka can send them, the buffer fills up. When this happens, calls to send() will block for up to max.block.ms while waiting for buffer space to free up. If space is still unavailable after this period, a timeout exception is thrown.Unlike most producer errors, this timeout occurs directly in send() rather than in the returned Future, making it a blocking-level failure rather than an async callback failure.compression.typeBy default, Kafka sends messages uncompressed. This parameter enables compression before data is sent to brokers, using algorithms such as snappy, gzip, lz4, or zstd.snappy: Designed by Google, offers good balance between CPU usage and compression speed; recommended when both performance and bandwidth matter.gzip: Provides better compression ratios but uses more CPU and is slower; useful when network bandwidth is limited.lz4 / zstd: Typically offer strong performance and good compression efficiency depending on workload.Compression reduces network usage and storage cost, which are often major bottlenecks in Kafka systems.batch.sizeThis parameter defines the maximum memory (in bytes) allocated for a batch of messages per partition.When multiple records are sent to the same partition, they are grouped into batches. Once a batch is full, it is sent immediately. However, Kafka does not wait for batches to fill completely—partial batches or even single-message batches can still be sent.A larger batch size does not introduce delay but increases memory usage and can improve throughput due to better batching and compression. A smaller batch size reduces memory usage but increases overhead due to more frequent sends.max.in.flight.requests.per.connectionThis controls how many request batches can be sent to the broker without waiting for responses.Higher values improve throughput but increase memory usage and can introduce ordering risks when retries are enabled.Kafka preserves message order within a partition, but with retries enabled and multiple in-flight requests, ordering can be broken if an earlier batch fails and is retried after a later batch has already been successfully written.To balance performance and safety, Kafka recommends enabling:enable.idempotence=trueThis ensures:Message ordering is preserved (even with retries)No duplicate messages are introducedSafe operation with up to multiple in-flight requests (typically up to 5)Ordering GuaranteesKafka guarantees ordering within a partition: messages are written and read in the same order they are sent.However, ordering can break in certain cases when:retries are enabledmax.in.flight.requests.per.connection is greater than 1In such cases, a later batch may succeed before an earlier failed batch is retried, causing reordering.The recommended solution is enabling idempotent producer mode, which preserves ordering and prevents duplicates while still allowing retries and multiple in-flight requests for performance.max.request.sizeThis parameter defines the maximum size of a single produce request sent to the broker. It limits:the maximum size of an individual messagethe total batch size of messages in one requestFor example, with a 1 MB limit:one 1 MB message can be sent, or1024 messages of 1 KB each can be batchedThe broker also has its own limit (message.max.bytes). These should be aligned to avoid producing messages that will be rejected by the broker.receive.buffer.bytes and send.buffer.bytesThese parameters control the size of the TCP socket buffers used for receiving and sending data between the Kafka client and brokers.send.buffer.bytes: size of the TCP send bufferreceive.buffer.bytes: size of the TCP receive bufferIf set to -1, the operating system default values are usedIncreasing these values is beneficial when clients communicate with brokers over high-latency or low-bandwidth networks, such as across different datacenters, because larger buffers help smooth network transmission and improve throughput.enable.idempotenceStarting from Kafka 0.11, Kafka supports idempotent producers, which are a key part of achieving exactly-once semantics (though full exactly-once behavior is broader and more complex).When a producer is configured for maximum reliability (for example, acks=all with retries enabled and a sufficiently large delivery.timeout.ms), Kafka guarantees that messages are delivered at least once. However, in failure scenarios, this can lead to duplicate messages.For example, if a broker successfully writes a message and replicates it, but crashes before sending an acknowledgment, the producer will retry. The retry may land on a new leader that already has the message, resulting in a duplicate.To solve this, setting enable.idempotence=true ensures that Kafka eliminates duplicates. The producer attaches a sequence number to each message, and brokers detect duplicate sequences and discard them. In this case, duplicates do not reach the log, and the producer receives a harmless DuplicateSequenceException.Requirements for idempotenceEnabling idempotence imposes strict configuration constraints to ensure correctness:acks must be set to allretries must be greater than 0max.in.flight.requests.per.connection must be ≤ 5If these conditions are not met, Kafka will throw a ConfigException at startup.Idempotent producer is a lightweight but very important mechanism because it allows Kafka to safely handle retries while preserving correctness, preventing duplicates without requiring application-level logic.Kafka Serialization with Custom Serializers and Apache AvroEvery Kafka producer must be configured with serializers to convert Java objects into byte arrays before sending them to Kafka. Kafka provides built-in serializers for common data types such as String, Integer, and ByteArray; however, real-world applications typically exchange complex business objects. In these scenarios, developers must either implement a custom serializer or use a standard serialization framework such as Apache Avro, Protocol Buffers (Protobuf), or Apache Thrift. Among these options, Apache Avro is generally recommended because it simplifies schema management and provides excellent support for schema evolution.Custom SerializersA custom serializer is required when an application needs to serialize domain-specific objects that are not supported by Kafka&#39;s built-in serializers. A serializer implements Kafka&#39;s Serializer&lt;T&gt; interface and is responsible for converting an object into a sequence of bytes.For example, a Customer object can be serialized by manually writing each field into a ByteBuffer. The serializer converts the customer identifier, the length of the customer&#39;s name, and finally the UTF-8 encoded name into a byte array that Kafka can transmit.Once configured in the producer, the custom serializer allows applications to publish records using strongly typed objects such as:ProducerRecord&lt;String, Customer&gt;instead of manually converting objects before sending them.Limitations of Custom SerializersAlthough custom serializers provide full control over the serialization process, they introduce several long-term maintenance challenges.The primary issue is schema evolution. Any modification to the object structure—such as changing a field type from int to long or introducing a new field—can break compatibility between producers and consumers using different versions of the serializer.Debugging these compatibility problems is also difficult because developers often have to inspect raw byte arrays to determine how objects were serialized.In distributed environments, the problem becomes even more significant. If multiple teams produce the same type of data, they must all use identical serialization logic and coordinate serializer updates simultaneously. This creates operational complexity and increases the risk of incompatible deployments.For these reasons, Kafka documentation recommends using established serialization frameworks such as Apache Avro, JSON, Protocol Buffers, or Thrift instead of implementing custom serializers.Apache AvroApache Avro is a language-independent serialization framework designed for efficient data exchange. Instead of relying on Java classes alone, Avro defines the structure of data using a JSON schema.Data is typically serialized into a compact binary format, although JSON serialization is also supported. During serialization and deserialization, both the producer and consumer rely on the schema to correctly interpret the binary data.One of Avro&#39;s greatest advantages is its support for schema evolution, making it particularly suitable for messaging systems such as Kafka.Schema EvolutionSchema evolution allows applications to change the structure of their data without breaking existing producers or consumers.For example, an initial Customer schema may contain the fields:idnamefaxNumberLater, the application may replace faxNumber with a new optional field named email.Because Avro resolves differences between compatible schemas, older consumers can continue reading new messages, while newer consumers can still process older records.If an older application attempts to read a message containing the new schema, the missing faxNumber simply returns null.Likewise, if a newer application reads an older message, the missing email field also resolves to null.This behavior enables gradual system upgrades without requiring all applications to be updated simultaneously or existing Kafka data to be rewritten.Requirements for Schema CompatibilitySchema evolution is only possible when the new schema remains compatible with the previous version.Avro defines explicit compatibility rules that determine whether schema changes are considered backward or forward compatible.In addition, the consumer must always have access to the writer&#39;s schema, even if it differs from the schema currently used by the application. Without access to the original schema, correct deserialization cannot be guaranteed.Schema RegistryUnlike Avro data files, Kafka messages should not include the complete schema within every record because doing so would significantly increase message size.Instead, Kafka applications typically use a Schema Registry.The Schema Registry stores every schema used by producers and assigns a unique identifier to each version.When a producer publishes a message, it stores only the schema identifier inside the Kafka record rather than embedding the full schema.During deserialization, the consumer retrieves the corresponding schema from the Schema Registry using this identifier and reconstructs the original object.This approach dramatically reduces message size while maintaining support for schema evolution.Although Schema Registry is not part of Apache Kafka itself, the Confluent Schema Registry is the most widely adopted implementation.Producing Avro RecordsKafka producers serialize Avro objects using the KafkaAvroSerializer.The producer configuration specifies:Kafka bootstrap serversKafkaAvroSerializer for both keys and valuesThe URL of the Schema RegistryOnce configured, producers can send Avro-generated objects exactly like any other Kafka record. The serializer automatically registers new schemas when necessary, stores the schema identifier in each record, and serializes the object into Avro binary format.This automation removes the need for developers to manually manage serialization or schema storage.Avro Generated ClassesAvro-generated classes are not ordinary Java POJOs.Instead, they are specialized classes generated directly from an Avro schema using tools such as:avro-tools.jarAvro Maven PluginThe KafkaAvroSerializer only supports these generated Avro classes and cannot serialize arbitrary Java objects.Applications therefore generate Java classes from their Avro schemas during the build process before producing Kafka messages.Generic Avro RecordsBesides generated classes, Avro also supports GenericRecord, which represents data as a flexible key-value structure.Unlike generated classes, GenericRecord requires the schema to be provided explicitly at runtime.The application creates the schema, constructs a GenericRecord, populates its fields, and sends it through the same KafkaAvroSerializer.The serializer extracts the schema from the GenericRecord, registers it if necessary, stores the schema identifier in Kafka, and serializes the associated data.GenericRecord is especially useful when schemas are dynamic or generated at runtime and Java code generation is undesirable.Kafka Partitions, Partitioning Strategies, and Record HeadersPartitions in KafkaEvery Kafka record consists of a topic, a key, and a value. While the key is optional, it plays a crucial role in determining which partition a record is written to. In addition to serving as business metadata, the record key is primarily used by Kafka&#39;s partitioning mechanism to ensure consistent data placement.When records share the same key, Kafka guarantees that they are always written to the same partition (as long as the partition count remains unchanged). This behavior ensures that all events related to a specific entity, such as a customer or user, are processed in order by the same consumer, simplifying stateful processing and maintaining event ordering.Message Distribution Without a KeyIf a producer sends records without specifying a key, the default Kafka partitioner distributes messages across the available partitions.Beginning with Apache Kafka 2.4, the default partitioner uses a Sticky Round-Robin strategy. Instead of sending every consecutive message to a different partition, Kafka temporarily selects one partition and fills an entire batch before moving to another partition.This optimization significantly reduces the number of producer requests, decreases network overhead, lowers broker CPU utilization, and improves overall throughput while still maintaining an even distribution of records.Message Distribution With a KeyWhen a key is present, Kafka uses its internal hashing algorithm to calculate the destination partition.The hashing algorithm is deterministic, meaning that the same key will always produce the same partition assignment. Kafka uses its own implementation of the Murmur2 hash algorithm to guarantee consistent partition mapping across different Java versions.This deterministic mapping allows applications to rely on partition-level ordering, ensuring that all events associated with a particular key are processed sequentially.Alternative Partitioning StrategiesBesides the default partitioner, Kafka provides additional partitioning strategies to support different workload characteristics.The RoundRobinPartitioner distributes records randomly across all partitions, regardless of whether a key exists.The UniformStickyPartitioner combines sticky batching with uniform workload distribution. Even when messages contain keys, this partitioner attempts to spread traffic evenly across partitions, preventing workload imbalance caused by highly active keys.These partitioners are particularly useful in applications where the record key is required only as metadata—for example, when ETL pipelines use Kafka record keys as primary keys while loading data into relational databases—but message ordering by key is not required.Effect of Changing the Number of PartitionsThe relationship between a key and its partition remains stable only while the number of partitions in the topic remains unchanged.If additional partitions are added later, Kafka recalculates the hash mapping. As a result, new records for the same key may be written to different partitions, while historical records remain in their original partitions.This breaks the guarantee that all records for a particular key reside in the same partition.For applications that rely on partition affinity or strict ordering, it is recommended to estimate future scalability requirements, create topics with a sufficient number of partitions from the beginning, and avoid increasing the partition count after production deployment.Custom Partitioning StrategyKafka allows developers to implement custom partitioning logic by creating a class that implements the Partitioner interface.A custom partitioner can route records according to business requirements instead of relying solely on hash-based partitioning.For example, if one customer generates a disproportionately large percentage of system traffic, assigning all of its records to a dedicated partition can prevent workload imbalance across the cluster.The remaining customers can continue using hash-based partitioning across the other partitions.A custom partitioner typically overrides the following methods:configure() for initialization and configuration.partition() to determine the destination partition for each record.close() for cleanup when the producer shuts down.The partition() method contains the core routing logic and can validate record keys, apply business-specific rules, or perform any desired partition selection algorithm.Benefits of Custom PartitioningCustom partitioning provides greater flexibility for optimizing data distribution based on business requirements.Common use cases include:Isolating high-volume customers or tenants.Balancing uneven workloads across partitions.Directing records to dedicated partitions for performance or compliance reasons.Implementing domain-specific routing strategies that cannot be achieved through simple hash partitioning.Although powerful, custom partitioners should be carefully designed because they directly affect data distribution, consumer parallelism, and system scalability.Kafka Record HeadersIn addition to the key and value, Kafka records can include headers.Headers provide a mechanism for attaching metadata to a record without modifying the message payload. This allows infrastructure components and applications to exchange contextual information independently of the business data.A Kafka header consists of an ordered collection of key-value pairs. Header keys are always strings, while header values are stored as serialized byte arrays, allowing virtually any type of information to be attached.Common Uses of HeadersHeaders are commonly used to transport technical metadata rather than business data.Typical use cases include:Recording the origin or lineage of a message.Identifying the producing application or service.Carrying routing information.Supporting distributed tracing and correlation IDs.Storing security or privacy classifications.Passing versioning or processing metadata between services.Because consumers and middleware can inspect headers without parsing the message payload, headers improve routing efficiency and are especially useful when message bodies are encrypted or encoded in binary formats.Adding Headers to a RecordKafka provides a simple API for attaching headers to a ProducerRecord.Headers can be added before sending the record by specifying a header name and its serialized value.For example, a producer may attach a header such as privacy-level to indicate the classification of the data without modifying the record key or value.This separation of metadata from business data results in cleaner message design and enables middleware, gateways, and consumers to make routing or processing decisions based solely on header information.Kafka Producer Interceptors, Quotas, and ThrottlingProducer InterceptorsKafka provides Producer Interceptors as a mechanism for extending or modifying producer behavior without changing the application&#39;s source code. This feature is particularly useful when organizations need to apply common functionality across multiple producer applications or when the original application code cannot be modified.Producer interceptors execute automatically during the producer lifecycle and allow developers to inspect, enrich, monitor, or transform records before they are sent to Kafka, as well as observe the broker&#39;s response after the records have been acknowledged.ProducerInterceptor InterfaceThe ProducerInterceptor interface defines four primary methods that control the interceptor lifecycle:configure() – Initializes the interceptor using the producer configuration before any records are processed.onSend() – Invoked immediately before a record is serialized and sent to Kafka. The interceptor can inspect or modify the ProducerRecord and must return the record that will actually be transmitted.onAcknowledgement() – Called after Kafka acknowledges a produced record. This method provides access to the metadata or any exception that occurred during delivery but does not allow modifying the broker&#39;s response.close() – Executed when the producer shuts down, allowing the interceptor to release resources such as threads, network connections, or file handles.These methods provide well-defined extension points for implementing custom producer behavior.Common Use Cases for Producer InterceptorsProducer interceptors are commonly used to implement cross-cutting concerns that should be applied consistently across producer applications.Typical use cases include:Collecting monitoring and performance metrics.Implementing distributed tracing.Automatically adding standard record headers.Recording data lineage information.Masking or removing sensitive information before transmission.Logging producer activity for auditing purposes.Because interceptors operate transparently, these capabilities can be introduced without modifying the business logic of existing producer applications.Example: Message Counting InterceptorA simple example of a producer interceptor counts the number of records sent and the number of acknowledgments received during fixed time intervals.When a producer sends a record, the onSend() method increments a message counter before returning the original record unchanged.Whenever Kafka acknowledges a record, the onAcknowledgement() method increments a separate acknowledgment counter.A scheduled task periodically prints both counters, allowing developers or operators to monitor producer activity in real time.This example demonstrates that interceptors can collect operational metrics without affecting normal message production.Configuring Producer InterceptorsProducer interceptors are configured entirely through producer configuration and do not require changes to application code.To enable an interceptor:Add the interceptor implementation JAR to the application&#39;s classpath.Specify the interceptor class using the interceptor.classes configuration property.Provide any custom configuration parameters required by the interceptor.Because interceptors are configured externally, the same interceptor implementation can easily be deployed across multiple producer applications, including Kafka&#39;s command-line producer tools.Kafka QuotasKafka brokers support quotas, which limit the amount of resources individual clients are allowed to consume. Quotas protect the cluster from overloaded or misbehaving clients and help ensure fair resource allocation among multiple applications.Kafka supports three types of quotas:Produce Quotas – Limit the rate at which producers can send data, measured in bytes per second.Consume Quotas – Limit the rate at which consumers can receive data.Request Quotas – Limit the percentage of broker processing time that a client may consume.These quotas improve cluster stability by preventing a single client from monopolizing broker resources.Applying QuotasQuotas can be configured at multiple levels depending on administrative requirements.Kafka supports quotas for:All clients (default quotas).Individual client IDs.Individual authenticated users.Specific combinations of users and client IDs.Default quotas are typically defined in the broker configuration file and apply to every client unless overridden.For more flexible administration, Kafka also supports dynamic quota configuration, allowing administrators to modify quotas without restarting brokers. Dynamic quotas can be managed using the kafka-configs command-line utility or the Kafka AdminClient API.This approach enables administrators to quickly adjust client limits as workloads change.ThrottlingWhen a client exceeds its assigned quota, Kafka automatically applies throttling.Rather than rejecting requests immediately, the broker intentionally delays responses to slow the client&#39;s effective throughput. Since Kafka clients maintain only a limited number of concurrent requests, these delays naturally reduce the rate at which new requests are sent.If a client continues sending requests aggressively while being throttled, Kafka may temporarily mute communication with that client until its traffic returns to the permitted rate.This mechanism protects broker resources while avoiding unnecessary request failures.Monitoring ThrottlingKafka exposes several metrics that allow administrators to monitor throttling behavior.Examples include:produce-throttle-time-avgproduce-throttle-time-maxfetch-throttle-time-avgfetch-throttle-time-maxThese metrics report the average and maximum amount of time producer or consumer requests were delayed due to quota enforcement.Monitoring these values helps identify clients that consistently exceed configured quotas and may require optimization or quota adjustments.Producer Buffering and Timeout BehaviorWhen producers use asynchronous send() operations, messages are initially stored in the producer&#39;s internal memory buffer before being transmitted to Kafka.If producers generate messages faster than brokers can accept them—whether due to broker capacity limits or enforced quotas—the buffered messages begin to accumulate in memory.As long as buffer space remains available, the producer continues accepting new messages. However, if the sending rate continues to exceed the broker&#39;s processing capacity, the producer eventually exhausts its available buffer space.At that point, subsequent send() operations may block until buffer space becomes available. If the waiting time exceeds the configured timeout, Kafka throws a TimeoutException.Additionally, records already waiting inside producer batches may exceed the configured delivery.timeout.ms value before reaching the broker. These expired records are discarded, and their callbacks are invoked with a TimeoutException.For this reason, production systems should continuously monitor producer throughput, broker capacity, buffer utilization, and timeout metrics to ensure that Kafka infrastructure can sustain the expected workload without causing excessive buffering or message expiration.Best PracticesTo ensure reliable producer performance:Use producer interceptors for monitoring, tracing, and metadata enrichment instead of modifying application code.Configure quotas to protect shared Kafka clusters from resource abuse.Prefer dynamic quotas over static broker configuration whenever possible.Continuously monitor throttling metrics to identify overloaded clients.Ensure broker throughput matches producer traffic to prevent excessive buffering and timeout exceptions.Tune producer timeout and buffer configurations according to expected workload characteristics.</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Sun, 12 Jul 2026 22:31:46 +0330</pubDate></item><item><title>Kafka : The Definitive Guide</title><link>https://virgool.ir/@farhadnosrati/بررسی-عمیق-داخلیات-broker-در-apache-kafka-d6ssajw0l84h</link><description>Messages and Batches A message is simply an array of bytes as far as Kafka is concerned, so the data contained within it does not have a specific format or meaning to Kafka. A message can have an optional piece of metadata, which is referred to as a key. The key is also a byte array and, as with the message, has no specific meaning to Kafka. Keys are used when messages are to be written to partitions in a more controlled manner. The simplest such scheme is to generate a consistent hash of the key and then select the partition number for that message by taking the result of the hash modulo the total number of partitions in the topic. This ensures that messages with the same key are always written to the same partition (provided that the partition count does not change). For efficiency, messages are written into Kafka in batches. A batch is just a collection of messages, all of which are being produced to the same topic and partition . Of course, this is a trade-off between latency and throughput: the larger the batches, the more messages that can be handled per unit of time, but the longer it takes an individual message to propagate. Batches are also typically compressed, providing more efficient data transfer and storage at the cost of some processing power. SchemasWhile messages are opaque byte arrays to Kafka itself, it is recommended that additional structure, or schema, be imposed on the message content so that it can be easily understood. There are many options available for message schema, depending on your application’s individual needs. Simplistic systems, such as JavaScript Object Notation (JSON) and Extensible Markup Language (XML), are easy to use and human readable. However, they lack features such as robust type handling and compatibility between schema versions. Many Kafka developers favor the use of Apache Avro, which is a serialization framework originally developed for Hadoop. Avro provides a compact serialization format, schemas that are separate from the message payloads and that do not require code to be generated when they change, and strong data typing and schema evolution, with both backward and forward compatibility.A consistent data format is important in Kafka, as it allows writing and reading messages to be decoupled. When these tasks are tightly coupled, applications that subscribe to messages must be updated to handle the new data format, in parallel with the old format. Only then can the applications that publish the messages be updated to utilize the new format. By using well-defined schemas and storing them in a common repository, the messages in Kafka can be understood without coordination. Topics and Partitions Messages in Kafka are categorized into topics. The closest analogies for a topic are a database table or a folder in a filesystem. Topics are additionally broken down into a number of partitions. Going back to the “commit log” description, a partition is a single log. Messages are written to it in an append-only fashion and are read in order from beginning to end. Note that as a topic typically has multiple partitions, there is no guarantee of message ordering across the entire topic, just within a single partition. Figure 1-5 shows a topic with four partitions, with writes being appended to the end of each one. Partitions are also the way that Kafka provides redundancy and scalability. Each partition can be hosted on a different server, which means that a single topic can be scaled horizontally across multiple servers to provide performance far beyond the ability of a single server. Additionally, partitions can be replicated, such that different servers will store a copy of the same partition in case one server fails. The term stream is often used when discussing data within systems like Kafka. Most often, a stream is considered to be a single topic of data, regardless of the number of partitions. This represents a single stream of data moving from the producers to the consumers. This way of referring to messages is most common when discussing stream processing, which is when frameworks—some of which are Kafka Streams, Apache Samza, and Storm—operate on the messages in real time. This method of operation can be compared to the way offline frameworks, namely Hadoop, are designed to work on bulk data at a later time.Producers and Consumers Kafka clients are users of the system, and there are two basic types: producers and consumers. There are also advanced client APIs—Kafka Connect API for data integration and Kafka Streams for stream processing. The advanced clients use producers and consumers as building blocks and provide higher-level functionality on top. Producers create new messages : In other publish/subscribe systems, these may be called publishers or writers. A message will be produced to a specific topic. By default, the producer will balance messages over all partitions of a topic evenly. In some cases, the producer will direct messages to specific partitions. This is typically done using the message key and a partitioner that will generate a hash of the key and map it to a specific partition. This ensures that all messages produced with a given key will get written to the same partition. The producer could also use a custom partitioner that follows other business rules for mapping messages to partitions. Consumers read messages : In other publish/subscribe systems, these clients may be called subscribers or readers. The consumer subscribes to one or more topics and reads the messages in the order in which they were produced to each partition. The consumer keeps track of which messages it has already consumed by keeping track of the offset of messages. The offset—an integer value that continually increases—is another piece of metadata that Kafka adds to each message as it is produced. Each message in a given partition has a unique offset, and the following message has a greater offset (though not necessarily monotonically greater). By storing the next possible offset for each partition, typically in Kafka itself, a consumer can stop and restart without losing its place. Consumers work as part of a consumer group, which is one or more consumers that work together to consume a topic. The group ensures that each partition is only consumed by one member. In Figure 1-6, there are three consumers in a single group consuming a topic. Two of the consumers are working from one partition each, while the third consumer is working from two partitions. The mapping of a consumer to a partition is often called ownership of the partition by the consumer. In this way, consumers can horizontally scale to consume topics with a large number of messages. Additionally, if a single consumer fails, the remaining members of the group will reassign the partitions being consumed to take over for the missing member.Brokers and Clusters A single Kafka server is called a broker. The broker receives messages from producers, assigns offsets to them, and writes the messages to storage on disk. It also services consumers, responding to fetch requests for partitions and responding with the messages that have been published. Depending on the specific hardware and its performance characteristics, a single broker can easily handle thousands of partitions and millions of messages per second. Kafka brokers are designed to operate as part of a cluster. Within a cluster of brokers, one broker will also function as the cluster controller (elected automatically from the live members of the cluster). The controller is responsible for administrative operations, including assigning partitions to brokers and monitoring for broker failures. A partition is owned by a single broker in the cluster, and that broker is called the leader of the partition. A replicated partition (as seen in Figure 1-7) is assigned to additional brokers, called followers of the partition. Replication provides redundancy of messages in the partition, such that one of the followers can take over leadership if there is a broker failure. All producers must connect to the leader in order to publish messages, but consumers may fetch from either the leader or one of the followers. A key feature of Apache Kafka is that of retention, which is the durable storage of messages for some period of time. Kafka brokers are configured with a default retention setting for topics, either retaining messages for some period of time (e.g., 7 days) or until the partition reaches a certain size in bytes (e.g., 1 GB). Once these limits are reached, messages are expired and deleted. In this way, the retention configuration defines a minimum amount of data available at any time. Individual topics can also be configured with their own retention settings so that messages are stored for only as long as they are useful. For example, a tracking topic might be retained for several days, whereas application metrics might be retained for only a few hours. Topics can also be configured as log compacted, which means that Kafka will retain only the last message produced with a specific key. This can be useful for changelog-type data, where only the last update is interesting. Multiple Clusters As Kafka deployments grow, using multiple clusters can become beneficial. There are several reasons for having multiple Kafka clusters:Segregation of different types of dataIsolation for security requirementsSupport for multiple datacenters and disaster recoveryWhen multiple datacenters are used, messages often need to be copied between clusters. This allows applications in different locations to access the same data.For example, if a user updates public information in their profile, that change should be available regardless of which datacenter handles the search request. Similarly, monitoring data from multiple locations can be collected into a central cluster where analysis and alerting systems run.Kafka’s built-in replication mechanism only works within a single cluster and does not replicate data between different clusters.To solve this problem, Kafka provides a tool called MirrorMaker. MirrorMaker is used to replicate messages between Kafka clusters. It works by acting as both a Kafka consumer and a Kafka producer connected through a queue:It consumes messages from one Kafka cluster.It produces those messages to another Kafka cluster.MirrorMaker can create advanced data pipelines, such as collecting data from multiple local clusters into a central cluster and then replicating that data to other datacenters.Although its design is simple, MirrorMaker enables powerful architectures for multi-cluster Kafka environments.Multiple ProducersKafka can handle multiple producers easily, whether they are producing messages to different topics or the same topic.This makes Kafka useful for collecting data from many frontend systems and standardizing it into a consistent format.For example, a website built with multiple microservices can use a single topic for page view events. All services can write their page view data to this topic using the same format.Consumer applications can then read one unified stream of page views from all services instead of managing multiple topics, one for each application.Multiple ConsumersKafka is also designed to support multiple consumers reading the same stream of messages without affecting each other.This is different from traditional messaging systems, where once a message is consumed by one client, it is usually unavailable to other clients.In Kafka, multiple consumers can independently read the same messages. They can also work together as part of a consumer group, where the messages are distributed among the group members, ensuring that each message is processed only once by the entire group.The Data EcosystemMany applications participate in data processing environments. Some applications create data or introduce it into the system as inputs, while others produce outputs such as metrics, reports, and other data products.Data flows in loops where some components read data, transform it using information from other sources, and send it back into the data infrastructure for further use. Different types of data have different characteristics in terms of content, size, and usage.Apache Kafka acts as the circulatory system of the data ecosystem. It transfers messages between different parts of the infrastructure and provides a consistent interface for clients. When combined with a message schema system, producers and consumers no longer need tight coupling or direct connections. Components can be added or removed easily, and producers do not need to know who consumes the data or how many consumers exist.Use CasesActivity TrackingKafka was originally designed at LinkedIn for user activity tracking. Users interact with frontend applications, which generate messages about their actions, such as page views, clicks, or profile updates.These messages are published to one or more topics and consumed by backend applications. These applications can generate reports, feed machine learning systems, update search results, or perform other operations required to improve user experience.MessagingKafka is also used for messaging systems where applications need to send notifications such as emails.Applications can produce messages without worrying about message formatting or delivery details. A centralized application can consume these messages and handle them consistently by:Formatting messages with a common style and structureCombining multiple messages into a single notificationApplying user preferences for notification deliveryThis approach avoids duplicating functionality across multiple applications and enables operations such as aggregation.Metrics and LoggingKafka is suitable for collecting application metrics and logs.Applications can publish metrics regularly to Kafka topics, and monitoring or alerting systems can consume them. These metrics can also be stored in offline systems such as Hadoop for long-term analysis, such as growth prediction.Logs can be published in the same way and routed to systems like Elasticsearch or security analysis platforms. Kafka also allows destination systems to change without modifying frontend applications or aggregation logic.Commit LogKafka is based on the concept of a commit log, allowing database changes to be published as streams of events.Applications can monitor these streams to receive real-time updates. The changelog stream can also be used for:Replicating database updates to remote systemsCombining changes from multiple applications into a single database viewDurable retention allows changelog data to be stored and replayed if consumer applications fail. Log-compacted topics can provide longer retention by keeping only the latest change for each key.Stream ProcessingStream processing is another major Kafka use case. Although many Kafka operations can be considered stream processing, the term usually refers to applications that provide functionality similar to Hadoop MapReduce.Unlike Hadoop, which typically processes aggregated data over long periods such as hours or days, stream processing works on data in real time as messages are produced.Stream processing frameworks allow developers to create applications that process Kafka messages by performing tasks such as:Counting metricsPartitioning messages for efficient processingTransforming messages using data from multiple sourcesStream processing enables real-time data processing and analysis using Kafka.ZooKeeper Apache Kafka uses Apache ZooKeeper to store metadata about the Kafka cluster, as well as consumer client details, as shown in Figure 2-1. ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. KRaft : Kafka-RaftThe Controller QuorumKRaft Controller nodes form a Raft Quorum responsible for managing the Kafka Metadata Log. This log maintains all cluster metadata changes such as Topic, Partition, ISR, and Configuration.Using Raft, controllers handleLeader Election and Consistency without requiring an external system. The metadata leader is called the Active Controller and processes all RPC requests from Brokers. Other controllers act as Follower, replicating data and serving as Hot Standby ready to take over in case the Active Controller fails.Brokers track the latest metadata version using Offset, enabling faster metadata propagation and quicker Controller Failover.KRaft requires a Majority of controllers to continue operating; for example, a 3-controller cluster tolerates one failure, while a 5-controller cluster tolerates two failures.Controllers periodically store a Snapsho of metadata on disk, which is conceptually similar to Log Compaction, with the difference that information is read from memory.Scaling Kafka with KRaftThe maximum number of Partitions in a cluster depends on two factors:* The partition limit per Broker* The partition limit for the entire clusterKRaft is designed to manage a very large number of Partitions, but Kafka&#39;s scalability still increases with the addition of new Brokers; therefore, the overall cluster limit remains the ultimate scalability ceiling.Additionally, the Quorum Controller significantly reduces Controller Failover time, enabling near-instantaneous controller recovery. Confluent&#39;s tests on a cluster with 2 million Partitions have shown that controlled shutdown time and recovery after failure have improved significantly compared to ZooKeeper.Manage Topic Assignment to Offline BrokersIn KRaft, new Topics can be assigned to Brokers that are offline as well. This capability allows performing Rolling Upgrades or reducing the number of Brokers (e.g., from 4 to 3) without encountering issues.If an offline Broker should no longer be a member of the cluster, it must be removed (Unregistered) from the cluster metadata using the kafka-cluster unregister command. This command invokes the UnregisterBroker API and removes all information related to that Broker from the Cluster Metadata.To view Brokers that may need removal, you can also use the kafka-cluster list-endpoints command.Kafka Internals Cluster MembershipKafka uses Apache ZooKeeper to maintain the list of brokers that belong to a cluster. Each broker has a unique broker ID, configured manually or generated automatically. When a broker starts, it registers itself in ZooKeeper by creating an ephemeral node under /brokers/ids.Kafka brokers, the controller, and other Kafka tools monitor /brokers/ids to detect when brokers join or leave the cluster.If another broker starts with the same broker ID, registration fails because that ID already exists in ZooKeeper.If a broker loses its connection to ZooKeeper (due to shutdown, network partition, or a long garbage collection pause), its ephemeral node is automatically removed. All components watching ZooKeeper are notified that the broker has left the cluster.Although the ZooKeeper node disappears, the broker ID is still stored in Kafka metadata, such as the replica assignments of topics. Therefore, if a new broker starts with the same broker ID, it automatically replaces the old broker and takes ownership of the same partitions and replicas.The ControllerThe controller is a Kafka broker responsible for partition leader election in addition to normal broker tasks. The first broker that starts becomes the controller by creating the /controller ephemeral node in ZooKeeper. Other brokers detect that the node already exists, watch it for changes, and ensure that only one controller exists at a time.If the controller stops or loses its ZooKeeper connection (e.g., shutdown, network partition, or missing heartbeats for longer than zookeeper.session.timeout.ms), its ephemeral node is removed. Other brokers detect this through their ZooKeeper watch and compete to become the new controller. The first broker to create the /controller node becomes the new controller, while the others continue watching it.Each new controller receives a higher controller epoch. Brokers track the current epoch and ignore messages from controllers with an older epoch. This prevents zombie controllers—controllers that resume after a long GC pause—from making outdated cluster changes (zombie fencing).When a controller starts, it first loads the latest replica state from ZooKeeper before managing metadata or performing leader elections. This loading process uses asynchronous, pipelined ZooKeeper requests, but in clusters with many partitions it can still take several seconds.When the controller detects that a broker has left the cluster (through ZooKeeper notifications or a ControlledShutdownRequest), it:Identifies all partitions whose leader was on the failed broker.Selects a new leader (typically the next replica in the replica list).Persists the new leader information to ZooKeeper.Sends LeaderAndISR requests to brokers hosting replicas so new leaders start serving clients and followers begin replicating from the new leader.Sends UpdateMetadata requests to all brokers so they update their MetadataCache with the latest leader information.When a broker rejoins the cluster, its replicas initially become followers and must catch up with the current leader before they are eligible to become leaders.In summary, Kafka uses ZooKeeper ephemeral nodes to elect a single controller and detect broker membership changes. The controller manages leader elections, updates cluster metadata, and uses the controller epoch to prevent split-brain and zombie-controller scenarios.KRaft: Kafka’s New Raft-Based ControllerStarting in 2019, the Apache Kafka community began working on KRaft, a new controller architecture based on the Raft consensus algorithm to replace the existing ZooKeeper-based controller. KRaft was introduced as a preview feature in Kafka 2.8, and Kafka 3.0 included the first production version. This allowed Kafka clusters to run either with the traditional ZooKeeper-based controller or the new KRaft controller.The main reason for replacing the existing controller was scalability. Although Kafka’s controller had gone through several improvements, the ZooKeeper-based design could not scale well with the increasing number of partitions and brokers. Several problems motivated this change:Metadata changes were written synchronously to ZooKeeper but propagated asynchronously to brokers. Also, brokers received updates from ZooKeeper asynchronously, which could create situations where metadata became inconsistent between ZooKeeper, the controller, and brokers. These inconsistencies were difficult to detect and troubleshoot.Another major issue was controller restart time. Whenever the controller restarted, it had to reload all metadata about brokers, partitions, and replicas from ZooKeeper and then send this information to all brokers. As Kafka clusters grew, this process became a significant performance bottleneck.The metadata management architecture was also complicated because some operations were handled by the controller, some by brokers, and some directly through ZooKeeper. In addition, ZooKeeper is itself a distributed system, meaning Kafka operators had to manage and understand two separate distributed systems instead of only Kafka.In the existing architecture, ZooKeeper had two main responsibilities: electing the Kafka controller and storing cluster metadata such as brokers, topics, partitions, replicas, and configurations. The controller was responsible for managing this metadata, including leader election, topic creation and deletion, and replica reassignment. All these responsibilities needed to be redesigned in the new controller architecture.The main idea behind KRaft is based on Kafka’s existing log-based design. Kafka already represents user data as a stream of events, where consumers can rebuild the latest state by replaying events from the log. This approach provides a clear ordering of changes and ensures that all consumers follow the same timeline. KRaft applies the same idea to Kafka’s internal metadata management.In the KRaft architecture, controller nodes form a Raft quorum that manages a metadata event log. This log contains every change made to cluster metadata, including topics, partitions, replicas, ISRs, configurations, and other information previously stored in ZooKeeper.Using the Raft algorithm, controller nodes elect a leader among themselves without depending on an external system. The leader of the metadata log is called the active controller. It handles requests from brokers, while follower controllers replicate the metadata and act as hot standbys. Because all controllers maintain the latest metadata state, failover does not require a long process of reloading and distributing metadata.Instead of the controller pushing metadata updates to brokers, brokers fetch updates from the active controller using the new MetadataFetch API. Brokers keep track of the offset of the latest metadata change they received and only request newer updates. Brokers also persist metadata locally on disk, allowing them to restart quickly even in clusters with millions of partitions.In KRaft, brokers register with the controller quorum and remain registered until an administrator removes them. If a broker shuts down, it becomes offline but remains known to the cluster. Brokers that are running but have outdated metadata are fenced, meaning they cannot serve client requests. This prevents situations where clients send data to a broker that is no longer the leader but is unaware of the leadership change.During migration to KRaft, all operations that previously required clients or brokers to communicate directly with ZooKeeper are redirected through the controller. This allows Kafka to replace the ZooKeeper-based controller without requiring changes to brokers.The overall design of KRaft is described in KIP-500. The adaptation of the Raft protocol for Kafka is explained in KIP-595, and the detailed design of the controller quorum, configuration options, and new metadata management tools are covered in KIP-631.ReplicationReplication is a core part of Kafka’s architecture. Kafka is often described as a distributed, partitioned, replicated commit log service because replication is responsible for providing availability and durability when brokers fail.Kafka data is organized into topics. Each topic is divided into partitions, and each partition can have multiple replicas stored across different brokers. A broker usually stores hundreds or thousands of replicas from different topics and partitions.There are two main types of replicas:Leader replicaEach partition has one replica that acts as the leader. All produce requests are handled by the leader to maintain consistency. Consumers can read data from either the leader replica or follower replicas.Follower replicaAll replicas that are not leaders are called followers. By default, followers do not serve client requests. Their main responsibility is to replicate messages from the leader and remain up-to-date. If the leader fails, one of the follower replicas can be promoted to become the new leader.Read From FollowerThe ability to consume messages directly from follower replicas was introduced in KIP-392. The main purpose of this feature is to reduce network costs by allowing consumers to read from the nearest in-sync replica instead of always reading from the leader.To enable this feature, consumers need to configure client.rack to identify their location. Brokers need to configure replica.selector.class. By default, Kafka uses LeaderSelector, which always selects the leader replica, but it can be changed to RackAwareReplicaSelector, which selects a replica located on a broker with a matching rack ID. Kafka also allows custom replica selection logic by implementing the ReplicaSelector interface.The replication protocol was extended to ensure that only committed messages are available when consuming from follower replicas. This keeps the same reliability guarantees as consuming from the leader.To achieve this, the leader sends the current high-water mark (the latest committed offset) to followers. However, propagating the high-water mark introduces a small delay, meaning messages may become available for consumption from the leader slightly earlier than from followers. Therefore, reading from followers does not always reduce consumer latency.Keeping Followers in SyncThe leader is responsible for tracking which follower replicas are synchronized with it. Followers replicate messages continuously, but they can fall behind because of issues such as network congestion or broker failures.Followers stay synchronized by sending Fetch requests to the leader, similar to the requests consumers use to retrieve messages. These requests include the offset of the next message the follower wants to receive. Since requests are always ordered, the leader can determine how many messages each follower has received and how far behind each replica is.If a follower has not requested messages for more than 10 seconds, or if it has requested messages but has not caught up with the latest messages within that time, it is considered out of sync. A replica that falls behind cannot become the leader during a failure because it may not contain all messages.Replicas that continuously receive the latest messages are called in-sync replicas (ISR). Only replicas in the ISR set are eligible to become partition leaders when the current leader fails.The maximum allowed time that a follower can be inactive or behind is controlled by the replica.lag.time.max.ms configuration parameter. This setting affects client behavior and data availability during leader elections.Preferred LeaderBesides the current leader, each partition also has a preferred leader. The preferred leader is the replica that was originally selected as the leader when the topic was created.Kafka tries to distribute leaders evenly across brokers when partitions are created. If the preferred leader is active for all partitions, the workload should remain balanced between brokers.By default, Kafka enables auto.leader.rebalance.enable=true. This feature checks whether the preferred leader is not currently the leader but is still in sync. If so, Kafka triggers a leader election to move leadership back to the preferred leader.Finding Preferred LeadersThe easiest way to identify the preferred leader is by checking the replica list of a partition. The first replica in the list is always the preferred leader, regardless of which replica is currently the leader.This remains true even if replicas have been reassigned to different brokers using the replica reassignment tool. When manually reassigning replicas, the first replica specified becomes the preferred replica. Therefore, preferred replicas should be distributed carefully across brokers to prevent some brokers from handling too many leaders while others remain underutilized.Request ProcessingA Kafka broker mainly processes requests sent by clients, partition replicas, and the controller. Kafka uses a binary TCP protocol that defines request and response formats, including error handling. Kafka clients (Java, C, Python, Go, and others) communicate with brokers using this protocol.Clients always initiate connections and send requests, while brokers process and respond to them. Requests from the same client are handled in the order they are received, which allows Kafka to provide message ordering guarantees.Each request contains a standard header including the request type (API key), request version, correlation ID for tracking and troubleshooting, and client ID for identifying the application.For each broker port, an acceptor thread accepts connections and passes them to network threads (processor threads). Network threads receive requests, place them in the request queue, and send responses back to clients. Some responses may be delayed and are stored in a purgatory until they can be completed.Request handler threads (I/O threads) take requests from the queue and process them. The most common request types are:Produce requests: Sent by producers to write messages to Kafka.Fetch requests: Sent by consumers and follower replicas to read messages.Admin requests: Used for administrative operations such as creating or deleting topics.Produce and fetch requests must always be sent to the leader replica of a partition. If a request reaches a broker that is not the leader, Kafka returns a “Not a Leader for Partition” error. Kafka clients are responsible for finding the correct broker.Clients discover partition leaders by sending metadata requests. The response contains information about topics, partitions, replicas, and current leaders. Since all brokers have metadata caches, metadata requests can be sent to any broker.Clients cache metadata and use it to route produce and fetch requests to the correct brokers. Because metadata can change, clients periodically refresh it using the metadata.max.age.ms configuration. They also refresh metadata immediately after receiving a “Not a Leader” error, which indicates that their metadata is outdated.Produce RequestsWhen a producer sends a message to Kafka, the acks configuration determines how many brokers must confirm receiving the message before the write is considered successful. Producers can use different levels of acknowledgment:acks=0: The producer considers the message successful immediately without waiting for a broker response.acks=1: Only the leader replica needs to accept the message.acks=all: All in-sync replicas (ISR) must acknowledge the message.When the broker containing the leader replica receives a produce request, it performs several validations:Checks whether the producer has permission to write to the topic.Verifies that the acks value is valid.If acks=all, checks whether enough in-sync replicas exist to safely store the message.After validation, the broker writes the message to local storage. On Linux, data is usually written to the filesystem cache, and Kafka does not wait for it to be physically written to disk. Kafka relies mainly on replication to provide durability.After writing the message to the leader replica, Kafka checks the acks setting. If acks=0 or acks=1, the broker responds immediately. If acks=all, the request is stored in a buffer called purgatory until follower replicas replicate the message. Once replication is confirmed, the broker sends a response to the producer.Fetch RequestsFetch requests are used by consumers and follower replicas to read messages from Kafka. The client sends a request containing the topics, partitions, and offsets it wants to read, for example requesting messages starting from a specific offset in a partition.Clients also specify a maximum amount of data the broker can return. This prevents brokers from sending very large responses that could cause clients to run out of memory.Fetch requests must be sent to the leader replicas of the requested partitions. Clients use metadata requests to find the correct brokers.When the leader receives a fetch request, it first validates the requested offset. If the offset does not exist or the requested data has already been deleted, the broker returns an error. Otherwise, the broker reads messages up to the requested limit and sends them to the client.Kafka uses a zero-copy technique when sending messages. Instead of copying data through multiple memory buffers, Kafka sends data directly from the file or Linux filesystem cache to the network connection. This reduces memory overhead and improves performance.Clients can also specify a minimum amount of data they want to receive. For example, a consumer can request that the broker waits until at least 10 KB of data is available before responding. This reduces unnecessary requests, CPU usage, and network overhead for low-traffic topics.Consumers can also define a timeout. If the minimum amount of data is not available within the specified time, the broker returns whatever data is currently available.Not all messages stored on the leader are immediately available to consumers. Kafka only allows consumers to read messages that have been replicated to all in-sync replicas. Messages that exist only on the leader are considered unsafe because if the leader fails before replication, those messages could be lost.Waiting for replication prevents inconsistent behavior. For example, if a consumer reads a message and then the leader fails before the message is replicated, other consumers might never see that message. Therefore, Kafka waits until the message is safely replicated before allowing consumers to read it.This also means that slow replication between brokers can increase consumer latency because messages must be replicated before they become visible to consumers. The allowed replication delay is controlled by replica.lag.time.max.msFetch Session CacheWhen consumers read from many partitions, sending the complete list of partitions and metadata with every fetch request creates unnecessary overhead because partition information usually changes rarely.To reduce this overhead, Kafka provides a fetch session cache. Consumers can create a cached session that stores the partitions they consume from and their metadata. After creating a session, consumers can send incremental fetch requests instead of sending the full partition list every time.The broker only returns metadata changes instead of sending all metadata repeatedly. This improves efficiency and reduces network traffic.The fetch session cache has limited capacity. Kafka prioritizes follower replicas and consumers with many partitions, so some sessions may not be created or may be removed. In these cases, the broker returns an error, and the consumer automatically falls back to normal full fetch requests.</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Sat, 11 Jul 2026 14:32:55 +0330</pubDate></item><item><title>Understanding Latency Percentiles (P50, P95, and P99)</title><link>https://virgool.ir/@farhadnosrati/understanding-latency-percentiles-p50-p95-and-p99-rqdmcpzlezed</link><description>Understanding Latency Percentiles (P50, P95, and P99)IntroductionLatency is one of the most important metrics for measuring the performance of an application or service. It represents the amount of time it takes for a request to be processed—from the moment a client sends the request until it receives the response.When monitoring applications, engineers rarely rely only on the average latency because averages can hide slow requests. Instead, modern monitoring systems report latency percentiles, such as P50, P95, and P99, which provide a much more accurate picture of the user experience.This document explains what latency percentiles are, how they are calculated, and why they are essential for monitoring production systems.What Is a Percentile?A percentile tells us the value below which a certain percentage of observations fall.For latency measurements:P50 means 50% of requests completed in that amount of time or less.P95 means 95% of requests completed in that amount of time or less.P99 means 99% of requests completed in that amount of time or less.To calculate percentiles, all request durations are first sorted from the fastest to the slowest.For example, suppose an API processes 10,000 requests in one hour. After sorting the response times, monitoring tools determine the latency values corresponding to the 50th, 95th, and 99th percentiles.Understanding P50 (Median Latency)The 50th percentile (P50) is also known as the median.If:P50 = 35 msthen:50% of requests completed in 35 ms or lessThe remaining 50% took longer than 35 msP50 represents the typical user experience, since half of all users receive responses faster than this value.Understanding P95The 95th percentile (P95) measures the experience of almost all users.Imagine your application processes 100 requests.After sorting the response times:RequestResponse Time110 ms212 ms......90120 ms91130 ms92140 ms93150 ms94160 ms95180 ms96220 ms97260 ms98350 ms99750 ms1002000 msThe 95th percentile is 180 ms.This means:95 out of 100 requests completed in 180 ms or lessOnly the slowest 5 requests took longer than 180 msNotice that the requests taking 220 ms, 260 ms, 350 ms, 750 ms, and 2000 ms are not included in the P95 value because they belong to the slowest 5%.Larger ExampleSuppose your service processes 10,000 requests.If:P95 = 180 msthen:9,500 requests completed in 180 ms or less500 requests took longer than 180 msThis does not mean every request took 180 ms.It simply means that 95% of all requests completed within 180 milliseconds.Understanding P99The 99th percentile (P99) focuses on the slowest requests and helps identify rare performance problems.Consider the same 100 requests:RequestResponse Time110 ms212 ms......95180 ms96220 ms97260 ms98350 ms99750 ms1002000 msThe 99th percentile is 750 ms.This means:99 out of 100 requests completed in 750 ms or lessOnly 1 request took longer than 750 msNotice that the slowest request (2000 ms) is not the P99 value. It belongs to the remaining 1% of requests that exceeded the 99th percentile.Larger ExampleSuppose your application processes 10,000 requests.If:P99 = 600 msthen:9,900 requests completed in 600 ms or less100 requests took longer than 600 msAgain, this does not mean every request took 600 ms.It simply means that 99% of requests finished within 600 milliseconds.Visual ExampleImagine the sorted response times below:10&#xA;12&#xA;14&#xA;15&#xA;16&#xA;18&#xA;20&#xA;...&#xA;30&#xA;35&#xA;40&#xA;60&#xA;80&#xA;120&#xA;180&#xA;250&#xA;400&#xA;750&#xA;900&#xA;1200&#xA;A monitoring system might report:MetricValueMinimum10 msAverage42 msP5035 msP95180 msP99750 msMaximum1200 msThis tells us:Most users receive responses within 35 ms.Nearly every user receives a response within 180 ms.Almost all users receive a response within 750 ms.Only a very small number of requests exceed 750 ms.The slowest request took 1200 ms.Why Average Latency Is Not EnoughAverage latency can be misleading because a small number of very slow requests may have little impact on the average.Consider two services.Service A99 requests = 20 ms1 request = 2000 msAverage latency:39.8 ms&#xA;Although the average looks good, one user experienced a delay of 2 seconds.Service B100 requests = 40 msAverage latency:40 ms&#xA;The average is almost identical to Service A, yet every user receives a consistent experience.This demonstrates why engineers rely on percentiles instead of averages.Why P95 and P99 MatterBoth P95 and P99 provide insights that average latency cannot.P95P95 represents the experience of almost all users.It is commonly used for:Measuring API performanceMonitoring web applicationsDefining Service Level Objectives (SLOs)Tracking customer experienceBecause P95 ignores only the slowest 5% of requests, it provides a stable measurement of overall performance while filtering out extreme outliers.P99P99 focuses on the slowest users and is often called tail latency.It is especially useful for detecting intermittent performance issues such as:Database locksNetwork latency spikesGarbage collection pausesThread starvationHigh CPU utilizationResource contentionInfrastructure problemsP99 is much more sensitive to performance spikes than P95.Even if only 1% of users are affected, those requests often reveal problems that deserve investigation.Real Monitoring ExampleSuppose your monitoring dashboard reports:MetricValueAverage55 msP95140 msP99820 msMaximum4200 msInterpretationMost requests complete quickly (around 55 ms on average).95% of users receive responses within 140 ms.99% of users receive responses within 820 ms.About 1% of requests experience noticeable delays.The slowest request took 4.2 seconds, indicating an occasional performance problem that should be investigated.Choosing the Right MetricEach latency metric answers a different question.MetricMeaningWhat It Tells YouMinimumFastest requestBest-case latencyAverageMean response timeOverall performance, but can hide slow requestsP50Median latencyTypical user experienceP9595% of requests are faster than this valueExperience of almost all usersP9999% of requests are faster than this valuePerformance of the slowest requests (tail latency)MaximumSlowest requestWorst-case latencyBest PracticesWhen monitoring production systems:Never rely solely on average latency.Monitor P50 to understand the typical user experience.Monitor P95 to ensure that almost all users receive acceptable performance.Monitor P99 to detect rare but significant performance issues.Investigate increases in P99 even if the average latency remains unchanged, as this often indicates intermittent bottlenecks.Key TakeawaysLatency measures how long it takes to process a request.Percentiles provide a much better representation of user experience than averages.P50 represents the typical request.P95 represents the experience of almost all users.P99 highlights rare but slow requests and is essential for identifying tail latency.Average latency can hide serious performance problems, whereas P95 and P99 reveal them.Successful production monitoring relies on tracking percentile metrics alongside averages to gain a complete understanding of application performance.</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Mon, 29 Jun 2026 23:58:07 +0330</pubDate></item><item><title>Task-based asynchronous programming</title><link>https://virgool.ir/@farhadnosrati/task-based-asynchronous-programming-n0523nfgjaql</link><description> کتابخانه موازی وظیفه (Task Parallel Library یا TPL) بر پایه مفهومی به نام &#34;وظیفه&#34; (Task) طراحی شده است که نشان‌دهنده یک عملیات غیرهمزمان است. از برخی جنبه‌ها، یک وظیفه شبیه به یک نخ (Thread) یا یک آیتم کاری در ThreadPool است، اما در سطحی بالاتر از انتزاع عمل می‌کند. اصطلاح &#34;موازی‌سازی وظیفه&#34; (Task Parallelism) به اجرای همزمان یک یا چند وظیفه مستقل اشاره دارد. وظایف (Tasks) دو مزیت اصلی ارائه می‌دهند:1. استفاده کارآمدتر و مقیاس‌پذیرتر از منابع سیستمدر پشت صحنه، وظایف به صف ThreadPool ارسال می‌شوند که این صف به وسیله الگوریتم‌هایی بهینه‌سازی شده است که تعداد نخ‌ها را تعیین و تنظیم می‌کنند. این الگوریتم‌ها تعادل بار (Load Balancing) را مدیریت می‌کنند تا بیشترین کارایی حاصل شود. این فرآیند وظایف را سبک و کم‌هزینه می‌کند و این امکان را می‌دهد که تعداد زیادی از آن‌ها ایجاد کنید تا موازی‌سازی جزئی‌تر و دقیق‌تری داشته باشید.2. کنترل بیشتر بر فرآیند نسبت به نخ‌ها یا آیتم‌های کاریوظایف و چارچوبی که حول آن‌ها ساخته شده است، مجموعه گسترده‌ای از APIها را ارائه می‌دهد که شامل قابلیت‌هایی مانند انتظار (Waiting)، لغو (Cancellation)، ادامه (Continuations)، مدیریت استثنائات قوی، گزارش وضعیت دقیق، زمان‌بندی سفارشی و بسیاری ویژگی‌های دیگر است.به همین دلایل، TPL بهترین گزینه برای نوشتن کدهای چندنخی، غیرهمزمان، و موازی در دات‌نت (.NET) است.ایجاد و اجرای وظایف به صورت غیرمستقیمروش Parallel.Invoke یک راه ساده برای اجرای همزمان چندین دستور مختلف ارائه می‌دهد. شما کافی است یک Action برای هر عملیات کاری ارائه دهید. ساده‌ترین روش برای تعریف این Actionها استفاده از عبارت‌های لامبدا (Lambda Expressions) است. این عبارت‌های لامبدا می‌توانند یک متد نام‌گذاری‌شده را فراخوانی کنند یا کد موردنظر را به صورت مستقیم ارائه دهند.در مثال زیر، یک فراخوانی ابتدایی به Parallel.Invoke نشان داده شده است که دو وظیفه ایجاد و اجرا می‌کند که به صورت همزمان اجرا می‌شوند. وظیفه اول یک عبارت لامبدا است که متدی به نام DoSomeWork را فراخوانی می‌کند و وظیفه دوم نیز متدی به نام DoSomeOtherWork را فراخوانی می‌کند:Parallel.Invoke(() =&gt; DoSomeWork(), () =&gt; DoSomeOtherWork());نکته : این مستندات از عبارات لامبدا (Lambda Expressions) برای تعریف نماینده‌ها (Delegates) در TPL استفاده می‌کند. اگر با عبارات لامبدا در C# یا Visual Basic آشنایی ندارید، پیشنهاد می‌کنم به مستندات Lambda Expressions in PLINQ and TPL مراجعه کنید.نکته : تعداد نمونه‌های وظیفه (Task Instances) که در پس‌زمینه توسط متد Invoke ایجاد می‌شوند، لزوماً برابر با تعداد نماینده‌هایی نیست که ارائه داده‌اید. TPL ممکن است بهینه‌سازی‌های مختلفی انجام دهد، به خصوص زمانی که تعداد نماینده‌ها زیاد باشد.یک وظیفه که مقدار بازگشتی ندارد، با کلاس System.Threading.Tasks.Task نمایش داده می‌شود. وظیفه‌ای که یک مقدار بازگشتی دارد، با کلاس System.Threading.Tasks.Task&lt;TResult&gt; نمایش داده می‌شود که از کلاس Task به ارث می‌برد. شیء وظیفه (Task Object) مدیریت جزئیات زیرساخت را انجام می‌دهد و متدها و خواصی را ارائه می‌دهد که در طول عمر وظیفه از نخ فراخوانی قابل دسترسی هستند. به عنوان مثال، شما می‌توانید در هر زمانی به ویژگی Status یک وظیفه دسترسی داشته باشید تا مشخص کنید که آیا وظیفه شروع به اجرا کرده، به پایان رسیده، لغو شده یا استثنایی (Exception) رخ داده است. وضعیت وظیفه با یک شمارش به نام TaskStatus نمایش داده می‌شود.هنگامی که یک وظیفه ایجاد می‌کنید، باید یک نماینده (Delegate) به آن بدهید که کدی را که وظیفه اجرا خواهد کرد، در بر می‌گیرد. این نماینده می‌تواند به صورت یک نماینده نام‌گذاری‌شده، یک متد ناشناس یا یک عبارت لامبدا (Lambda Expression) تعریف شود. عبارات لامبدا می‌توانند شامل فراخوانی به یک متد نام‌گذاری‌شده باشند، همان‌طور که در مثال زیر نشان داده شده است. این مثال شامل فراخوانی متد Task.Wait است تا اطمینان حاصل شود که وظیفه قبل از پایان برنامه در حالت کنسول اجرا را کامل می‌کند.Task taskA = new Task( () =&gt; Console.WriteLine(&#34;Hello from taskA.&#34;));       taskA.Start();taskA.Wait();شما می‌توانید از متدهای Task.Run برای ایجاد و شروع یک وظیفه به صورت همزمان استفاده کنید. متدهای Run همیشه از زمان‌بند پیش‌فرض وظایف استفاده می‌کنند، حتی اگر نخ فعلی از زمان‌بند دیگری استفاده کند.متدهای Run گزینه مناسبی هستند زمانی که نیازی به کنترل بیشتر روی ایجاد و زمان‌بندی وظایف ندارید.Task taskA = Task.Run( () =&gt; Console.WriteLine(&#34;Hello from taskA.&#34;));taskA.Wait();شما همچنین می‌توانید از متد TaskFactory.StartNew برای ایجاد و شروع یک وظیفه به صورت همزمان استفاده کنید. همانطور که در مثال زیر نشان داده شده است، می‌توانید از این متد زمانی استفاده کنید که:ایجاد و زمان‌بندی نیازی به تفکیک نداشته باشند و شما به گزینه‌های اضافی برای ایجاد وظیفه یا استفاده از یک زمان‌بند خاص نیاز داشته باشید.نیاز دارید که وضعیت اضافی را به وظیفه ارسال کنید که بتوانید از طریق ویژگی Task.AsyncState آن را بازیابی کنید.Task&lt;Double&gt;.Factory.StartNew(() =&gt; DoComputation(1000.0));برای اطلاعات بیشتر، به مستندات زیر مراجعه کنید. https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-return-a-value-from-a-task زمانی که از یک عبارت لامبدا برای ایجاد یک نماینده استفاده می‌کنید، به تمامی متغیرهایی که در آن نقطه از کد شما قابل مشاهده هستند دسترسی دارید. با این حال، در برخی موارد، به‌ویژه در داخل حلقه‌ها، لامبدا متغیر را به‌طور مورد انتظار ذخیره نمی‌کند. لامبدا فقط مرجع (Reference) متغیر را ذخیره می‌کند، نه مقدار آن، چون این متغیر بعد از هر بار تکرار تغییر می‌کند. شما می‌توانید با فراهم کردن یک شیء وضعیت (state object) به یک وظیفه از طریق سازنده آن، به مقدار در هر تکرار دسترسی پیدا کنید. این وضعیت به‌عنوان یک آرگومان به نماینده وظیفه (task delegate) ارسال می‌شود و می‌توان از طریق ویژگی Task.AsyncState آن را از شیء وظیفه دسترسی کرد.Task IDهر وظیفه یک شناسه عددی دریافت می‌کند که آن را به‌طور منحصر به فرد در دامنه برنامه شناسایی می‌کند و می‌توان از طریق ویژگی Task.Id به آن دسترسی پیدا کرد. این شناسه برای مشاهده اطلاعات وظایف در پنجره‌های Parallel Stacks و Tasks در دیباگر ویژوال استودیو مفید است. شناسه به‌طور تنبل (lazy) ایجاد می‌شود، به این معنی که تا زمانی که درخواست نشود، ایجاد نمی‌شود. بنابراین، ممکن است هر بار که برنامه اجرا می‌شود، شناسه متفاوتی به وظیفه اختصاص یابد. برای اطلاعات بیشتر در مورد نحوه مشاهده شناسه‌های وظیفه در دیباگر، به مستندات زیر  مراجعه کنید. https://learn.microsoft.com/en-us/visualstudio/debugger/using-the-parallel-stacks-window?view=vs-2022  https://learn.microsoft.com/en-us/visualstudio/debugger/using-the-tasks-window?view=vs-2022 Task creation optionsبیشتر APIهایی که وظایف ایجاد می‌کنند، اضافه‌بارهایی دارند که یک پارامتر از نوع TaskCreationOptions را قبول می‌کنند. با مشخص کردن یک یا چند گزینه از این موارد، شما به زمان‌بند وظیفه می‌گویید که چگونه وظیفه را روی استخر نخ زمان‌بندی کند. این گزینه‌ها ممکن است با استفاده از عملگر OR بیتی ترکیب شوند.مثال زیر یک وظیفه را نشان می‌دهد که گزینه‌های LongRunning و PreferFairness را دارد.var task3 = new Task(() =&gt; MyLongRunningMethod(),                     TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness); task3.Start();Tasks, threads, and cultureهر نخ یک فرهنگ و فرهنگ UI مرتبط دارد که به ترتیب توسط ویژگی‌های Thread.CurrentCulture و Thread.CurrentUICulture تعریف می‌شود. فرهنگ یک نخ در عملیاتی مانند قالب‌بندی، تجزیه و تحلیل، مرتب‌سازی و مقایسه رشته‌ها استفاده می‌شود. فرهنگ UI یک نخ در جستجوی منابع استفاده می‌شود.فرهنگ سیستم فرهنگ پیش‌فرض و فرهنگ UI پیش‌فرض نخ را تعریف می‌کند. با این حال، می‌توانید یک فرهنگ پیش‌فرض برای تمام نخ‌ها در یک دامنه برنامه از طریق ویژگی‌های CultureInfo.DefaultThreadCurrentCulture و CultureInfo.DefaultThreadCurrentUICulture مشخص کنید. اگر فرهنگ یک نخ را به‌طور صریح تنظیم کنید و یک نخ جدید راه‌اندازی کنید، نخ جدید فرهنگ نخ فراخوانی را به ارث نمی‌برد؛ در عوض، فرهنگ آن فرهنگ پیش‌فرض سیستم است. با این حال، در برنامه‌نویسی مبتنی بر وظیفه (Task)، وظایف از فرهنگ نخ فراخوانی استفاده می‌کنند، حتی اگر وظیفه به‌صورت غیرهمزمان روی نخ دیگری اجرا شود.مثال زیر یک توضیح ساده ارائه می‌دهد. این مثال فرهنگ فعلی برنامه را به فرانسوی (فرانسه) تغییر می‌دهد. اگر فرانسوی (فرانسه) قبلاً فرهنگ فعلی باشد، به انگلیسی (ایالات متحده) تغییر می‌دهد. سپس یک نماینده به نام formatDelegate فراخوانی می‌شود که برخی از اعداد را به‌عنوان مقادیر ارزی در فرهنگ جدید فرمت می‌کند. چه نماینده به‌طور همزمان یا غیرهمزمان توسط یک وظیفه فراخوانی شود، وظیفه از فرهنگ نخ فراخوانی استفاده می‌کند.نکته : در نسخه‌های پیش از .NET Framework 4.6، فرهنگ یک وظیفه توسط فرهنگ نخی که وظیفه روی آن اجرا می‌شود تعیین می‌شود، نه فرهنگ نخ فراخوانی. برای وظایف غیرهمزمان، فرهنگ استفاده‌شده توسط وظیفه ممکن است با فرهنگ نخ فراخوانی متفاوت باشد.Creating task continuationsمتدهای Task.ContinueWith و Task&lt;TResult&gt;.ContinueWith به شما این امکان را می‌دهند که یک وظیفه را مشخص کنید که پس از اتمام وظیفه قبلی اجرا شود. نماینده وظیفه ادامه‌دهنده یک مرجع به وظیفه قبلی دریافت می‌کند تا بتواند وضعیت آن را بررسی کند. و با بازیابی مقدار ویژگی Task&lt;TResult&gt;.Result، می‌توانید از خروجی وظیفه قبلی به‌عنوان ورودی برای وظیفه ادامه‌دهنده استفاده کنید.چون Task.ContinueWith یک متد نمونه‌ای است، شما می‌توانید فراخوانی‌های متد را به هم متصل کنید، به جای اینکه برای هر وظیفه قبلی یک شیء Task&lt;TResult&gt; ایجاد کنید. Creating detached child tasksزمانی که کدی که در یک وظیفه در حال اجرا است، یک وظیفه جدید ایجاد می‌کند و گزینه AttachedToParent را مشخص نمی‌کند، وظیفه جدید به‌طور خاص با وظیفه والد هماهنگ نمی‌شود. این نوع وظیفه غیرهماهنگ به نام &#34;وظیفه تو در تو جدا شده&#34; یا &#34;وظیفه فرزند جدا شده&#34; شناخته می‌شود.Creating child tasksزمانی که کدی که در یک وظیفه در حال اجرا است، یک وظیفه با گزینه AttachedToParent ایجاد می‌کند، وظیفه جدید به عنوان &#34;وظیفه فرزند متصل&#34; به وظیفه والد شناخته می‌شود. شما می‌توانید از گزینه AttachedToParent برای بیان موازی‌سازی وظایف ساختاریافته استفاده کنید، زیرا وظیفه والد به‌طور ضمنی منتظر اتمام تمام وظایف فرزند متصل می‌ماند. در مثال زیر، یک وظیفه والد ایجاد می‌شود که ۱۰ وظیفه فرزند متصل ایجاد می‌کند. در این مثال، متد Task.Wait برای انتظار برای اتمام وظیفه والد فراخوانی می‌شود و نیازی به انتظار صریح برای اتمام وظایف فرزند متصل نیست.Waiting for tasks to finishانتظار برای اتمام وظایفانواع System.Threading.Tasks.Task و System.Threading.Tasks.Task&lt;TResult&gt; چندین باراضافه از متدهای Task.Wait را فراهم می‌کنند که به شما امکان می‌دهند برای اتمام یک وظیفه صبر کنید. علاوه بر این، باراضافه‌های متدهای ایستا Task.WaitAll و Task.WaitAny به شما این امکان را می‌دهند که برای اتمام هرکدام یا همه وظایف در یک آرایه از وظایف صبر کنید.به‌طور معمول، شما برای یکی از این دلایل برای اتمام یک وظیفه صبر می‌کنید:نخ اصلی به نتیجه نهایی محاسبه‌شده توسط یک وظیفه وابسته است.باید استثناهایی که ممکن است از وظیفه پرتاب شوند را مدیریت کنید.ممکن است برنامه قبل از اتمام تمام وظایف به اتمام برسد. به‌عنوان مثال، برنامه‌های کنسولی پس از اجرای تمام کدهای همگام در متد Main (نقطه ورودی برنامه) خاتمه می‌یابند.مثال زیر الگوی پایه‌ای را نشان می‌دهد که شامل مدیریت استثنا نیست:برای مثالی که مدیریت استثناها را نشان می‌دهد، به &#34;مدیریت استثناها&#34; مراجعه کنید.برخی از باراضافه‌ها به شما این امکان را می‌دهند که یک زمان‌سنج تعیین کنید، و برخی دیگر یک پارامتر اضافی به نام CancellationToken دریافت می‌کنند تا بتوانید خود عمل انتظار را هم به صورت برنامه‌نویسی و هم به پاسخ به ورودی کاربر لغو کنید.وقتی برای یک وظیفه صبر می‌کنید، به‌طور ضمنی برای تمام فرزندان آن وظیفه که با استفاده از گزینه TaskCreationOptions.AttachedToParent ایجاد شده‌اند، صبر می‌کنید. متد Task.Wait بلافاصله اگر وظیفه قبلاً تکمیل شده باشد، بازمی‌گردد. متد Task.Wait هر استثنایی که توسط یک وظیفه پرتاب شده باشد را پرتاب می‌کند، حتی اگر متد Task.Wait پس از اتمام وظیفه فراخوانی شده باشد.Composing tasksکلاس‌های Task و Task&lt;TResult&gt; چندین متد را برای کمک به ترکیب چندین تسک فراهم می‌کنند. این متدها الگوهای رایج را پیاده‌سازی کرده و از ویژگی‌های زبان‌های C#، Visual Basic و F# برای برنامه‌نویسی غیرهمزمان به‌خوبی استفاده می‌کنند. در این بخش متدهای WhenAll، WhenAny، Delay و FromResult توضیح داده شده‌اند.Task.WhenAllمتد Task.WhenAll به‌صورت غیرهمزمان منتظر می‌ماند تا چندین شیء Task یا Task&lt;TResult&gt; تمام شوند. این متد نسخه‌های اضافه‌شده‌ای دارد که به شما این امکان را می‌دهد تا منتظر مجموعه‌های غیر یکنواختی از تسک‌ها باشید. به‌عنوان مثال، می‌توانید منتظر بمانید تا چندین شیء Task و Task&lt;TResult&gt; از یک فراخوانی متد به پایان برسند.Task.WhenAnyمتد Task.WhenAny به‌صورت غیرهمزمان منتظر می‌ماند تا یکی از چندین شیء Task یا Task&lt;TResult&gt; تمام شود. مشابه متد Task.WhenAll، این متد نسخه‌های اضافه‌شده‌ای دارد که به شما این امکان را می‌دهد تا منتظر مجموعه‌های غیر یکنواختی از تسک‌ها باشید. متد WhenAny به‌ویژه در سناریوهای زیر مفید است:عملیات‌های اضافی: فرض کنید الگوریتم یا عملیاتی دارید که می‌تواند به طرق مختلف انجام شود. می‌توانید از متد WhenAny برای انتخاب عملیاتی که زودتر تمام می‌شود استفاده کنید و سپس عملیات‌های باقی‌مانده را لغو کنید.عملیات‌های درهم: می‌توانید چندین عملیات را شروع کنید که باید تمام شوند و از متد WhenAny برای پردازش نتایج هر عملیات به محض اتمام آن استفاده کنید. پس از اتمام یک عملیات، می‌توانید یک یا چند تسک را آغاز کنید.عملیات‌های محدود شده: می‌توانید از متد WhenAny برای گسترش سناریو قبلی با محدود کردن تعداد عملیات‌های همزمان استفاده کنید.عملیات‌های منقضی‌شده: می‌توانید از متد WhenAny برای انتخاب بین یک یا چند تسک و یک تسک که بعد از زمان خاصی تمام می‌شود استفاده کنید، مانند تسکی که از متد Delay برمی‌گردد. متد Delay در بخش بعدی توضیح داده شده است.Task.Delayمتد Task.Delay شیء Task ایجاد می‌کند که پس از مدت زمان مشخصی به پایان می‌رسد. می‌توانید از این متد برای ساخت حلقه‌هایی که داده‌ها را به‌طور دوره‌ای بررسی می‌کنند، تعیین زمان‌گذاری‌ها، به تأخیر انداختن پردازش ورودی کاربر و غیره استفاده کنید.Task&lt;TResult&gt;.FromResultبا استفاده از متد Task.FromResult می‌توانید شیء Task&lt;TResult&gt; ایجاد کنید که یک نتیجه پیش‌محاسبه‌شده را نگه می‌دارد. این متد زمانی مفید است که عملیاتی غیرهمزمان انجام می‌دهید که شیء Task&lt;TResult&gt; باز می‌گرداند و نتیجه آن Task&lt;TResult&gt; قبلاً محاسبه شده باشد. برای مثال، از FromResult می‌توان برای دریافت نتایج عملیات دانلود غیرهمزمان که در کش نگهداری می‌شوند، استفاده کرد.Handling exceptions in tasksهنگامی که یک تسک یک یا چند استثنا پرتاب می‌کند، استثناها در یک استثنای AggregateException بسته می‌شوند. این استثنا به رشته‌ای که با تسک ملحق شده است، منتقل می‌شود. معمولاً این رشته‌ای است که منتظر پایان تسک یا دسترسی به ویژگی Result است. این رفتار سیاست .NET Framework را اعمال می‌کند که طبق آن، تمام استثناهای بدون پردازش به‌طور پیش‌فرض باید فرآیند را خاتمه دهند. کد فراخوانی می‌تواند استثناها را با استفاده از هر یک از موارد زیر در یک بلوک try/catch مدیریت کند:متد Waitمتد WaitAllمتد WaitAnyویژگی Resultرشته ملحق‌شده همچنین می‌تواند استثناها را با دسترسی به ویژگی Exception قبل از جمع‌آوری زباله‌ها مدیریت کند. با دسترسی به این ویژگی، از انتقال استثناهای بدون پردازش که باعث خاتمه فرآیند هنگام نهایی شدن شیء می‌شود، جلوگیری می‌کنید.برای اطلاعات بیشتر در مورد استثناها و تسک‌ها، به مدیریت استثناها مراجعه کنید.Canceling tasksکلاس Task از لغو همکاری پشتیبانی می‌کند و به‌طور کامل با کلاس‌های System.Threading.CancellationTokenSource و System.Threading.CancellationToken که در .NET Framework 4 معرفی شده‌اند، یکپارچه است. بسیاری از سازنده‌های کلاس System.Threading.Tasks.Task یک شیء CancellationToken را به‌عنوان پارامتر ورودی می‌پذیرند. بسیاری از اضافه‌شده‌های متدهای StartNew و Run نیز شامل پارامتر CancellationToken هستند.شما می‌توانید توکن را ایجاد کرده و درخواست لغو را در زمان بعدی با استفاده از کلاس CancellationTokenSource صادر کنید. توکن را به‌عنوان آرگومان به تسک ارسال کرده و همچنین از همان توکن در دلیگیت کاربر خود استفاده کنید که وظیفه پاسخ به درخواست لغو را انجام می‌دهد.برای اطلاعات بیشتر، به لغو تسک‌ها و نحوه لغو یک تسک و فرزندان آن مراجعه کنید.The TaskFactory classکلاس TaskFactory متدهای ایستا را فراهم می‌کند که الگوهای رایج برای ایجاد و شروع تسک‌ها و تسک‌های ادامه‌ای را در بر می‌گیرد.رایج‌ترین الگو، StartNew است که در یک بیانیه، تسک را ایجاد و شروع می‌کند.هنگامی که تسک‌های ادامه‌ای از چندین پیش‌نیاز ایجاد می‌کنید، از متدهای ContinueWhenAll یا ContinueWhenAny یا معادل‌های آنها در کلاس Task&lt;TResult&gt; استفاده کنید. برای اطلاعات بیشتر، به زنجیره‌ای کردن تسک‌ها با استفاده از تسک‌های ادامه‌ای مراجعه کنید.برای گنجاندن متدهای BeginX و EndX مدل برنامه‌نویسی غیرهمزمان در یک شیء Task یا Task&lt;TResult&gt;، از متدهای FromAsync استفاده کنید. برای اطلاعات بیشتر، به TPL و برنامه‌نویسی غیرهمزمان سنتی .NET Framework مراجعه کنید.و TaskFactory پیش‌فرض می‌تواند به‌عنوان یک ویژگی ایستا در کلاس Task یا Task&lt;TResult&gt; دسترسی داشته باشد. همچنین می‌توانید یک TaskFactory را مستقیماً نمونه‌سازی کرده و گزینه‌های مختلفی از جمله یک توکن لغو، گزینه‌های TaskCreationOptions، گزینه‌های TaskContinuationOptions یا یک TaskScheduler را مشخص کنید. هر گزینه‌ای که هنگام ایجاد کارخانه تسک مشخص کنید، به تمام تسک‌هایی که ایجاد می‌کند اعمال می‌شود، مگر اینکه تسک با استفاده از enumeration TaskCreationOptions ایجاد شود، در این صورت گزینه‌های تسک گزینه‌های کارخانه تسک را لغو می‌کنند.Tasks without delegatesدر برخی موارد، ممکن است بخواهید از یک تسک برای دربر گرفتن عملیاتی غیرهمزمان که توسط یک مؤلفه خارجی انجام می‌شود به جای دلیگیت کاربر خود استفاده کنید. اگر عملیات بر اساس الگوی Begin/End مدل برنامه‌نویسی غیرهمزمان باشد، می‌توانید از متدهای FromAsync استفاده کنید. اگر اینطور نباشد، می‌توانید از شیء TaskCompletionSource&lt;TResult&gt; برای پیچیدن عملیات در یک تسک استفاده کنید و به این ترتیب از برخی مزایای برنامه‌نویسی تسک‌ها بهره‌مند شوید. به‌عنوان مثال، پشتیبانی از انتقال استثنا و تسک‌های ادامه‌ای. برای اطلاعات بیشتر، به TaskCompletionSource&lt;TResult&gt; مراجعه کنید.Custom schedulersبیشتر توسعه‌دهندگان برنامه یا کتابخانه نیازی به دانستن اینکه تسک روی کدام پردازنده اجرا می‌شود، چگونه کار خود را با سایر تسک‌ها هماهنگ می‌کند یا چگونه روی System.Threading.ThreadPool زمان‌بندی می‌شود، ندارند. آنها فقط نیاز دارند که تسک به‌طور کارآمد بر روی کامپیوتر میزبان اجرا شود. اگر نیاز به کنترل دقیق‌تری بر جزئیات زمان‌بندی دارید، TPL به شما این امکان را می‌دهد که برخی تنظیمات را روی زمان‌بند پیش‌فرض تسک پیکربندی کنید و حتی به شما اجازه می‌دهد که یک زمان‌بند سفارشی ارائه دهید. برای اطلاعات بیشتر، به TaskScheduler مراجعه کنید.Related data structuresث TPL چندین نوع جدید عمومی را فراهم می‌کند که در سناریوهای موازی و ترتیبی مفید هستند. این شامل. چندین کلاس مجموعه thread-safe، سریع و مقیاس‌پذیر در فضای نام System.Collections.Concurrent و چندین نوع همگام‌سازی جدید است. برای مثال، System.Threading.Semaphore و System.Threading.ManualResetEventSlim که برای برخی از انواع بارها کارآمدتر از نسخه‌های قبلی خود هستند. انواع جدید دیگر در .NET Framework 4، مانند System.Threading.Barrier و System.Threading.SpinLock، عملکردهایی را فراهم می‌کنند که در نسخه‌های قبلی موجود نبودند. برای اطلاعات بیشتر، به ساختار داده‌ها برای برنامه‌نویسی موازی مراجعه کنید.Custom task typesما توصیه می‌کنیم که از کلاس‌های System.Threading.Tasks.Task یا System.Threading.Tasks.Task&lt;TResult&gt; ارث‌بری نکنید. به‌جای آن، توصیه می‌کنیم که از ویژگی AsyncState برای ارتباط دادن داده‌ها یا وضعیت اضافی با شیء Task یا Task&lt;TResult&gt; استفاده کنید. همچنین می‌توانید از متدهای افزونه برای گسترش عملکرد کلاس‌های Task و Task&lt;TResult&gt; استفاده کنید. برای اطلاعات بیشتر در مورد متدهای افزونه، به متدهای افزونه مراجعه کنید.رفرنس : https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming </description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Mon, 03 Feb 2025 16:00:34 +0330</pubDate></item><item><title>همزمانی vs مواردی سازی</title><link>https://virgool.ir/@farhadnosrati/همزمانی-vs-مواردی-سازی-fmy4w4lczxv2</link><description>همزمانی vs مواردی سازیهمان‌طور که راب پایک (یکی از سازندگان زبان Go) گفته است: هم‌زمانی یعنی رسیدگی به چندین کار به‌طور هم‌زمان.موازی‌سازی یعنی انجام چندین کار به‌صورت هم‌زمان.هم‌زمانی (Concurrency)هم‌زمانی به این معناست که یک برنامه هم‌زمان در حال پیشبرد چندین وظیفه است. اگر کامپیوتر فقط یک پردازنده (CPU) داشته باشد، ممکن است برنامه نتواند چندین وظیفه را دقیقاً در یک لحظه انجام دهد، اما وظایف به طور هم‌زمان در داخل برنامه در حال پردازش هستند. این بدین معناست که برنامه، بدون اینکه یک وظیفه را کاملاً به پایان برساند، وظیفه دیگری را شروع می‌کند.برای توضیح بهتر، می‌توان مثالی از زندگی واقعی زد: چالشی وجود دارد که شما باید هم یک کیک بزرگ را بخورید و هم یک آهنگ کامل بخوانید. شما برنده می‌شوید اگر بتوانید سریع‌تر از دیگران هم کیک را بخورید و هم آهنگ را به پایان برسانید. قانون این است که باید هم‌زمان کیک بخورید و آهنگ بخوانید، اما چگونگی انجام این کار به خود شما بستگی دارد. می‌توانید ابتدا کل کیک را بخورید و بعد کل آهنگ را بخوانید، یا می‌توانید نصف کیک را بخورید و نصف آهنگ را بخوانید و این روند را تکرار کنید.هم‌زمانی به این معناست که چندین وظیفه به طور هم‌زمان اجرا می‌شوند، اما نه الزاماً به صورت فیزیکی و همزمان. در یک پردازنده تک‌هسته‌ای، CPU تصمیم می‌گیرد که کدام وظیفه را ابتدا اجرا کند، یا ممکن است بخشی از یک وظیفه و سپس بخشی از وظیفه دیگر را اجرا کند. دو وظیفه می‌توانند هم‌زمان شروع شوند، اجرا شوند و به پایان برسند، به این معنا که وظیفه دوم ممکن است قبل از اتمام وظیفه اول شروع شود. این موضوع بستگی به معماری سیستم دارد. به طور خلاصه، هم‌زمانی به معنای اجرای چند وظیفه به طور هم‌زمان است، اما نه الزاماً به صورت همزمان واقعی.موازی‌سازی (Parallelism)موازی‌سازی به این معناست که یک برنامه وظایف خود را به وظایف کوچک‌تری تقسیم می‌کند که می‌توانند به صورت موازی پردازش شوند، مثلاً روی چندین پردازنده به طور همزمان.موازی‌سازی نیاز به وجود دو وظیفه مجزا ندارد. بلکه ممکن است بخش‌های مختلف یک وظیفه یا چندین وظیفه به طور هم‌زمان اجرا شوند. این کار با استفاده از معماری چند‌هسته‌ای CPU انجام می‌شود، به طوری‌که هر هسته به یک وظیفه یا زیر وظیفه اختصاص داده می‌شود.اگر به مثال قبلی بازگردیم، این بار همچنان قانون این است که شما باید هم‌زمان بخوانید و کیک بخورید، اما این بار شما در یک تیم دو نفره بازی می‌کنید. احتمالاً شما کیک را می‌خورید و دوستتان آهنگ می‌خواند (چون او بهتر می‌خواند و شما بهتر کیک می‌خورید). بنابراین این بار وظایف واقعاً به طور هم‌زمان انجام می‌شوند و این همان موازی‌سازی است.موازی‌سازی به سخت‌افزاری با چندین واحد پردازشی نیاز دارد. در یک پردازنده تک‌هسته‌ای ممکن است هم‌زمانی وجود داشته باشد، اما موازی‌سازی نخواهد بود. موازی‌سازی نوع خاصی از هم‌زمانی است که در آن وظایف واقعاً به طور هم‌زمان اجرا می‌شوند.تفاوت بین برنامه‌نویسی موازی و برنامه‌نویسی هم‌زمانحال بیایید تفاوت‌های اصلی بین هم‌زمانی و موازی‌سازی را فهرست کنیم. به عنوان مثال، کتاب &#34;هنر هم‌زمانی&#34; این تفاوت را به این صورت تعریف می‌کند:یک سیستم زمانی هم‌زمان گفته می‌شود که بتواند از دو یا چند عملیات به صورت هم‌زمان پشتیبانی کند. یک سیستم زمانی موازی گفته می‌شود که بتواند از دو یا چند عملیات به صورت همزمان (فیزیکی) پشتیبانی کند.مفهوم کلیدی و تفاوت اصلی بین این تعاریف، عبارت &#34;در حال پیشرفت&#34; است. این تعریف می‌گوید که در سیستم‌های هم‌زمان، چندین عملیات می‌توانند به طور هم‌زمان در حال پیشرفت باشند (اما الزاماً اجرا نمی‌شوند). در حالی که در سیستم‌های موازی، چندین عملیات به طور هم‌زمان اجرا می‌شوند. در واقع، هم‌زمانی و موازی‌سازی از نظر مفهومی تا حدی هم‌پوشانی دارند، اما عبارت &#34;در حال پیشرفت&#34; آن‌ها را متمایز می‌کند.هم‌زمانی به معنای پردازش هم‌زمان چندین وظیفه است. موازی‌سازی به معنای اجرای هم‌زمان چندین وظیفه است.یک برنامه می‌تواند هم‌زمان باشد اما موازی نباشد، یعنی چندین وظیفه را هم‌زمان پردازش کند، اما هیچ دو وظیفه‌ای به طور همزمان (فیزیکی) اجرا نشوند.یک برنامه می‌تواند موازی باشد اما هم‌زمان نباشد، یعنی چندین زیر وظیفه از یک وظیفه را به طور هم‌زمان در یک پردازنده چند‌هسته‌ای اجرا کند.یک برنامه می‌تواند نه موازی باشد و نه هم‌زمان، یعنی تمام وظایف را به صورت ترتیبی، یکی پس از دیگری پردازش کند.یک برنامه می‌تواند هم موازی و هم هم‌زمان باشد، یعنی چندین وظیفه را به طور هم‌زمان و به طور فیزیکی روی یک CPU چند‌هسته‌ای اجرا کند.</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Wed, 02 Oct 2024 22:18:25 +0330</pubDate></item><item><title>IHttpClientFactory</title><link>https://virgool.ir/@farhadnosrati/ihttpclientfactory-aqkklgc8qpyk</link><description>When you call any of the AddHttpClient extension methods, you&#39;re adding the IHttpClientFactory and related services to the IServiceCollection.Manages the caching and lifetime of underlying HttpClientHandler instances. Automatic management avoids common Domain Name System (DNS) problems that occur when manually managing HttpClient lifetimes.Each time CreateClient is called:A new instance of HttpClient is created.The configuration action is called.Provide a single location to configure and interact with a particular HttpClient. For example, a single typed client might be used:For a single backend endpoint.To encapsulate all logic dealing with the endpoint.The typed client is registered as transient with DI.Using typed clients in singleton services can be dangerous .Avoid typed clients in singleton servicesHttpClient lifetime managementA new HttpClient instance is returned each time CreateClient is called on the IHttpClientFactory. One HttpClientHandler instance is created per client name.The factory manages the lifetimes of the HttpClientHandler instances.IHttpClientFactory caches the HttpClientHandler instances created by the factory to reduce resource consumption. An HttpClientHandler instance may be reused from the cache when creating a new HttpClient instance if its lifetime hasn&#39;t expired.Caching of handlers is desirable as each handler typically manages its own underlying HTTP connection pool. Creating more handlers than necessary can result in socket exhaustion and connection delays. Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS changes.The default handler lifetime is two minutes. To override the default value, call SetHandlerLifetime for each client, on the IServiceCollection:services.AddHttpClient(&#34;Named.Client&#34;).SetHandlerLifetime(TimeSpan.FromMinutes(5));HttpClient instances created by IHttpClientFactory are intended to be short-lived.As soon as a typed client instance is created, IHttpClientFactory has no control over it. If a typed client instance is captured in a singleton, it may prevent it from reacting to DNS changes, defeating one of the purposes of IHttpClientFactory.Recycling and recreating HttpMessageHandler&#39;s when their lifetime expires is essential for IHttpClientFactory to ensure the handlers react to DNS changes. HttpClient is tied to a specific handler instance upon its creation, so new HttpClient instances should be requested in a timely manner to ensure the client will get the updated handler.Disposing of such HttpClient instances created by the factory will not lead to socket exhaustion, as its disposal will not trigger disposal of the HttpMessageHandler. IHttpClientFactory tracks and disposes of resources used to create HttpClient instances, specifically the HttpMessageHandler instances, as soon their lifetime expires and there&#39;s no HttpClient using them anymore.reference : https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factoryhttps://www.linkedin.com/posts/farhad-nosrati-%D9%81%D8%B1%D9%87%D8%A7%D8%AF-%D9%86%D8%B5%D8%B1%D8%AA%DB%8C-231b84185_ihttpclientfactory-when-you-call-any-of-activity-7106673689277911040-Y7CE?utm_source=share&amp;utm_medium=member_desktop</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Fri, 29 Sep 2023 22:23:03 +0330</pubDate></item><item><title>توضیحات chatGTP در مورد solid .</title><link>https://virgool.ir/@farhadnosrati/توضیحات-chatgtp-در-مورد-solid-vwm2rk6eikt5</link><description>SOLID :یک مجموعه‌ی اصول طراحی شیءگرا است که برای ساختاردهی بهتر و قابل گسترش‌تر کردن کد، معرفی شده است. این اصول، به صورت خلاصه به صورت زیر هستند:Single Responsibility Principle (اصل مسئولیت تکی): هر کلاس باید فقط یک مسئولیت داشته باشد و باید تنها به یک دلیل برای تغییر مورد نیاز باشد.Open/Closed Principle (اصل باز/بسته): کد باید برای تغییرات بسته باشد ولی برای افزودن قابل باز باشد.Liskov Substitution Principle (اصل جایگزینی لیسکوف): هر شیء از یک کلاس، باید بتواند به عنوان یک شیء ازکلاس پدرش استفاده شود، بدون اینکه برنامه تغییر کند.Interface Segregation Principle (اصل جداسازی رابط): کلاس‌ها نباید از روش‌هایی که نیاز ندارند استفاده کنند و نباید به کلاس‌های دیگر وابسته باشند.Dependency Inversion Principle (اصل وابستگی معکوس): باید به جای وابستگی به جزئیات پیاده‌سازی، به ابسترکشن‌ها و رابط‌ها وابسته باشیم و وابستگی‌های بین کلاس‌ها را به صورتی طراحی کنیم که برای تغییرات کمتری در کد نیاز باشد.اصول SOLID به صورت جداگانه و در کنار یکدیگر، به ما کمک می‌کنند که کدی را را طراحی کنیم که قابلیت گسترش و تغییرات بیشتری داشته باشد و عملکرد بهتری داشته باشد. در ادامه، به توضیح هر یک از این اصول می‌پردازیم:Single Responsibility Principle: این اصل بیان می‌کند که هر کلاس فقط باید یک مسئولیت داشته باشد. به این صورت که هر کلاس باید فقط برای یک کار خاص طراحی شود و اگر برای کار دیگری نیاز به تغییرات داشته باشد، باید از آن کلاس جدا شود. به طور مثال، در یک برنامه مدیریت فروشگاه، دو کلاس مجزا برای مدیریت محصولات و مدیریت سفاردهی باید طراحی شوند، به جای آنکه هر دو مسئولیت در یک کلاس قرار داشته باشد.Open/Closed Principle: این اصل بیان می‌کند که کد باید برای تغییرات بسته باشد ولی برای افزودن قابل باز باشد. به این صورت که باید بتوانیم قابلیت‌های جدید به برنامه خود اضافه کنیم، بدون اینکه کد قبلی را تغییر دهیم. به طور مثال در یک برنامه بازی، باید بتوانیم شخصیت‌های جدید را به بازی اضافه کنیم، بدون اینکه کد اصلی بازی را تغییر دهیم.Liskov Substitution Principle: این اصل بیان می‌کند که هر شیءاز یک کلاس، باید بتواند به عنوان یک شیء از کلاس پدرش استفاده شود، بدون اینکه برنامه تغییر کند. به عبارت دیگر، باید بتوانیم شیء‌های جدید را به برنامه خود اضافه کنیم، بدون اینکه تغییراتی در برنامه اصلی ایجاد شود. به طور مثال، در یک برنامه مدیریت حساب بانکی، باید بتوانیم بدون تغییر کد اصلی، حساب‌های جدید را به برنامه اضافه کنیم.Interface Segregation Principle: این اصل بیان می‌کند که کلاس‌ها نباید از روش‌هایی که نیاز ندارند استفاده کنند و نباید به کلاس‌های دیگر وابسته باشند. به عبارت دیگر، باید روش‌هایی که برای کاری لازم نیستند، در کلاس‌ها قرار نگیرند و هر کلاس فقط به آن چیزی که نیاز دارد، وابسته باشد. به طور مثال، در یک برنامه مدیریت فروشگاه، کلاس مدیریت محصولات نباید از روش‌هایی که برای سفارش‌ها نیاز داریم استفاده کند.Dependency Inversion Principle: این اصل بیان می‌کند که باید به جای وابستگی به جزئیات پیاده‌سازی، به ابسترکشن‌ها و رابط‌ها وابسته باشیم و وابستگی‌های بین کلاس‌ها را به صورتی طراحی کنیم که برایتغییرات کمتری در کد نیاز باشد. به عبارت دیگر، باید به جای استفاده از قطعات کد و جزئیات پیاده‌سازی، از رابط‌ها و ابسترکشن‌ها استفاده کنیم تا بتوانیم وابستگی‌های بین کلاس‌ها را به صورت مناسبی طراحی کنیم. به طور مثال، در یک برنامه مدیریت سفارش‌ها، باید از رابط‌ها برای ارتباط بین کلاس‌های مدیریت محصولات، مدیریت مشتریان و مدیریت پرداخت‌ها استفاده کنیم.</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Sun, 21 May 2023 14:35:55 +0330</pubDate></item><item><title>تفاوت بین : DI (Dependency Injection) و  IoC (Inversion of Control) و  DI Container (Dependency Injection Container)</title><link>https://virgool.ir/@farhadnosrati/توضیحات-chatgpt-تفاوت-بین-di-dependency-injection-و-ioc-inversion-of-control-و-di-container-dependency-injection-container-bi3uaznglyzy</link><description>همه‌ی این الگوهای برنامه‌نویسی، مربوط به مفهوم Dependency Injection هستند که در زبان‌های برنامه‌نویسی مختلف مورد استفاده قرار می‌گیرند.در الگوی DI (Dependency Injection)، مسئولیت ایجاد شیء وابستگی‌های آن به دسترسی به دیگر شیء‌ها را به یک سرویس‌گیرنده (Service Consumer) منتقل می‌کنیم. به این صورت که به جای اینکه سرویس‌گیرنده خودش شیء مورد نیازش را ایجاد کند، آن شیء را از طریق پارامترهایی که به متد سازنده یا متد‌های دیگر ماژول ارسال می‌کنیم، دریافت می‌کند.درالگوی IoC (Inversion of Control)، کنترل اجرای برنامه از دست برنامه‌نویس خارج شده و به یک کتابخانه یا چارچوب نرم‌افزار مانند Spring Framework و Unity Container منتقل شده است. در این الگو، کتابخانه یا چارچوب نرم‌افزار برای مدیریت تمامی اجزای برنامه و ایجاد اتصالات بین آنها و اجرای لازم برنامه مسئولیت دارد.در الگوی DI Container (Dependency Injection Container)، یک کتابخانه یا چارچوب نرم‌افزار برای مدیریت و ایجاد شیء‌هایی که به یکدیگر وابسته هستند مسئولیت دارد. این الگو به برنامه‌نویس این امکان راهداف مربوط به توسعه‌ی اپلیکیشن، برای رسیدن به یک نتیجه‌ی مشخص و مطلوب، تعریف می‌شود و شامل چندین مرحله است. برخی از اهداف مهم توسعه‌ی اپلیکیشن عبارتند از:برآوردن نیازهای کاربران: یکی از مهم‌ترین اهداف توسعه‌ی اپلیکیشن، برآوردن نیازهای کاربران است. برای رسیدن به این هدف، تیم توسعه‌ی باید با کاربران و مشتریان برای درک نیازهای آن‌ها ارتباط برقرار کند و بر اساس آن، ویژگی‌ها و امکانات مورد نیاز را در اپلیکیشن پیاده‌سازی کند.ارائه‌ی یک راه حل مناسب: برای رسیدن به این هدف، تیم توسعه‌ی باید برای پیاده‌سازی اپلیکیشن، یک راه حل مناسب و بهینه ارائه دهد. برای این کار، تیم توسعه‌ی باید با تکنولوژی‌های جدید و به‌روز آشنا باشد و بر اساس نیازهای اپلیکیشن، از آن‌ها استفاده کند.افزایش کیفیت و کارایی: یکی از اهداف مهم توسعه‌ی اپلیکیشن، افزایش کیفیت و کارایی آن است. برای رسیدن به این هدف، تیم توسعه‌ی باید از بهترین روش‌های پیاده‌سازی و با استفاده از اصول و مفاهیم به‌روز استفاده کند. الگوی DI Container به برنامه‌نویس این امکان را می‌دهد تا با استفاده از یک کتابخانه یا چارچوب نرم‌افزار، کد خود را به صورت ماژولار پیاده‌سازی کند و برای ایجاد اتصالات بین اجزای مختلف برنامه از Dependency Injection استفاده کند. در این الگو، کتابخانه یا چارچوب نرم‌افزار برای مدیریت و ایجاد شیء‌هایی که به یکدیگر وابسته هستند مسئولیت دارد و برنامه‌نویس می‌تواند با استفاده از این الگو، کد خود را به صورت قابل و ماژولار پیاده‌سازی کند و برای ایجاد اتصالات بین اجزای مختلف برنامه از Dependency Injection استفاده کند. در این الگو، کتابخانه یا چارچوب نرم‌افزار برای مدیریت و ایجاد شیء‌هایی که به یکدیگر وابسته هستند مسئولیت دارد و برنامه‌نویس می‌تواند با استفاده از این الگو، کد خود را به صورت قابل توسعه و قابل انعطاف پیاده‌سازی کند.-----------------------DI یا Dependency Injection، IoC یا Inversion of Control و Container یا DI Container هر سه مفهوم مستقل و مختلفی هستند، اما با هم ارتباط دارند:Dependency Injection (DI):DI به معنی تزریق وابستگی‌ها است و به این معنی است که یک شیء می‌تواند وابستگی‌های خود را به جای این که خودش آن‌ها را ایجاد کند، به شیء دیگری با این وظیفه بسپارد. به عبارت دیگر، شیء مورد نظر خودش نمی‌تواند وابستگی‌های خود را بسازد، بلکه آن‌ها را از منابع خارجی دریافت می‌کند.Inversion of Control (IoC):IoC به معنی برعکس کردن کنترل است و به این معنیاست که کنترل اجرای برنامه از سمت شیء اصلی به شیء دیگری منتقل می‌شود. در IoC، شیء اصلی کنترل برنامه را به جای آن که خودش کد را اجرا کند، به شیء دیگری منتقل می‌کند. به عبارت دیگر، در IoC، شیء اصلی برای اجرای کد خود به شیء دیگری وابسته می‌شود.DI Container:DI Container یا اینورتر کنترل به عنوان یک container برای مدیریت وابستگی‌های یک برنامه استفاده می‌شود. این کانتینر قادر است که وابستگی‌های موجود در یک برنامه را مدیریت کند و در زمان لازم آن‌ها را به شیء‌های دیگر تزریق کند. بدین ترتیب، DI Container با استفاده از مکانیزم DI، کنترل اجرای برنامه را تغییر می‌دهد و به جای این که شیء اصلی وابستگی‌های خود را ایجاد کند، از DI Container برای دریافت وابستگی‌های خود استفاده می‌کند.به طور خلاصه، Dependency Injection (DI) و Inversion of Control (IoC) دو مفهوم مستقل و مختلف هستند، که هر دو به منظور کاهش وابستگی‌های بین کلاس‌ها و ماژول‌های یک برنامه استفاده می‌شوند. در حالی که DI Container به عنوان یک container برای مدیریت وابستگی‌های یک برنامه استفاده می‌شود و با استفاده ازمکانیزم DI، وابستگی‌ها را در زمان اجرای برنامه تزریق می‌کند. به عبارت دیگر، DI Container یک ابزار است که به شیء اصلی کمک می‌کند تا وابستگی‌های خود را مدیریت کند و به شیء‌های دیگر تزریق کند.</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Sun, 21 May 2023 13:28:35 +0330</pubDate></item><item><title>concurrency and parallelism</title><link>https://virgool.ir/@farhadnosrati/concurrency-and-parallelism-tefqc2ge3jv8</link><description>concurrency and parallelismfirst thing is concurrency and parallelism this sounds kind of a similar but they are not concurrency basically means that you execute things that are overlapping in time so you could have two or three any number of things executing and there seem to be executing at the same time but not necessarily they are happening in parallel so in order to do parallelism then you have you can execute multiple processes or have multiple threads right if you have a multi-core system Hardware then they could execute literally at the same time so what is the process and what is a thread basically there are many resources that on the internet that define them very well and they are also goods etc but here I will I will give you a very quick summary basically both are independent flows of execution in a in a program let&#39;s say but a process is at the program level and a threat is inside the process right so you can have multiple processes in each of the process having multiple threads up thread of a process share the memory thesame memory space so if you have different processes they will have their own memory space but if you have a thread like all the threads inside the process will share them the same memory space and that has some connotations where you are tackling concurrency when programming using multiple threadsthen it&#39;s called multi-threading programming .multi-threading programming can be very useful for your program because it will allow you to execute things in parallel and so times improved efficiency or the execution time of your program it&#39;s verytricky sometimes to know how many threads you should use and it would depend a lot on your use case on the programming languages are you using on the operating system the machine that you are then used in basically if you really really want to have an optimized the most optimized version you need todo benchmarking and check what is improving or not the performance of your applicationduring general there are different roles that allow you to more or less have an idea at least how to discuss the performance of your program if you want to do a concurrency or if you want toimplement multi-threaded solution traditional rule of thumb is to have as many threads of course have your hardware machine right and this is a rule of thumb but it&#39;s it&#39;s not always the way to go that&#39;s a because you can have things for example that execute always sequentially right so even if youhave four core or eight cores or how many cores you will need still to execute things sequentially you are not going to do better having four or eight threat versus having only one there are also other cases where you&#39;re doing a lot of calculations that can be wrong in powering in which case you cannot domore calculations that the CPU allows you at the same time so if you have four cores then you will have probably four threads executing calculations on all of them but also if you have IO or input I would operations during those times the CPU is going to be idle so it doesn&#39;t matter if you only have four threads in the four of them are doing I all like waiting for the t-score doing a requested data base or resolving a request from a user on the web server then the CPU is going to be either in those cases so in n that case might be useful to have more threats than the CPU cores because you will be able to alwayshave a tray that is executing something if you have to feel or too many threads your application can run significantly slower so it&#39;s very important to have this kind of reflection when you are thinking about doing things concurrently or and this is something very important to have when you&#39;re discussing with your teammates when you are a new solution and you want to define what is going to be the best way to optimize your server for example or your application it&#39;s good to have these kind of discussions but of course it&#39;s also very important or anything like an interviews if you need to you know think about these topics right like why you&#39;re taking one decision versus another what is going to be the performance impact of doing something versus another in terms of concurrency so that&#39;s it for today.Thank you very much for Reade and if you liked the Article don&#39;t  forget to click the like button, follow, share it. And if I missed something, or you want  me to explain something a little bit better don&#39;t forget to mention it in the comments.</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Tue, 26 Jul 2022 19:51:24 +0430</pubDate></item><item><title>software architecture and design (part 1)</title><link>https://virgool.ir/@farhadnosrati/software-architecture-and-design-volez1uivtcg</link><description>software architecture and designatchitecture (part 1)In this series of Article I will be covering many concepts and fundamentals of software architecture so hopefully by the end of it you will be more prepared to tackle software  design challenges, have better discussions  and even be more prepared for a technical  interview.So without further ado let&#39;s start Software architecture has many definitions one  of the most famous one is from Ralph Johnson  where he says: &#34;architecture is about  the important stuff, whatever that is&#34;  but...what is important stuff? In the  software architecture we focus more on the  structure more than implementation details.Software architecture is also about making  the expensive choices that are costly to change  after they are implemented.It&#39;s also about making explicit the core decisions that will allow  the software to have high quality.Concepts are better understood in practice, so let&#39;s  build an e-commerce site and see how that looks.So, for example in our e-commerce site we need to allow our users to do certain things like search the inventory, check reviews, buy a product, review past orders and maybe other features as well.These are the functional requirements  of the application. Besides of what the system  should do we also need to focus on how should  the system behave.These are also called the  non-functional requirements.These are sometimes  defined as the &#34;-ilities&#34; that the system can have  like: functionality, reliability, usability,  efficiency, these kind of things. For example,  in our e-commerce site let&#39;s say that we wanted it  to be maintainable for several years and this is  a maintainability requirement; we also want to be  able to serve millions of users: in this case it&#39;s  scalability; we also want to make it available  24/7: which is a reliability to make sure that the  system is very stable.We also want to have good  response latency: which is efficiency; and we can  have many others.Finally, besides functional  and non-functional requirements you may also have additional restrictions that will limit the  options that you will have for your architecture.So, for example, we could have some legal  compliance, costs, time to market, standards, etc.several restrictions that will limit the number  of options that we will have to design our system. Let&#39;s say in our e-commerce site we need  to comply with the European Privacy Law:GDPR.So, with that we need to take into  consideration architecture how to handle that.So after you get the context, you know all  the things that the system needs to do,how it should behave and what restrictions are in  place that you need to take into consideration. So  after you have all of these things, you need  to prioritize them.Some requirements and  restrictions will conflict between them.For  example, if you have a strict time to market,  maybe you need to drop some features.There  can also be other things like non-functional  requirements that need to be prioritized.So for  example, in our case of the e-commerce site, we  might not care too much about portability because  we will have a strong control of where it&#39;s going to be deployed the application and after it&#39;s  deployed in there we don&#39;t plan to move it to other platforms. So we could drop portability in  favor of scalability or maintainability. So after you have prioritized the list and you have made  this trade-off you need to think about if it&#39;s acceptable or not so after it&#39;s acceptable  then you start designing the architecture. How do you start designing the system? So the  first thing is once you have it prioritized start with one important thing at a time. If you try  to tackle everything at the beginning and trying to think about all the possible scenarios in the  future, you might end up having an over-engineered solution and this is not good because it is an  unnecessarily complex system. There is also an acronym for that is YAGNI: You Ain&#39;t Going to Need  It. So if you are not sure about something or if it&#39;s not prioritized then try not to tackle at  first. Try to postpone it to when you have better context and can make a better decision about it.  Now that you have this you might start thinking about what are the possible architectures that  might fit your system. For that, a good book that I recommend and that is useful for me, is  this free ebook from O&#39;Reilly that is: &#34;Software Architecture Patterns&#34;. It is a good book to get  a grasp of different architectural approaches and you can see several architectural patterns  like: Layered, Event Driven, Microkernel, Microservices and Space based. This book shows pros and cons  of each of these patterns and might help you at  the beginning when you&#39;re designing the system  what to look for and what would be best for your system based on your current requirements. So we  have decided what are some of the features that we want to have our system implemented.We have  also mentioned that maintainability is one of the non-functional requirements that is very important  for us, so with that we can start designing our system and we can take for example a Layered  approach. We could have a Database or a Storage Layer where we would sort data; then we will  have a Logic Layer where we will have the backend servers that will be taking care of handling  any business logic that we want to handle; and then the Visualization part or UI, where we&#39;ll  be allowing the users to interact with the system and this is how we get to the Layered Architecture So here we have defined the architecture  with the structure that the system will have. The features can then be implemented following  this layered architecture. And if you want to learn a little bit more about how to implement the  features in a scalable nice way then I recommend you to check out my other video about design  patterns. It&#39;s very typical in web applications to use a layered architecture but it&#39;s not the  only architectural pattern that we can use. There is no silver bullet so make sure that in  your context you look to different approaches and pick the architecture pattern that would better  fit your use case. It&#39;s also normal that the architecture would evolve over time and sometimes  even in unintended ways that would make expensive changes to the architecture. So you need to make  a balance about foreseeing the certain things that you will need to cover versus the things  that you need to prioritize in the short term.If you try to tackle everything then you can  end up having an overengineered solution.One of the most expensive things can be scaling. So, in our case, we already have the architecture: How can we make it scale to serve  millions and millions of user requests? For that check out my next Article. And  that&#39;s it for today! Thank you very much for Reade and if you liked the Article don&#39;t  forget to click the like button, follow, share it. And if I missed something, or you want  me to explain something a little bit better don&#39;t forget to mention it in the comments.</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Tue, 26 Jul 2022 19:48:38 +0430</pubDate></item><item><title>Asynchronous programming</title><link>https://virgool.ir/@farhadnosrati/asynchronous-programming-qq3v8v5iurpr</link><description>قبل شروع یک rollback بزنیم به Synchronous programming .در این نوع programming برنامه Sequential  اجرا میشه یعنی ؟نرم افزار بر روی یک thread  اجرا شده و clr شروع میکند خط به خط کد ها را اجرا کردن و اگر خط قبلی اجرا نشود هیچوقت خط بعدی اجرا نخواهد شد .اشکال : اگر اجرای یک خط کد ۱۰ ثانیه زمان ببرد ، چون عملیات فوق (عملیات ۱۰ ثانیه ) بر روی thread اصلی نرم افزار ( main thread )  اجرا شده است ، نرم افزار تا اتمام آن خط در همان حالت و وضعیت منتظر میماند تا ۱۰ ثانیه به پایان برسد بعد وارد خط بعدی شود .مثلا فرض کنید اگر ما یک عملیات زمانبری داشته باشیم مثل گذارش گیری ماهانه یا قرار باشد همزمان علاوه بر گذارش گیری چند عملیات دیگر هم انجام دهیم ، در این شرایط انجام عملیات همزمان ( parallel ) غیر ممکن است چون عملیات گذارشگیری بر روی  main thread نرم افزار اجرا شده ، در این شرایط نه تنها عملیات دیگری نمیتواند اجرا شود بلکه  کل نرم افزار صبر میکند تا عملیات اول ( گذارشگیری ) به اتمام برسد بعد وارد عملیات بعدی شود  .ما نیاز داریم نرم افزار ، همزمان با گذارشگیری ، عملیات دیگری هم اجرا کند . اینجاست که باید برویم سراغ Asynchronous programming :در Asynchronous programming نرم افزار ما در شرایط ذکر شده صبر نمیکند و میتواند همزمان با گذارش گیری ، عملیات های دیگری هم اجرا کند .  سوال : چگونه این اتفاق شدنیست ؟ این سوال مربوط  به multi threading  است ولی در این متن تصميم ندارم وارد مباحثش شوم ولی توضیح مختصری ازش میگویم که گوشیه زهنمان باشد .سی پی یو cpu چند هسته ای هست و هر هسته از چندین thread تشکیل شده که ما میتونیم برای این که سرعت و کارایی نرم افزارمان را بالا ببریم و از تمام قدرت cpu برای اجرای نرم افزارمان استفاده کنیم .چون یک thread نمیتواند بیشتر از یک process را هندل کند ، میتوانیم جدا از main thread  ، از thread های دیگر cpu به طور مستقل از هم و همزمان استفاده‌ کنیم .نکته : در حالت multi threading نباید چند عملیات که همزمان اجرا میشوند به هم وابسته باشند چون اگر اجرا شدن متدی ، وابسته به نتیجه ( return ) متد دیگر باشد ، خطا رخ میدهد و برای این نوع عملیات حتما باید صبر کرد .کاربرد اصلی Asynchronous این است که نرم افزار منتظر نمیماند به این شکل که main thread نرم افزار را آزاد میکنیم و برای هر عملیات یک thread دیگر و مستقل تخصیص میدهیم .دو کلمه کلیدی Async awayt :Async :  این کلمه کلیدی در ابتدای متد اعلام میکرد که متد جاری یک متد از نوع Asynchronous است ‌.توجه : در اتمام نام متد های Asynchronous حتما باید با  Async پایان یابد که زمان خوانده نام متد ، مشخص شود این متد یک متد Asynchronous است .مثل نام متد زیر :GetCarNameAsync();وقتی متدی از نوع Async تعریف می شود باید از یک سری قوانین تبعیت کند :متدی که از نوع Async است حتما باید یک Task را اجرا کند و خروجی از نوع Task  برگرداند که میتواند یک Task از نوع T باشد .به شکل زیر :Task  Task&lt;T&gt; ==&gt; Task&lt;int&gt; , Task&lt;string&gt; voidیک task میتواند اجرای یک یا چند thread  باشد که وقتی یک متد  async اجرا میشود در واقع clr آن را در یک thread مستقل و همزمان با thread های دیگر و در background اجرا میکند .نکته:  توجه داشته باشید که یک thread  نمیتواند مقدار بازگشتی داشته باشد ولی Task میتوان و یک thread میتواند فقط یک process رو هندل کند ولی Task چندیدن کار را میتوند انجام دهد .البته همیشه و حتما معنای task در ابتدای متد ها به معنای run شدن آن متد در یک thread جدید نیست که جلوتر توضیحات کامل ارائه میشود . Await : اگر متدی در یک thread مستقل اجرا شود در واقع ، وقتی کامپایلر به بلاک آخر متد برسد آن thread بسته میشود و هیچ وقت از وضعیات عملیاش و در صورت بروز هر گونه خطا در زمان اجرا باخبر نخواهیم شد پس حتما برای متد های Async task صبر کنیم مگر در شرایط خواص .برای این کارمیتوانیم از کلمه کلیدی  await استفاده کنیم .این کلمه کلیدی این امکان را میدهد که ما در thread اجرا شده صبر کنیم و تا به پایان نرسیدن عملیات ،  thread بسته نشود .به مثال زیر توجه کنید :static async Task Main(string[] args)         {             var threadId = Thread.CurrentThread.ManagedThreadId;             Console.WriteLine(&#34;Main:&#34; + threadId);             await GettaskAsync();             threadId = Thread.CurrentThread.ManagedThreadId;             Console.WriteLine(&#34;Main:&#34; + threadId);             Console.ReadKey();         }         public static async Task GetTaskAsync()         {             var threadId = Thread.CurrentThread.ManagedThreadId;             Console.WriteLine(&#34;GettaskAsync:&#34; + threadId);             await Task.Delay(1000);             threadId = Thread.CurrentThread.ManagedThreadId;             Console.WriteLine(&#34;GettaskAsync:&#34; + threadId);         }شرح کد : متدی از نوع async به نام Main بر روی thread اصلی نرم افزارمان اجرا است ، وقتی کامپایلر بر روی await داخل متد Main میرسد ، با همان thread جاری وارد متد async فراخوان شده ( GetTaskAsync )  میشود و شروع به پردازش متد دوم میکند ، حال اگر در بدن متد فراخوان شده await داشته باشیم و به آن برسد ، در صورت این که پردازش آن Await زمان بر باشد و باعث معلق شدن نرم افزار یا thread اصلی شود ، در این لحضه thread اصلی ( جاری ) را آزاد کرده و یک thread جدید اجرا میکند و به صورت yield تا پایان عملیات به متد فراخوانی شده ما گوش میدهد و در زمان پایان عملیات thread جدید ، ادامه عملیات خود را با همان thread جدید ساخته شده ادامه داده و به متد اول باز می گردد ، که این کار یک روش . net ، برای جلوگیری از thread deadlock است .حال اگر در متد دوم ( thread دوم ) هم یک await باشد ، عملیات بالا را تکرار میکند .در این شرایط نرم افزار هنگ نمیکند چون این روش یک multi tasking بود و در دات نت ، async یک signature برای پیاده سازی multi tasking است .قطعا برنامه نویسی muti threading پیچیده است به همین دلیل .net با روش TAP این امکان را به ما میدهد که با استفاده از کلمات کلیدی async/await/Task کد های مشابه sync بنویسیم و کامپایلر زحمت تغییرش به multi threading برایمان بکشد .قوانین استفاده از async/await/Task :یک بار async و همواره async : • هیچوقت یک متد async را در یک متد sync فراخوان نکنید . برای متد های sync متد معادل sync توسعه دهید و برای متد های async متد های معادل async توسعه دهید و استفاده کنید .• هیچوقت از دستور .result و یا .wait استفاده نکنیند چرا که این کار باعث بروز deadlock شود . به کد زیر توجه کنید :public  string GetCarAsync()         {             return GetCarNameAsync():         }         public async Task&lt;string&gt; GetCarNameAsync()         {             await Task.Run(() =&gt; Task.Delay(1000));             return &#34;praid&#34;;         }شرح :امکان return نتیجه متد async در یک متد sync وجود ندارد چون متد async یک Task برمیگرداند . متن خطای کد بالا &#34;امکان تغییر string یه یک Task&lt;straing&gt; به روش implicit  وجود ندارد&#34; .  پس برای این که بتوانیم صورت مسئله را پاک کنیم ، از دستور .result در انتهای فراخوانی اضافه میکنیم تا مقدار result  (که مقدار string باشد) را از Task گرفته ( جداکرده ) و نتیجه یک string بازگرداند .return GetCarNameAsync().result; علت خطا : این کار باعث اجرا شدن عملیات async به روش sync میشود ولی همواره دو thread درگیر هندل کردن یک عملیات sync هستند ، در این شرایط دات نت thread را lock کرده و deadlock رخ میدهد  .پیشنهاد : استفاده از GetAwaitr().GetResult(); به جای دیتور .result. در شرایطی که ناچار به استفاده از متد async در متد sync شدید ، به جای دستور .result میتوانید از دستور زیر برای این کار استفاده کنید return GetCarNameAsync().GetAwaitr().GetResult();این دستور اجازه میدهد زمان بروز exception در thread ، به جای throw کردن aggregate exception ، میتواند exception را برگرداند .• دستور  Task.Run .متد Run به عنوان ورودی یک Delegate از نوع Action میگیرد و بلافاصله یک thread اجرا کرده و delegate دریافت کرده را در thread جدید اجرا می کند . پس به جز مواقع نیاز و ضروری ، از این دستور استفاده نکنیم چون یک thread از thread pool را اشغال میکند .نکته مهم : در صورت لزوم میتوانید ، به جای Task.Run از دستور Task.FromResult استفاده کنید ، با همان عملکرد و با این تفاوت که thread جدید اجرا نمیکند .توجه : ما به جای delegate action از lambda expression استفاده کرده ایم .به متد های زیر توجه کنید :public async Task&lt;string&gt; GetCarAsync()         {             return await GetCarNameAsync();         }         public async Task&lt;string&gt; GetCarNameAsync()         {  await Task.Run(() =&gt; Task.Delay(1000));             return &#34;praid&#34;;         }نکته : اگر برای دستور Task.Run مانند دستور بالا await استفاده کنید یعنی برایش صبر کنید ( مانند کد بالا)  ، clr برای اجرا شدن آن thread ، در همان line صبر میکند و بعد از پایان عملیات به خط بعدی میرود ولی اگر await نداشته باشد ، thread جدید ساخته و همزمان با thread اصلی در background اجرا میشود ، ولی با این تفاوت که clr منتظر پایان این thread نمیماند و بعد از اجرای خط Task.Run به خط بعدی میرود و همزمان thread جدید در backgroun اجرا است  .توجه : اگر متد خروجی نداشته باید فقط Task برمیگرداند پس به ۳ دلیل هیچگاه از دستور async Task void استفاده نکنید . 1 - در متد های async دیگر نمیتوانیم فراخوانی و await کنیم ، چون خروجی آن از نوع void است نه Task ، پس حتما نتیجه بازگشتی آن با Task متفاوت است و احتمال بروز dead lock در عملیات آن وجود دارد .نکته : استفاده از این روش در شرایط خیلی خواص و با دانش و اگاهی کامل از عملیاتی که در حال توسعه و پیاده سازی ان هستیم بلامانع است . 2- در متد async Task void اگر exception رخ دهد ، نمیتوانید آن را throw catch کنید چون نتیجه متد از نوع void است و مقداری بر نمیگرداند .دوستان عزیز ، خیلی ممنون که وقت گران قدرتونو گذاشتید و تا به اینجا همراهیم کردید .اگه اشتباه مفهومی داشتم یا ایراد تایپی داشتم یا ایراد توضیح داشتم خیلی خوشحال میشم بهم بگید تا اصلاح کنم .مخلصیم ...</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Fri, 25 Feb 2022 16:57:30 +0330</pubDate></item><item><title>آموزش IHttpClientFactory در DotNetCore</title><link>https://virgool.ir/@farhadnosrati/آموزش-ihttpclientfactory-در-dotnetcore-n1coattjo2l0</link><description>سلام دوستان .آموزش کامل IHttpClientFactory رو تو کانال YouTube آپلود کردم ، از این لینک میتونید مستقیم برید برای دیدنش .فقط چند تا نکته رو فراموش کردم بگم که اینجا مینویسم .نکته که فراموش کردم بگم : مختصر : کلاس HttpClient یه کلاس thread safe هست به جز اون چهارتا property داخلش که thread safe نیست ( ینی اگه بین دوتا درخواست baseaddres رو عوض کنید کل درخواستاتون baseaddresش تغییر میکنه ) که میتونیم به صورت singleton تو کل نرم افزار استفاده کنیم ولی باز از تغییرات dns هاتون باخبر نمیشیم .لطفا و حتما نظرتونو برام بنویسید .مخلصیم ...</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Wed, 16 Feb 2022 23:05:19 +0330</pubDate></item><item><title>static در #C ( سی شارپ  )</title><link>https://virgool.ir/@farhadnosrati/تعریف-static-تو-سی-شارپ-c-lhdjfy9ob0ig</link><description>برای هر نوع پروسه‌ای که در آن بارگذاری می‌شود RAM به سه قسمت تقسیم می‌گردد : Stack ، Heap و Static (استاتیک در دات نت در حقیقت قسمتی از Heap است که به آن High Frequency Heap نیز گفته می‌شود ).این قسمت استاتیک حافظه، محل نگهداری متدها و متغیرهای استاتیک است. آن متدها و یا متغیرهایی که نیاز به نمونه سازی از کلاس برای ایجاد ندارند، به صورت استاتیک ایجاد می‌گردند. در سی شارپ از واژه کلیدی static برای معرفی آن‌ها کمک گرفته می‌شود. برای مثال:گفته شد که کامپایلر تضمین می کند هیچ شی ای (object) از نوع کلاس های استاتیک (static class) ساخته نشود و فراخوانی اعضای کلاس از طریق نام کلاس انجام می شود.کلاس استاتیک ( Static class )از دیدگاه کلی یک کلاس استاتیک (static class) مانند یک کلاس غیر استاتیک (none static class) است با همان بدنه و تعاریف ، هر چند در بین تعریف این دو نوع از کلاس (class) تفاوت ها یی وجود دارد :قبل از کلمه کلید class از کلمه کلیدی static استفاده می شود.از یک کلاس استاتیک (static class) نمی توان نمونه (instance) و یا شی (object) ایجاد کرد به عبارت دیگر استفاده از کلمه کلیدی new برای ایجاد و مقدار دهی متغیری (شی) از نوع  داده یک کلاس استاتیک(static class)  بی معنا است. در این نوع از کلاس  یعنی کلاس استاتیک (static class) برای دسترسی به اعضای کلاس (members) مانند ، فیلد ها (fields) و متد ها (methods) از نام کلاس استاتیک (static class)  استفاده می شود .مثال : فراخوانی یک متد (method) با دسترسی عمومی (public) در یک کلاس استاتیک (static class).● تمامی اعضای یک کلاس استاتیک (static class) از نوع استاتیک (static) هستند. در واقع یک کلاس استاتیک (static class) را می توان مخزنی از متد های استاتیکی (static methods) دانست که برای انجام عملی که به منظور انجام آن بوجود آمده اند تنها با پارامترهای ورودی خود ، کار می کنند .● کلاس استاتیک (static class)  مهر وموم شده (sealed) هستند در نتیجه نمی توان از انها ارث (inheritance) برد.● کلاس استاتیک (static class)  نمی تواند از هیچ  کلاسی ارث بری (inheritance) داشته باشند بجز کلاس object ( تمام کلاس ها به صورت مستقیم و غیر مستقیم از کلاس object ارث بری داند)● کلاس استاتیک (static class) نمی توانند سازنده (constructor )  غیر استاتیک داشته باشند البته  قطعا از سازنده استاتیک (static constructor) می توانند برای مقدار دهی متغیر های استاتیک خود استفاده کنندنکته : یک کلاس استاتیک (static class) مانند یک کلاس غیر استاتیک (none static class) با سازنده خصوصی (private constructor) است  که تمامی اعضای داده ای (data members) واعضای تابعی (functional members) در آن از نوع استاتیک (static) تعریف شده باشند. در این حالت سازنده  خصوصی (private constructor) می تواند تضمین کننده این باشد که هیچ شی ای از این  کلاس ساخته  نشود.  اما در مورد یک کلاس استاتیک (static class) این برتری  وجود دارد که کامپایلر تضمین می کند که هیچ شی ای (object) از یک کلاس استاتیک(static class) ساخته نمی شود، در کنار خصوصیات دیگری که در بالا از ان ها نام برده شد.اعضای استاتیک ( Static members )● اعضای استاتیک (static members) نه تنها در کلاس های استاتیک (static class) قابل استفاده هستنند بلکه در کلاس های غیر استاتیک (none static class) نیز می توانند تعریف شوند.● متد های استاتیک (static methods) و خصوصیات استاتیک (static properties) نمی توانند دربدنه خود به فیلدهای غیراستاتیک (none static fields) و یا رخدادهای غیراستاتیک (none static events) و یا شی (object) تعریف شده در بدنه کلاس دسترسی داشته باشند مگر اینکه به صورت پارامتر به ان ها ارسال شوند.● اعضای غیر استاتیک (none static members) می توانند دربدنه خود به اعضای استاتیک (static members)  دسترسی داشته باشند .تفاوت اعضای استاتیک و غیراستاتیک● اعضای استاتیک (static members) بین تمامی شی های یک کلاس مشترک هستند و مقدار ان ها در بین تمامی شی های یک کلاس مشترک است. به عبارت دیگر متعلق به کلاس هستند .● اعضای غیر استاتیک متعلق به شی های ایجاد شده هستند نه کلاس.فیلدهای استاتیک :نکته :دو تا از موارد استفاده از فیلد های استاتیک :• در نگه داشتن  تعداد شی های ایجاد شده از یک نوع داده کلاس درسی شارپ• نگه داشتن مقادیر مشترک بین تمام نمونه های ایجاد شده از یک نوع داده کلاس درسی شارپمتد های استاتیک :نکته● در کلاس استاتیک میتوانیم متود overloading داشته باشیم و تو کلاس عادی هم میتونیم کلاس استاتیک overloading داشته باشیم .● متد های استاتیک (static methods) می توانندoverload شوند اما نمی توانند override شوند. زیرا انها متعلق به کلاس هستند و نه یک شی. ( در رابطه با overloading و overriding در مقالات بعدی بحث خواهد شد. )● سی شارپ از متغیرهای استاتیک (static variables) که به صورت محلی در بدنه متدهای استاتیک (static methods) و غیراستانیک (none static methods) ، تعریف می شوند حمایت نمی کند و ایجاد خطای زمان کامپایل می کند.● از نظر سرعت اجرا بین دو تا متد استاتیک (static method) و غیراستاتیک (none static) با بدنه یکسان ، متد استاتیک (static method) یک مقدار سریع تر اجرا می شود. زیرا در فراخوانی متد غیراستاتیک ، باید غیر نال بودن شی (object) نیز چک شود که زمان بیشتری را به اجرا تحمیل می کند. هرچند اختلاف زمانی در بیشتر موارد بسیار ناچیز استنکته‌ی مهمی که در اینجا وجود دارد این است که متدهای استاتیک تنها قادر به استفاده از متغیرهای استاتیک تعریف شده در سطح کلاس هستند.دوستان عزیز ، خیلی ممنون که وقت گران قدرتونو گذاشتید و تا به اینجا همراهیم کردید .اگه اشتباه مفهومی داشتم یا ایراد تایپی داشتم یا ایراد توضیح داشتم خیلی خوشحال میشم بهم بگید تا اصلاح کنم .مخلصیم ...</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Tue, 15 Feb 2022 22:47:24 +0330</pubDate></item><item><title>Interface segregation Principle و dependency Inversion Principle</title><link>https://virgool.ir/@farhadnosrati/interface-segregation-principle-vs-dependency-inversion-principle-jeev9w5ct4xt</link><description> دوتا اصل اخر solid که تلاش برای استفاده درست از Interface  ها دارد .Interface قابلیت های یکسان و پیاده سازی های متفاوت .هدف : اعمال قوانین و رفتار ( behavior ) به نرم افزار ‌که پیشنهاد شده برای تمام ،  ( اصطلاحا module هامون ) قوانین داشته باشیم . Interface segregation Principle چهارمین اصل solid : اصل جداسازی Interface ها .در این اصل تاکید میشود که کلاس ها نباید متد هایی که نیاز ندارن را پیاده سازی کنند ( اصطلاحا ، چرا باید متد هایی را در اختیار کاربر ( developer ) قرار دهیم که هیچوقت قرار نیست برایشان کد تولید شود و فقط NotImplimentedException ، throw میکند ) مثال : فرض کنید یه روال کار ( workflow ) داریم ، که سه مرحله دارد .زمان پیاده سازی : تمام این سه مرحله را در یک interface قرار دهیم ( اصطلاحا : سه signature در یک interface ) و به اعضای هر مرحله ، در سه کلاس جدا پیاده سازی کنیم ‌( یعنی یک پدر و ۳ فرزند یا inheritance ) . یادآوری : وقتی یک کلاس از Interface ارث بری میکن موظف است تمام signature های آن را پیاده سازی کند )‌پس این عمل موجب میشود در هر پیاده سازی فقط یکی از مراحل ( method ها ) رفتار یا بدنه مربوط به آن مرحله را داشته باشه و دوتا متد دیگه  NotImplimentedException پرتاپ کنند چون عملکرد انها مربوط به آن کلاس نیست .حالا فرض کنید ، اگر بعد ها یک مرحله دیگر به مراحلمان اضافه شود، چیکار باید بکنیم ؟؟؟ :)در این شرایط دو تا اصل solid را ( srp ، isp ) رعایت نمیکنیم .نتیجه : به همان اندازه که کاربرمان ( developer ) نیاز دارد ، متد در اختیارش قرار میدهیم ، نه بیشتر . Dependency Injection pattern یکی از راه های پیاده سازی اصل پنجم solid یعنی dependency Inversion Principle  (معکوس سازی وابستگی ها ) استفاده از Dependency Injection pattern هست که ، از اصل delegation استفاده میکند .معکوس سازی وابستگی ها یعنی ماژول های سطح بالا ( high-level module ) ، نباید به ماژول های سطح پایین ( low-level module ) وابسته باشند و یا از آنها خبر داشته باشند ( اصطلاحا،  لایه services نباید به لایه DAL وابسته باشه ). : Delegation یا DIهردو یک وظیفه دارند : وابستگی ها از کلاس ها خارج میکنند و به جای این که کلاس هایمان به پیاده سازی ( concrete ) وابسته شوند ، انهارا به یک سری قرار داد ( contract ) وابسته میکنند . تعریف delegation : تصور کنید ، یک سرویس داریم که یک سری وظایف دارد ، برای انجام وظایفش نیاز به یک سری اشیاع بیرون دارد ( مثل class ) که اگر خود سرویس ، مسئولیت تهیه این وابستگیها را داشته باشد ، کاملا به انها وابسته میشود ( مثل نمونه سازی از کلاس اشیا داخل سرویس ) .برای این که وابستگی ایجاد نشود ، میتوان مسئولیت ساخت نیازمندیها را به کلاس سرویس گیرنده ( استفاده کننده از سرویس ) داد و برایش قانون وضع کرد که اگر کسی خواست از کلاس سرویس دهنده استفاده کند ، باید خودش مسئولیت ساخت نیازمندیهایش را زمان نمونه سازی از سرویس‌دهنده ، برایش براورده کند .با این روش ما تمام وابستگی ها را از داخل سرویس دهنده خارج یا حذف میکنیم و مسئولیت آن را به سرویس گیرنده میدهیم ولی این روش مشکل ساز و سخت است . چطور از اصل DI یا delegation به درستی استفاده کنیم و مسئولیت تهیه نیازمندی هارا از سرویس گیرنده ، بگیریم و آن را به یک کلاس خارجی دیگر وابسته کنیم ( ارجاع بدیم جای دیگر ) . : DIContinerیه نوع service provider هست ( با رعایت اصل Delegation ) .تعریف : همه وابستگی ها را یکجا ( در یک subsystem ) تعريف میکنیم و همان subsystem مسئول مديريت همه وابستگی های نرم افزار ماست .برایش قانون وضع میکنیم  :مثال قانون : هر کی از سرویس Isms خواست استفاده کند ، کلاس sms را بهش پاس بده و بعد ها اگر business سرویس sms مان تغییر کند ، فقط کافی اسم کلاس sms فعلی را به اسم کلاس جدید مثل NewSMS تغییر دهیم .بدون این که به کد هایمان دست بخورد ، رفتار جدید را جایگزین رفتار قبلی کردیم .هدف :جداسازی از وابستگی .منعطف بودن نرم افزار یعنی قسمتی از نرم افزار رو تغییر بدیم بدون این که به جای دیگر نرمافزار دست بخوره .قابلیت تست بالا .اگه ایراد تایپی داشتم یا ایراد توضیح داشتم خیلی خوشحال میشم بهم بگید تا اصلاح کنم .</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Tue, 23 Nov 2021 19:05:11 +0330</pubDate></item><item><title>انواع تست DotNet Core</title><link>https://virgool.ir/@farhadnosrati/انواع-تست-dotnet-core-qdxk7bngjmlx</link><description>انواع تست .1.  Ui Test . 2. Subcutaneous Testنوعی high-level Test هست که برای تست اولین لایه بعد از ui از این تست استفاده میشه . تو این نوع تست api به بعد و خود api هارو تست میکنیم و تمام نیاز مندی هاشونو مثل ارسال  data های ورودی ، تمام Dependency injaction هارو براشون میسازیم و ارسال می کنیم ، برای مثال اگر به دیتابیس connect میشه و persistent data انجام میده ، تو تستمون هم باید به طور واقعی باید data ذخیره کنیم .تقریبا میشه گفت یه شبیه‌سازی کامل میشه .3. Integration Testبرای تست لایه logic از این تست استفاده میکنم .توجه کنید طبق گفته خودشون : لایه logic و لایه infrastructure  به هم و هیچ تکنولوژی نباید وابسته باشه . ما فرض میکنیم لایه infrastructure به ORM وابسته هست و کنارش با سرویس ها هم کار میکنه .اگر تست ما با ساخت وابستگی ها و ارسالشون به هدفمون باشه ، نوع تستمون میشه Integration Test ولی اگه بدون ساخت وابستگیهاش باشه ، میشه Unit .برای ساخت وابستگیها میشه از Moq استفاده کرد .تست ها ۳ فاز دارن که اصطلاحا بهشون AAA هم میگن .1. A : arrangeقراره یه لاجیک تست کنیم ، نیازه که ما نیازمندی های هدفمون رو شبیه‌سازی کنیم ، یعنی : نمونه سازی کنیم یا یه سری متغیر تاریف کنیم تا بتونیم تست کنیم . 2. A : act متدی که باید تست بشرو ، تست میکنیم ( call میکنیمش ).3. A : assert نتیجه یا خروجی تست .#infrastructure #ui #data #unittesting #moq #xunit #ddd</description><category>Farhad Nosrati فرهاد نصرتی</category><author>Farhad Nosrati فرهاد نصرتی</author><pubDate>Sun, 21 Nov 2021 00:47:11 +0330</pubDate></item></channel></rss>