SpringCloud:搭建一个简单Spring Cloud项目

5 篇文章 0 订阅
3 篇文章 0 订阅

最近在学习springcloud微服务框架,看了很多博客和文章之后,自己模拟一个简单的业务场景搭建了一个springcloud项目。本次练习包括对springcloud核心组件:eureka、ribbon、hystrix的使用,以及feign和Gateway的简单尝试!

模拟业务场景介绍:

​ 假设现在有个订单服务(order-service)r,要实现订单支付功能,流程如下:

  • 调用订单服务的支付接口
  • 订单服务调用商品服务的扣除库存接口
  • 订单服务调用积分服务的增加积分接口

一、项目搭建

1.1、创建一个maven父项目

​ 新建一个空白的maven project

New Project -> Maven -> Next

​ pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.xdemo</groupId>
    <artifactId>springcloud_2</artifactId>
    <version>1.0-SNAPSHOT</version>

</project>

这个maven 是一个工程包,打包用。

1.2、服务的注册和发现(Eureka)

eureka说明:

这里借用一下别人描述:https://www.jianshu.com/p/31dfb595170c

咱们来考虑第一个问题:订单服务想要调用库存服务、仓储服务,或者是积分服务,怎么调用?

  • 订单服务压根儿就不知道人家库存服务在哪台机器上啊!他就算想要发起一个请求,都不知道发送给谁,有心无力!
  • 这时候,就轮到Spring Cloud Eureka出场了。Eureka是微服务架构中的注册中心,专门负责服务的注册与发现。

咱们来看看下面的这张图,结合图来仔细剖析一下整个流程:

img

如上图所示,库存服务、仓储服务、积分服务中都有一个Eureka Client组件,这个组件专门负责将这个服务的信息注册到Eureka Server中。说白了,就是告诉Eureka Server,自己在哪台机器上,监听着哪个端口。而Eureka Server是一个注册中心,里面有一个注册表,保存了各服务所在的机器和端口号

订单服务里也有一个Eureka Client组件,这个Eureka Client组件会找Eureka Server问一下:库存服务在哪台机器啊?监听着哪个端口啊?仓储服务呢?积分服务呢?然后就可以把这些相关信息从Eureka Server的注册表中拉取到自己本地缓存起来。

这时如果订单服务想要调用库存服务,不就可以找自己本地的Eureka Client问一下库存服务在哪台机器?监听哪个端口吗?收到响应后,紧接着就可以发送一个请求过去,调用库存服务扣减库存的那个接口!同理,如果订单服务要调用仓储服务、积分服务,也是如法炮制。

总结一下:

  • Eureka Client:负责将这个服务的信息注册到Eureka Server中
  • Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号
创建一个module:

在这里插入图片描述

在这里插入图片描述
2020-11-19 补充:注意:这里不要使用默认的新发布的springboot 2.4.0,否则启动项目会报错。
在这里插入图片描述
可以选择springboot 2.3.6
在这里插入图片描述
又或者创建好项目之后在pom.xml里面把版本号手动改为:<version>2.3.5.RELEASE</version>
在这里插入图片描述

配置文件:application.yml

spring:
  application:
    name: registry-service
server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false #该实例是否向Eureka Server注册自己。因为自身为Eureka Server,所以false
    fetch-registry: false #该实例是否向 Eureka 服务器获取所有的注册信息表  同上
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

最后在项目启动类上加上注解

@EnableEurekaServer //eureka服务注册中心

package com.xdemo.registry;

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

@SpringBootApplication
@EnableEurekaServer
public class RegistryApplication {

    public static void main(String[] args) {
        SpringApplication.run(RegistryApplication.class, args);
    }
}
测试:

启动服务,访问localhost:8761

在这里插入图片描述

到此,eureka注册中心就配置成功了。

1.3 创建服务

概念:服务提供者和服务消费者

服务提供者:服务的被调用方,即为其他服务提供服务的服务。

服务消费者:服务的调用方,即依赖其他服务的服务。

这个概念网上都是这么说的,但是两者代码没有什么区别。网上查询资料和思考,这里它们还真的只是概念。它没有调用其他服务,只提供服务它就是提供者;它调了其他服务,它就是消费者,它要是两者都是也行。

创建一个module:user-service

依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

application.yml配置:

spring:
  application:
    name: user-service
server:
  port: 8090
eureka:
  client:
    service-url:
      deaultZone: http://localhost:8761/eureka/

在启动类上添加注解:

@EnableDiscoveryClient或者@EnableEurekaClient两个都是注册。

@EnableDiscoveryClient与@EnableEurekaClient区别

1,@EnableDiscoveryClient注解是基于spring-cloud-commons依赖,并且在classpath中实现;

2,@EnableEurekaClient注解是基于spring-cloud-netflix依赖,只能为eureka作用;

简单来说就是注册中心是eureka使用@EnableEurekaClient这个只为Eureka服务,其他的注册中心使用

@EnableDiscoveryClient,包括Eureka。所以这里咱们使用@EnableDiscoveryClient

package com.xdemo.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}
创建另外一个module:order-service

依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

application.yml配置:

spring:
  application:
    name: order-service
server:
  port: 8081
eureka:
  client:
    service-url:
      deaultZone: http://localhost:8761/eureka/

在启动类上添加注解:

@EnableDiscoveryClient

测试:

至此,两个项目就已经搭建完成,分别启动两个项目,然后访问: http://localhost:8761/
在这里插入图片描述

可以看到我们创建的两个项目都已经被注册到注册中心了。

1.4 服务间的通信及负载均衡

1.4.1使用Feign实现服务间的通信
Spring Cloud核心组件:Feign描述

看一下官方的解释:Feign 是一个声明式 WebService 客户端。使用 Feign 能让编写的 WebService 客户端更加简洁,它的使用方法式定义一个接口,然后在上面添加注解。Spring Cloud 对 Feign 进行了封装,使其支持了 Spring MVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。

原文描述:https://www.jianshu.com/p/31dfb595170c

Feign客户端是一个web声明式http远程调用工具,提供了接口和注解方式进行调用(用来调用其他服务)

Feign的一个关键机制就是使用了动态代理。

  1. 首先,如果你对某个接口定义了@FeignClient注解,Feign就会针对这个接口创建一个动态代理

  2. 接着你要是调用那个接口,本质就是会调用 Feign创建的动态代理,这是核心中的核心

  3. Feign的动态代理会根据你在接口上的@RequestMapping等注解,来动态构造出你要请求的服务的地址

  4. 最后针对这个地址,发起请求、解析响应

使用:

首先,我们在user-service中写一个接口供order-service调用:

package com.xdemo.userservice.controller;

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

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 11:00
 */
@RestController
@RequestMapping("user")
public class UserController {
    @Value("${spring.application.name}")
    private String serverName;

    @Value("${server.port}")
    private String port;

    @RequestMapping("/getUser")
    public String getUser(){
        return "服务名:"+serverName+",端口:"+port;
    }
}

在order-service中添加Feign依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在启动类上加上注解:@EnableFeignClients

package com.xdemo.orderservice;

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

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {

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

创建一个接口:UserApi

package com.xdemo.orderservice.api;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 11:04
 */
@FeignClient(value = "user-service")
@Service
public interface UserApi {

    @RequestMapping("user/getUser")
    String getUser();
}

@FeignClient(value = "user-service") //告诉RPC访问那个服务
@RequestMapping("user/getUser") //调用服务哪个接口

控制器:

package com.xdemo.orderservice.controller;

import com.xdemo.orderservice.api.UserApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 11:05
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    UserApi userApi;

    @RequestMapping("/orderGetUser")
    public String orderGetUser(){
        return userApi.getUser();
    }
}

最后,启动项目,访问 http://localhost:8081/order/orderGetUser

在这里插入图片描述

到此,我们创建的两个服务就能进行通信了!

1.4.3使用Ribbon+Feign实现负载均衡
Spring Cloud核心组件:Ribbon描述

该处借用别人的描述:https://www.jianshu.com/p/31dfb595170c

现在我们假设用户服务部署在三台服务器上面,如下图。

  • 192.168.169:9000
  • 192.168.170:9000
  • 192.168.171:9000
  • 192.168.172:9000
  • 192.168.173:9000

那么Feign怎么知道该请求哪台机器呢?

这时Spring Cloud Ribbon就派上用场了。Ribbon就是专门解决这个问题的。它的作用是负载均衡,会帮你在每次请求时选择一台机器,均匀的把请求分发到各个机器上

Ribbon的负载均衡默认使用的最经典的Round Robin轮询算法。这是啥?简单来说,就是如果订单服务对库存服务发起10次请求,那就先让你请求第1台机器、然后是第2台机器、第3台机器、第4台机器、第5台机器,接着再来—个循环,第1台机器、第2台机器。。。以此类推。

此外,Ribbon是和Feign以及Eureka紧密协作,完成工作的,具体如下:

  • 首先Ribbon会从 Eureka Client里获取到对应的服务注册表,也就知道了所有的服务都部署在了哪些机器上,在监听哪些端口号。
  • 然后Ribbon就可以使用默认的Round Robin算法,从中选择一台机器
  • Feign就会针对这台机器,构造并发起请求。
使用:

我们创建多个user-service服务,这些服务除了端口不一样,其他都相同

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在user-service中加入依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

注意:此处可以导入ribbon依赖,亦可以导入Feign依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

启动所有服务,然后访问 http://localhost:8081/order/orderGetUser

第一次访问的时候端口是8090

在这里插入图片描述

F5刷新一下,变成了8091:

在这里插入图片描述

再刷新,变为8092:

在这里插入图片描述

到这,负载均衡也成功了!

1.5 Hystrix

简单模拟业务:

让我们回到最开始模拟的业务场景:

创建一个goods-service服务,该服务提供扣减商品库存的接口

导入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在启动类上添加注解:@EnableDiscoveryClient

package com.xdemo.goodsservice;

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

@SpringBootApplication
@EnableDiscoveryClient
public class GoodsServiceApplication {

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

application.yml配置:

spring:
  application:
    name: goods-service
server:
  port: 9001
eureka:
  client:
    service-url:
      deaultZone: http://localhost:8761/eureka/

控制层:模拟扣减库存业务

package com.xdemo.goodsservice.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:02
 */
@RestController
@RequestMapping("goods")
public class GoodsController {
    private static final Logger LOGGER= LoggerFactory.getLogger(GoodsController.class);

    @RequestMapping("/reduceStock")
    public Integer reduceStock(){
        LOGGER.info("商品服务被调用,扣减库存成功!");
        return 1;
    }
}

创建一个integral-service服务,该服务提供增加积分的接口

导入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在启动类上添加注解:@EnableDiscoveryClient

package com.xdemo.integralservice;

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

@SpringBootApplication
@EnableDiscoveryClient
public class IntegralServiceApplication {

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

}

application.yml配置:

spring:
  application:
    name: integral-service
server:
  port: 9005
eureka:
  client:
    service-url:
      deaultZone: http://localhost:8761/eureka/

控制层:模拟增加积分业务

package com.xdemo.integralservice.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:19
 */
@RestController
@RequestMapping("integral")
public class IntegralController {
    private static final Logger LOGGER= LoggerFactory.getLogger(IntegralController.class);
    @RequestMapping("/addIntegral")
    public Integer addIntegral(){
        LOGGER.info("调用积分服务,增加积分成功!");
        return 1;
    }
}
使用order-service服务分别调用goods-service的扣减库存接口和integral-service增加积分接口:

在order-service中新建两个接口:

GoodsApi:

package com.xdemo.orderservice.api;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:06
 */
@FeignClient(value = "goods-service")
public interface GoodsApi {

    @RequestMapping("/goods/reduceStock")
    public Integer reduceStock();
}

IntegralApi:

package com.xdemo.orderservice.api;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:22
 */
@FeignClient(value = "integral-service")
public interface IntegralApi {

    @RequestMapping("/integral/addIntegral")
    public Integer addIntegral();
}

控制层模拟订单支付业务:

package com.xdemo.orderservice.controller;

import com.xdemo.orderservice.api.GoodsApi;
import com.xdemo.orderservice.api.IntegralApi;
import com.xdemo.orderservice.api.UserApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 11:05
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    UserApi userApi;

    @Autowired
    GoodsApi GoodsApi;

    @Autowired
    IntegralApi integralApi;

    @RequestMapping("/orderGetUser")
    public String orderGetUser(){
        return userApi.getUser();
    }

    @RequestMapping("/orderPayment")
    public Integer orderPayment(){
        Integer result1 = GoodsApi.reduceStock();
        Integer result2 = integralApi.addIntegral();
        return result1+result2;
    }
}

启动服务,访问 http://localhost:8081/order/orderPayment

如果所有服务都正常,则

页面显示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wZhzzFXs-1605173237283)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112154016098.png)]

控制台分别打印:

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LdV7WrvT-1605173237289)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112154151462.png)]

此时我们将积分服务给关闭,再次访问http://localhost:8081/order/orderPayment ,则会发现

页面会显示错误:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NmbDKm3X-1605173237294)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112154325698.png)]

order-service服务控制台会抛出异常:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlNOCiJd-1605173237297)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112160133373.png)]

此处借用别人的描述:https://www.jianshu.com/p/31dfb595170c

在微服务架构里,一个系统会有很多的服务。以本文的业务场景为例:订单服务在一个业务流程里需要调用三个服务。现在假设订单服务自己最多只有100个线程可以处理请求,然后呢,积分服务不幸的挂了,每次订单服务调用积分服务的时候,都会卡住几秒钟,然后抛出—个超时异常。

咱们一起来分析一下,这样会导致什么问题?

  • 如果系统处于高并发的场景下,大量请求涌过来的时候,订单服务的100个线程都会卡在请求积分服务这块。导致订单服务没有一个线程可以处理请求
  • 然后就会导致别人请求订单服务的时候,发现订单服务也挂了,不响应任何请求了

上面这个,就是微服务架构中恐怖的服务雪崩问题,如下图所示:

img

如上图,这么多服务互相调用,要是不做任何保护的话,某一个服务挂了,就会引起连锁反应,导致别的服务也挂。比如积分服务挂了,会导致订单服务的线程全部卡在请求积分服务这里,没有一个线程可以工作,瞬间导致订单服务也挂了,别人请求订单服务全部会卡住,无法响应。

但是我们思考一下,就算积分服务挂了,订单服务也可以不用挂啊!为什么?

  • 我们结合业务来看:支付订单的时候,只要把库存扣减了,然后通知仓库发货就OK了
  • 如果积分服务挂了,大不了等他恢复之后,慢慢人肉手工恢复数据!为啥一定要因为一个积分服务挂了,就直接导致订单服务也挂了呢?不可以接受!

现在问题分析完了,如何解决?

这时就轮到Hystrix闪亮登场了。Hystrix是隔离、熔断以及降级的一个框架。啥意思呢?说白了,Hystrix会搞很多个小小的线程池,比如订单服务请求库存服务是一个线程池,请求仓储服务是一个线程池,请求积分服务是一个线程池。每个线程池里的线程就仅仅用于请求那个服务。

打个比方:现在很不幸,积分服务挂了,会咋样?

当然会导致订单服务里的那个用来调用积分服务的线程都卡死不能工作了啊!但是由于订单服务调用库存服务、仓储服务的这两个线程池都是正常工作的,所以这两个服务不会受到任何影响。

这个时候如果别人请求订单服务,订单服务还是可以正常调用库存服务扣减库存,调用仓储服务通知发货。只不过调用积分服务的时候,每次都会报错。但是如果积分服务都挂了,每次调用都要去卡住几秒钟干啥呢?有意义吗?当然没有!所以我们直接对积分服务熔断不就得了,比如在5分钟内请求积分服务直接就返回了,不要去走网络请求卡住几秒钟,这个过程,就是所谓的熔断!

那人家又说,兄弟,积分服务挂了你就熔断,好歹你干点儿什么啊!别啥都不干就直接返回啊?没问题,咱们就来个降级:每次调用积分服务,你就在数据库里记录一条消息,说给某某用户增加了多少积分,因为积分服务挂了,导致没增加成功!这样等积分服务恢复了,你可以根据这些记录手工加一下积分。这个过程,就是所谓的降级。

为帮助大家更直观的理解,接下来用一张图,梳理一下Hystrix隔离、熔断和降级的全流程:

img

Hystrix使用:

在order-service的配置文件中添加开启Hystrix功能配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5VpreISN-1605173237308)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112161812860.png)]

为我们刚刚写的IntegralApi接口编写一个具体的实现类,重写接口中的方法实现服务降级。

package com.xdemo.orderservice.api.impl;

import com.xdemo.orderservice.api.IntegralApi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * @author xdemo
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:55
 */
@Service
public class IntegralApiImpl implements IntegralApi {
    private static final Logger LOGGER= LoggerFactory.getLogger(IntegralApi.class);
    @Override
    public Integer addIntegral() {
        LOGGER.error("积分服务出现异常,服务熔断!");
        return 0;
    }
}

然后在IntegralApi接口中,通过@FeignClient注解的failback属性来指定对应的服务降级实现类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0pNPtroM-1605173237310)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112162238522.png)]

此时,我们再将项目全部重新启动,但是将提供增加积分接口的integral-service服务关闭,然后访问:

http://localhost:8081/order/orderPayment

此时我们发现页面没有报错,控制台也没有抛出异常:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VfQvAiiJ-1605173237313)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112162435631.png)]

order-service服务控制台打印:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Rela2X8-1605173237316)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112162504761.png)]

到此,Hystrix也已经整合成功了!

1.6 服务网关Gateway

描述:

Spring Cloud Gateway 是 Spring Cloud 新推出的网关框架,之前是 Netflix Zuul。网关通常在项目中为了简化

前端的调用逻辑,同时也简化内部服务之间互相调用的复杂度;具体作用就是转发服务,接收并转发所有内外

部的客户端调用;其他常见的功能还有权限认证,限流控制等等。

Spring Cloud Gateway 功能特征
  • 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
  • 动态路由:能够匹配任何请求属性;
  • 集成 Spring Cloud 服务发现功能;
  • 可以对路由指定 Predicate(断言)和 Filter(过滤器);
  • 易于编写的 Predicate(断言)和 Filter(过滤器);
  • 集成Hystrix的断路器功能;
  • 请求限流功能;
  • 支持路径重写。

详情参考:https://blog.csdn.net/qq_38380025/article/details/102968559

使用:

本次只会简单使用一下网关的基本转发功能

创建一个module,用于作为Gateway网关:

导入依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

启动类上添加注解:@EnableDiscoveryClient

package com.xdemo.gateway;

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

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {

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

}

application.yml配置:

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #表明gateway开启服务注册和发现的功能,
          #并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,
          # 这个router将以服务名开头的请求路径转发到对应的服务
          lower-case-service-id: true #将请求路径上的服务名配置为小写
      routes:
      #我们自定义的路由 ID,保持唯一
      - id: user-service
        #目标服务器地址,表示从微服务注册中心(如Eureka)订阅user-service服务,并且进行服务的路由。
        uri: lb://user-service
        #路由条件,接收一个输入参数,返回一个布尔值结果。
        predicates:
        - Path=/api/user/**
        #过滤规则
        filters:
        - StripPrefix=1
  #- StripPrefix=1表示路由(转发)时会去掉/user
  #- StripPrefix后面的数字是几表示去除几层路径
  #例如写2的话就会去除/api/user
  #即访问/api/user/getUser会变为/user/getUser
  #上面的路由配置表示所有包含/api/user/的url都会被路由(转发)到user-service服务
  #例如访问http://localhost:8088/api/user/getUser会转发到http://localhost:8090/user/getUser
server:
  port: 8088

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/


把我们之前创建的三个user-service服务都给启动,访问 http://localhost:8088/api/user/getUser

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k0mejtJ3-1605173237319)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112170405806.png)]

而且当我们刷新页面后,发现端口也发生了变化,说明Gateway网关可以结合ribbon完成负载均衡的功能:
在这里插入图片描述

  • 9
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值