微服务架构学习-进阶篇--02,声明式服务调用

第一节:声明式服务调用的作用是什么?它解决了什么问题?

1,什么是Feign?
Feign是一种声明式,模板化的HTTP客户端(仅在consumer中使用)
2,什么是声明式?有什么作用?解决了什么问题?
a.声明式调用就像调用本地方法一样调用远程方法,无感知远程HTTP请求。
b.Spring cloud的声明式调用,可以做到使用HTTP请求远程服务时,就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。
c.它像dubbo一样,consumer直接调用接口方法调用provider,而不需要通过常规的HTTPclient构造请求再解析返回数据。
d.它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。

3,一个例子,只做服务提供者provider的调用。
(1)在spring-cloud-in-action模块下新建子模块e-book。在e-book子模块下再新建子模块e-book-product。在e-book-product模块下新建两个项目,分别为e-book-product-api和e-book-product-core。
(2)改造e-book-product-api。
A.修改pom文件,加入spring boot的web依赖,和maven打包插件。
B.加一个实体类Product,代码和eureka模块中的Product一致。
C.加一个接口类ProductFacade,代码如下:

package com.twf.e.book.product.api.facade;

import java.util.List;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.twf.e.book.product.api.domain.Product;

@RequestMapping("/product")
public interface ProductFacade {

	@RequestMapping(value="list",method=RequestMethod.GET)
	public List<Product> listProduct();
}

(3)改造e-book-product-core。
A.修改pom文件。

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.twf.springcloud</groupId>
    <artifactId>e-book-product</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>e-book-product-core</artifactId>
  <name>e-book-product-core</name>
  <url>http://maven.apache.org</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-eureka</artifactId>
	</dependency>
	
	<dependency>
		<groupId>${project.parent.groupId}</groupId>
		<artifactId>e-book-product-api</artifactId>
		<version>${project.parent.version}</version>
	</dependency>
  </dependencies>
  <build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

B.添加一个接口实现类,实现ProductFacade,代码如下:

package com.twf.e.book.product.core.facade;

import java.util.ArrayList;
import java.util.List;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.twf.e.book.product.api.domain.Product;
import com.twf.e.book.product.api.facade.ProductFacade;

@RestController
public class ProductFacadeImpl implements ProductFacade{

	@RequestMapping(value="list",method=RequestMethod.GET)
	public List<Product>listProduct() {
		List<Product> list = new ArrayList<Product>();
		list.add(new Product(1, "登山包"));
		list.add(new Product(2, "登山杖"));
		list.add(new Product(3, "冲锋衣"));
		list.add(new Product(4, "帐篷"));
		list.add(new Product(5, "睡袋"));
		return list;
	}
}

C.添加一个启动类

package com.twf.e.book.product.core;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@SpringBootApplication
public class ProductCoreApplication {

	public static void main(String[] args) {
		SpringApplication.run(ProductCoreApplication.class, args);
	}
}

D.添加一个配置文件,添加如下配置

spring.application.name=e-book-product
server.port=8088

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8762/eureka/

(4)启动注册中心,并启动这个e-book-product-core的启动类。
(5)浏览器访问http://localhost:8088/product/list,可得如下结果:
在这里插入图片描述
(6)源码点这里

第二节:声明式服务调用的简单入门例子讲解

第一节的例子是服务端调用自己的接口,这个例子是客户端调用服务端的接口。
(1)在e-book模块下,新建新模块e-book-consumer,然后在该模块下新建一个maven项目,命名为e-book-consumer-feign。
(2)修改pom文件,加入如下依赖及maven打包依赖。

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.twf.springcloud</groupId>
    <artifactId>e-book-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>e-book-consumer-feign</artifactId>
  <name>e-book-consumer-feign</name>
  <url>http://maven.apache.org</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>${project.parent.groupId}</groupId>
		<artifactId>e-book-product-api</artifactId>
		<version>${project.parent.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-feign</artifactId>
	</dependency>
	
  </dependencies>
  
  <build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

(3)添加一个controller,代码如下:

package com.twf.e.book.consumer.feign.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.twf.e.book.consumer.feign.service.ProductService;
import com.twf.e.book.product.api.domain.Product;

@RestController
public class ProductController {
	
	@Autowired
	private ProductService productService;

	@RequestMapping(value="list",method=RequestMethod.GET)
	public List<Product>listProduct() {
		List<Product> list = productService.listProduct();
		return list;
	}
}

(4)添加一个service接口类,继承ProductFacade这个接口类,代码如下:

package com.twf.e.book.consumer.feign.service;

import org.springframework.cloud.netflix.feign.FeignClient;

import com.twf.e.book.product.api.facade.ProductFacade;

@FeignClient(name="e-book-product")
public interface ProductService extends ProductFacade{
	
}

(5)添加一个启动类,代码如下:

package com.twf.e.book.consumer.feign;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerFeignApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConsumerFeignApplication.class, args);
	}
}

(6)添加一个配置文件。

spring.application.name=e-book-consumer
server.port=8089

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8762/eureka/

(7)运行启动类,及第一章的ProductCoreApplication启动类。
(8)浏览器访问http://localhost:8089/list,结果如下:
在这里插入图片描述
从代码可以看出,消费方调用接口,不需再去显示地获取服务的IP端口,也不需要额外地用其他工具或方法发起远程请求。这里的调用,就好像远程的接口在本地,直接就可以调用了。
(9)源码点这里

第三节:剖析:feign对复杂参数请求的处理

(1)在e-book-consumer模块下新建一个maven项目,命名为e-book-consumer-feign-param。
(2)将e-book-consumer-feign的文件和代码拷贝到这个新项目。
(3)修改配置文件的应用名和端口,配置文件代码如下。

spring.application.name=e-book-consumer-param
server.port=8090

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8762/eureka/

(4)在ProductController中加入如下代码:
在这里插入图片描述
(5)在e-book-product-api的接口类中加入如下代码:
在这里插入图片描述
(6)在e-book-product-core的实现类中加入如下代码:
在这里插入图片描述
(7)浏览器访问http://localhost:8090/get?id=1,结果如下:
在这里插入图片描述
(8)源码点这里

第四节:加入基于GZip压缩算法,提升网络通信速度

1,gzip压缩介绍
(1)gzip介绍:gzip是一种数据格式,采用deflate算法压缩data;gzip是一种流行的文件压缩算法,应用十分广泛,尤其是在Linux平台。
(2)gzip能力:当gzip压缩到一个纯文本文件时,效果是非常明显得,大约可以减少70%以上的文件大小。
(3)gzip作用:网络数据通过压缩之后实际上降低了网络传输的字节数,最明显的好处是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是gzip与搜索引擎的抓取工具有着更好的关系,例如Google就可以通过直接读取gzip文件来比普通手工抓取更快地检索网页。
2,HTTP协议中关于压缩传输的规定
第一:客户端向服务器请求中带有:Accept-Encoding:gzip,deflate字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。
第二:服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式压缩过的。
第三:客户端接收到请求之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。
3,一个例子
(1)在e-book-consumer模块下新建maven项目e-book-consumer-feign-gzip,拷贝e-book-consumer-feign的各个文件和代码到这个项目。
(2)修改配置文件,加入springboot启用GZIP压缩的配置,及修改端口和应用名

#---------------------------------------feign 的 gzip------------------------------------------------
##配置请求gzip压缩
#feign.compression.request.enabled=true
##配置响应gzip压缩
#feign.compression.response.enabled=true
#
##配置压缩支持的mime-type
#feign.compression.request.mime-types=text/xml,application/xml,application/json
##配置压缩数据大小的下限
#feign.compression.request.min-request-size=512

#---------------------------------------spring boot 的gzip------------------------------------------------
#是否启用压缩
server.compression.enabled=true
server.compression.mime-types=application/xml,application/json,text/xml,text/html,text/plain

(3)启动注册中心,并启动该项目,访问http://localhost:8091/list,界面如下:
在这里插入图片描述
(4)源码点这里

第五节:采用连接池,提升feign的并发吞吐量

1,为什么http连接池能提升性能?
(1)HTTP的背景原理。
A.两台服务器建立HTTP连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间。
B.HTTP连接需要的3次握手4次分手开销很大,这一开销对于大量比较小的HTTP消息来说更大。
(2)优化解决方案。
A.如果我们直接采用HTTP连接池,节约了大量的3次握手4次分手;这样能大大提升吞吐率。
B.feign的HTTP客户端支持3种框架:HttpURLConnection,httpclient,okhttp;默认是HttpURLConnection。
C.传统的HttpURLConnection是jdk自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,也没有必要自己去管理连接对象。
D.httpclient相比较传统JDK自带的HttpURLConnection,它封装了访问HTTP的请求头,参数,内容体,响应等等;它不仅使客户端发送HTTP请求变得容易,而且也方便了开发人员测试接口(基于HTTP协议的),即提高了开发的效率,也方便提高代码的健壮性;另外高并发大量的请求网络的时候,还是用“连接池”提升吞吐量。

2,一个例子。
(1)在e-book-consumer模块下新建maven项目,命名为e-book-consumer-feign-httpclient,拷贝e-book-consumer-feign-param的文件和代码到这个项目中。
(2)修改pom文件,在原来的依赖中添加如下依赖:

    <dependency>
		<groupId>org.apache.httpcomponents</groupId>
	    <artifactId>httpclient</artifactId>
	    <version>4.5.3</version>
	</dependency>
	<dependency>
	    <groupId>com.netflix.feign</groupId>
	    <artifactId>feign-httpclient</artifactId>
	    <version>8.15.1</version>
	</dependency>

(3)修改配置文件,加入启用httpclient的配置,修改应用名和端口,代码如下:

spring.application.name=e-book-consumer-httpclient
server.port=8093

eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8762/eureka/

#启用httpclient
feign.httpclient.enabled=true

(4)处理第三节遗留下来的一个问题。为什么feign会将请求自动转换为post请求?
因为feign的HTTP客户端默认支持的是HttpURLConnection,在多参数请求的情况下,会将请求自动转换为post,这与指定的get请求不符合。现在我们启用httpclient,就可以处理这种请求了,不过需要在接口文件里面加入如下代码:
在这里插入图片描述
(5)在实现类中加入如下代码:
在这里插入图片描述
(6)启动注册中心,启动e-book-product-core,在启动这个项目。
(7)浏览器访问http://localhost:8093/get2?id=1&name=fff,结果如下:
在这里插入图片描述
(8)源码点这里

第六节:如何在微服务的日志中记录每个接口URL,状态码和耗时信息。

(1)在e-book-consumer模块下新建一个maven项目,命名为e-book-consumer-feign-log,拷贝e-book-consumer-feign的各个文件和代码到这个项目。
(2)修改启动类,加上如下代码:
在这里插入图片描述
(3)修改logback.xml,将日志级别由info改为debug。
(4)启动,浏览器访问http://localhost:8094/list,后台打印如下日志:
在这里插入图片描述
(5)源码点这里

第七节:对feign的负载均衡进行优化配置

这里的负载均衡用ribbon实现。ribbon的配置分全局配置和局部配置。
(1)在e-book-consumer模块下新建maven项目,命名为e-book-consumer-feign-ribbon。
(2)拷贝e-book-consumer-feign的文件和代码到这个项目。
全局配置
(3)修改配置文件,加上如下配置,即设置超时时间为5秒。

#全局配置
#请求连接的超时时间
ribbon.ConnectTimeout=5000
#请求处理的超时时间
ribbon.ReadTimeout=5000

(4)修改provider,修改e-book-product-core项目的接口实现类,修改list接口,加上如下代码:
在这里插入图片描述
(5)启动项目,浏览器访问http://localhost:8095/list,后台接口等5秒,发生了如下异常:
在这里插入图片描述
局部配置
(6)修改配置文件,注释掉全局配置,加入如下局部配置。

#局部配置,就是针对某个provider进行配置
#对所有操作请求都进行重试
e-book-product.ribbon.OkToRetryOnAllOperations=true

#对当前实例的重试次数
e-book-product.ribbon.MaxAutoRetries=2

#切换实例的重试次数
e-book-product.ribbon.MaxAutoRetriesNextServer=0

#请求连接的超时时间
e-book-product.ribbon.ConnectTimeout=3000

#请求处理的超时时间
e-book-product.ribbon.ReadTimeout=3000

#指定具体的服务实例清单
#e-book-product.ribbon.listOfServers=

(7)在实现类中加入一条输出。
在这里插入图片描述
(8)启动,浏览器访问,可以发现后台打印了3次输出。
在这里插入图片描述
(9)源码点这里

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值