一、Spring Cloud Alibaba一站式解决方案
- 在springCloud的基础上延伸出来的微服务技术栈
为什么学习spring cloud alibaba?
spring cloud多项组件宣布闭源或者停止维护了!!!
比如spring cloud2.x 注册中心Eureka 2.0停止维护了
spring cloud已近不再完整了
Spring Cloud Alibaba优于spring cloud
1.1微服务下的常见问题?
- 如何管理微服务?(注册中心)
- 微服务如何进行调用?(restful RPC)
- 客户端如何访问微服务?(网关)
- 微服务出问题,如何自处理?(容错)
- 微服务出问题,程序员如何排查?(链路追踪)
1.2微服务架构的常见概念
1.2.1.微服务治理
微服务治理:进行服务自动化管理,其核心是服务的注册与发现。
服务注册:服务实例将自身服务信息注册到注册中心。
服务发现:服务实例通过注册中心,将获取注册到启动的服务实例的信息,通过这些信息去请求他们提供服务。
服务剔除:服务注册中心将出问题的服务自动剔除到可用列表外,使其不能被调用到。
1.2.2.服务调用
- 基于HTTP—>restful接口
- 基于TCP—>RPC协议
服务雪崩及常见解决方案
spring cloud alibaba的功能和组件介绍
spring cloud alibaba:致力于提供微服务开发的一站式解决方案
二、Springcloud-Alibaba商品案例
1、父工程搭建(springcloud-alibaba)
- 创建一个Maven工程(父工程)
- 删除src【不需要写代码】
- pom.xml进行版本依赖和锁定
<!--父工程版本-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<!--依赖版本锁定-->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
注意版本对应问题 2.1.0—Greenwich.RELEASE
2、创建公共模块(shop-common)
- 创建一个Maven模块Moudel【用来装实体类】
- 导入依赖
<dependencies>
<!--springboot的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--lombok印度人开发-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
</dependencies>
-
实体类创建
User
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
@Entity(name = "user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //数据库自增主键
private Integer id; //编号
private String username; //账号
private String password; //密码
private String phone; //手机号码
private String status; //账号状态
}
Role
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;
@Entity(name = "role")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键自增
private Integer id; //编号
private String roleName; //编号名称
private String roleDescribe; //编号描述
}
3、创建用户的微服务(shop-user)
- 创建模块 导入依赖
<dependencies>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shop-common公共模块-->
<dependency>
<groupId>org.carter</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 启动类+配置application.yaml+dao层配置
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);//启动配置
}
}
server:
port: 8071
spring:
application:
name: service-user
datasource:
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&userSSL=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
# 自动创建数据库
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
4、创建产品微服务(shop-product)
- 创建模块 导入依赖
<dependencies>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shop-common公共模块-->
<dependency>
<groupId>org.carter</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 启动类+配置application.yaml+dao层配置
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class,args);
}
}
server:
port: 8081
spring:
application:
name: service-product
datasource:
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&userSSL=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
# 自动创建数据库
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
5、进行商品信息查询
Controller层
@RestController
@Slf4j
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/product/{pid}") //商品信息查询
public Product getOneProduct(@PathVariable("pid") Integer id){
log.info("接下来进行{}号商品信息的查询",id);
Product product = productService.findById(id);
log.info("商品信息查询成功,内容为{}", JSONObject.toJSONString(product));
return product;
}
Service层
public interface ProductService {
Product findById(Integer id);
}
ServiceImpl接口实现层
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public Product findById(Integer id) {
return productDao.findById(id).get();//jpa提供的方法
}
}
6、进行远程调用
- OrderApplication中设置RestTemplate
@Bean
public RestTemplate restTemplate(){
return new RestTemplate(); //远程调用
}
- Controller层
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
@RequestMapping("/order/product/{pid}") //下单
public Order order(@PathVariable("pid") Integer pid){
log.info("收到{}号商品下单请求,接下来调用微服务查询此商品信息",pid);
Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
log.info("查询到{}号商品的信息,内容是{}",pid, JSONObject.toJSONString(product));
//创建订单
Order order = new Order();
order.setUid(1); //订单编号
order.setUsername("测试用户"); //下订单的用户名称
order.setPid(pid); //商品的编号
order.setPname(product.getName()); //商品的名称
order.setPprice(product.getPrice()); //商品的单价
order.setNumber(1); //每次一个
orderService.createOrder(order);
log.info("创建订单成功,订单信息是{}",JSONObject.toJSONString(order));
return order;
}
}
JPA 工具包不能创建数据表名称是order的直接报错
order是mysql关键字,不能作为字段名/表名。
- Pojo层
@Entity(name = "shop_order")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //订单编号
private Integer uid; //客户编号
private String uname; //客户姓名
private Integer pid; //产品编号
private String pname; //产品名称
private Double pprice; //产品价格
}
- Service层
public interface OrderService {
void createOrder(Order order); //创建订单
}
- ServiceImpl接口实现
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Override
public void createOrder(Order order) {
orderDao.save(order); //JPA中的存储方法
}
}
- Dao层
@Repository
public interface OrderDao extends JpaRepository<Order,Long> {
}
7、存在的问题
- Product product = restTemplate.getForObject(“http://localhost:8081/product/”+pid,Product.class);
服务地址写的太死了,一旦发生改变怎么处理?
- 解决方案nacos
- 服务治理
这时候我们就需要Nacos组件
三、Nacos组件的介绍与使用
- 下载安装nacos-server端【软件】
- 项目中nacos-client的使用【jar包】
- 进行注册与调用
1、nacos-server启动
Nacos官网下载:https://github.com/alibaba/nacos/releases
账户/密码:nacos
下载 nacos-server-1.2.1.zip版本,最新版本的有配置问题
访问:http://192.168.43.170:8848/nacos/index.html
2、nacos-client配置
- 导包
在商品shop-product/shop-order项目的pom.xml导入
<dependencies>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shop-common公共模块-->
<dependency>
<groupId>org.carter</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
- 在主类上添加注解@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient //开启nacos
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class,args);
}
}
- 在application.yaml中添加nacos服务地址
#服务端口
server:
port: 8081
#spring的配置
spring:
application:
name: service-product
#数据库配置
datasource:
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&userSSL=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
# 自动创建数据库
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
#nacos服务地址配置
cloud:
nacos:
discovery:
server-addr: localhost:8848
其他服务同上
- 启动服务,观察控制nacos面板
3、获取nacos上的服务
- OrderController编辑
import com.alibaba.fastjson.JSONObject;
import com.carter.pojo.Order;
import com.carter.pojo.Product;
import com.carter.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
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;
import java.util.List;
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;//远程调用
@Autowired
private OrderService orderService;//业务层
@Autowired
private DiscoveryClient discoveryClient;//nacos的服务发现
@GetMapping("/order/product/{pid}") //下单
public Order order(@PathVariable("pid") Integer pid){
log.info("收到{}号商品下单请求,接下来调用微服务查询此商品信息",pid); //打印pid信息
List<ServiceInstance> instanceList = discoveryClient.getInstances("service-product"); //得到service-product服务集群
ServiceInstance instance = instanceList.get(0); //取到第一个实例
String host = instance.getHost(); //取地址
int port = instance.getPort(); //取到端口
Product product = restTemplate.getForObject("http://"+host+":"+port+"/product/"+pid,Product.class); //进行远程调用
log.info("查询到{}号商品的信息,内容是{}",pid, JSONObject.toJSONString(product)); //打印查询到的商品信息
Order order = new Order();//创建订单对象
order.setUid(2); //下单客户编号
order.setUname("测试用户"); //下单用户名称
order.setPid(pid);//商品编号
order.setPname(product.getName());//商品名称
order.setPprice(product.getPrice());//商品单价
orderService.createOrder(order);//创建订单
log.info("创建订单成功,订单信息是{}",JSONObject.toJSONString(order));//打印输出订单信息
return order;//返回到前端
}
}
- 运行即可
4、存在的问题
问题:如果做了集群ServiceInstance instance = instanceList.get(0); //只能取到第一个?
解决方案:负载均衡
四、Ribbon负载均衡组件介绍与使用
多次请求----第一次请求8080端口、第二次8081端口…
服务端的负载均衡:shop-product提供端,访问方法时进行分配 nginx
客户端的负载均衡:shop-order消费端,请求发送之前就定义好【一般使用】
1、增加shop-product提供者
增加好提供者后
2、设置shop-order消费者
2.1、Controller层—自定义随机负载均衡
import com.alibaba.fastjson.JSONObject;
import com.carter.pojo.Order;
import com.carter.pojo.Product;
import com.carter.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Random;
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/order/product/{pid}") //下单---自定义负载均衡
public Order order(@PathVariable("pid") Integer pid){
log.info("收到{}号商品下单请求,接下来调用微服务查询此商品信息",pid); //打印pid信息
List<ServiceInstance> instanceList = discoveryClient.getInstances("service-product"); //得到service-product服务集群
//随机负载均衡实现
int index = new Random().nextInt(instanceList.size());//nacos中service-product的数量【随机数0-size】
ServiceInstance instance = instanceList.get(index); //取到一个随机的服务
String host = instance.getHost(); //取地址
int port = instance.getPort(); //取到端口
Product product = restTemplate.getForObject("http://"+host+":"+port+"/product/"+pid,Product.class); //进行远程调用
log.info("查询到{}号商品的信息,内容是{}",pid, JSONObject.toJSONString(product)); //打印查询到的商品信息
Order order = new Order();//创建订单对象
order.setUid(2); //下单客户编号
order.setUname("测试用户"); //下单用户名称
order.setPid(pid);//商品编号
order.setPname(product.getName());//商品名称
order.setPprice(product.getPrice());//商品单价
orderService.createOrder(order);//创建订单
log.info("创建订单成功,订单信息是{}",JSONObject.toJSONString(order));//打印输出订单信息
return order;//返回到前端
}
}
2.2、Controller层—Ribbon组件的负载均衡
- 在RestTemplate的生成方法上添加**@LoadBalanced**注解
@SpringBootApplication
@EnableDiscoveryClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
@LoadBalanced //Ribbon负载均衡实现的注解
public RestTemplate restTemplate(){ //远程连接
return new RestTemplate();
}
}
- 修改服务调用方法Controller层
import com.alibaba.fastjson.JSONObject;
import com.carter.pojo.Order;
import com.carter.pojo.Product;
import com.carter.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/order/product/{pid}") //下单---Ribbon负载均衡
public Order order(@PathVariable("pid") Integer pid){
log.info("收到{}号商品下单请求,接下来调用微服务查询此商品信息",pid); //打印pid信息
//Ribbon负载均衡实现
Product product = restTemplate.getForObject("http://service-product/product/"+pid,Product.class); //进行远程调用
log.info("查询到{}号商品的信息,内容是{}",pid, JSONObject.toJSONString(product)); //打印查询到的商品信息
Order order = new Order();//创建订单对象
order.setUid(2); //下单客户编号
order.setUname("测试用户"); //下单用户名称
order.setPid(pid);//商品编号
order.setPname(product.getName());//商品名称
order.setPprice(product.getPrice());//商品单价
orderService.createOrder(order);//创建订单
log.info("创建订单成功,订单信息是{}",JSONObject.toJSONString(order));//打印输出订单信息
return order;//返回到前端
}
}
Ribbon负载均衡—>默认是循环调用
策略类 | 命名 | 说明 |
---|---|---|
RandomRule | 随机策略 | 随机选择 Server |
RoundRobinRule | 轮训策略 | 按顺序循环选择 Server(默认) |
RetryRule | 重试策略 | 在一个配置时问段内当选择 Server 不成功,则一直尝试选择一个可用的 Server |
BestAvailableRule | 最低并发策略 | 逐个考察 Server,如果 Server 断路器打开,则忽略,再选择其中并发连接最低的 Server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直连接失败并被标记为 circuit tripped 的 Server,过滤掉那些高并发连接的 Server(active connections 超过配置的网值) |
ResponseTimeWeightedRule | 响应时间加权策略 | 根据 Server 的响应时间分配权重。响应时间越长,权重越低,被选择到的概率就越低;响应时间越短,权重越高,被选择到的概率就越高。这个策略很贴切,综合了各种因素,如:网络、磁盘、IO等,这些因素直接影响着响应时间 |
ZoneAvoidanceRule | 区域权衡策略 | 综合判断 Server 所在区域的性能和 Server 的可用性轮询选择 Server,并且判定一个 AWS Zone 的运行性能是否可用,剔除不可用的 Zone 中的所有 Server |
Ribbon负载均衡调用方式配置
server:
port: 8091
spring:
application:
name: service-order
datasource:
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&userSSL=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
# 自动创建数据库
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
cloud:
nacos:
discovery:
server-addr: localhost:8848
### 针对单个服务的 Ribbon 配置
service-product:
ribbon:
# 基于配置文件形式的 针对单个服务的 Ribbon 负载均衡策略
# RoundRobinRule 轮训策略 RandomRule 随机策略
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule //RetryRule
3、存在的问题
- restTemplate调用url的方式可读性不好
- 编程风格不统一
Product product = restTemplate.getForObject("http://service-product/product/"+pid,Product.class);
解决方案:Fegin组件
五、Feign组件介绍及使用
- Fegin是springcloud提供的一个声明式的伪HTTP客户端,使得调用远程如同调用本地一样简单
- Nacos很好的兼容Fegin,Fegin内部集成了Ribbon
1、添加Feign的依赖
shop-order添加Fegin依赖
<dependencies>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shop-common公共模块-->
<dependency>
<groupId>org.carter</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Fegin依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
2、在主类上添加Fegin注解
@SpringBootApplication //开启springboot微服务功能
@EnableDiscoveryClient //开启nacos服务注册功能
@EnableFeignClients //开启Fegin服务调用功能
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
3、创建一个本地的Service接口,并使用Fegin实现微服务调用
创建一个ProductService借口
import com.carter.pojo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "service-product")//指定调用nacos下的维服务
public interface ProductService {
@GetMapping("/product/{pid}")//指定请求的url部分
Product findById(@PathVariable("pid") Integer id);
@PostMapping("/product/addProduct")//传对象+@RequestBody+@PostMapping
Boolean addProduct(@RequestBody final Product product, @RequestParam("roleId") Integer roleId);
}
远程Server
@PostMapping("/product/addProduct")//对象+@RequestBody
public Boolean addProduct(@RequestBody Product product, Integer roleId){
return productService.addProduct(product,roleId);
}
4、修改Controller层代码,并启动验证
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;//远程调用
@Autowired
private OrderService orderService;//订单生成业务
@Autowired
private DiscoveryClient discoveryClient;//nacos中的服务调用
@Autowired
private ProductService productService;//Fegin使用的本地Service接口
@GetMapping("/order/product/{pid}") //下单---Ribbon负载均衡
public Order order(@PathVariable("pid") Integer pid){
log.info("收到{}号商品下单请求,接下来调用微服务查询此商品信息",pid); //打印pid信息
Product product = productService.findById(pid);
log.info("查询到{}号商品的信息,内容是{}",pid, JSONObject.toJSONString(product)); //打印查询到的商品信息
Order order = new Order();//创建订单对象
order.setUid(2); //下单客户编号
order.setUname("测试用户"); //下单用户名称
order.setPid(pid);//商品编号
order.setPname(product.getName());//商品名称
order.setPprice(product.getPrice());//商品单价
orderService.createOrder(order);//创建订单
log.info("创建订单成功,订单信息是{}",JSONObject.toJSONString(order));//打印输出订单信息
return order;//返回到前端
}
}
5、超时yaml配置
#第一次调用超时处理
feign:
client:
config:
default:
connectTimeout: 10000 #毫秒
readTimeout: 10000 #毫秒【只需要这一个】
6、存在的问题
- 高并发环境下—>单个服务出现问题—>调用服务就会出现延迟—>大量请求堆积—>导致服务瘫痪
解决方案:服务容错Sentinel
六、Sentinel–服务容错介绍与使用
整个微服务瘫痪
1、高并发模拟+服务雪崩效应
由于order囤积了大量的请求,这导致其它访问很慢—>服务雪崩问题雏形
服务出现问题—>请求阻塞—>服务崩溃
2、常见容错方案
常见的容错思路:
- 隔离:线程池隔离+信号量隔离
- 超时:到达最大响应时间—>自动废弃本次请求
- 限流:限制流量的输入和输出(1s只处理3个请求,其他的请排队)
- 熔断:请求—>过一段时间—>继续请求
- 降级:A服务—>B服务不行—>A服务调用自己准备的类B服务
3、Sentinel【哨兵】
Sentinel是阿里巴巴一款开源的断路器实现,非常稳定!!!
4、导入Sentinel依赖
上游微服务容错order中导入依赖
<dependencies>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shop-common公共模块-->
<dependency>
<groupId>org.carter</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--nacos服务注册客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Fegin服务调用优化依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--Sentinel服务容错依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
OrderController测试类
@RestController
@Slf4j
public class OrderController1 {
@GetMapping("/order/message1")
public String message1(){
return "message1";
}
@GetMapping("/order/message2")
public String message2(){
return "message2";
}
}
5、控制台搭建【Dashboard】
Sentinel控制台下载地址
https://github.com/alibaba/Sentinel/releases
控制台启动命令
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
登录 http://localhost:8080/ 密码:sentinel
6、Sentinel在项目yaml中的配置
server:
port: 8091
spring:
application:
name: service-order
datasource:
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&userSSL=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
# 自动创建数据库
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
cloud:
nacos:
discovery:
server-addr: localhost:8848
#sentinel服务容错配置
sentinel:
transport:
port: 8719 #随便一个端口
dashboard: localhost:8080 #指定控制台服务地址
### 针对单个服务的 Ribbon 配置
service-product:
ribbon:
# 基于配置文件形式的 针对单个服务的 Ribbon 负载均衡策略
# RoundRobinRule 轮训策略 RandomRule 随机策略
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
7、控制台查看观察
- 懒加载—>必须访问才有
8、Sentinel功能介绍
- 流量控制
请求不规则问题【第1s:1个,第2s:10个,…】
漏斗:每次最多只能3个
9、Sentinel规则介绍
- 流控模式:链路
·需要升级springcloud Alibaba的版本 2.1.1.RELEASE
filter.enable=false
注意:Sentinel 1.6.3版本以后,Sentinel Web过滤器默认收敛所有URL的入口上下文,因此互连限流不生效。1.7.0和1.7.1版本可以使用配置类的方式关闭URL PATH聚合,示例如下:
- 流控效果
- 快速失败 超出的请求—>直接失败
- warm up 预热
- 排队等待 超出的请求不直接失败—>等待时间
- 降级处理
-
热点限流 —根据参数限流
-
授权规则 A访问都放行 B访问都不放行
授权规则的配置(测试:所有带有serviceName的才能进行访问)
- 系统规则(不被外界环境影响)
10、Sentinel异常页面自定义
11、Feign整合Sentinel
- 引入Sentinel的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 在配置文件中开启Feign对Sentinel的支持
feign:
sentinel:
enabled: true
sentinel:
transport:
port: 9999
dashboard: localhost:8080 #Sentinel控制台启动的端口
- 创建一个与远程一样的ServiceImpl实现类
- 修改远程调用@FeignClient
问题:不知道发生了什么异常 fallback可以将异常信息也告诉我吗?
- 写fallbackFactory类的出现—实现获取远程调用时候发生的异常
使用即可
12、存在的问题:
1:客户端需要维护服务端的各个地址 代码困难
2:认证 鉴权复杂
3:跨域问题
解决方案:网关
七、服务网关—gateway
1、网关介绍
API网关:系统的统一入口,为客户端提供统一服务:认证、鉴权、监控、路由转发等等.
从nacos中发现,网关也是一个微服务
2、业界流行的网关名称
3、spring cloud Gateway的介绍
springboot必须2.0以上
4、spring cloud gateway快速入门
第1步:创建一个api-gateway的模块,导入相关依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
第2步:创建主类
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class,args);
}
}
第3步:添加配置文件
aplication.yaml
#设置网关
server:
port: 7000
#服务网关的名称
spring:
application:
name: api-gateway
#配置路由数组
cloud:
gateway:
routes: #路由数组 【满足条件--转发到微服务】
- id: user_route #当前路由的标识,要求唯一,默认是UUID产生的
uri: http://localhost:8078/ #请求最终要被转发的地址,uri加/表示8078端口有其它方法
order: 1 #路由的优先级 数字小,优先级高
predicates: #断言(条件判断,返回的是boolean 转发请求要满足的条件)
- Path=/user-serv/** #当请求路径满足Path指定的规则时,此路由信息才会正常转发
filters: #过滤器请求地址(在请求传递过程中,对请求做一些手脚)
- StripPrefix=1 #在请求转发之前,去掉一层路径
第4步:启动项目
问题:uri: http://localhost:8078 【不能写死】
5、存在的问题
1:uri写死
2:负载均衡未设置
6、将gateway注册到nacos中
第1步:网关中加入nacos依赖
<dependencies>
<!--gateway依赖 注意 此模块不可进入 starter-web-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
第2步:网关中加入注解
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class,args);
}
}
第3步:修改配置文件
#设置网关
server:
port: 7000
#服务网关的名称
spring:
application:
name: api-gateway
cloud:
#将gateway注册到nacos中
nacos:
discovery:
server-addr: localhost:8848
#配置路由数组
gateway:
discovery:
locator:
enabled: true #让gateway在nacos中获取服务中心 不然404错误
routes: #路由数组 【满足条件--转发到微服务】
- id: user_route #当前路由的标识,要求唯一,默认是UUID产生的
uri: lb://user-service/ #lb:负载均衡,后面跟的是具体微服务的标识,加/表示后面还有方法
order: 1 #路由的优先级 数字小,优先级高
predicates: #断言(条件判断,返回的是boolean 转发请求要满足的条件)
- Path=/user-serv/**,/static/** #当请求路径满足Path指定的规则时,此路由信息才会正常转发+放行静态资源
filters: #过滤器请求地址(在请求传递过程中,对请求做一些手脚)
- StripPrefix=1 #在请求转发之前,去掉一层路径
uri后面必须加上/,表示该负载均衡的下的服务还有许多接口方法
注意:uri后面最好加 “/ 斜杠”
第4步:测试
问题:点击另外的连接需要重新输入网址
7、gateway的全局过滤器
- GlobalFilter
统一鉴权案例
package com.carter.filters;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
//自定义的全局过滤器(作用是:统一鉴权)
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
//过滤器逻辑
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//统一鉴权逻辑编写 网址参数必须有token=admin 否则401没有权限
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (!StringUtils.equals("admin",token)){
//认证失败
log.info("认证失败了...");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
//标识当前过滤器的优先级
@Override
public int getOrder() {
return 0;
}
}
8、Gateway网关解决跨域问题
java代码1
package com.carter.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
//跨域问题
@Configuration
public class GatewayConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
//1、配置跨域
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
1、RedirectTo GatewayFilter Factory
该过滤器有一个 status 和一个 url参数。status是300类重定向HTTP代码,如301。该URL应为有效的URL,这将是 Location header的值。
2、RewritePath GatewayFilter Factory
包含一个 regexp
正则表达式参数和一个 replacement
参数. 通过使用Java正则表达式灵活地重写请求路径。
9、网关重定向问题
#设置网关
server:
port: 7000
#服务网关的名称
spring:
application:
name: api-gateway
cloud:
#将gateway注册到nacos中
nacos:
discovery:
server-addr: localhost:8848
#配置路由数组
gateway:
discovery:
locator:
enabled: true #让gateway在nacos中获取服务中心
routes: #路由数组 【满足条件--转发到微服务】
- id: starter_route #当前路由的标识,要求唯一,默认是UUID产生的
order: 1 #路由的优先级 数字小,优先级高
uri: lb://starter-service/ #后加个/表示负载均衡的starter-service后面还有其他的方法
filters:
- PreserveHostHeader #发送网关原始主机头---解决重定向问题
predicates: #断言(条件判断,返回的是boolean 转发请求要满足的条件)
- Path=/** #放行所有路径
#开启日志打印
logging:
pattern:
#控制台输出格式
console: "%d 【%p】 %msg%n"
#日志文件输出格式
file: "%d 【%p】 %msg%n"
#日志输出的路径
path: C:\Users\海角天涯S\Desktop\springcloud-shop
#日志的名字
file: C:\Users\海角天涯S\Desktop\springcloud-shop\gateway.log
【全局过滤器配置】
#设置网关
server:
port: 7000
#服务网关的名称
spring:
application:
name: api-gateway
cloud:
#将gateway注册到nacos中
nacos:
discovery:
server-addr: localhost:8848
#配置路由数组
gateway:
default-filters:
- PreserveHostHeader #发送网关原始主机头---解决重定向问题 【全局配置】
discovery:
locator:
enabled: true #让gateway在nacos中获取服务中心
routes: #路由数组 【满足条件--转发到微服务】
- id: starter_route #当前路由的标识,要求唯一,默认是UUID产生的
order: 1 #路由的优先级 数字小,优先级高
uri: lb://starter-service/ #后加个/表示负载均衡的starter-service后面还有其他的方法
predicates: #断言(条件判断,返回的是boolean 转发请求要满足的条件)
- Path=/** #放行所有路径
#开启日志打印
logging:
pattern:
#控制台输出格式
console: "%d 【%p】 %msg%n"
#日志文件输出格式
file: "%d 【%p】 %msg%n"
#日志输出的路径
path: C:\Users\海角天涯S\Desktop\springcloud-shop
#日志的名字
file: C:\Users\海角天涯S\Desktop\springcloud-shop\gateway.log
10、微服务架构问题?
1:配置文件相对分散
11、网关自定义过滤器
作用:用于进行统一的鉴权
八、服务配置中心 Nacos-config
1、服务配置中心介绍
-
配置文件相对分散
-
配置文件无法区分环境
-
配置文件无法实时更新
2、Nacos Config入门
1:搭建nacos环境
启动nacos-starter.cmd,打开网页版—研究配置管理中的功能
2:在微服务中引用nacos配置中心的依赖
<!--nacos配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
3:在配置中心设置和微服务bootstrap.yaml的使用
spring:
application:
name: user-service
cloud:
nacos:
config:
file-extension: yaml
server-addr: localhost:8848
profiles:
active: dev
注意:不能如果用mysql,please 屏蔽mybatis-spring-boot-starter的pom依赖,,否则会报错url
3、nacos持久化配置
将nacos放置到数据库中
修改nacos中的application.properties
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=root
九、分布式事务 Seata
1、解决思想
1.1 全局事务
全局事务基于DTP模型实现 的。DTP是由X/Open组织提出的
一种分布式事务模型X/Open Distributed Transaction Processing Reference Model。他规定实现分布式事务,需要三种角色:
- AP:Aplication 应用系统(微服务)
- TM:Transaction Mannager事务管理器(全局事务管理器)
- RM:Resource Mannager 资源管理器 (数据库)
第一步:预提交–都OK
第二步:执行 回滚
降低分布式事务出现的概率
优点
- 提高数据的一致性的概率,实现成本较低
缺点
- 单点问题:事务协调者宕机
- 同步阻塞:延迟了提交时间,加长了资源阻塞时间
- 数据不一致:提交第二阶段,依然存在commit结果未知的情况,有可能导致数据不一致
1.2 可靠消息服务事务
使用 消息中间件【MQ】保证上、下游数据的一致性
- 超时询问机制:A正常、失败、处理中
- 投递到下游系统:有投递限制
1.3 最大努力提交
- 业界不使用(开发成本高)
1.4 TCC事务
try confirm Canncel—>后者认为前者一定成功,失败人工解决
2、Seata事务应用
2019年1月阿里巴巴的开源项目Fescar---->seata简单+高效
Seata主要由3个重要组件组成:
- TC:Transaction Coordination 事务协调器,管理全局的分支事务状态,用于全局事务的提交和回滚
- TM:Transaction Mannager 事物管理器,用于开启、提交回滚全局事务。
- RM:Rsource Mannager 资源管理器 用于分支事务上的资源管理,向TC注册分支事务,上报分支事务的状况,接收TC的命令来来提交或回滚事务。
Seata实现2PC与传统的2PC的差别:
- 架构层次方面,传统的2PC方案的RM实际上是在数据层,RM本质上是数据库本身,通过XA协议实现,而Seata 的RM是以jar包的形式作为中间件层部署在应用程序这一侧的
- 两阶段提交方面 传统的2PC无论第二阶段的决议是commit还是rollback,事务资源锁都是保持到Phase2完全释放。而Seata的做法是将Phase1就将本地事务提交,这样可以省去Phase2持锁的时间,整体提高效率
安装Seata
下载地址 :https://github.com/seata/seata/releases
修改配置
seata.config
启动
seata-server.bat -p 9000 -m file
添加表undo_log
微服务
- 添加依赖
<dependence>
<groupId>com.alibaba.cloud<groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependence>
-
代理数据源DataSourceProxyConfig
-
添加一系列配置
十、链路追踪GPS技术
1. 技术介绍
如何定位耗时久的微服务
- 如何快速发现问题
- 如何判断故障影响范围
- 如何梳理服务依赖以及依赖的合理性?
- 如何分析链路性能问题以及实时容量规划?
分布式链路追踪技术(Distributed Tracing),就是将一个分布式请求还原成调用链路,进行日志记录,性能监控
并将一次分布式请求的调用情况集中展示,比如各个服务节点上的耗时 具体请求到哪 每个服务节点请求的状态等等。
链路追踪技术
- cat
- zipkin
- pinpoint
- skywalking
- Sleuth
spring alibaba 链路追踪—>Sleuth链路+zipkin可视化工具
2.Sleuth入门
spring cloud Sleuth主要提供链路追踪解决方案
- 导入依赖
<!--Sleuth链路追踪-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
-
启动
【微服务名 TraceId SpanId 是否将链路追踪的结果输出到第三方平台】
3.Zipkin的集成
Zipkin是Twitter的一个开源项目,基于Google Dapper实现
下载地址
https://search.maven.org/search?q=g:org.apache.zipkin
- 运行jar
http://localhost:9941/
- 微服务导入依赖客户端
<!--Zipkin链路追踪UI界面-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
- 配置Zipkin
spring:
zipkin:
base-url: http://localhost:9941/ #Zikpin的服务端地址
discovery-client-enabled: false #让nacos把它当成一个url,而不是服务名
sleuth:
sampler:
probability: 1.0 #采样的百分比 0.0~0.1就行
- Zipkin的数据持久化
spring cloud security OAuth2.0分布式
- 基于session认证 :安全,不跨域
- 基于token认证 :可拓展性高
OAuth2.0支持不注册----微信、QQ登录
客户端认证+用户本身认证
1、环境
Spring Security Oauth2是对OAuth2的一种实现,与spring security相辅相成
- 授权服务UAA
AuthorizationEndpoint 服务于认证请求,默认URL: /oauth/authorize 【验证用户】
TokenEndpoint服务于访问令牌的请求,默认URL: /oauth/token【是否有权限】
- 资源服务Order、Product…
OAuth2AuthenticationProcessingFilter:校验token[令牌]的合法性
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
server:
port: ${PORT:40400}
servlet:
context-path: /auth
spring:
application:
name: xc-service-ucenter-auth
redis:
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
timeout: 5000 #连接超时 毫秒
jedis:
pool:
maxActive: 3
maxIdle: 3
minIdle: 1
maxWait: -1 #连接池最大等行时间 -1没有限制
datasource:
druid:
url: ${MYSQL_URL:jdbc:mysql://localhost:3306/xc_user?characterEncoding=utf-8}
username: root
password: mysql
driverClassName: com.mysql.jdbc.Driver
initialSize: 5 #初始建立连接数量
minIdle: 5 #最小连接数量
maxActive: 20 #最大连接数量
maxWait: 10000 #获取连接最大等待时间,毫秒
testOnBorrow: true #申请连接时检测连接是否有效
testOnReturn: false #归还连接时检测连接是否有效
timeBetweenEvictionRunsMillis: 60000 #配置间隔检测连接是否有效的时间(单位是毫秒)
minEvictableIdleTimeMillis: 300000 #连接在连接池的最小生存时间(毫秒)
auth:
tokenValiditySeconds: 1200 #token存储到redis的过期时间
clientId: XcWebApp
clientSecret: XcWebApp
cookieDomain: java.itcast.cn
cookieMaxAge: -1
encrypt:
key-store:
location: classpath:/xc.keystore
secret: xuechengkeystore
alias: xckey
password: xuecheng
eureka:
client:
registerWithEureka: true #服务注册开关
fetchRegistry: true #服务发现开关
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址,多个中间用逗号分隔
defaultZone: ${EUREKA_SERVER:http://localhost:50101/eureka/,http://localhost:50102/eureka/}
instance:
prefer-ip-address: true #将自己的ip地址注册到Eureka服务中
ip-address: ${IP_ADDRESS:127.0.0.1}
instance-id: ${spring.application.name}:${server.port} #指定实例id
ribbon:
MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试,如果eureka中找不到服务则直接走断路器
MaxAutoRetriesNextServer: 3 #切换实例的重试次数
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 6000 #请求处理的超时时间
然后启动测试
2、微服务如何接入OAuth2授权码验证
用于第三方接口接入
-
将publicKey.txt放入微服务的resources下
-
添加oauth2依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
-
创建ResourceServerConfig类
package com.xuecheng.manage_course.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.stream.Collectors; /** * @author Administrator * @version 1.0 **/ @Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//激活方法上的PreAuthorize注解 public class ResourceServerConfig extends ResourceServerConfigurerAdapter { //公钥 private static final String PUBLIC_KEY = "publickey.txt"; //定义JwtTokenStore,使用jwt令牌 @Bean public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) { return new JwtTokenStore(jwtAccessTokenConverter); } //定义JJwtAccessTokenConverter,使用jwt令牌 @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setVerifierKey(getPubKey()); return converter; } /** * 获取非对称加密公钥 Key * @return 公钥 Key */ private String getPubKey() { Resource resource = new ClassPathResource(PUBLIC_KEY); try { InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream()); BufferedReader br = new BufferedReader(inputStreamReader); return br.lines().collect(Collectors.joining("\n")); } catch (IOException ioe) { return null; } } //Http安全配置,对每个到达系统的http请求链接进行校验 @Override public void configure(HttpSecurity http) throws Exception { //所有请求必须认证通过 http.authorizeRequests() //下边的路径放行 .antMatchers("/v2/api-docs", "/swagger-resources/configuration/ui", "/swagger-resources","/swagger-resources/configuration/security", "/swagger-ui.html","/webjars/**").permitAll() .anyRequest().authenticated(); } }
3、密码模式授权
-
用于用户登录
-
令牌刷新---->用于后台自动刷新令牌(提高用户体验)
传统授权方法的问题是用户每次请求资源服务,资源服务都需要携带令牌访问认证服务去校验令牌的合法性,并根
据令牌获取用户的相关信息,性能低下。
解决:使用JWT
4、JWT令牌介绍
JWT令牌的优点:
1、jwt基于json,非常方便解析。
2、可以在令牌中自定义丰富的内容,易扩展。
3、通过非对称加密算法及数字签名技术,JWT防止篡改
4、资源服务使用JWT可不依赖认证服务即可完成授
缺点:
ue, securedEnabled = true)//激活方法上的PreAuthorize注解
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
//公钥
private static final String PUBLIC_KEY = "publickey.txt";
//定义JwtTokenStore,使用jwt令牌
@Bean
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
//定义JJwtAccessTokenConverter,使用jwt令牌
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(getPubKey());
return converter;
}
/**
* 获取非对称加密公钥 Key
* @return 公钥 Key
*/
private String getPubKey() {
Resource resource = new ClassPathResource(PUBLIC_KEY);
try {
InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
BufferedReader br = new BufferedReader(inputStreamReader);
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException ioe) {
return null;
}
}
//Http安全配置,对每个到达系统的http请求链接进行校验
@Override
public void configure(HttpSecurity http) throws Exception {
//所有请求必须认证通过
http.authorizeRequests()
//下边的路径放行
.antMatchers("/v2/api-docs", "/swagger-resources/configuration/ui",
"/swagger-resources","/swagger-resources/configuration/security",
"/swagger-ui.html","/webjars/**").permitAll()
.anyRequest().authenticated();
}
}
### 3、密码模式授权
- 用于用户登录
- 令牌刷新---->用于后台自动刷新令牌(提高用户体验)
传统授权方法的问题是用户**每次**请求资源服务,资源服务都需要携带令牌访问认证服务去校验令牌的**合法性**,并根
据令牌获取用户的相关信息,**性能低下**。
> 解决:使用JWT
### 4、JWT令牌介绍
JWT令牌的优点:
1、jwt基于json,非常方便解析。
2、可以在令牌中自定义丰富的内容,易扩展。
3、通过非对称加密算法及数字签名技术,JWT防止篡改
4、资源服务使用JWT可不依赖认证服务即可完成授
缺点:
1、JWT令牌较长,占存储空间比较大。