Spring Cloud Alibaba一站式解决方案

一、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)

  1. 创建一个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)

  1. 创建一个Maven模块Moudel【用来装实体类】
  2. 导入依赖
<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>
  1. 实体类创建

    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)

  1. 创建模块 导入依赖
<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>
  1. 启动类+配置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)

  1. 创建模块 导入依赖
<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>
  1. 启动类+配置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组件的介绍与使用

  1. 下载安装nacos-server端【软件】
  2. 项目中nacos-client的使用【jar包】
  3. 进行注册与调用

在这里插入图片描述

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组件的负载均衡
  1. 在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();
    }
}
  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.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、常见容错方案

常见的容错思路:

  1. 隔离:线程池隔离+信号量隔离
  2. 超时:到达最大响应时间—>自动废弃本次请求
  3. 限流:限制流量的输入和输出(1s只处理3个请求,其他的请排队)
  4. 熔断:请求—>过一段时间—>继续请求

在这里插入图片描述

  1. 降级: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功能介绍

  1. 流量控制

请求不规则问题【第1s:1个,第2s:10个,…】

漏斗:每次最多只能3个

9、Sentinel规则介绍

  1. 流控模式:链路

·需要升级springcloud Alibaba的版本 2.1.1.RELEASE

在这里插入图片描述
在这里插入图片描述

filter.enable=false
    注意:Sentinel 1.6.3版本以后,Sentinel Web过滤器默认收敛所有URL的入口上下文,因此互连限流不生效。1.7.01.7.1版本可以使用配置类的方式关闭URL PATH聚合,示例如下:

在这里插入图片描述

  1. 流控效果
  • 快速失败 超出的请求—>直接失败
  • warm up 预热
  • 排队等待 超出的请求不直接失败—>等待时间
  1. 降级处理
  • 热点限流 —根据参数限流

  • 授权规则 A访问都放行 B访问都不放行

    授权规则的配置(测试:所有带有serviceName的才能进行访问)

在这里插入图片描述

  1. 系统规则(不被外界环境影响)

10、Sentinel异常页面自定义

在这里插入图片描述
在这里插入图片描述

11、Feign整合Sentinel

  1. 引入Sentinel的依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  1. 在配置文件中开启Feign对Sentinel的支持
feign:
  sentinel:
	enabled: true
sentinel:
      transport:
        port: 9999
        dashboard: localhost:8080 #Sentinel控制台启动的端口
  1. 创建一个与远程一样的ServiceImpl实现类

在这里插入图片描述

  1. 修改远程调用@FeignClient

在这里插入图片描述

问题:不知道发生了什么异常 fallback可以将异常信息也告诉我吗?

  1. 写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的差别:

  1. 架构层次方面,传统的2PC方案的RM实际上是在数据层,RM本质上是数据库本身,通过XA协议实现,而Seata 的RM是以jar包的形式作为中间件层部署在应用程序这一侧的
  2. 两阶段提交方面 传统的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授权码验证

用于第三方接口接入

在这里插入图片描述

  1. 将publicKey.txt放入微服务的resources下

  2. 添加oauth2依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    
  3. 创建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令牌较长,占存储空间比较大。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值