OpenFeign高級用法:緩存、QueryMap、MatrixVariable、CollectionFormat優雅地遠程調用

碼到三十五 : 個人主頁

微服務架構中,服務之間的通信變得尤為關鍵。OpenFeign,一個聲明式的Web服務客戶端,使得REST API的調用變得更加簡單和優雅。OpenFeign集成了Ribbon和Hystrix,具有負載均衡和容錯的能力,并且在Spring Cloud的加持下,可以輕松地與注冊中心(nacos,Eureka)和配置中心(Spring Cloud Config,nacos)等組件集成。

目錄

      • 一、OpenFeign簡介
      • 二、OpenFeign的使用
        • 1. 添加依賴
        • 2. 啟用OpenFeign
        • 3. 定義Feign客戶端
        • 4. 注入和使用Feign客戶端
      • 三、@FeignClient注解
      • 四、feign緩存
        • 1. Feign緩存的意義
        • 2. feign緩存的使用
      • 五、@QueryMap支持
        • 使用方法
      • 六、@MatrixVariable支持
        • 使用 `@MatrixVariable`
        • URI 結構
      • 七、@CollectionFormat支持
      • 八、其他高級特性
      • 總結

一、OpenFeign簡介

OpenFeign是一個聲明式的Web服務客戶端,它使得編寫HTTP客戶端變得更簡單。使用Feign,你只需要創建一個接口并用注解來配置它,Feign會自動完成接口的實現。OpenFeign具有可插拔的注解特性,同時也支持JAX-RS注解和Feign注解。此外,它還支持負載均衡和容錯,通過集成Spring Cloud alibaba和Spring Cloud,可以輕松地查找服務并進行調用。
在這里插入圖片描述

二、OpenFeign的使用

1. 添加依賴

首先需要在項目中添加OpenFeign的依賴。Maven可以在pom.xml文件中添加以下依賴:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2. 啟用OpenFeign

在spring Boot應用主類上添加@EnableFeignClients注解,以啟用OpenFeign。

@SpringBootApplication
@EnableFeignClients
public class MyApp {public static void main(String[] args) {SpringApplication.run(MyApp.class, args);}
}
3. 定義Feign客戶端

定義Feign客戶端接口,并使用@FeignClient注解進行配置。假設有一個名為user-service的遠程服務,可以這樣定義一個Feign客戶端:

@FeignClient("user-service")
public interface UserClient {@GetMapping("/users/{id}")User getUser(@PathVariable("id") Long id);
}

@FeignClient注解指定了要調用的遠程服務的名稱(在本例中是user-service),而接口中的方法則定義了具體的HTTP請求。@GetMapping注解表示這是一個GET請求,@PathVariable注解則用于從URL路徑中提取參數。

4. 注入和使用Feign客戶端

一旦你定義了Feign客戶端接口,你就可以在你的服務中注入并使用它了。例如:

@Service
public class UserService {private final UserClient userClient;@Autowiredpublic UserService(UserClient userClient) {this.userClient = userClient;}public User getUser(Long userId) {return userClient.getUser(userId);}
}

在這個例子中,UserService類注入了UserClient的實例,并通過它來調用遠程服務。當你調用getUser方法時,OpenFeign會自動將HTTP請求發送到指定的遠程服務,并將響應映射到Java對象上。

在這里插入圖片描述

三、@FeignClient注解

@FeignClient注解是用于標記Feign客戶端接口的關鍵注解。通過此注解,OpenFeign能夠自動生成接口的實現,使得開發者能夠以聲明式的方式調用遠程服務。

下面是@FeignClient注解的詳細參數及其用法:

  1. name

    • 含義:指定Feign客戶端的名稱,這個名稱通常用于服務發現和服務間的負載均衡。在微服務架構中,這個名稱通常與服務提供者在服務注冊中心注冊的服務名相對應。
    • 用法:@FeignClient(name = "serviceName"),其中"serviceName"是遠程服務的名稱。
  2. url

    • 含義:直接指定Feign客戶端的請求地址。當該參數被設置時,Feign將不再從服務注冊中心獲取服務地址,而是直接使用這個URL作為請求的目標地址。
    • 用法:@FeignClient(url = "http://localhost:8080"),指定具體的服務地址。
    • 注意:如果同時指定了name和url,url會覆蓋name中定義的地址。
  3. configuration

    • 含義:指定Feign客戶端的配置類。在這個配置類中,可以自定義Feign的各種參數,如請求攔截器、編碼器、解碼器等。
    • 用法:@FeignClient(configuration = CustomFeignConfiguration.class),其中CustomFeignConfiguration是自定義的配置類。
  4. fallback

    • 含義:指定Feign客戶端的降級方案。當請求出現異常時,如遠程服務不可用或請求超時,Feign會返回fallback指定的實例的數據。這通常用于實現熔斷和降級邏輯,以提高系統的穩定性。
    • 用法:@FeignClient(fallback = UserClientFallback.class),其中UserClientFallback是實現了Feign客戶端接口的降級處理類。
  5. fallbackFactory

    • 含義:與fallback類似,但是提供了一個工廠類來創建降級邏輯的實例。這個工廠類需要實現FallbackFactory接口,并覆蓋create(Throwable cause)方法。這個方法返回一個實現了Feign客戶端接口的實例,用于處理降級邏輯。
    • 用法:通常與Hystrix等熔斷框架結合使用,提供更靈活的降級策略。
  6. path

    • 含義:定義所有方法請求的基礎路徑。這樣可以在Feign客戶端的接口方法中省略這個基礎路徑。
    • 用法:@FeignClient(path = "/api/users"),指定所有請求的基礎路徑。
  7. qualifier

    • 含義:當使用Spring的自動裝配功能時,可以使用qualifier參數來指定一個bean的名稱,以便在多個相同類型的Feign客戶端之間進行區分。
    • 用法:通常與@Qualifier注解結合使用,用于解決自動裝配時的歧義。
  8. decode404

    • 含義:指定當服務端返回404狀態時是否拋出FeignException。默認情況下,Feign會將404視為一個正常的響應(即不會拋出異常)。如果將此參數設置為true,則當服務端返回404狀態時,Feign會拋出一個FeignException
    • 用法:在需要特殊處理404響應的場景中使用。
  9. primary

    • 含義:當存在多個相同類型的bean時,將此參數設置為true可以將該bean設置為首選bean。這樣,在使用@Autowired進行自動裝配時,Spring會優先選擇標記為primary的bean。
    • 用法:在需要指定首選bean的場景中使用。

此外,@FeignClient注解還支持使用Spring Expression Language (SpEL) 表達式來動態地指定某些參數值。例如,可以使用${property.name}來從配置文件或環境變量中讀取值。這種動態配置方式使得Feign客戶端更加靈活和可配置。

四、feign緩存

1. Feign緩存的意義

在網絡請求中,頻繁地調用相同的接口會消耗大量的時間和資源。通過引入緩存機制,可以將一些不經常變化且查詢頻繁的數據存儲在本地,從而減少對遠程服務的調用,提高系統的響應速度和性能。

Feign 緩存是通過結合 Spring Framework 的緩存抽象來實現的。當你使用 @EnableCaching 注解時,Spring Boot 會啟用對 @Cacheable@CacheEvict@CachePut 等緩存相關注解的支持。對于 Feign 客戶端來說,這意味著你可以在 Feign 接口的方法上使用這些注解來控制緩存行為。

2. feign緩存的使用

啟用緩存

首先在 Spring Boot 應用中使用 @EnableCaching 注解來啟用緩存支持。這通常是在配置類或者啟動類上添加的。

@EnableCaching
@SpringBootApplication
public class MyApp {public static void main(String[] args) {SpringApplication.run(MyApp.class, args);}
}

使用 @Cacheable 注解

在Feign 客戶端接口中,可以使用 @Cacheable 注解來標記需要緩存的方法。這個注解指定了緩存的名稱(cacheNames)和用于生成緩存鍵的表達式(key)。

@FeignClient("some-service")
public interface SomeServiceClient {@Cacheable(cacheNames = "infos", key = "#id")@GetMapping("/info/{id}")public Object info(@PathVariable("id") Integer id);
}

info 方法被標記為可緩存的。當這個方法被調用時,Spring 會檢查名為 infos 的緩存中是否已經有對應 id 的緩存項。如果有,就直接返回緩存的數據;如果沒有,就執行遠程調用,并將結果存儲到緩存中。

配置緩存管理器

通過配置文件來指定緩存管理器的類型和其他相關設置。使用了Redis 作為緩存存儲,并配置了 Redis 緩存的相關參數。

spring:cache:type: redis # 指定緩存類型為 Redisredis:key-prefix: 'feign:' # 設置 Redis 緩存鍵的前綴time-to-live: 60s # 設置緩存的有效期為 60 秒

這樣配置后,Feign 客戶端的緩存數據會被存儲到 Redis 中,并且每個緩存項的有效期為 60 秒。這意味著在 60 秒內對同一個 id 的多次請求都會直接從 Redis 緩存中獲取數據,而不會觸發遠程調用。當緩存過期后,下一次請求會觸發遠程調用,并更新緩存。

五、@QueryMap支持

在Feign中使用@QueryMap或Spring Cloud OpenFeign中的@SpringQueryMap注解可以非常方便地將一個POJO(Plain Old Java Object,即簡單的Java對象)或Map的屬性和值映射為HTTP GET請求的查詢參數。這在構建具有多個查詢參數的API調用時特別有用,因為它允許你以聲明式的方式組織這些參數,而不是手動構建查詢字符串。

簡單點說:可以將一個對象中的所有屬性值以get的方式拼接到地址欄進行傳遞。

@QueryMap 和 @SpringQueryMap

  • @QueryMap: OpenFeign原生提供的注解,用于將POJO映射為查詢參數。但是,它與Spring不兼容,因為它沒有value屬性。
  • @SpringQueryMap: Spring Cloud OpenFeign提供的與Spring兼容的注解,功能和@QueryMap類似,但可以在Spring環境中使用。
使用方法
  1. 定義參數對象:
    創建一個簡單的Java類Params,并為其添加屬性和getter方法。這些屬性和方法將用于生成查詢參數。
   public class Params {private Long id;private String name;// getters and setters}
  1. 在Feign客戶端中使用@SpringQueryMap:
    在Feign客戶端接口中,使用@GetMapping注解來定義一個GET請求,并使用@SpringQueryMap注解來標記你的POJO參數。
   @FeignClient("some-service")public interface DemoFeign {@GetMapping("/format")Object format(@SpringQueryMap Params params);}
  1. 調用Feign客戶端:
    在服務中創建一個Params對象,設置其屬性,并通過Feign客戶端調用該方法。
   @GetMapping("/format")public Object format(Params params) {return demoFeign.format(params);}
  1. 日志輸出:
    當調用這個方法時,Feign會自動將Params對象的屬性轉換為查詢參數,并附加到GET請求的URL上。可以通過查看Feign的請求日志來確認這一點。
[DemoFeign#format] ---> GET http://localhost:8088/demos/format?name=test&id=111 HTTP/1.1

通過這種方式,可以簡潔地將復雜的查詢參數組織到一個對象中,并讓Feign自動處理參數到查詢字符串的轉換,從而簡化了代碼并提高了可讀性。

六、@MatrixVariable支持

在Spring Cloud OpenFeign中,@MatrixVariable 注解被用來處理 URI 中的矩陣變量。矩陣變量是 URI 的一種特殊參數形式,允許你在 URI 路徑段中直接包含鍵值對。這種形式的參數在某些 RESTful API 設計中很有用,尤其是當你需要在 URI 中傳遞多個參數,并且這些參數都是針對同一資源的時候。

使用 @MatrixVariable

@MatrixVariable 被用來接收一個映射(Map),其中鍵是字符串,值是字符串列表。這在處理可能包含多個值的矩陣變量時非常有用。

@GetMapping("/m3/{params}")
public Object matrix3(@MatrixVariable Map<String, List<String>> params) {// ...
}

{params} 是一個路徑占位符,它表示這個部分將由傳遞的矩陣變量填充。@MatrixVariable 注解告訴 Spring,params 參數應該接收來自 URI 矩陣變量的值。

URI 結構

一個包含矩陣變量的 URI 看起來像這樣:

/m3/someValue;key1=value1a,value1b;key2=value2

在這個 URI 中:

  • /m3/someValue 是基本的路徑。
  • ;key1=value1a,value1b;key2=value2 是矩陣變量部分,其中 key1key2 是矩陣變量的名稱,value1a,value1bvalue2 是相應的值。

注意事項

  • 路徑占位符的名稱(在這個例子中是 {params})必須與 @MatrixVariable 注解的變量名一致,或者你可以在注解中明確指定名稱。
  • 矩陣變量的值可以是單個值,也可以是用逗號分隔的多個值,這取決于你的 API 如何設計。
  • 使用矩陣變量時,要確保你的 URI 編碼和解碼邏輯能夠正確處理這些變量,特別是當值中包含特殊字符時。

七、@CollectionFormat支持

在Feign中,@CollectionFormat 注解用于指定集合參數的格式化方式。當你在Feign客戶端的方法中使用集合類型的參數(如 List<String>)時,這個注解能夠幫助你定義如何將集合中的元素串聯起來,以便通過HTTP請求發送給服務端。

@CollectionFormat 注解接受一個 feign.CollectionFormat 枚舉值,該枚舉定義了不同的格式化選項。在你提供的例子中,展示了兩種格式化方式:CSVPIPES

  1. CSV (Comma Separated Values):
    當使用 feign.CollectionFormat.CSV 時,集合中的元素會使用逗號 , 進行分隔。這是最常見和默認的格式。

    示例:

    @GetMapping("/cf")
    @CollectionFormat(feign.CollectionFormat.CSV)
    public Object cf(@RequestParam("ids") List<String> ids);
    

    調用該方法并傳遞 List.of("S1", "S2", "S3") 時,生成的URL查詢參數會是 ids=S1,S2,S3

  2. PIPES (| separator):
    當使用 feign.CollectionFormat.PIPES 時,集合中的元素會使用豎線 | 進行分隔。

    示例:

    @GetMapping("/cf")
    @CollectionFormat(feign.CollectionFormat.PIPES)
    public Object cf(@RequestParam("ids") List<String> ids);
    

    同樣地,調用該方法并傳遞 List.of("S1", "S2", "S3") 時,生成的URL查詢參數會是 ids=S1|S2|S3

這個注解在處理需要特定格式的集合參數時非常有用,尤其是當服務端期望接收特定格式的集合數據時。通過使用 @CollectionFormat,你可以確保Feign客戶端發送的請求符合服務端的期望,從而避免格式錯誤導致的問題。

@CollectionFormat 可以應用于整個Feign客戶端類或者單獨的方法上。如果應用于類上,它將影響該類中所有使用集合參數的方法。如果只需要對某個特定方法應用不同的集合格式,可以將注解直接應用于該方法上。

八、其他高級特性

OpenFeign還提供了許多高級特性,如負載均衡、容錯、請求攔截和重試等。這些特性可以幫助構建更健壯、更可靠的微服務架構。

  • 負載均衡:OpenFeign集成了Ribbon,支持多種負載均衡策略,如輪詢、隨機等。你可以根據需要選擇合適的策略來分發請求。
  • 容錯:通過與Hystrix的集成,OpenFeign提供了熔斷和降級功能。當遠程服務出現故障或響應過慢時,可以自動切換到降級邏輯,保證系統的穩定性。
  • 請求攔截:可以使用RequestInterceptor接口實現自定義的請求攔截器,用于在發送請求之前或之后執行特定的邏輯,如添加認證信息、記錄日志等。
  • 重試機制:OpenFeign支持配置請求的重試次數和間隔,以提高系統的可靠性。

總結

OpenFeign是一個強大的聲明式Web服務客戶端,它簡化了遠程服務調用的復雜性,并提供了負載均衡、容錯等高級特性。通過使用OpenFeign,你可以更加專注于業務邏輯的實現,而無需關心底層的HTTP通信細節。希望本文能幫助你更好地理解和使用OpenFeign,構建高效、穩定的微服務架構。


聽說...關注下面公眾號的人都變牛了,純技術,純干貨 !

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13402.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13402.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13402.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

線性回歸模型之套索回歸

概述 本案例是基于之前的嶺回歸的案例的。之前案例的完整代碼如下&#xff1a; import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import Ridge, LinearRegression from sklearn.datasets import make_regression from sklearn.model_selectio…

NegativePrompt:利用心理學通過負面情緒刺激增強大型語言模型

【摘要】大型語言模型 (LLM) 已成為各種應用不可或缺的一部分&#xff0c;從傳統的計算任務到高級人工智能 (AI) 應用。這種廣泛的應用促使社會科學等各個學科對 LLM 進行了廣泛的研究。值得注意的是&#xff0c;研究表明 LLM 具有情商&#xff0c;可以通過積極的情緒刺激進一步…

C++:深入理解多態

一、多態的概念 多態的概念&#xff1a;通俗來說&#xff0c;就是多種形態&#xff0c;具體點就是去完成某個行為&#xff0c;當不同的對象去完成時會產生出不同的狀態。 那究竟多態的實際價值體現在哪里呢&#xff1f;&#xff1f; 1、舉個例子比如說購買高鐵票這個行為&…

Spring Boot | SpringBoot 中 自定義 “用戶授權管理“ : 自定義“用戶訪問控制“、自定義“用戶登錄控制“

目錄: 一、SpringBoot 中 自定義 "用戶授權管理" ( 總體內容介紹 ) :二、 自定義 "用戶訪問控制" ( 通過 "HttpSecurity類" 的 authorizeRequests( )方法來實現 "自定義用戶訪問控制" ) :1.基礎項目文件準備2.實現 "自定義身份認…

4. 分布式鏈路追蹤客戶端工具包Starter設計

前言 本文將從零搭建分布式鏈路追蹤客戶端工具包的Starter&#xff0c;并將在后續文章中逐步豐富支持的場景。這里首先將搭建一個最基礎的Starter&#xff0c;能提供的功能和1. 看完這篇文章我奶奶都懂Opentracing了一文中的示例demo類似。 相關版本依賴如下。 opentracing-…

Scala學習2: 控制結構和函數

目錄 第二章 控制結構和函數1- 條件表達式2- 語句終止3- 塊表達式和賦值4- 輸入和輸出5- 循環6- 高級for循環和for推到式7- 函數8- 默認參數和帶名參數9- 可變參數10- 過程11- 懶值12- 異常end 第二章 控制結構和函數 1- 條件表達式 Scala的 if/esle 語法結構與java一樣, 但是…

MySQL表突然卡死,刪、查操作加載不停解決辦法

今天遇到了MySQL刪表的時候卡死情況。然后通過網上查閱資料和項目組溝通&#xff0c;了解到了有多人同時對同一張表進行了操作。我和另一個同事同時進行了刪除操作&#xff0c;然后另兩位同時進行了查詢操作&#xff0c;然后還有一位同事用dolphin調度&#xff0c;用datax采集數…

【SQL】SQL常見面試題總結(4)

目錄 1、空值處理1.1、統計有未完成狀態的試卷的未完成數和未完成率1.2、0 級用戶高難度試卷的平均用時和平均得分 2、高級條件語句2.1、篩選限定昵稱成就值活躍日期的用戶&#xff08;較難&#xff09;2.2、篩選昵稱規則和試卷規則的作答記錄&#xff08;較難&#xff09;2.3、…

SmartEDA助力電工基礎實驗:打造高效、智能的學習新體驗

在電工基礎實驗的教學與學習中&#xff0c;傳統的實驗設備往往存在著操作復雜、數據處理繁瑣等問題&#xff0c;給學生的學習帶來了不小的挑戰。然而&#xff0c;隨著科技的不斷發展&#xff0c;一種名為SmartEDA的智能電工實驗輔助設備正逐漸走入課堂&#xff0c;以其高效、智…

Es6-對象新增了哪些擴展?

?&#x1f308;個人主頁&#xff1a;前端青山 &#x1f525;系列專欄&#xff1a;Javascript篇 &#x1f516;人終將被年少不可得之物困其一生 依舊青山,本期給大家帶來Javascript篇專欄內容:Es6-對象新增了哪些擴展&#xff1f; 目錄 一、參數 二、屬性 函數的length屬性 …

Unsupervised Out-of-Distribution Detection with Diffusion Inpainting

Unsupervised Out-of-Distribution Detection with Diffusion Inpainting 摘要1.介紹2 背景3 3. Lift, Map, Detect摘要 無監督的異常分布檢測(OOD)旨在通過僅從未標記的域內數據中學習來識別域外數據。我們提出了一種用于此任務的新方法——提升、映射、檢測(LMD),該方法…

數據結構-棧(帶圖)

目錄 棧的概念 畫圖理解棧 棧的實現 fun.h fun.c main.c 棧的概念 棧&#xff08;Stack&#xff09;是一種基本的數據結構&#xff0c;其特點是只允許在同一端進行插入和刪除操作&#xff0c;這一端被稱為棧頂。遵循后進先出&#xff08;Last In, First Out, LIFO&#…

瀏覽器下載附件流建議

大文件下載可采用附件流的方式&#xff0c;后端設置一下響應參數&#xff0c;然后以流的方式返回前端 res.set({ "Content-Type": "application/octet-stream", "Content-Disposition": "attachment;filename* UTF-8"fixedEncodeUR…

【論文粗讀|arXiv】GaSpCT: Gaussian Splatting for Novel CT Projection View Synthesis

Abstract 本文提出了一種新穎的視圖合成和3D場景表示方法&#xff0c;用于為計算機斷層掃描&#xff08;CT&#xff09;生成新的投影視圖。 方法采用了Gaussian Splatting 框架&#xff0c;基于有限的2D圖像投影集&#xff0c;無需運動結構&#xff08;SfM&#xff09;方法&am…

CSPM-4是什么?報考條件有哪些?

2021年10月&#xff0c;《國家標準化發展綱要》明確提出構建多層次從業人員培養培訓體系&#xff0c;開展專業人才培養培訓和國家質量基礎設施綜合教育。建立健全人才的職業能力評價和激勵機制。由中國標準化協會&#xff08;CAS&#xff09;組織開展的項目管理專業人員能力評價…

Swift 5.9 中 if 與 switch 語句簡潔新語法讓擼碼更帶勁

概覽 在實際代碼開發中&#xff0c;可能初學 Swift 語言的小伙伴們在擼碼時最常用的得數 if 和 switch…case 條件選擇語句了。不過在某些場景下它們顯得略有那么一丟丟“矯揉造作”&#xff0c;還好從 Swift 5.9 開始蘋果知趣的為其簡化了語法且增強了它們的表現力。 在本篇…

Vitis HLS 學習筆記--優化本地存儲器訪問瓶頸

目錄 1. 簡介 2. 代碼解析 2.1 原始代碼 2.2 優化后 2.3 分析優化措施 3. 總結 1. 簡介 在Vitis HLS中&#xff0c;實現II&#xff08;迭代間隔&#xff09; 1是提高循環執行效率的關鍵。II1意味著每個時鐘周期都可以開始一個新的迭代&#xff0c;這是最理想的情況&…

Java實現音頻轉文本(語音識別)

在Java中實現音頻轉文本&#xff08;也稱為語音識別或ASR&#xff09;通常涉及使用專門的語音識別服務&#xff0c;如Google Cloud Speech-to-Text、IBM Watson Speech to Text、Amazon Transcribe、Microsoft Azure Speech Services&#xff0c;或者一些開源庫如CMU Sphinx。 …

2024年第四屆長三角高校數學建模競賽C題思路

賽道C:汽后配件需求預測問題 在汽后行業的供應鏈管理中, 精準的需求預測是后續管理及決策的基礎。 各個汽后配件即為一個庫存單位(SKU, Stock Keeping Unit), 如果可以準確預知未來對于各個配件的市場需求, 就可以提前將庫存放在靠近需求的倉庫中, 從而降低庫存成本,…

HNCTF ——baby_python

H&NCTF 2024 官方WP (qq.com) OpCodes Pickle.jl (juliahub.com) nc之后 PS D:\ForCode\pythoncode\.idea> nc hnctf.yuanshen.life 33267 # Python 3.10.12 from pickle import loads main b"\x80\x04ctypes\nFunctionType\n(ctypes\nCodeType\n(I1\nI0\nI0\n…