目录
1创建简单的springcloud项目
1.1先创建一个maven的空项目
1.2创建子模块
2.将两个模块加入到Eureka服务中
2.1创建Eureka模块
2.2将其他模块注册到Eureka服务中
2.3实现负载均衡
2.4修改负载均衡策略
3.Nacos
3.1下载配置Nacos
3.2Springcloud集成Nacos
3.3Nacos的集群配置
3.4修改权重
3.5 命名空间和临时实例
3.6Nacos统一管理配置
4.Feign替代RestTemplate
4.1配置Feign
4.2Feign的性能优化
4.3把feign提取出来成为一个服务
5.gateway网关
5.1创建一个服务
2.predicates Factory断言工厂
3.Gatewayfiler网关过滤器
4.GlobaFilter全局过滤器
5.跨域问题处理
6.Sentinel
6.1流控
6.1.1qps表示每秒只能多少次访问
6.1.2线程表示每秒只能有多少个线程访问
6.1.3关联流控
6.1.4Warmup
6.1.5排队等待
这里直接下一步就好
创建好了后直接把src目录删了
然后就是写xml配置
这里看需求
4.0.0
org.example
Springcloud
1.0-SNAPSHOT
pom
org.springframework.boot
spring-boot-starter-parent
2.6.1
UTF-8
UTF-8
1.8
2021.0.1
5.1.47
2.1.1
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
mysql
mysql-connector-java
${mysql.version}
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis.version}
org.projectlombok
lombok
配置xml文件
Springcloud
org.example
1.0-SNAPSHOT
4.0.0
org.user
user-service
8
8
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
com.baomidou
mybatis-plus
com.baomidou
mybatis-plus-boot-starter
配置创建springboot启动类
在配置文件里面写数据库的连接不然会报错
最后写一个接口测试一下是否成功,端口号是自己设置的
我们在创建一个模块重复步骤我们就省略了
我们可以看到订单模块有一个用户我们让订单模块调用户模块的服务
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
一样的我们先创建一个子模块这个是依赖文件
Springcloud
org.example
1.0-SNAPSHOT
4.0.0
Springclude-eureka
8
8
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
这个是配置文件
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了
在其他模块中引入这个依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
配置文件中加入这些配置端口你们的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();
}
}
我们把实例都注册的到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了
默认的话是轮询的策略我们这里配置一个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();/*改变负载均衡的策略这个是随机默认是轮训方式*/
}
}
https://nacos.io/zh-cn/
下载好了之后解压到一个没有中文目录的文件夹里面
startup.sh -m standalone
cmd运行起来
我这里因为有浏览器缓存所以不用密码账号和密码是nacos
因为我们这前用了Eureka所以我们先把Eureka的配置注释掉
在到父文件里面引入Nacos的依赖
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.2.6.RELEASE
pom
import
在到用户和订单模块里面添加Nacos的依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
在配置文件里面添加Nacos的服务地址配置
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
注意要把之前Eureka的配置全部注释掉然后运行服务要先把Nacos的服务开启
如果出现这个界面显示了三个服务就ok了
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
我们可以通过修改改服务的权重来控制服务器的访问量
新建一个命名空间
然后在配置文件里面配置
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
cluster-name: SZ #集群名称南昌
namespace: lijia520 #命名空间
ephemeral: false #是否为临时实例
然后就是临时实例的问题如果不是临时实例的话服务挂了之后Nacos是不会把该服务剔除的
如果是临时实例的话就会把该服务剔除而是会一直等待该服务恢复,除非你主动把他剔除
点击加号新建配置
然后就是写bootstrap文件因为这个文件会比application文件先被读取
spring:
application:
name: orderservice-dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
添加依赖,如果cloud的版本是2020.0以上的还要多加一个依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
org.springframework.cloud
spring-cloud-starter-bootstrap
然后我们写一个接口测试一下
@Value("${pattern.dateformat}")
String name;
@GetMapping("/name")
public String name(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(name));
}
那就说明读取到了里面的配置,如果需要热加载的话在controller上面加一个注解
@RefreshScope
这个注解是配置热更新的注解
导依赖开启注解支持
org.springframework.cloud
spring-cloud-starter-openfeign
在启动 类上添加这个注解开启自动装配
@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里面的不使用
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
2.2.6.RELEASE
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
这个困扰了我一下午终于解决
我们这边用阿帕奇的http连接池
io.github.openfeign
feign-httpclient
编写配置文件
feign:
client:
config:
default: #全局日志
loggerLevel: BASIC #日志级别
httpclient:
enabled: true #开启feign对httpclient的支持
max-connections: 200 #最大连接数
max-connections-per-route: 50 #每个路径的最大连接数
@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;
}
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-loadbalancer
配置文件
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服务里面
上面我们用的就是Path
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factoriesz
这个是链接可以去看看
我们来试一个
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年之后就符合规则
我们就设置成功,还有很多断言可以去试试
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了
具体代码
/**
* @Classname LandFilter
* @Description TODO
* @Date 2022/4/28 17:27
* @Created lijiafen
*/
public class LandFilter implements GatewayFilter , Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//我们获取前端传来的参数
ServerHttpRequest request = exchange.getRequest();
//获取请求的参数
MultiValueMap 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; /*这里是定义过滤器的执行顺序*/
}
}
这是一个很明显的跨域问题
配置如下
globalcors:
cors-configurations:
'[/**]':
# 允许任何域名使用
allowedOrigins: "*"
# 允许任何头
allowedHeaders: "*"
# 允许任何方法(post、get等)
allowedMethods: "*"
# sessionid 多次访问一致
allowCredentials: true
# 允许来自所有域名(allowedOrigins)的所有请求方式(allowedMethods)发出CORS请求
add-to-simple-url-handler-mapping: true # 允许来自所有域名(allowedOrigins)的所有请求方式(allowedMethods)发出CORS请求
这个是他的中文文档介绍 · alibaba/Sentinel Wiki · GitHub
我们下好了后直接java-jar运行
账号密码都是sentinel
然后我们创建一个服务把它接入nacos
dependency>
org.springframework.boot
spring-boot-starter-web
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
com.alibaba.csp
sentinel-datasource-nacos
org.springframework.cloud
spring-cloud-starter-openfeign
配置文件
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的话加这个配置
要执行一次之后就可以显示
这个是我们开放的两个后端接口也就是一个链路下面的两个节点
添加流控规则
这个就是每秒超过了1次访问就报错
这边手速不够就不试了。。。。。
如果name这个接口访问数超标的话我们user就会挂掉
我们用apipost一直访问这个 接口然后我们的user就挂掉了
这个就不必多说了吧