创建一个父工程maven项目
字符编码
java编译版本
springcloud和springboot版本
- springcloud必须使用它支持的springboot版本
- springcloud版本参考:https://spring.io/projects/spring-cloud#overview
- springAlibaba版本参考:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
- 管理依赖,子工程可以继承版本号
- mvn install 跑一下,子工程就可以自己用父工程的依赖了
- 删除掉父工程的所有目录留一个pom.xml
<!-- springcloud的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springboot的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.12.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springAlibaba cloud-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
其他配置
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
- 热部署只适用于开发阶段,生产就关掉
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--打包时不排除Devtools-->
<excludeDevtools>false</excludeDevtools>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
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>org.example</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Maven</name>
<!-- FIXME change it to the project's website -->
<url>http://maven.apache.org/</url>
<inceptionYear>2001</inceptionYear>
<distributionManagement>
<site>
<id>website</id>
<url>scp://webhost.company.com/www/website</url>
</site>
</distributionManagement>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<lombok.version>1.18.20</lombok.version>
<mysql-connector-java.version>8.0.21</mysql-connector-java.version>
<druid.version>1.1.8</druid.version>
<junit.version>4.13</junit.version>
<log4j.version>1.2.17</log4j.version>
<mybatis-plus.version>3.2.0</mybatis-plus.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- springcloud的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springboot的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.12.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springAlibaba cloud-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- lombok插件:主要就是使用注解的方式代替get、set、构造方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- java连接mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<!-- 配置数据源依赖:连接数据库要用的,这里用的是druid,常见的有c3p0、dbcp等-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- junit依赖: 一个单元测试框架 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<configuration>
<locales>en,fr</locales>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
</plugin>
</plugins>
</reporting>
</project>
创建一个api-common子工程
- 存放封装的HttpResult
- 存放实体类
在父工程添加子模块
<modules>
<module>api-common</module>
</modules>
- HttpResult
package com.konglz.apicommon.httpResult;
/**
* @Description 自定义统一响应体
* @Param null
* @Return {@link null}
* @Author konglz
* @Date 2022/8/28 22:52
*/
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel
public class HttpResult<T> {
@ApiModelProperty(value = "状态码")
private Integer code;
@ApiModelProperty(value = "响应信息", notes = "来说明响应情况")
private String msg;
@ApiModelProperty(value = "响应的具体数据")
private T data;
public HttpResult(ResultEnum resultCodeEnum, T data) {
this.code = resultCodeEnum.getCode();
this.msg = resultCodeEnum.getMessage();
this.data = data;
}
public HttpResult(String msg){
this.msg = msg;
}
public HttpResult(T data) {
this.data = data;
}
public HttpResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public HttpResult(int value, String message, T data) {
this.code = value;
this.msg = message;
this.data = data;
}
}
- ResultEnum
package com.konglz.apicommon.httpResult;
/**
* @Description 响应枚举码 可以参考参考这个类:HttpStatus
* @Param null
* @Return {@link null}
* @Author konglz
* @Date 2022/8/28 22:39
*/
public enum ResultEnum {
//常规错误
OK(200, "数据响应成功"),
SERVER_ERROR(500,"服务器异常"),
NOT_FOUND(404,"未发现资源"),
BAD_REQUEST(400,"错误的请求"),
//登录模块
VERIFY_CODE_ERROR(2006,"验证码错误"),
USERNAMEORPASSWORD_ERROR(2007,"用户名或者密码错误"),
REGISTER_VERIFYCODE_SEND_ERROR(2008,"邮箱验证码发送失败"),
USERNAME_EXIST_ERROR(2009,"该用户名已经存在"),
EMAIL_EXIST_ERROR(2010,"邮箱已经存在"),
USER_REGISTER_ERROR(2011,"用户注册失败"),
REGISTER_CODE_ERROR(2012, "注册码错误"),
INSERT_MESSGE_ERROR(2013, "插入留言失败"),
AREADY_COLLECTION(2014, "已经收藏"),
//参数
FIELD_VALIDATE_FAIL(1001,"参数校验失败"),
//异常
UNKNOWN_CODE(1002,"未知异常"),
//权限
UNAUTHORIZED(401,"未授权"),
//自定义
CREATE_TOKEN_FAIL(2001,"创建token失败"),
VERIFY_TOKEN_FAIL(2002,"验证token失败"),
GET_USERNAME_FROM_TOKEN(2003,"从token获取username失败"),
GET_CURRENTTIME_FROM_TOKEN(2004,"从token获取currentTime失败"),
GET_CLAIM_FROM_TOKEN(2005,"从token获取claim失败"),
NOT_EXISTS_USER(3001,"用户不存在"),
UNKNOWN_EXCEPTION(5000,"未知异常"),
TOKEN_EXPIRED_EXCEPTION(5001,"token失效异常");
private Integer code;
private String message;
private ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return this.code;
}
public String getMessage() {
return this.message;
}
}
创建eureka-server子工程
- 依赖 spring-cloud好像没有eureka的依赖,需要自己写个版本号、父工程依赖
<parent>
<groupId>org.example</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
- 在启动类添加@EnableEurekaServer注解
- yml配置
server:
port: 10086
spring:
application:
name: eurekaserver
eureka:
client:
service-url: #eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
- 注册中心界面
创建子工程eureka-service子工程
- 依赖: 父工程、eruka-client
<parent>
<groupId>org.example</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
- yml配置,注意不是配置在eureka-service下,在具体模块下配置
spring:
application:
name: userservice
eureka:
client:
service-url: #eureka地址信息
defaultZone: http://localhost:10086/eureka
- 从上面可以看出我们其实不需要建一个eureka-service子工程,可以将依赖引入具体的服务即可,在配置yml
- eureka集群:相互注册
服务拉取
- 之前讲RestTemplate可以调用帮我们发送一个http请求,请求到别的业务的接口,并返回对应的数据对象例子:首先在启动类注册一个RestTemplate对象,然后使用 restTemplate.getObject(url, Entity.class); 缺点是这种方式是硬编码,不适用于与并发集群、profieles
- 服务拉取注册中心所有的生产者,现在我们要讲url地址替换为服务名,然后通过@LoadBalanced做负载均衡
String url = "http://userservice/user/";
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
Ribbon负载均衡
-
服务器是识别不了"http://userservice/user/"这个地址,会通过Ribbon拉取注册中心的userservice,获得真实的地址,再通过负载均衡算法选一个生产者地址区访问
-
@LoadBalanced是Ribbon默认的策略,是轮询算法实现的
-
切换负载均衡算法,配置在具体服务的启动类,这种是全局的
@Bean
public IRule randomRule(){ //这里是随机算法
reutrn new RandomRule();
}
另外一种设置方法局部的针对单一微服务
userservice:
ribbon:
NFLoadBalancerClassName: com.netflix.loadbalancer.RandomRule #复杂均衡
- Ribbon默认使用的是懒加载,第一次非常慢
- 将Ribbon设置未饿加载,写在具体服务中
ribbon
eager-load:
enable: true #开启饿加载
clients: #是一个集合多个写成下面这样
-userService
-orderService
Nacos下载
- 下载:https://github.com/alibaba/nacos
- 启动命令
- windows: startup.cmd -m standalone (单机启动)
- linux: sh startup.sh -m standalone
nacos引入
- 父工厂引入springcloud-alibaba的依赖(注意版本问题)
<!--springAlibaba cloud-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
- 具体业务引入nacos服务发现依赖(没有starter-web的话可能导致管理页面不显示服务)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 在具体业务的配置文件中配置
spring:
application:
name: userService
cloud:
nacos:
server-addr: localhost:8848
nacos服务存储模型
- 服务
- 集群: 同在一个地域的实例被称为集群,能够防止熔灾
- 实例
- 集群: 同在一个地域的实例被称为集群,能够防止熔灾
- 在服务中配置集群名称
spring:
application:
name: userService
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ
点击服务的详情可看到服务所在的集群
- 配置优先访问同集群,然后随机访问同集群下的实例
spring:
application:
name: xxxService
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ
userService:
ribbon:
NFLoadBalancerClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #复杂均衡
- 配置同集群下实例的权重,可以将权重配置为0,但是该服务将不会被使用方便用于更新版本
nacos环境隔离
- namespace:默认的命名空间是public
- group
- service/data(实例)
- group
- 修改服务的命名空间
spring:
application:
name: userService
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ
namespace: 19da4d50-37b3-4d37-ac66-35226066f37e
注册中心nacos与eureka对比
相同:
- 支持服务注册和服务拉取、会定时拉取注册中心的数据,服务消费存在一个服务缓存列表
不同:
- nacos会见服务提供者划分为:临时实例、永久实例 (默认是临时实例),临时实例用的是心跳检测、非临时实例(永久实例)会被nocas主动询问,永久实例不会被nocas剔除,会显示这个实例是否健康
- nocas会主动推送变更消息给消费者
- nocas集群默认采用AP方式、当集群中存在非临时实例会采用CP模式、eureka采用AP模式
spring:
application:
name: userService
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ
namespace: 19da4d50-37b3-4d37-ac66-35226066f37e
ephemeral: true #将实例设置为永久实例
nacos配置管理
服务器多的时候,方便管理,所以nacos配置管理
- 配置更改热更新
服务获取nocas统一配置
- 依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
- 在resource下创建boostrap.yml: 因为在application.yml前执行的bootstap.yml,服务启动前需要获取nacos配置的一些信息
spring:
application:
name: userService
profiles:
active: dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
- 测试
package com.konglz.user.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @Description
* @Author konglz
* @Data 2022/9/26 12:14
*/
@RestController
public class TestController {
@Value("${pattern.dateformat}")
private String date;
@GetMapping("/now")
public String getNow(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(date));
}
}
nacos配置热更新
- 第一种: @Value所在的类加上@RefreshScope
- 第二种: @ConfigurationProperties(prefix = “pattern”)
@Data
@ConfigurationProperties(prefix = "pattern")
@Component
public class PatternProperties {
private String date;
}
nacos多环节配置共享
- userService.yml: 配置一些多环境都需要的配置,这里面的配置一定会被加载,优先级是带环境的高于这个
- userService-dev.yml
- userService-prod.yml
- userService-test.yml
nacos集群搭建
- 将cluster.conf.example改为cluster.conf
#因为只有一台机器,这里使用不同端口号代表3台机器
127.0.0.1:8848
127.0.0.1:8849
127.0.0.1:8847
- application.properties打开关于数据源的配置
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=nacos
db.password.0=nacos
- nginx.conf配置
upstream nocas-cluster{
server 127.0.0.1:8848;
server 127.0.0.1:8848;
server 127.0.0.1:8847
}
server {
listen: 80;
server_name: localhost;
location /nacos {
proxy_pass http://nacos-cluster;
}
}
- 服务配置集群地址
#端口号与nginx的listen一直,ip地址应该可以在集群的地址中选一个
server-addr: 服务器地址:80
feign远程调用
-
之前讲的服务拉取用的是RestTemplate,它不支持集群,采用的是硬编码不好管理
-
feign是一个声明式的http客户端,feign集成了负载均衡,支持Restful(表现层状态转移)的接口,代替了RestTemplate
-
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 启动类添加开启feign的注解
@EnableFeignClients
- 编写Feign客户端:在其它服务中创建userService服务的客户端,实现userService服务的远程调用
package com.kongz.role.clients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @Description
* @Author konglz
* @Data 2022/9/26 13:42
*/
@FeignClient("userServie")
public interface UserClient {
@GetMapping("/user/id")
User findUserById(@PathVariable("id") Long id);
}
//调用
@Autowired
UserClient userClient;
userClient.方法名();
feign的自定义配置
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
- feign日志的两种配置第一种
feign:
client:
config:
userservice: # 针对某个具体微服务的配置(配置服务名即可)
loggerLevel: FULL # 日志级别
feign:
client:
config:
default: # 针对全局微服务的配置,用default即可
loggerLevel: FULL # 日志级别
- feign日志的两种配置第二种
//加在具体的Client上(具体业务)
@FeignClient(value = "userServie",configuration = DefaultFeignConfiguration.class)
//加载启动类(全局)
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
public class RoleServiceApplication {
public static void main(String[] args) {
SpringApplication.run(RoleServiceApplication.class, args);
}
}
feign的性能优化
- Feign的底层客户端实现
- URLConnection: 默认实现,不支持连接池
- Apache HttpClient: 支持连接池
- OKHttp: 支持连接池
- 优化:
- 不使用默认URLConnection,使用支持连接池的底层实现
- 日志级别使用Basic(必要的请求相应信息)或者none(无),减少性能损耗
- 依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
- 连接池配置
feign:
client:
config:
default:
loggerLevel: FULL
httpclient:
enabled: true #开启feign对httpClient的支持
max-connections: 200 #最大连接数
max-connections-per-route: 50 #每个路径最大连接数
feign的最佳实践
-
方式一:继承的思想,给消费者的FeignClient和提供者的controller定义统一的父接口作为标准,上面的feign远程调用就是这种方式实现的,这种方式耦合度太高,官方不推荐
-
方式二:抽取的思想,创建一个feign-api项目,服务用的时候直接添加feign-api依赖,但是会将一些不需要的东西引入服务
-
创建一个feign-api项目
- 创建一个clients包: 存放所有的服务调用要用的接口
- 创建一个config包: 配置自定义Feign
- 创建一个pojo包: 存放实体类
-
依赖:使用feign-api
<dependency>
<groupId>com.example</groupId>
<artifactId>feign-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
//启动类配置
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
- feignClient不在springbootApplication的扫描范围时,第一种解决方式(扫描所有的client)
@EnableFeignClients(basePackages = "com.example.feign-api",defaultConfiguration = DefaultFeignConfiguration.class)
- feignClient不在springbootApplication的扫描范围时,第二种解决方式(加载指定的client)
@EnableFeignClients(clients = {UserClient.class},defaultConfiguration = DefaultFeignConfiguration.class)
搭建网关服务
- 创建一个网关的项目
- 依赖
<!--服务注册依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 网关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
- 配置路由
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: user-service # 路由id,自定义,只要唯一即可
#uri: http://localhost:8080 #路由的目标地址 http就是固定地址
uri: lb://userService #路由的目标地址 lb就是负载均衡, 后面跟的是服务名称
predicates: #路由断言,也就是判断请求是否符合路由规则的要求
- Path=/user/** #这个是按照路径匹配,只要以/user/开头就符合要求
- id: role-service
uri: lb://orderServicce
predicates:
- Path=/order/**
- 断言工厂官网参考:https://docs.spring.io/spring-cloud-gateway/docs/3.1.4-SNAPSHOT/reference/html/#gateway-request-predicates-factories
网关过滤器
- 过滤器官网参考:https://docs.spring.io/spring-cloud-gateway/docs/3.1.4-SNAPSHOT/reference/html/#gatewayfilter-factories
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: user-service # 路由id,自定义,只要唯一即可
#uri: http://localhost:8080 #路由的目标地址 http就是固定地址
uri: lb://userService #路由的目标地址 lb就是负载均衡, 后面跟的是服务名称
predicates: #路由断言,也就是判断请求是否符合路由规则的要求
- Path=/user/** #这个是按照路径匹配,只要以/user/开头就符合要求
filters:
- AddRequestHeader=author, klz
- 测试
@GetMapping("/prop")
public PatternProperties properties(@RequestHeader(value = "author",required = false)String author){
System.out.println(author);
return patternProperties;
}
- 配置全局路由
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: user-service # 路由id,自定义,只要唯一即可
#uri: http://localhost:8080 #路由的目标地址 http就是固定地址
uri: lb://userService #路由的目标地址 lb就是负载均衡, 后面跟的是服务名称
predicates: #路由断言,也就是判断请求是否符合路由规则的要求
- Path=/user/** #这个是按照路径匹配,只要以/user/开头就符合要求
- id: role-service
uri: lb://orderServicce
predicates:
- Path=/order/**
default-filters:
- AddRequestHeader=author, klz
网关自定义全局过滤器GobalFilter
- 与default-filters一样是全局的,但是前者是约定好的,如果要采用编程式自定义需要编写的类实现GobalFilter
- 执行顺序(order一样): default_filters > filter > gobalFilter , order越小执行越早
package com.konglz.gateway;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Description
* @Author konglz
* @Data 2022/9/26 15:49
*/
@Order(-1)
@Component
public class AuthrizationFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String,String> params = request.getQueryParams();
String auth = params.getFirst("Authorization");
if("admin".equals(auth)){
return chain.filter(exchange);
}
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
网关配置跨域
- 跨域:域名不一致就是跨域,包括:端口号、二级域名、一级域名不同
- 跨域问题:浏览器禁止请求的发起者与服务端发生跨域请求ajax,请求将被浏览器拦截
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "*" # 都能访问
#- "http://localhost:8080"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期