从浅入深 学习 SpringCloud 微服务架构(十一)–SpringCloudGateWay(1)
一、SpringCloudGateway:概述
1、Zuul 网关存在的问题
1.1 Zuul 中,整个请求的过程
首先将请求给 zuulservlet 处理,zuulservlet 中有一个 zuulRunner 对象,该对象中初始化了 Requestcontext: 作为存储整个请求的一些数据,并被所有的 zuulfilter 共享。
zuulRunner 中还有 FilterProcessor, FilterProcessor 作为执行所有的 zuulfilter 的管理器。
FilterProcessor 从 filterloader 中获取 zuulfilter,而 zuulfilter 是被 filterFileManager 所加载,并支持 groovy 热加载,采用了轮询的方式热加载。
有了这些 filter 之后,zuulservelet 首先执行的 Pre 类型的过滤器,再执行 route 类型的过滤器,最后执行的是 post 类型的过滤器。
如果在执行这些过滤器有错误的时候则会执行 error 类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。
1.2 在实际使用中我们会发现直接使用 Zuul 会存在诸多问题,包括:
- 性能问题
Zuul1x 版本本质上就是一个同步 Servlet,采用多线程阻塞模型进行请求转发。
简单讲,每来一个请求,Servlet 容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户端这个线程才会被释放返回容器线程池。
如果后台服务调用比较耗时,那么这个线程就会被阻塞,阻塞期间线程资源被占用,不能干其它事情。
我们知道 Servlet 容器线程池的大小是有限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成容器无法接受新的请求。
- 不支持任何长连接,如 webspcket。
2、Zuul 网关的替换方案
- Zuul2.x版本
- SpringCloud Gateway
3、微服务网关 GateWay
Zuul 1.x 是一个基于阻塞 IO 的 APIGateway 以及 Servlet;
直到 2018年5月, u1 2.x(基于Netty,也是非阻塞的,支持长连接)才发布,但 Spring Cloud 暂时还没有整合计划。
Spring Cloud Gateway 比 Zuul1.x 系列的性能和功能整体要好。
3.1 Gateway 简介
Spring Cloud Gateway.是 Spring 官方基于 Spring5.0, Spring Boot 2.0 和 Project Reactor 等技术开发的网关,
旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式,统一访问接口。
SpringCloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 NetflixZUUL,
其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,
例如:安全,监控/埋点,和限流等。它是基于Nttey的响应式开发模式。
3.2 Spring Cloud Gateway 与Zuul 的性能对比
组件 | RPS(request per second) |
---|---|
Spring Cloud Gateway | Requests/sec: 32213.38 |
Zuu11X | Requests/sec: 20800.13 |
从结果可知,Spring Cloud Gateway 的 RPS 是 Zuul 的 1.6 倍。
3.3 springcloud gateway 网关 功能模块
- 路由配置
- 过滤器
- 统-鉴权
- 网关限流
- 网关的高可用
二、SpringCloudGateway 路由:基本配置
1、前言:SpringCloudGateway 路由 核心概念:
1.1 路由(route) :
路由是网关最基础的部分,路由信息由一个 ID、一个目的 URL、一组断言工厂和一组 Filter 组成。如果断言为真,则说明请求 URL 和配置的路由匹配。
1.2 断言(predicates):
java8 中的断言函数,Spring Cloud Gateway 中的断言函数输入类型是 Spring5.0 框架中的 ServerWebExchange。Spring Cloud Gateway 中的断言函数允许开发者去定义匹配来自 Http Request 中的任何信息,比如请求头和参数等。
1.3 过滤器(filter ):
一个标准的 Spring webFilter,Spring Cloud Gateway 中的 Filter 分为两种类型,分别是 Gateway Filter 和 Global Filter。过滤器 Filter 可以对请求和响应进行处理。
2、SpringCloudGateWay 网关:入门案例
1)创建工程,导入依赖坐标。
2)配置启动类
3)编写配置文件
2.1 SpringCloudGateWay 网关:入门案例–搭建环境,准备数据库。
— 创建数据库:
create database shop;
— 使用数据库:
use shop;
— 创建数据表:
CREATE TABLE `tb_product` (`id` int NOT NULL AUTO_INCREMENT,`product_name` varchar(40) DEFAULT NULL COMMENT '名称',`status` int DEFAULT NULL COMMENT '状态',`price` decimal(10,2) DEFAULT NULL COMMENT '单价',`product_desc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '描述',`caption` varchar(255) DEFAULT NULL COMMENT '标题',`inventory` int DEFAULT NULL COMMENT '库存',PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3
— 插入数据:
insert into `tb_product` (`id`, `product_name`, `status`, `price`, `product_desc`, `caption`, `inventory`) values('1','iPhone 15 Pro','1','7999.00','iPhone 15 Pro 6.7 英寸或 6.1 英寸, 超视网膜 XDR 显示屏,ProMotion 自适应刷新率技术,钛金属搭配亚光质感玻璃背板, 灵动岛功能, A17 Pro 芯片,配备 6 核图形处理器, Pro 级摄像头系统,主摄 4800 万像素 | 超广角 | 长焦, 10 倍, 支持 USB 3, 视频播放最长可达 29 小时。 ','iPhone 15 Pro 巅峰之作','99');
2.2 SpringCloudGateWay 网关:入门案例–搭建环境–打开 idea 创建父工程
创建 artifactId 名为 spring_cloud_demo 的 maven 工程。
--> idea --> File --> New --> Project --> Maven Project SDK: ( 1.8(java version "1.8.0_131" ) --> Next --> Groupld : ( djh.it )Artifactld : ( spring_cloud_demo )Version : 1.0-SNAPSHOT--> Name: ( spring_cloud_demo )Location: ( ...\spring_cloud_demo\ ) --> Finish
2.3 SpringCloudGateWay 网关:入门案例–搭建环境–在父工程 spring_cloud_demo 的 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>djh.it</groupId><artifactId>spring_cloud_demo</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>product_service</module><module>order_service</module><module>eureka_service</module><module>import_eurekaserver_test</module><module>api_zuul_service</module></modules><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.4</version><scope>provided</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Greenwich.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><repositories><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>http://repo.spring.io/libs-snapshot-local</url><snapshots><enabled>true</enabled></snapshots></repository><repository><id>spring-milestones</id><name>Spring Milestones</name><url>http://repo.spring.io/libs-milestone-local</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-releases</id><name>Spring Releases</name><url>http://repo.spring.io/libs-release-local</url><snapshots><enabled>false</enabled></snapshots></repository></repositories><pluginRepositories><pluginRepository><id>spring-snapshots</id><name>Spring Snapshots</name><url>http://repo.spring.io/libs-snapshot-local</url><snapshots><enabled>true</enabled></snapshots></pluginRepository><pluginRepository><id>spring-milestones</id><name>Spring Milestones</name><url>http://repo.spring.io/libs-milestone-local</url><snapshots><enabled>false</enabled></snapshots></pluginRepository></pluginRepositories><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
<!-- spring_cloud_demo\pom.xml -->
3、SpringCloudGateWay 网关:入门案例–搭建环境–在父工程 spring_cloud_demo 下,创建子工程 eureka_service (子模块)。
3.1 创建 eureka_service 子工程(子模块)
--> 右键 spring_cloud_demo 父工程--> Modules --> Maven --> Groupld : ( djh.it )Artifactld : ( eureka_service )Version : 1.0-SNAPSHOT--> Next --> Module name: ( eureka_service )Content root : ( \spring_cloud_demo\eureka_service )Module file location: ( \spring_cloud_demo\eureka_service )--> Finish
3.2 在子工程 eureka_service (子模块)中的 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"><parent><artifactId>spring_cloud_demo</artifactId><groupId>djh.it</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>eureka_service</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency></dependencies></project>
<!-- spring_cloud_demo\eureka_service\pom.xml -->
3.3 在子工程 eureka_service (子模块)中,创建配置文件 application.yml 配置文件。
## spring_cloud_demo\eureka_service\src\main\resources\application.ymlserver:port: 9000 # 启动端口 命令行注入。spring:application:name: service-eureka #spring应用名, # 注意 FeignClient 不支持名字带下划线eureka: # 配置 eureka_server
# instance:
# hostname: localhostclient:register-with-eureka: false # 是否将自己注册到注册中心,不配置时,默认 true。 配置高可用时,须注销此行,或配置为 truefetch-registry: false # 是否从 Eureka 中获取注册信息,不配置时,默认 true。 配置高可用时,须注销此行,或配置为 trueservice-url: # 配置暴露给 EurekaClient 的请求地址defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
3.4 在子工程 eureka_service (子模块)中,创建 启动类 EurekaServerApplication.java
/*** spring_cloud_demo\eureka_service\src\main\java\djh\it\eureka\EurekaServerApplication.java** 2024-4-19 启动类 EurekaServerApplication.java*/
package djh.it.eureka;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication
@EnableEurekaServer //激活 Eureka
public class EurekaServerApplication {//运行启动类,浏览器地址栏输入:localhost:9000 进行访问测试。public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);}
}
3.5 在子工程 eureka_service (子模块)中,运行启动类,进行测试
浏览器地址栏输入:http://localhost:9000 输出界面如下:
4、SpringCloudGateWay 网关:入门案例–搭建环境–在父工程 spring_cloud_demo 下,创建子工程 product_service(子模块)
4.1 创建 生产者 子工程 product_service(子模块)
--> 右键 spring_cloud_demo 父工程--> Modules --> Maven --> Groupld : ( djh.it )Artifactld : ( product_service )Version : 1.0-SNAPSHOT--> Next --> Module name: ( product_service )Content root : ( spring_cloud_demo\product_service )Module file location: ( spring_cloud_demo\product_service )--> Finish
4.2 在 生产者 子工程 product_service (子模块)中的 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"><parent><artifactId>spring_cloud_demo</artifactId><groupId>djh.it</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>product_service</artifactId><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><!-- <version>5.1.32</version>--><version>8.0.26</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- 引入 EurekaClient 依赖坐标 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency></dependencies></project><!-- spring_cloud_demo\product_service\pom.xml -->
4.3 在 生产者 子工程 product_service (子模块)中,创建 商品实体类 Product.java
/*** spring_cloud_demo\product_service\src\main\java\djh\it\product\domain\Product.java** 2024-4-17 商品实体类 Product.java*/
package djh.it.product.domain;import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;@Data
@Entity
@Table(name="tb_product") //对应数据库中的数据表
public class Product {@Idprivate Long id;private String productName;private Integer status;private BigDecimal price;private String productDesc;private String caption;private Integer inventory;
}
4.4 在 生产者 子工程 product_service (子模块)中,创建 dao 持久层接口类 ProductDao.java
/*** spring_cloud_demo\product_service\src\main\java\djh\it\product\dao\ProductDao.java** 2024-4-17 dao 持久层接口 ProductDao.java*/
package djh.it.product.dao;import djh.it.product.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;public interface ProductDaoextends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {}
4.5 在 生产者 子工程 product_service (子模块)中,创建 service 服务层 接口类 ProductService.java
/*** spring_cloud_demo\product_service\src\main\java\djh\it\product\service\ProductService.java** 2024-4-17 service 服务层 接口 ProductService.java*/
package djh.it.product.service;import djh.it.product.domain.Product;public interface ProductService {//根据id查询Product findById(Long id);//保存void save(Product product);//更新void update(Product product);//删除void delete(Long id);
}
4.6、在 生产者 子工程 product_service (子模块)中,创建 service 服务层 实现类 ProductServiceImpl.java
/*** spring_cloud_demo\product_service\src\main\java\djh\it\product\service\Impl\ProductServiceImpl.java** 2024-4-17 service 服务层 实现类*/
package djh.it.product.service.Impl;import djh.it.product.domain.Product;
import djh.it.product.dao.ProductDao;
import djh.it.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class ProductServiceImpl implements ProductService {@Autowiredprivate ProductDao productDao;@Overridepublic Product findById(Long id) {return productDao.findById(id).get();}@Overridepublic void save(Product product) {productDao.save(product);}@Overridepublic void update(Product product) {productDao.save(product);}@Overridepublic void delete(Long id) {productDao.deleteById(id);}
}
4.7 在 生产者 子工程 product_service (子模块)中,创建 商品的 controller 类 ProductController.java
/*** C:\java-test\idea2019\spring_cloud_demo\product_service\src\main\java\djh\it\product\controller\ProductController.java** 2024-4-17 商品的 controller 类 ProductController.java*/
package djh.it.product.controller;import djh.it.product.domain.Product;
import djh.it.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/product")
public class ProductController {@Autowiredprivate ProductService productService;//获取服务器端口号@Value("${server.port}")private String port;//获取服务器IP地址@Value("${spring.cloud.client.ip-address}") //springcloud 自动获取当前应用的IP地址private String ip;@RequestMapping(value = "/{id}", method = RequestMethod.GET)public Product findById(@PathVariable Long id){Product product = productService.findById(id);product.setProductName("访问的服务地址是:" + ip + " : " + port);return product;}@RequestMapping(value = "", method = RequestMethod.POST)public String save (@RequestBody Product product){productService.save(product);return "保存成功";}}
4.8 在 生产者 子工程 product_service (子模块)中,创建配置文件 application.yml
## C:\java-test\idea2019\spring_cloud_demo\product_service\src\main\resources\application.ymlserver:port: 9001 # 启动端口 命令行注入。
# port: ${port:9001} # 启动端口设置为动态传参,如果未传参数,默认端口为 9001
# servlet:
# context-path: /application1spring:application:name: service-product #spring应用名, # 注意 FeignClient 不支持名字带下划线
# main:
# allow-bean-definition-overriding: true # SpringBoot2.1 需要设定。datasource:driver-class-name: com.mysql.jdbc.Driver # mysql 驱动url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: 12311jpa:database: MySQLshow-sql: trueopen-in-view: trueeureka: # 配置 Eurekaclient:service-url:defaultZone: http://localhost:9000/eureka/
# defaultZone: http://localhost:9000/eureka/,http://localhost:8000/eureka/ # 高可用,注册多个 eurekaserver 用 , 隔开。instance:prefer-ip-address: true # 使用 ip 地址注册instance-id: ${spring.cloud.client.ip-address}:${server.port} # 向注册中心注册服务的id(IP 地址)。lease-renewal-interval-in-seconds: 10 # 设置向注册中心每10秒发送一次心跳,告诉注册中心此服务没有岩机,此参数默认是30秒。lease-expiration-duration-in-seconds: 20 # 设置每20秒如果注册中心没收到此服务的心跳,就认为此服务岩机了,此参数默认是90秒。
4.9 在 生产者 子工程 product_service (子模块)中,创建 启动类 ProductApplication.java
/*** C:\java-test\idea2019\spring_cloud_demo\product_service\src\main\java\djh\it\product\ProductApplication.java** 2024-4-17 启动类 ProductApplication.java*/
package djh.it.product;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import java.io.Serializable;@SpringBootApplication
@EntityScan("djh.it.product.domain")
@EnableEurekaClient //激活 EurekaClient, 同 @EnableDiscoveryClient 注解相同。
public class ProductApplication implements Serializable {//运行启动类,浏览器地址栏输入:localhost:9001/product/1 进行访问测试。public static void main(String[] args) {SpringApplication.run(ProductApplication.class, args);}
}
4.10 在 生产者 子工程 product_service (子模块)中,运行启动类,进行测试
浏览器地址栏输入:http://127.0.0.1:9001/product/1
输出 mysql 数据库的第一条记录:
5、SpringCloudGateWay 网关:入门案例–搭建环境–在父工程 spring_cloud_demo 下,创建子 order_service 工程(子模块)
5.1 创建 消费者 子工程 order_service(子模块)
--> 右键 spring_cloud_demo 父工程--> Modules --> Maven --> Groupld : ( djh.it )Artifactld : ( order_service )Version : 1.0-SNAPSHOT--> Next --> Module name: ( order_service )Content root : ( \spring_cloud_demo\order_service )Module file location: ( \spring_cloud_demo\order_service )--> Finish
5.2 在消费者子工程(子模块) order_service 的 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"><parent><artifactId>spring_cloud_demo</artifactId><groupId>djh.it</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>order_service</artifactId><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><!-- <version>5.1.32</version>--><version>8.0.26</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- 引入 EurekaClient 依赖坐标 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><!-- 引入 spring-retry 请求重试 依赖坐标 --><dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency></dependencies></project>
<!-- spring_cloud_demo\order_service\pom.xml -->
5.3 在消费者子工程(子模块) order_service 中,创建配置文件 application.yml
## spring_cloud_demo\order_service\src\main\resources\application.ymlserver:port: 9002 # 启动端口 命令行注入。spring:application:name: service-order #spring应用名, # 注意 FeignClient 不支持名字带下划线
# main:
# allow-bean-definition-overriding: true # SpringBoot2.1 需要设定。datasource:driver-class-name: com.mysql.jdbc.Driver # mysql 驱动url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: 12311jpa:database: MySQLshow-sql: trueopen-in-view: trueeureka: # 配置 Eurekaclient:service-url:defaultZone: http://localhost:9000/eureka/instance:prefer-ip-address: true # 使用 ip 地址注册
5.4 在消费者子工程(子模块) order_service 中,创建 商品实体类 Product.java
/*** spring_cloud_demo\order_service\src\main\java\djh\it\order\domain\Product.java** 2024-4-19 商品实体类 Product.java*/
package djh.it.order.domain;import lombok.Data;
import java.math.BigDecimal;@Data
public class Product {private Long id;private String productName;private Integer status;private BigDecimal price;private String productDesc;private String caption;private Integer inventory;
}
5.5 在消费者子工程(子模块) order_service 中,创建 controller 类 OrderController.java
/*** spring_cloud_demo\order_service\src\main\java\djh\it\order\controller\OrderController.java** 2024-4-19 订单的 controller 类 OrderController.java*/
package djh.it.order.controller;import djh.it.order.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;@RestController
@RequestMapping("/order")
public class OrderController {// 注入 restTemplate 对象@Autowiredprivate RestTemplate restTemplate;/*** 使用 基于 ribbon 的形式调用远程微服务:* 1、使用 @LoadBalanced 注解 声明 RestTemplate* 2、使用服务名称 service-product 替换 IP 地址 。** @param id* @return*/@RequestMapping(value = "/buy/{id}", method = RequestMethod.GET)public Product findById(@PathVariable Long id){Product product = null;//product = restTemplate.getForObject("http://127.0.0.1:9001/product/1", Product.class);product = restTemplate.getForObject("http://service-product/product/1", Product.class);return product;}
}
5.6 在消费者子工程(子模块) order_service 中,创建 启动类 OrderApplication.java
/*** C:\java-test\idea2019\spring_cloud_demo\order_service\src\main\java\djh\it\order\OrderApplication.java** 2024-4-19 启动类 OrderApplication.java*/
package djh.it.order;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;@SpringBootApplication
@EntityScan("djh.it.order.domain")
@EnableEurekaClient //激活 EurekaClient, 同 @EnableDiscoveryClient 注解相同。
public class OrderApplication {/*** 使用 spring 提供的 RestTemplate 发送 http 请求到商品服务。* 1)创建 RestTemplate 对象交给容器管理。* 2)在使用的时候,调用其方法完成操作(getXX, postXX)。 **/@LoadBalanced //使用 基于 ribbon 的形式调用远程微服务:使用 @LoadBalanced 注解 声明 RestTemplate@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}//运行启动类,浏览器地址栏输入:localhost:9002/order/buy/1 进行访问测试。public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}
}
5.7 运行启动类,进行测试
浏览器地址栏输入:http://127.0.0.1:9002/order/buy/1
输出 mysql 数据库的第一条记录:
6、SpringCloudGateWay 网关:入门案例–搭建环境–在父工程 spring_cloud_demo 下,创建子 api_gateway_service 工程(子模块)
6.1 创建 子工程 api_gateway_service(子模块)
--> 右键 spring_cloud_demo 父工程--> Modules --> Maven --> Groupld : ( djh.it )Artifactld : ( api_gateway_service )Version : 1.0-SNAPSHOT--> Next --> Module name: ( api_gateway_service )Content root : ( \spring_cloud_demo\api_gateway_service )Module file location: ( \spring_cloud_demo\api_gateway_service )--> Finish
6.2 在子工程(子模块) api_gateway_service 的 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"><parent><artifactId>spring_cloud_demo</artifactId><groupId>djh.it</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>api_gateway_service</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency></dependencies></project>
<!-- spring_cloud_demo\api_gateway_service\pom.xml -->
6.3 在子工程(子模块) api_gateway_service 中,创建配置文件 application.yml
## spring_cloud_demo\api_gateway_service\src\main\resources\application.ymlserver:port: 8088 # 启动端口 命令行注入。
spring:application:name: api-gateway-service #spring应用名, # 注意 FeignClient 不支持名字带下划线# 配置 SpringCloudGateway 的路由cloud:gateway:routes: # 配置路由,路由Id,路由到微服务的 uri, 断言(判断条件)- id: product-serviceuri: http://127.0.0.1:9001predicates:- Path=/product/**
6.4 在子工程(子模块) api_gateway_service 中,创建 启动类 GatewayServerApplication.java
/*** spring_cloud_demo\api_gateway_service\src\main\java\djh\it\gateway\GatewayServerApplication.java** 2024-5-7 启动类 GatewayServerApplication.java*/package djh.it.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class GatewayServerApplication {public static void main(String[] args) {SpringApplication.run(GatewayServerApplication.class, args);}
}
7、SpringCloudGateway 路由:gateway 依赖问题和内置断言条件介绍
7.1 启动 父工程 spring_cloud_demo 下 全部子项目的启动类,进行测试:
会发现在启动 子工程 api_gateway_service(子模块) 时,idea 控制台抛出异常,如下:
7.2 在各子模块( eureka_service, product_service, order_service )中的 pom.xml 文件中,导入 web 依赖坐标后,同时注销掉父工程 spring_cloud_demo 中 pom.xml 文件中的 web 依赖,重新运行各子模块启动类,进行测试。
<!-- springcloudgateway 的内部是通过 netty + webflux 实现。webflux 实现和 springmvc 存在冲突,需要注销掉父工程中的 web 依赖,在各子模块中导入 web 依赖。--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
7.3 浏览器地址栏输入:http://localhost:8088 相当于 http://127.0.0.1:9001 所以:http://localhost:8088/product/1 相当于 http://127.0.0.1:9001/product/1
7.4 SpringCloudGateway 路由:gateway 内置断言条件介绍
## spring_cloud_demo\api_gateway_service\src\main\resources\application.ymlserver:port: 8088 # 启动端口 命令行注入。
spring:application:name: api-gateway-service #spring应用名, # 注意 FeignClient 不支持名字带下划线# 配置 SpringCloudGateway 的路由cloud:gateway:routes: # 配置路由,路由Id,路由到微服务的 uri, 断言(判断条件)- id: product-service # 保持唯一uri: http://127.0.0.1:9001 # 目标微服务请求地址predicates: # 断言(判断条件)设置- Path=/product/** # 路由条件 path : 路由路径匹配条件。 # 即:http://localhost:8088/product/1 相当于 http://127.0.0.1:9001/product/1
# - After=xxxxxx # 路由断言之后匹配。
# - Before=xxxxxx # 路由断言之前匹配。
# - Between=xxxx.xxxx # 路由断言之间匹配。
# - Cookie=chocolate, ch.p # 路由断言 cookie 匹配,此 predicate 匹配给定名称(chocolate)和正则表达式(ch.p)
# - Header=X-Request-Id, /d+ # 路由断言 Header 匹配,header 名称匹配 X-Request-Id,且正则表达式匹配 /d+
# - Host=**.somehost.org, **.anotherhost.org # 路由断言 Host 匹配,匹配 host 主机列表, ** 代表可变参数。
# - Method=GET # 路由断言 Method 匹配,匹配的是请求的 HTTP 方法。
# # - Path=/foo/{segment},/bar/{segment} # 路由断言匹配,{segment} 为可变参数。
# - Query=baz # 或 Query=foo,ba. # 路由断言 Query 匹配,将请求的参数 param(bax)进行匹配,也可以进行 regexp 正则表达式匹配(参数包含 foo, 并且 foo 的值匹配 ba.)。
# - RemoteAddr=192.168.1.1/24 # 路由断言 RemoteAddr 匹配,将匹配 192.168.1.1~192.168.1.254 之间的 ip 地址,其中 24 为子网掩码位。
上一节关联链接请点击:
# 从浅入深 学习 SpringCloud 微服务架构(十)–zuul(2)