Feign是一種聲明式、模板化的HTTP<font color='red'>客戶端(僅在consumer中使用)</font>。html
聲明式調用就像調用本地方法同樣調用遠程方法,無感知遠程HTTP請求。java
1.SpringCloud的聲明式調用,能夠作到使用HTTP請求遠程服務時就像調用本地方法同樣的體驗,開發者徹底感知不到這是遠程方法。更感知不到這是一個HTTP請求。web
2.它像Dubbo同樣,consumr直接調用接口方法調用provider,而不須要經過常規的Http Client構造請求再解析返回數據。算法
3.它解決了讓開發者調用遠程接口就跟調用本地方法同樣,無需關注與遠程的交互細節,更無需關注分佈式環境開發。spring
實現電商平臺的基本操做apache
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.luyi</groupId> <artifactId>springcloud-ego-product-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloud-ego-product-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
/** * 描述: 商品接口 */ @RequestMapping("/product") public interface ProductService { //查詢全部商品 @RequestMapping(value = "/findAll", method = RequestMethod.GET) public List<Product> findAll(); }
/** * 描述: 商品實體 */ public class Product { private Integer id; private String name; public Product() { } public Product(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.luyi</groupId> <artifactId>springcloud-ego-product-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloud-ego-product-provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!--product service--> <dependency> <groupId>com.luyi</groupId> <artifactId>springcloud-ego-product-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
spring.application.name=ego-product-provider server.port=9001 #設置服務註冊中心地址,向全部註冊中心作註冊 eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
/** * Product-Provider服務 */ @RestController public class ProductController implements ProductService { @Override public List<Product> findAll() { ArrayList<Product> list = new ArrayList<>(); list.add(new Product(1, "電視")); list.add(new Product(2, "電腦")); list.add(new Product(3, "冰箱")); list.add(new Product(4, "手電筒")); return list; } }
@EnableEurekaClient @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.luyi</groupId> <artifactId>springcloud-ego-product-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloud-ego-product-consumer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!--添加feign的座標--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!--product service--> <dependency> <groupId>com.luyi</groupId> <artifactId>springcloud-ego-product-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
spring.application.name=ego-product-consumer server.port=9002 #設置服務註冊中心地址,向全部註冊中心作註冊 eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
/** * Product-Consumer服務 */ @RestController public class ProductController { @Autowired private ProductConsumerService consumerService; /** * Consumer中查詢全部商品的方法 * @return */ @RequestMapping(value = "/list", method = RequestMethod.GET) public List<Product> list(){ return consumerService.findAll(); } }
//指定實現該接口的服務 @FeignClient(name = "ego-product-provider") public interface ProductConsumerService extends ProductService { }
//添加以下兩個註解開啓對feign的支持 @EnableDiscoveryClient @EnableFeignClients @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
//根據商品id查詢商品 @RequestMapping(value = "/getProductById", method = RequestMethod.GET) //RequestParam必須指定參數 public Product getProductById(@RequestParam("id") Integer id);
@Override public Product getProductById(Integer id) { return new Product(id, "SpringCloud"); }
/** * Consumer中根據商品id查詢商品 */ @RequestMapping(value = "/get", method = RequestMethod.GET) public Product getProduct(@RequestParam("id") Integer id){ return consumerService.getProductById(id); }
//添加商品,傳遞多個參數,get方式 @RequestMapping(value = "/add", method = RequestMethod.GET) public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);
@Override public Product addProduct(Integer id, String name) { return new Product(id, name); }
/** * 商品添加,傳遞多個參數,get方式 */ @RequestMapping(value = "/add", method = RequestMethod.GET) public Product addProduct(Product product){ return consumerService.addProduct(product.getId(), product.getName()); }
//添加商品,傳遞多個參數,post方式 @RequestMapping(value = "/add2", method = RequestMethod.POST) public Product addProduct2(@RequestBody Product product);
@Override public Product addProduct2(@RequestBody Product product) { return product; }
/** * 商品添加,傳遞多個參數,post方式 */ @RequestMapping(value = "/add2", method = RequestMethod.GET) public Product addProduct2(Product product){ return consumerService.addProduct2(product); }
gzip原理:gzip是一種數據格式,採用deflate算法壓縮數據,gzip是一種流行的文件壓縮算法,應用十分普遍,尤爲是在Linux平臺。json
gzip能力:當gzip壓縮到一個純文本文件時效果是很是明顯的,大約能夠減小70%以上的文件大小。瀏覽器
gzip的做用:網絡數據通過壓縮後也就較低了網絡傳輸的字節數,最明顯的就是能夠提升網頁加載的速度。網頁加載速度加快的好處不言而喻,除了節省流量、改善用戶的瀏覽體驗外,另外一個潛在的好處就是gzip與搜索引擎的提取工具備着更好的關係。例如Google就能夠直接經過讀取gzip文件來比普通手工抓取更快的檢索網頁。性能優化
第一:客戶端向服務器請求中帶有:Accept-Encoding:gzip,deflate字段,向服務器表示,客戶端支持的壓縮格式(gzip或者deflate),若是不發送該消息頭,服務器是不會壓縮的。服務器
第二:服務端在收到請求以後,若是發現請求頭中含有Accept-Encoding字段,而且支持該類型的壓縮,就對響應報文壓縮以後返回給客戶端。而且攜帶Content-Encoding:gzip消息頭,表示響應報文是根據該格式壓縮過的。
第三:客戶端接收請求以後,先判斷是否有Content-Encoding:消息頭,若是有,按改格式解壓報文,不然按正常報文處理。
#配置請求GZIP壓縮 feign.compression.request.enabled=true #配置響應GZIP壓縮 feign.compression.respinse.enabled=true #配置壓縮支持MIME TYPE feign.compression.request.mime-types=text/xml,application/xml,application/json #配置壓縮數據大小的最小閾值,默認2048 feign.compression.request.min-request-size=512
#-------------spring boot gzip #是否啓用壓縮 server.compression.enabled=true server.compression.mime-types=application/json,application/xml,text/html,text/xml,type/plain
爲何http鏈接池能提升性能
a.兩臺服務器創建http鏈接的過程是很複雜的過程,涉及到多個數據包的交換,而且也很消耗時間
b.Http鏈接須要三次握手四次揮手,開銷很大。這樣的開銷對於請求比較多但信息量又比較小的請求開銷更大。
a.若是咱們直接採用http鏈接池,節約了大量三次握手四次揮手的時間,這樣能大大提高吞吐量。
b.feign的http客戶端支持3種框架:HttpURLConnection、HttpClient、okhttp,默認是HttpURLConnection。
c.傳統的HttpURLConnection是JDK自帶的,並不支持鏈接池,若是要實現鏈接池的機制。還須要本身來管理鏈接對象。對於網絡請求這種底層相對複雜的操做,若是有可用的其餘方案,也沒有必要本身去管理鏈接對象。
d.HttpClient相比於JDK自帶的HttpURLConnection,它封裝了訪問http的請求頭、參數、內容體、響應等等。它不只使發送http請求變得容易,並且也方便開發人員測試接口(基於HTTP協議的),即提升了開發的效率,也方便提升代碼的健壯性,另外高併發大量的請求的時候,仍是用鏈接池提升吞吐量。
<!--Apache HttpClient替換Feign原生httpURLConnection--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-httpclient</artifactId> <version>8.17.0</version> </dependency>
#啓用httpclient feign.httpclient.enabled=true
注意:若是使用HttpClient做爲Feign做爲Feign的客戶端工具,那麼在定義接口上的註解時須要注意,若是傳遞的是一個自定義對象(對象會使用json類型來傳遞),須要添加指定類型
/** * 描述: 商品接口 */ @RequestMapping("/product") public interface ProductService { //查詢全部商品 @RequestMapping(value = "/findAll", method = RequestMethod.GET) public List<Product> findAll(); //根據商品id查詢商品 @RequestMapping(value = "/getProductById", method = RequestMethod.GET) public Product getProductById(@RequestParam("id") Integer id); //添加商品,傳遞多個參數,get方式 @RequestMapping(value = "/add", method = RequestMethod.GET) public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name); //----------------------------------HttpClient------------------------------------ //添加商品,傳遞多個參數,post方式 @RequestMapping(value = "/add2", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) public Product addProduct2(@RequestBody Product product); //使用HttpClient工具添加商品,傳遞多個參數,基於Get方式 @RequestMapping(value = "/add3", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE) public Product addProduct3(Product product); }
將輸出日誌級別設置爲DEBUG
<!-- 日誌輸出級別 --> <root level="DEBUG"> <appender-ref ref="Stdout" /> <appender-ref ref="RollingFile" /> </root>
//添加以下兩個註解開啓對feign的支持 @EnableDiscoveryClient @EnableFeignClients @SpringBootApplication public class ConsumerApplication { /** * NONE:不記錄任何信息,默認值 * BASIC:記錄請求url、請求方法、狀態碼和用時的時候使用 * HEADERS:在BASIC基礎上再記錄一些經常使用信息 * FULL:記錄請求和響應的全部信息 */ @Bean public Logger.Level getLog(){ return Logger.Level.FULL; } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
Feign的負載均衡底層用的就是Ribbon
#全局配置 #請求鏈接的超時時間,默認爲1000ms ribbon.ConnectTimeout=5000 #處理請求的超時時間 ribbon.ReadTimeout=5000
#局部配置 #對全部操做請求都進行重試 ego-product-provider.ribbon.OkToRetryOnAllOperations=true #對當前實例的重試次數 ego-product-provider.ribbon.MaxAutoRetries=2 #切換實例的重試次數 ego-product-provider.ribbon.MaxAutoRetriesNextServer=0 #請求鏈接的超時時間 ego-product-provider.ribbon.ConnectTimeout=3000 #請求處理的超時時間 ego-product-provider.ribbon.ReadTimeout=3000