目录
1创建简单的springcloud项目
1.1先创建一个maven的空项目
这里直接下一步就好
创建好了后直接把src目录删了
然后就是写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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.1</spring-cloud.version>
<mysql.version>5.1.47</mysql.version>
<mybatis.version>2.1.1</mybatis.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- springCloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
1.2创建子模块
配置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>Springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.user</groupId>
<artifactId>user-service</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
配置创建springboot启动类
在配置文件里面写数据库的连接不然会报错
最后写一个接口测试一下是否成功,端口号是自己设置的
没问题的话这个user模块就ok了
我们在创建一个模块重复步骤我们就省略了
我们可以看到订单模块有一个用户我们让订单模块调用户模块的服务
RestTemplate这个是夸模块调用的核心
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @Classname OrderApplication
* @Description TODO
* @Date 2022/3/26 17:09
* @Created lijiafen
*/
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
import com.order.dao.OrderMapper;
import com.order.pojo.tbOrder;
import com.order.pojo.tbUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @Classname OrderController
* @Description TODO
* @Date 2022/3/26 17:14
* @Created lijiafen
*/
@RestController()
@RequestMapping("order")
public class OrderController {
@Autowired
OrderMapper mapper;
@Autowired
RestTemplate restTemplate;
@GetMapping("{id}")
public Object getOrder(@PathVariable("id") Long id) {
if (id==null){
return "未输入id";
}
tbOrder tbOrder = mapper.selectById(id);
String url = "http://localhost:8890/user/"+tbOrder.getUserId();
tbUser user = restTemplate.getForObject(url, tbUser.class);
tbOrder.setUser(user);
return tbOrder;
}
}
这里就是调用用户模块的实现
但是你怎么知道用户模块没有挂呢,下面我们引入一个springcloud的核心之一Euake
2.将两个模块加入到Eureka服务中
2.1创建Eureka模块
一样的我们先创建一个子模块这个是依赖文件
<?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>Springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Springclude-eureka</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<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-server</artifactId>
</dependency>
</dependencies>
</project>
这个是配置文件
server:
port: 5561
eureka:
instance:
hostname: localhost
client:
fetch-registry: false
register-with-eureka: false//这个是是否把自己添加到服务中
service-url:
defaultZone: http://localhost:5561/eureka/
一定要在启动类上加上这个注解
package com.fen;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @Classname Application
* @Description TODO
* @Date 2022/3/26 18:54
* @Created lijiafen
*/
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
如果进入这个页面的话就说明ok了
2.2将其他模块注册到Eureka服务中
在其他模块中引入这个依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
配置文件中加入这些配置端口你们的Eureka模块是什么端口这里就写什么
eureka:
client:
service-url:
defaultZone: http://localhost:5561/eureka/
在主类上加上这个注解@EnableEurekaClient
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @Classname OrderApplication
* @Description TODO
* @Date 2022/3/26 17:09
* @Created lijiafen
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
如果出现这个就ok了
2.3实现负载均衡
我们把实例都注册的到eureka中之后前面有订单模块调用用户模块
@LoadBalanced我们加上这个注解
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
@LoadBalanced //这个注解是开启负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
我们把user模块分出一个服务
点击复制配置修改端口
然后就变成这样的
我们全部重启一下看看Eureka里面有了两个user的服务
package com.order.controller;
import com.order.dao.OrderMapper;
import com.order.pojo.tbOrder;
import com.order.pojo.tbUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @Classname OrderController
* @Description TODO
* @Date 2022/3/26 17:14
* @Created lijiafen
*/
@RestController()
@RequestMapping("order")
public class OrderController {
@Autowired
OrderMapper mapper;
@Autowired
RestTemplate restTemplate;
@GetMapping("{id}")
public tbOrder getOrder(@PathVariable("id") Long id) {
tbOrder tbOrder = mapper.selectById(id);
String url = "http://userservice/user/"+tbOrder.getUserId();
这里就不填端口号了我们直接填userservice不指定端口
tbUser user = restTemplate.getForObject(url, tbUser.class);
tbOrder.setUser(user);
return tbOrder;
}
}
我们用postman多测试几遍这个接口
我们可以看到两个user模块都输出了日志那说明就ok了
2.4修改负载均衡策略
默认的话是轮询的策略我们这里配置一个bean修改为随机
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
@LoadBalanced //这个注解是开启负载均
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public IRule iRule(){
return new RandomRule();/*改变负载均衡的策略这个是随机默认是轮训方式*/
}
}
3.Nacos
3.1下载配置Nacos
下载好了之后解压到一个没有中文目录的文件夹里面
startup.sh -m standalone
cmd运行起来
我这里因为有浏览器缓存所以不用密码账号和密码是nacos
3.2Springcloud集成Nacos
因为我们这前用了Eureka所以我们先把Eureka的配置注释掉
在到父文件里面引入Nacos的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
在到用户和订单模块里面添加Nacos的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
在配置文件里面添加Nacos的服务地址配置
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
注意要把之前Eureka的配置全部注释掉然后运行服务要先把Nacos的服务开启
如果出现这个界面显示了三个服务就ok了
3.3Nacos的集群配置
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
cluster-name: NC #集群名称南昌
我们运行两个userService的服务
然后在修改配置文件把南昌改成别的城市
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
cluster-name: SZ #集群名称深圳
然后打开Nacos的控制台
这样的话那就是配置成功了
总结一下
我们为orderservice也配置一下集群和上面做法一样
我们让他优先访问本地集群配置yml文件就会优先访问本地集群
userservice:
ribbon:
NFLoadBalancerRuleClassNane: com.alibaba.cloud.nacos.ribbon.NacosRule
3.4修改权重
我们可以通过修改改服务的权重来控制服务器的访问量
3.5 命名空间和临时实例
新建一个命名空间
然后在配置文件里面配置
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
cluster-name: SZ #集群名称南昌
namespace: lijia520 #命名空间
ephemeral: false #是否为临时实例
然后就是临时实例的问题如果不是临时实例的话服务挂了之后Nacos是不会把该服务剔除的
如果是临时实例的话就会把该服务剔除而是会一直等待该服务恢复,除非你主动把他剔除
3.6Nacos统一管理配置
点击加号新建配置
然后就是写bootstrap文件因为这个文件会比application文件先被读取
spring:
application:
name: orderservice-dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
添加依赖,如果cloud的版本是2020.0以上的还要多加一个依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--让这个模块先加载bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
然后我们写一个接口测试一下
@Value("${pattern.dateformat}")
String name;
@GetMapping("/name")
public String name(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(name));
}
那就说明读取到了里面的配置,如果需要热加载的话在controller上面加一个注解
@RefreshScope
这个注解是配置热更新的注解
4.Feign替代RestTemplate
4.1配置Feign
导依赖开启注解支持
<!--feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在启动 类上添加这个注解开启自动装配
@EnableFeignClients //开启feign自动装配支持
然后我们创建一个包
里面创建一个接口
package com.order.clients;
import com.order.pojo.tbUser;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
tbUser GetById(@PathVariable("id") Long id);
}
这里的参数对应提供者的参数下面是调用的代码
@Autowired
UserClient userClient;
@GetMapping("{id}")
public tbOrder getOrder(@PathVariable("id") Long id) {
tbOrder tbOrder = mapper.selectById(id);
Long userId= tbOrder.getUserId();
tbUser user = userClient.GetById(userId);
tbOrder.setUser(user);
return tbOrder;
}
还要注意的就是如果使用的是springcloud是2021.0.1的话我们要改一下因为fegin自带了rebbin
我们要把nacos里面的不使用
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.6.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
这个困扰了我一下午终于解决
4.2Feign的性能优化
我们这边用阿帕奇的http连接池
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
编写配置文件
feign:
client:
config:
default: #全局日志
loggerLevel: BASIC #日志级别
httpclient:
enabled: true #开启feign对httpclient的支持
max-connections: 200 #最大连接数
max-connections-per-route: 50 #每个路径的最大连接数
4.3把feign提取出来成为一个服务
然后调用这个接口就ok了
@Autowired
UserClient userClient;
@GetMapping("{id}")
public tbOrder getOrder(@PathVariable("id") Long id) {
tbOrder tbOrder = mapper.selectById(id);
Long userId= tbOrder.getUserId();
tbUser user = userClient.GetById(userId);
tbOrder.setUser(user);
return tbOrder;
}
5.gateway网关
5.1创建一个服务
<dependency>
<!--nacos服务发现依赖-->
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<!--gateway网关依赖-->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
配置文件
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 127.0.0.1:8848 #加入到nacos里面
gateway:
routes:
- id: user-service #路由标识必须唯一
uri: lb://userservice #路由的目标地址
predicates:
- Path=/user/** #断言判断请求路径是否以/user开头如果是就符合规则这里一定要大写P
- id: order-service
uri: lb://orderservice-dev
predicates:
- Path=/order/**
server:
port: 10086
这里我们请求的是网关的端口通过网关转发到user服务里面
2.predicates Factory断言工厂
上面我们用的就是Path
这个是链接可以去看看
我们来试一个
gateway:
routes:
- id: user-service #路由标识必须唯一
uri: lb://userservice #路由的目标地址
predicates:
- Path=/user/** #断言判断请求路径是否以/user开头如果是就符合规则这里一定要大写P
- After=2032-01-20T17:42:47.789-07:00[Asia/Shanghai]
- id: order-service
uri: lb://orderservice-dev
predicates:
- Path=/order/**
- After=2032-01-20T17:42:47.789-07:00[Asia/Shanghai] #这个规则是需要在2017年之后就符合规则
我们就设置成功,还有很多断言可以去试试
3.Gatewayfiler网关过滤器
spring官网有很多的过滤器可以去看看
gateway:
routes:
- id: user-service #路由标识必须唯一
uri: lb://userservice #路由的目标地址
predicates:
- Path=/user/** #断言判断请求路径是否以/user开头如果是就符合规则这里一定要大写P
- After=2021-01-20T17:42:47.789-07:00[Asia/Shanghai]
filters:
- AddRequestHeader=Name,lijiafen #请求头添加信息
我们来试一个,我们配的是user服务我们在user服务的controller接口里面接收一下看看能不能接收的名字
@RequestMapping("{id}")
public tbUser selectUser(@PathVariable("id") int id,@RequestHeader("Name") String name) {
System.out.println("name是"+name);
return userDao.selectById(id);
}
控制台也打印了就ok了这样一个一个服务配有点麻烦我们有一种全局配置的方法
gateway:
routes:
- id: user-service #路由标识必须唯一
uri: lb://userservice #路由的目标地址
predicates:
- Path=/user/** #断言判断请求路径是否以/user开头如果是就符合规则这里一定要大写P
- After=2021-01-20T17:42:47.789-07:00[Asia/Shanghai]
filters:
- AddRequestHeader=Name,lijiafen #请求头添加信息
- id: order-service
uri: lb://orderservice-dev
predicates:
- Path=/order/**
- After=2021-01-20T17:42:47.789-07:00[Asia/Shanghai] #这个规则是需要在2017年之后就符合规则
default-filters: #全局配置过滤器
- AddRequestHeader=Name,lijiafen #请求头添加信息
我们在order服务里面也添加一下看看能不能生效
说明我们全局配置就ok了
4.GlobaFilter全局过滤器
具体代码
/**
* @Classname LandFilter
* @Description TODO
* @Date 2022/4/28 17:27
* @Created lijiafen
*/
public class LandFilter implements GatewayFilter , Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//我们获取前端传来的参数
ServerHttpRequest request = exchange.getRequest();
//获取请求的参数
MultiValueMap<String, String> queryParams = request.getQueryParams();
String name = queryParams.getFirst("Name");
if ("小p".equals(name)){
/*如果相等就放行*/
return chain.filter(exchange);
}
/*设置状态码*/
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1; /*这里是定义过滤器的执行顺序*/
}
}
5.跨域问题处理
这是一个很明显的跨域问题
配置如下
globalcors:
cors-configurations:
'[/**]':
# 允许任何域名使用
allowedOrigins: "*"
# 允许任何头
allowedHeaders: "*"
# 允许任何方法(post、get等)
allowedMethods: "*"
# sessionid 多次访问一致
allowCredentials: true
# 允许来自所有域名(allowedOrigins)的所有请求方式(allowedMethods)发出CORS请求
add-to-simple-url-handler-mapping: true # 允许来自所有域名(allowedOrigins)的所有请求方式(allowedMethods)发出CORS请求
6.Sentinel
这个是他的中文文档介绍 · alibaba/Sentinel Wiki · GitHub
我们下好了后直接java-jar运行
账号密码都是sentinel
然后我们创建一个服务把它接入nacos
dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<!--nacos服务发现依赖-->
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<!--sentinel核心依赖-->
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<!--为了以后持久化作准备-->
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
配置文件
server:
port: 1314
spring:
application:
name: sentinel
cloud:
nacos:
server-addr: 127.0.0.1:8848
sentinel:
transport:
dashboard: 127.0.0.1:8080 #这个是sentinel前台端口
port: 8719 #这个是后台端口
main:
allow-circular-references: true #如果是使用的2021.0的话加这个配置
要执行一次之后就可以显示
这个是我们开放的两个后端接口也就是一个链路下面的两个节点
6.1流控
添加流控规则
6.1.1qps表示每秒只能多少次访问
这个就是每秒超过了1次访问就报错
6.1.2线程表示每秒只能有多少个线程访问![](https://img-blog.csdnimg.cn/b0b55597d3724ed6ac60b5d438d44b01.png)
这边手速不够就不试了。。。。。
6.1.3关联流控
如果name这个接口访问数超标的话我们user就会挂掉
我们用apipost一直访问这个 接口然后我们的user就挂掉了
6.1.4Warmup
6.1.5排队等待
这个就不必多说了吧