Java之 Spring Cloud 微服务搭建 Feign组件(第二个阶段)SpringBoot项目实现商品服务器端是调用

Posted 北芳科技

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之 Spring Cloud 微服务搭建 Feign组件(第二个阶段)SpringBoot项目实现商品服务器端是调用相关的知识,希望对你有一定的参考价值。

SpringCloud学习目录点击跳转对应的文章
Java之 Spring Cloud 微服务搭建(第一个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务 Eureka (第一个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建Ribbon(第一个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Consul(第一个阶段)【四】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Feign组件(第二个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Hystrix (第二个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建Sentinel (第二个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建网关 nginx,Zuul(第三个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务的链路追踪 Sleuth 和 Zipkin(第三个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务的 Spring Cloud Stream(第四个阶段)【一】【SpringBoot项目实现商品服务器端调用】
Java之 Spring Cloud 微服务的 SpringCloud Config 配置中心(第四个阶段)【二】【SpringBoot项目实现商品服务器端调用】
Java之 Spring Cloud 微服务的开源配置中心Apollo(第四个阶段)【三】【SpringBoot项目实现商品服务器端调用】

Java之 Spring Cloud 微服务搭建 Feign组件(第二个阶段)【一】【SpringBoot项目实现商品服务器端是调用】


Java之 Spring Cloud 微服务搭建 Feign组件(第二个阶段)【一】【SpringBoot项目实现商品服务器端是调用】

一、服务调用Feign入门

前面我们使用的RestTemplate实现REST API调用,代码大致如下:

@RequestMapping(value = "/buy/id", method = RequestMethod.GET)
    public Product findById(@PathVariable Long id) 
        //如何调用商品服务?
        return restTemplate.getForObject("http://service-product/product/"+id,Product.class);
    

由代码可知,我们是使用拼接字符串的方式构造URL的,该URL只有一个参数。
但是,在现实中,URL中往往含有多个参数。

这时候我们如果还用这种方式构造URL,那么就会非常痛苦。那应该如何解决?

我们带着这样的问题进入到本章的学习。

1、 Feign简介

Feign是Netflix开发的声明式,模板化的HTTP客户端,其灵感来自Retrofit,JAXRS-2.0以及WebSocket.

  • Feign可帮助我们更加便捷,优雅的调用HTTP API。
  • 在SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。
  • Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
  • SpringCloud对Feign进行了增强,使Feign支持了SpringMVC注解,并整合了Ribbon和- Eureka,从而让Feign的使用更加方便。

2、 基于Feign的服务调用

(1)引入依赖

在我们上节课程当中对应的第一个项目当中spring_cloud_demo
order_service当中的pom.xml引入依赖

        <!--SpringCloud整合的openFeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

(2)创建Feign相关接口


package cn.itbluebox.order.feign;
import cn.itbluebox.order.entity.Product;
import org.bouncycastle.cert.ocsp.Req;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.persistence.Id;
/*
 * ProductFeignClient上声明需要调用的微服务名称
 * @FeignClient
 *      name: 服务提供者的名称
 */
@FeignClient(name = "service-product")
public interface ProductFeignClient 
    /*
    配置需要调用的微服务接口
     */
    @RequestMapping(value = "/product/id", method = RequestMethod.GET)
    public Product findById(@PathVariable("id") Long id);

上述的操作配置相当于之前的微服务调用的时候的

restTemplate.getForObject("http://service-product/product/"+id,Product.class);

(3)启动类激活FeignClient

@SpringBootApplication
@EntityScan("cn.itbluebox.order.entity")
@EnableFeignClients
public class OrderApplication 
    /*
    使用Spring提供的 RestTemplate 发送http请求到商品服务
        1、创建RestTemplate对象交给容器管理
        2、在使用的时候,调用其方法完成操作(getXX,postXX)
        @LoadBalanced : 是Ribbon提供的负载均衡的注解
     */
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate()
        return new RestTemplate();
    
    public static void main(String[] args) 
        SpringApplication.run(OrderApplication.class,args);
    

(4)修改OrderController

package cn.itbluebox.order.controller;

import cn.itbluebox.order.entity.Product;
import cn.itbluebox.order.feign.ProductFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController 

    @Autowired
    private ProductFeignClient productFeignClient;

    @RequestMapping(value = "/buy/id", method = RequestMethod.GET)
    public Product findById(@PathVariable Long id) 
        //如何调用商品服务?
        return productFeignClient.findById(id);
    



(5)运行测试


访问测试
http://localhost:9002/order/buy/1

3、 Feign和Ribbon的联系

Ribbon是一个基于 HTTP 和 TCP 客户端 的负载均衡的工具。

它可以 在客户端 配置RibbonServerList(服务端列表),使用 HttpClient 或 RestTemplate 模拟http请求,步骤相当繁琐。

Feign 是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。采用接口的方式, 只需要创建一个接口,然后在上面添加注解即可 ,将需要调用的其他服务的方法定义成抽象方法即可, 不需要自己构建http请求。

然后就像是调用自身工程的方法调用,而感觉不到是调用远程方法,使得编写客户端变得非常容易

4、负载均衡

Feign中本身已经集成了Ribbon依赖和自动配置,因此我们不需要额外引入依赖,也不需要再注册RestTemplate 对象。

另外,我们可以像上节课中讲的那样去配置Ribbon,可以通过 ribbon.xx 来进行全局配置。

也可以通过 服务名.ribbon.xx 来对指定服务配置:

(1)修改OrderService的启动类设置OrderApplication

package cn.itbluebox.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EntityScan("cn.itbluebox.order.entity")
@EnableFeignClients
public class OrderApplication 
    /*
    使用Spring提供的 RestTemplate 发送http请求到商品服务
        1、创建RestTemplate对象交给容器管理
        2、在使用的时候,调用其方法完成操作(getXX,postXX)
        @LoadBalanced : 是Ribbon提供的负载均衡的注解
     */

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


(2)启动第二个product_service

修改product_service的pom.xml
设置端口号为9011

启动测试

启动成功

查看Eurekahttp://localhost:9000/

重新启动order_service

(3)访问测试http://localhost:9002/order/buy/1


刷新页面再次访问

二、服务调用Feign高级

1、Feign的配置

从Spring Cloud Edgware开始,Feign支持使用属性自定义Feign。对于一个指定名称的FeignClient(例如该Feign Client的名称为 feignName ),Feign支持如下配置项:

feign:
	client:
		config:
			feignName: ##定义FeginClient的名称
				connectTimeout: 5000 # 相当于Request.Options
				readTimeout: 5000 # 相当于Request.Options
				# 配置Feign的日志级别,相当于代码配置方式中的Logger
				loggerLevel: full
				# Feign的错误解码器,相当于代码配置方式中的ErrorDecoder
				errorDecoder: com.example.SimpleErrorDecoder
				# 配置重试,相当于代码配置方式中的Retryer
				retryer: com.example.SimpleRetryer
				# 配置拦截器,相当于代码配置方式中的RequestInterceptor
				requestInterceptors:
				- com.example.FooRequestInterceptor
				- com.example.BarRequestInterceptor
				decode404: fals
  • feignName:FeginClient的名称
  • connectTimeout : 建立链接的超时时长
  • readTimeout : 读取超时时长
  • loggerLevel: Fegin的日志级别
  • errorDecoder :Feign的错误解码器
  • retryer : 配置重试
  • requestInterceptors : 添加请求拦截器
  • decode404 : 配置熔断不处理404异常

2、请求压缩

Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过下面的参数即可开启请求与响应的压缩功能:

feign:
	compression:
		request:
			enabled: true # 开启请求压缩
		response:
			enabled: true # 开启响应压缩

同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置:

feign:
	compression:
		request:
			enabled: true # 开启请求压缩
			mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
			min-request-size: 2048 # 设置触发压缩的大小下限

注:上面的数据类型、压缩大小下限均为默认值。

3、打印Feign的日志

在开发或者运行阶段往往希望看到Feign请求过程的日志记录,默认情况下Feign的日志是没有开启的。要想用属性配置方式来达到日志效果,只需在 application.yml 中添加如下内容即可:
修改order_service当中的application.yml

feign:
  client:
    config:
      service-product:
        loggerLevel: FULL
logging:
  level:
    cn.itbluebox.order.feign.ProductFeignClient : debug
  • logging.level.xx : debug : Feign日志只会对日志级别为debug的做出响应
  • feign.client.config.shop-service-product.loggerLevel : 配置Feign的日志Feign有四种日志级别:
    • NONE【性能最佳,适用于生产】:不记录任何日志(默认值)
    • BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间
    • HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
    • FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据。

重新启动运行测试

刷新页面访问测试http://localhost:9002/order/buy/1

4、源码分析

三、高并发问题

1、创建工程

在这里的工程在之前都已经重复多次创建,我们在这里省略创建的过程直接导入对应的代码即可
工程下载地址:https://download.csdn.net/download/qq_44757034/46931029

2、模拟环境

(1)设置tomcat最大线程数

设置order_service当中的application.yml

server:
  port: 9002 #端口
  tomcat:
    max-threads: 10

(2)设置product_service延时返回数据

在ProductController当中修改

	@RequestMapping(value = "/id",method = RequestMethod.GET)
	public Product findById(@PathVariable Long id) 
		try 
			Thread.sleep(2000l);
		 catch (InterruptedException e) 
			e.printStackTrace();
		
		Product product = productService.findById(id);
		product.setProductName("访问的服务地址:"+ip + ":" + port);
		return product;
	

(3)运行启动所有的工程测试


访问:http://localhost:9002/order/buy/1


我们可以看到访问时间是2秒多

访问http://localhost:9002/order/1

我们可以看到访问时间是3毫秒

3、微服务架构的高并发问题

(1)性能工具Jmetter

Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。

它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI 脚本、Java 对象、数据库、FTP 服务器, 等等。JMeter 可以用于对服务器、网络或对象模拟巨大的负载,来自不同压力类别下测试它们的强度和分析整体性能。

另外JMeter能够对应用程序做功能/回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。

为了最大限度的灵活性,JMeter允许使用正则表达式创建断言。

1)安装Jmetter

Jmetter安装十分简单,下载完整压缩包,解压找到安装目录下bin/jmeter.bat 已管理员身份启动即可
https://jmeter.apache.org/



解压以后找到bin目录启动


2) 配置Jmetter

(1)创建新的测试计划
进入界面后已经自动创建了对应的测试计划

(2)测试计划下创建发起请求的线程组

可以配置请求的线程数
以及每个请求发送的请求次数
(3)创建http请求模板


(4)添加结果树

3)测试

(1)设置发送线程数量

运行成功

发送对应的请求


在上述没有运行完的情况下我们在浏览器上访问http://localhost:9002/order/1

我们发现访问时长为将近4秒比之前的访问时间长了不少

4) 上述问题分析


Tomcat会以线程池的形式对所有的请求进行统一的管理,对于某个方法可能存在的耗时的问题的时候,会随着外面挤压的请求越来越多,势必造成整个系统的奔溃

(2 )系统负载过高存在的问题(解决上述问题)

1) 问题分析

在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累计,导致服务瘫痪。
在SpringBoot程序中,默认使用内置tomcat作为web服务器。

单tomcat支持最大的并发请求是有限的,如果某一接口阻塞,待执行的任务积压越来越多,那么势必会影响其他接口的调用。

2)线程池的形式实现服务隔离

为了不影响其他接口的正常访问:对多个服务之间进行隔离

(1) 配置坐标(在order_service当中)引入hystrix
为了方便实现线以线程池的形式完成资源隔离,需要引入如下依赖

<dependency>
	<groupId>com.netflix.hystrix</groupId>
	<artifactId>hystrix-metrics-event-stream</artifactId>
	<version>1.5.12</version>
</dependency>
<dependency>
	<groupId>com.netflix.hystrix</groupId>
	<artifactId>hystrix-javanica</artifactId>
	<version>1.5.12</version>
</dependency>

(2) 配置线程池
配置HystrixCommand接口的实现类,再实现类中可以对线程池进行配置,创建OrderCommand类

package cn.itbluebox.order.command;

import cn.itbluebox.order.entity.Product;
import com.netflix.hystrix.*;
import org.springframework.web.client.RestTemplate;

public class OrderCommand extends HystrixCommand<Product> 

    private RestTemplate restTemplate;

    private Long id;

    public OrderCommand(RestTemplate restTemplate, Long id) 
        super(setter());
        this.restTemplate = restTemplate;
        this.id = id;
    

    private static Setter setter() 

        // 服务分组
        HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("order_product");
        // 服务标识
        HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("product");
        // 线程池名称
        HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order_product_pool");
        /**
         * 线程池配置
         *     withCoreSize :  线程池大小为10
         *     withKeepAliveTimeMinutes:  线程存活时间15秒
         *     withQueueSizeRejectionThreshold  :队列等待的阈值为100,超过100执行拒绝策略
         */
        HystrixThreadPoolProperties.Setter以上是关于Java之 Spring Cloud 微服务搭建 Feign组件(第二个阶段)SpringBoot项目实现商品服务器端是调用的主要内容,如果未能解决你的问题,请参考以下文章

Java之 Spring Cloud 微服务搭建 Consul(第一个阶段)SpringBoot项目实现商品服务器端是调用

Java之 Spring Cloud 微服务搭建Sentinel (第二个阶段)SpringBoot项目实现商品服务器端是调用

Java之 Spring Cloud 微服务搭建 Hystrix (第二个阶段)SpringBoot项目实现商品服务器端是调用

Java之 Spring Cloud 微服务搭建(第一个阶段)SpringBoot项目实现商品服务器端是调用

Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)SpringBoot项目实现商品服务器端是调用

Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)SpringBoot项目实现商品服务器端是调用