検索
ホームページJava&#&チュートリアルBelay the Metamorphosis: Kafka プロジェクトの分析

您有沒有想過跨國公司的專案原始碼中可能潛藏著哪些錯誤?不要錯過在開源 Apache Kafka 專案中發現 PVS-Studio 靜態分析器偵測到的有趣錯誤的機會。

Belay the Metamorphosis: analyzing Kafka project

介紹

Apache Kafka 是一個著名的開源項目,主要用 Java 寫。 LinkedIn 於 2011 年將其開發為訊息代理,即各種系統元件的資料管道。如今,它已成為同類產品中最受歡迎的解決方案之一。

準備好看看引擎蓋下的內容了嗎?

附註
只是想簡單說明一下標題。它參考了弗朗茨·卡夫卡的《變形記》,其中主角變成了可怕的害蟲。我們的靜態分析器致力於防止您的專案變身為可怕的害蟲轉變為一個巨大的錯誤,所以對「變形記」說不。

喔不,蟲子

所有的幽默都源自於痛苦

這不是我的話;這句話出自理查德·普賴爾之口。但這有什麼關係呢?我想告訴你的第一件事是一個愚蠢的錯誤。然而,在多次嘗試理解程式無法正常運作的原因後,遇到以下範例的情況令人沮喪:

@Override
public KeyValueIterator<windowed>, V> backwardFetch(
  K keyFrom,
  K keyTo,
  Instant timeFrom,
  Instant timeTo) {
  ....
  if (keyFrom == null && keyFrom == null) {   // 



<p>如您所見,這是任何開發人員都無法避免的事情——一個微不足道的拼字錯誤。在第一個條件下,開發人員希望使用下列邏輯表達式:<br>
</p>

<pre class="brush:php;toolbar:false">keyFrom == null && keyTo == null

分析器發出兩個警告:

V6001 在「&&」運算子的左邊和右邊有相同的子運算式「keyFrom == null」。 ReadOnlyWindowStoreStub.java 327、ReadOnlyWindowStoreStub.java 327

V6007 表達式「keyFrom == null」總是 false。 ReadOnlyWindowStoreStub.java 329

我們可以明白為什麼。對於每個開發人員來說,這種可笑的打字錯誤都是永恆的。雖然我們可以花很多時間尋找它們,但要回憶起它們潛伏的地方可不是小菜一碟。

在同一個類別中,另一個方法中存在完全相同的錯誤。我認為稱其為複製麵食是公平的。

@Override
public KeyValueIterator<windowed>, V> fetch(
  K keyFrom,
  K keyTo,
  Instant timeFrom,
  Instant timeTo) {
  ....
  NavigableMap<k v> kvMap = data.get(now);
  if (kvMap != null) {
    NavigableMap<k v> kvSubMap;
    if (keyFrom == null && keyFrom == null) {      // 



<p>以下是相同的警告:</p>

<p>V6007 表達式「keyFrom == null」總是 false。 ReadOnlyWindowStoreStub.java 273 </p>

<p>V6001 在「&&」運算子的左邊和右邊有相同的子運算式「keyFrom == null」。 ReadOnlyWindowStoreStub.java 271, ReadOnlyWindowStoreStub.java 271</p>

<p>不用擔心——我們不必一次查看數百行程式碼。 PVS-Studio 非常擅長處理這類簡單的事情。解決一些更具挑戰性的事情怎麼樣? </p>

<h3>
  
  
  可變同步
</h3>

<p>Java 中 <em>synchronized</em> 關鍵字的用途是什麼?在這裡,我將只關注同步方法,而不是區塊。根據 Oracle 文檔,<em>synchronized</em> 關鍵字將方法聲明為同步,以確保與實例的線程安全互動。如果一個執行緒呼叫該實例的同步方法,則嘗試呼叫相同實例的同步方法的其他執行緒將被阻塞(即它們的執行將被掛起)。它們將被阻塞,直到第一個執行緒呼叫的方法處理其執行。當實例對多個執行緒可見時,需要執行此操作。此類實例的讀取/寫入操作只能透過同步方法執行。 </p>

<p>開發人員違反了 <em>Sensor</em> 類別中的規則,如下面的簡化程式碼片段所示。實例欄位的讀取/寫入操作可以透過同步和非同步兩種方式執行。它可能會導致競爭條件並使輸出變得不可預測。 <br>
</p>

<pre class="brush:php;toolbar:false">private final Map<metricname kafkametric> metrics;

public void checkQuotas(long timeMs) {                  // 



<p>分析器警告如下:</p>

<p>V6102 “metrics”欄位同步不一致。考慮在所有用途上同步該欄位。感測器.java 49,感測器.java 254</p>

<p>如果不同的執行緒可以同時變更實例狀態,則允許此操作的方法應該同步。如果程式沒有預料到多個執行緒可以與實例交互,則使其方法同步是沒有意義的。最壞的情況下,甚至會損害程式效能。 </p>

<p>程式中有很多這樣的錯誤。這是分析器發出警告的類似程式碼片段:<br>
</p>

<pre class="brush:php;toolbar:false">private final PrefixKeyFormatter prefixKeyFormatter; 

@Override
public synchronized void destroy() {                // (
    prefixKeyFormatter.addPrefix(record.key),
    record.value
    ), batch
  );
} 

@Override
public synchronized void deleteRange(....) {        // 



<p>分析器警告:</p>

<p>V6102 “prefixKeyFormatter”欄位同步不一致。考慮在所有用途上同步該欄位。 LogicalKeyValueSegment.java 60、LogicalKeyValueSegment.java 247</p>

<h3>
  
  
  Iterator, iterator, and iterator again...
</h3>

<p>In the example, there are two rather unpleasant errors within one line at once. I'll explain their nature within the part of the article. Here's a code snippet:<br>
</p>

<pre class="brush:php;toolbar:false">private final Map<string uuid> topicIds = new HashMap(); 

private Map<string kafkafuturevoid> handleDeleteTopicsUsingNames(....) { 
  ....
  Collection<string> topicNames = new ArrayList(topicNameCollection);

  for (final String topicName : topicNames) {
    KafkaFutureImpl<void> future = new KafkaFutureImpl();

    if (allTopics.remove(topicName) == null) {
      ....
    } else {
      topicNames.remove(topicIds.remove(topicName));      // 



<p>That's what the analyzer shows us:</p>

<p>V6066 The type of object passed as argument is incompatible with the type of collection: String, Uuid. MockAdminClient.java 569</p>

<p>V6053 The 'topicNames' collection of 'ArrayList' type is modified while iteration is in progress. ConcurrentModificationException may occur. MockAdminClient.java 569</p>

<p>Now that's a big dilemma! What's going on here, and how should we address it?! </p>

<p>First, let's talk about collections and generics. Using the generic types of collections helps us avoid <em>ClassCastExceptions</em> and cumbersome constructs where we convert types. </p>

<p>If we specify a certain data type when initializing a collection and add an incompatible type, the compiler won't compile the code. </p>

<p>Here's an example:<br>
</p>

<pre class="brush:php;toolbar:false">public class Test {
  public static void main(String[] args) {
    Set<string> set = new HashSet();
    set.add("str");
    set.add(UUID.randomUUID()); // java.util.UUID cannot be converted to
                                // java.lang.String
  }
}
</string>

However, if we delete an incompatible type from our Set, no exception will be thrown. The method returns false.

Here's an example:

public class Test {
  public static void main(String[] args) {
    Set<string> set = new HashSet();
    set.add("abc");
    set.add("def");
    System.out.println(set.remove(new Integer(13))); // false
  }
}
</string>

It's a waste of time. Most likely, if we encounter something like this in the code, this is an error. I suggest you go back to the code at the beginning of this subchapter and try to spot a similar case.

Second, let's talk about the Iterator. We can talk about iterating through collections for a long time. I don't want to bore you or digress from the main topic, so I'll just cover the key points to ensure we understand why we get the warning.

So, how do we iterate through the collection here? Here is what the for loop in the code fragment looks like:

for (Type collectionElem : collection) {
  ....
}

The for loop entry is just syntactic sugar. The construction is equivalent to this one:

for (Iterator<type> iter = collection.iterator(); iter.hasNext();) {
  Type collectionElem = iter.next();
  ....
}
</type>

We're basically working with the collection iterator. All right, that's sorted! Now, let's discuss ConcurrentModificationException.

ConcurrentModificationException is an exception that covers a range of situations both in single-threaded and multi-threaded programs. Here, we're focusing on single-threading. We can find an explanation quite easily. Let's take a peek at the Oracle docs: a method can throw the exception when it detects parallel modification of an object that doesn't support it. In our case, while the iterator is running, we delete objects from the collection. This may cause the iterator to throw a ConcurrentModificationException.

How does the iterator know when to throw the exception? If we look at the ArrayList collection, we see that its parent, AbstactList, has the modCount field that stores the number of modifications to the collection:

protected transient int modCount = 0;

Here are some usages of the modCount counter in the ArrayList class:

public boolean add(E e) {
  modCount++;
  add(e, elementData, size);
  return true;
}

private void fastRemove(Object[] es, int i) {
  modCount++;
  final int newSize;
  if ((newSize = size - 1) > i)
    System.arraycopy(es, i + 1, es, i, newSize - i);
  es[size = newSize] = null;
}

So, the counter is incremented each time when the collection is modified.

Btw, the fastRemove method is used in the remove method, which we use inside the loop.

Here's the small code fragment of the ArrayList iterator inner workings:

private class Itr implements Iterator<e> {
  ....
  int expectedModCount = modCount;            

  final void checkForComodification() {
  if (modCount != expectedModCount)               // 



<p>Let me explain that last fragment. If the collection modifications don't match the expected number of modifications (which is the sum of the initial modifications before the iterator was created and the number of the iterator operations), a <em>ConcurrentModificationException</em> is thrown. That's only possible when we modify the collection using its methods while iterating over it (i.e. <strong>in parallel</strong> with the iterator). That's what the second warning is about.</p>

<p>So, I've explained you the analyzer messages. Now let's put it all together: </p>

<p>We attempt to delete an element from the collection when the <em>Iterator</em> is still running:<br>
</p>

<pre class="brush:php;toolbar:false">topicNames.remove(topicIds.remove(topicName)); 
// topicsNames – Collection<string>
// topicsIds – Map<string uuid>
</string></string>

However, since the incompatible element is passed to ArrayList for deletion (the remove method returns a UUID object from topicIds), the modification count won't increase, but the object won't be deleted. Simply put, that code section is rudimentary.

I'd venture to guess that the developer's intent is clear. If that's the case, one way to fix these two warnings could be as follows:

Collection<string> topicNames = new ArrayList(topicNameCollection);

List<string> removableItems = new ArrayList();

for (final String topicName : topicNames) {
  KafkaFutureImpl<void> future = new KafkaFutureImpl();

  if (allTopics.remove(topicName) == null) {
    ....
  } else {
    topicIds.remove(topicName);
    removableItems.add(topicName);
    future.complete(null);
  }
  ....
}
topicNames.removeAll(removableItems);
</void></string></string>

Void, sweet void

Where would we go without our all-time favorite null and its potential problems, right? Let me show you the code fragment for which the analyzer issued the following warning:

V6008 Potential null dereference of 'oldMember' in function 'removeStaticMember'. ConsumerGroup.java 311, ConsumerGroup.java 323

@Override
public void removeMember(String memberId) {
  ConsumerGroupMember oldMember = members.remove(memberId);
  ....
  removeStaticMember(oldMember);
  ....
}

private void removeStaticMember(ConsumerGroupMember oldMember) {
  if (oldMember.instanceId() != null) {
    staticMembers.remove(oldMember.instanceId());
  }
}

If members doesn't contain an object with the memberId key, oldMember will be null. It can lead to a NullPointerException in the removeStaticMember method.

Boom! The parameter is checked for null:

if (oldMember != null && oldMember.instanceId() != null) {

The next error will be the last one in the article—I'd like to wrap things up on a positive note. The code below—as well as the one at the beginning of this article—has a common and silly typo. However, it can certainly lead to unpleasant consequences.

Let's take a look at this code fragment:

protected SchemaAndValue roundTrip(...., SchemaAndValue input) {
  String serialized = Values.convertToString(input.schema(),
                                             input.value());

  if (input != null && input.value() != null) {   
    ....
  }
  ....
}

Yeah, that's right. The method actually accesses the input object first, and then checks whether it's referencing null.

V6060 The 'input' reference was utilized before it was verified against null. ValuesTest.java 1212, ValuesTest.java 1213

Again, I'll note that such typos are ok. However, they can lead to some pretty nasty results. It's tough and inefficient to search for these things in the code manually.

Conclusion

In sum, I'd like to circle back to the previous point. Manually searching through the code for all these errors is a very time-consuming and tedious task. It's not unusual for issues like the ones I've shown to lurk in code for a long time. The last bug dates back to 2018. That's why it's a good idea to use static analysis tools. If you'd like to know more about PVS-Studio, the tool we have used to detect all those errors, you can find out more here.

That's all. Let's wrap things up here. "Oh, and in case I don't see ya, good afternoon, good evening, and good night."

Belay the Metamorphosis: analyzing Kafka project

I almost forgot! Catch a link to learn more about a free license for open-source projects.

以上がBelay the Metamorphosis: Kafka プロジェクトの分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
JVMのクラスローダーサブシステムは、プラットフォームの独立性にどのように貢献していますか?JVMのクラスローダーサブシステムは、プラットフォームの独立性にどのように貢献していますか?Apr 23, 2025 am 12:14 AM

クラスローダーは、統一されたクラスファイル形式、動的読み込み、親代表団モデル、プラットフォーム非依存バイトコードを通じて、さまざまなプラットフォーム上のJavaプログラムの一貫性と互換性を保証し、プラットフォームの独立性を実現します。

Javaコンパイラはプラットフォーム固有のコードを作成しますか?説明する。Javaコンパイラはプラットフォーム固有のコードを作成しますか?説明する。Apr 23, 2025 am 12:09 AM

Javaコンパイラによって生成されたコードはプラットフォームに依存しませんが、最終的に実行されるコードはプラットフォーム固有です。 1。Javaソースコードは、プラットフォームに依存しないバイトコードにコンパイルされます。 2。JVMは、特定のプラットフォームのバイトコードをマシンコードに変換し、クロスプラットフォーム操作を保証しますが、パフォーマンスは異なる場合があります。

JVMは、さまざまなオペレーティングシステムでマルチスレッドをどのように処理しますか?JVMは、さまざまなオペレーティングシステムでマルチスレッドをどのように処理しますか?Apr 23, 2025 am 12:07 AM

マルチスレッドは、プログラムの応答性とリソースの利用を改善し、複雑な同時タスクを処理できるため、最新のプログラミングで重要です。 JVMは、スレッドマッピング、スケジューリングメカニズム、同期ロックメカニズムを介して、異なるオペレーティングシステム上のマルチスレッドの一貫性と効率を保証します。

Javaの文脈では、「プラットフォームの独立」とはどういう意味ですか?Javaの文脈では、「プラットフォームの独立」とはどういう意味ですか?Apr 23, 2025 am 12:05 AM

Javaのプラットフォームの独立性とは、書かれたコードがJVMが変更なしでインストールされた任意のプラットフォームで実行できることを意味します。 1)JavaソースコードはBytecodeにコンパイルされ、2)BytecodeはJVMによって解釈および実行されます、3)JVMは、プログラムが異なるオペレーティングシステムで実行されることを確認するために、メモリ管理とガベージコレクション機能を提供します。

Javaアプリケーションは、プラットフォーム固有のバグや問題に遭遇する可能性がありますか?Javaアプリケーションは、プラットフォーム固有のバグや問題に遭遇する可能性がありますか?Apr 23, 2025 am 12:03 AM

JavaApplicationScanIndEDENCOUNTIONPLATFORM-SPECISTESUESUSESEJVM'SABSTRACTION.REASONSINCLUDE:1)NativeCodeandLibraries、2)OperatingSystemDifferences、3)JVMimplementationVariations、および4)HardweardePencies.TomiteTETETETESES、DEVELAPERSHOULD:1)

クラウドコンピューティングは、Javaのプラットフォーム独立の重要性にどのような影響を与えますか?クラウドコンピューティングは、Javaのプラットフォーム独立の重要性にどのような影響を与えますか?Apr 22, 2025 pm 07:05 PM

クラウドコンピューティングにより、Javaのプラットフォームの独立性が大幅に向上します。 1)JavaコードはBytecodeにコンパイルされ、異なるオペレーティングシステムでJVMによって実行され、クロスプラットフォーム操作が確保されます。 2)DockerとKubernetesを使用してJavaアプリケーションを展開して、携帯性とスケーラビリティを向上させます。

Javaのプラットフォームの独立性は、その広範な採用においてどのような役割を果たしましたか?Javaのプラットフォームの独立性は、その広範な採用においてどのような役割を果たしましたか?Apr 22, 2025 pm 06:53 PM

java'splatformendenceallowsdevelopersowritecodeodeonceanceandonitondeviceoros withajvm.

コンテナ化テクノロジー(Dockerなど)は、Javaのプラットフォーム独立性の重要性にどのように影響しますか?コンテナ化テクノロジー(Dockerなど)は、Javaのプラットフォーム独立性の重要性にどのように影響しますか?Apr 22, 2025 pm 06:49 PM

Dockerなどのコンテナ化技術は、Javaのプラットフォームの独立性を置き換えるのではなく、強化します。 1)環境全体の一貫性を確保し、2)特定のJVMバージョンを含む依存関係を管理する、3)展開プロセスを簡素化して、Javaアプリケーションをより順応性と管理しやすくする。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

MantisBT

MantisBT

Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。