声明式调用就像调用 本地方法 一样调用远程方法,无感知远程HTTP请求。
1.SpringCloud的声明式调用,可以做到使用HTTP请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是 远程方法 。更 感知不到这是一个HTTP请求 。
2.它像Dubbo一样,consumr直接调用接口方法调用provider,而不需要通过常规的Http Client构造请求再解析返回数据。
3.它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
实现电商平台的基本操作
<?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平台。
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 复制代码
/** * 描述: 商品接口 */ @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 复制代码