• 【SpringCloud】02 搭建springcloud微服务项目


    搭建springcloud微服务项目

    技术栈:

    1. springcloud-alibaba
    2. mybatis-plus 持久性框架
    3. mysql数据库5.7以上
    4. springboot来搭建每个微服务。

    1. 微服务父工程

    在这里插入图片描述
    把其他的全删掉,只保留一个pom文件

    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.3.12.RELEASEversion>
            <relativePath/> 
        parent>
        <groupId>com.aaagroupId>
        <artifactId>qy156-shop-parentartifactId>
        <version>0.0.1-SNAPSHOTversion>
        <packaging>pompackaging>
        <name>qy156-shop-parentname>
        <description>Demo project for Spring Bootdescription>
        
        <properties>
            <java.version>1.8java.version>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF- 8project.reporting.outputEncoding>
            <spring-cloud.version>Hoxton.SR8spring-cloud.version>
            <spring-cloud-alibaba.version>2.2.3.RELEASEspring-cloud-alibaba.version>
        properties>
        
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloudgroupId>
                    <artifactId>spring-cloud-dependenciesartifactId>
                    <version>${spring-cloud.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
                <dependency>
                    <groupId>com.alibaba.cloudgroupId>
                    <artifactId>spring-cloud-alibaba-dependenciesartifactId>
                    <version>${spring-cloud-alibaba.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
            dependencies>
        dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                plugin>
            plugins>
        build>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    2. 创建子模块-shop-common

    把其他模块公共得代码放入到该模块。- - -实体 工具类

    在这里插入图片描述

      <dependencies>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
            dependency>
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>mybatis-plus-boot-starterartifactId>
                <version>3.5.0version>
            dependency>
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    定义相关得实体类

    @Data
    @TableName(value = "shop_order")
    public class Order {
        //订单id
        @TableId
        private Long oid;
        //用户id
        private Integer uid;
        //用户名
        private String username;
        //商品id---购买时99---->活动结束后199
        private Long pid;
        //商品得名称
        private String pname;
        //商品得价格
        private Double pprice;
        //购买得数量
        private Integer number;
    }
    
    @Data
    @TableName("shop_product")
    public class Product {
        @TableId
        private Long pid;
        private String pname;
        private Double pprice;
        private Integer stock;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    3. 创建子模块–shop-product

    关于商品表操作的接口
    在这里插入图片描述

    	<dependencies>
              
            <dependency>
                <groupId>com.aaagroupId>
                <artifactId>shop-commonartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
            dependency>
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    配置文件

    # 定义端口号 [8001~8009 未来方便搭建集群]
    server:
      port: 8001
    
    #数据源得信息
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=Asia/Shanghai
        username: root
        password: root
    
    # mybatis打印日志
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    dao接口

    /**
     * @program: qy156-shop-parent
     * @description:
     * @author: 闫克起
     * @create: 2022-11-17 16:22
     **/
    public interface ProductDao extends BaseMapper<Product> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    service代码

    @Service
    public class ProductService implements IProductService {
        
        @Autowired
        private ProductDao productDao;
        @Override
        public Product findById(Long pid) {
            return productDao.selectById(pid);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    controller

    @RestController
    @RequestMapping("product")
    public class ProductController {
    
        @Autowired
        private IProductService productService;
    
        @GetMapping("/getById/{pid}")
        public Product getById(@PathVariable Long pid){
             return productService.findById(pid);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    主启动类

    @SpringBootApplication
    @MapperScan(basePackages = "com.aaa.product.dao")
    public class ProductApp {
        public static void main(String[] args) {
            SpringApplication.run(ProductApp.class,args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    4. 创建子模块–shop-order

    关于订单表得所有操作接口
    在这里插入图片描述

    	<dependencies>
            
            <dependency>
                <groupId>com.aaagroupId>
                <artifactId>shop-commonartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
            dependency>
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    配置

    #端口号---[9001~9009]集群模式
    server:
      port: 9001
    
    #数据源得信息
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=Asia/Shanghai
        username: root
        password: root
    # sql显示在控制台
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    dao接口

    package com.aaa.order.dao;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    
    /**
     * @program: qy156-shop-parent
     * @description:
     * @author: 闫克起
     * @create: 2022-11-18 14:34
     **/
    public interface OrderDao extends BaseMapper<OrderDao> {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    service代码

    @Service
    public class OrderService implements IOrderService {
    
        @Autowired
        private OrderDao orderDao;
        @Override
        public int save(Order order) {
            return orderDao.insert(order);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    配置类中注入restTemplate

    @SpringBootApplication
    public class AppOrder {
        public static void main(String[] args) {
            SpringApplication.run(AppOrder.class, args);
        }
    
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    controller代码

    package com.aaa.order.controller;
    
    import com.aaa.entity.Order;
    import com.aaa.entity.Product;
    import com.aaa.order.service.IOrderService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    /**
     * @program: qy156-shop-parent
     * @description:
     * @author: 闫克起2
     * @create: 2022-11-17 16:33
     **/
    @RestController
    @RequestMapping("/order")
    public class OrderController {
    
    
        //必须创建并交于spring容器管理。这样才能被注入到相应的类属性上
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private IOrderService orderService;
    
        @GetMapping("/saveOrder")
        public String saveOrder(Long pid,Integer num){
    
            Order order=new Order();
            //用户得信息可以从登录后获取--Session  redis  jwt
            order.setUid(1);
            order.setUsername("张成");
            //为订单对象设置商品得信息
            order.setPid(pid);
           //需要远程调用商品微服务中的指定接口[注意:凡是关于商品的操作都有商品微服务来执行。]
            //远程调用的方式:第一种基于TCP协议的RPC远程调用   第二种基于http协议Restful风格的调用。
            //分布式架构:TCP协议的
            //微服务架构:http协议的。---在spring框架中封装了一个工具RestTemplate。 如果不是使用的spring框架。你需要自己封装HttpClient工具
            Product product = restTemplate.getForObject("http://localhost:8001/product/getById/"+pid, Product.class);
            order.setPname(product.getPname());
            order.setPprice(product.getPprice());
            order.setNumber(num);
    
    
            orderService.save(order);
    
            return "下单成功";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    思考: 上面我们写远程调用代码 是否存在缺陷?


    [1] 我们把商品微服务地址写死在自己代码中 硬编码—如果商品微服务地址发生改变。需要修改我们自己当前微服务的代码


    [2] 如果商品微服务 搭建了集群模式。 订单微服务这边如何调用相应的商品微服务从而达到负载均衡的特性。

    服务器治理组件。

    eureka:当作服务治理组件。---->netflix公司的产品—停止更新维护


    zookeeper: 服务治理组件。---->dubbo分布式框架配合


    nacos: 服务治理组件。---->阿里巴巴的产品。

    这里使用nacos

    服务治理组件

    在这里插入图片描述

    1. 如何使用nacos

    https://github.com/alibaba/nacos/releases

    nacos1.3以后支持了集群模式。1.3以前不支持。

    安装nacos服务端。
    必须安装jdk并配置环境变量。而且不能把nacos放入中文目录

    在这里插入图片描述
    bin目录下startup.cmd启动nacos
    在这里插入图片描述
    访问nacos服务器
    在这里插入图片描述
    在这里插入图片描述

    2. 微服务客户端连接到nacos注册中心

    (1)引入依赖

    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2)修改配置文件
    在这里插入图片描述

    测试:
    在这里插入图片描述

    3. 消费端如何通过nacos调用提供者

    引入nacos依赖和配置nacos地址
    修改控制层代码

    package com.aaa.order.controller;
    
    import com.aaa.entity.Order;
    import com.aaa.entity.Product;
    import com.aaa.order.service.IOrderService;
    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.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    
    /**
     * @program: qy156-shop-parent
     * @description:
     * @author: 闫克起2
     * @create: 2022-11-17 16:33
     **/
    @RestController
    @RequestMapping("/order")
    public class OrderController {
    
    
        //必须创建并交于spring容器管理。这样才能被注入到相应的类属性上
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private IOrderService orderService;
    
       	//在nacos中封装了一个类DiscoveryClient,该类可以获取注册中心中指定的清单列表。
        @Autowired
        private DiscoveryClient discoveryClient;
    
        @GetMapping("/saveOrder")
        public String saveOrder(Long pid,Integer num){
            Order order=new Order();
            //用户得信息可以从登录后获取--Session  redis  jwt
            order.setUid(1);
            order.setUsername("张成");
            //为订单对象设置商品得信息
            order.setPid(pid);
            
            //获取指定的实例
            List<ServiceInstance> instances = discoveryClient.getInstances("shop-product");
            ServiceInstance serviceInstance = instances.get(0);
    //        String path = serviceInstance.getHost().toString();
    //        System.out.println(path+"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    //        Integer port = serviceInstance.getPort();
    //        System.out.println(port+"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    
            String uri = serviceInstance.getUri().toString();
    //        System.out.println(uri+"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
            Product product = restTemplate.getForObject(uri+"/product/getById/"+pid, Product.class);
            order.setPname(product.getPname());
            order.setPprice(product.getPprice());
            order.setNumber(num);
    
            orderService.save(order);
    
            return "下单成功";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    如果后期 提供者的地址发生改变,也不影响消费者的代码。

    思考: 上面使用从nacos拉取服务清单的模式是否存在问题?
    没有实现负载均衡的问题。如果后期商品微服务在部署时 是一个集群。调用者应该把请求均摊到每个服务器上。

    负载均衡

    通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。

    模拟搭建多个商品微服务。
    在这里插入图片描述


    在这里插入图片描述


    在这里插入图片描述

    1. 自己控制负载均衡

    人为的完成订单微服务调用商品微服务负载均衡的特性。

    package com.aaa.order.controller;
    
    import com.aaa.entity.Order;
    import com.aaa.entity.Product;
    import com.aaa.order.service.IOrderService;
    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.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    import java.util.Random;
    
    /**
     * @program: qy156-shop-parent
     * @description:
     * @author: 闫克起2
     * @create: 2022-11-17 16:33
     **/
    @RestController
    @RequestMapping("/order")
    public class OrderController {
    
    
        //必须创建并交于spring容器管理。这样才能被注入到相应的类属性上
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private IOrderService orderService;
    
        @Autowired
        private DiscoveryClient discoveryClient;
    
        @GetMapping("/saveOrder")
        public String saveOrder(Long pid,Integer num){
            Order order=new Order();
            //用户得信息可以从登录后获取--Session  redis  jwt
            order.setUid(1);
            order.setUsername("张成");
            //为订单对象设置商品得信息
            order.setPid(pid);
            //在nacos中封装了一个类DiscoveryClient,该类可以获取注册中心中指定的清单列表。
            //获取指定的实例
            List<ServiceInstance> instances = discoveryClient.getInstances("shop-product");
    
            //随机产生一个下标--0~size
            int index = new Random().nextInt(instances.size());
            ServiceInstance serviceInstance = instances.get(index);
    
            String uri = serviceInstance.getUri().toString();
    
            Product product = restTemplate.getForObject(uri+"/product/getById/"+pid, Product.class);
            order.setPname(product.getPname());
            order.setPprice(product.getPprice());
            order.setNumber(num);
    
            orderService.save(order);
    
            return "下单成功";
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    这两行是控制使用哪个微服务

    //随机产生一个下标--0~size
    int index = new Random().nextInt(instances.size());
    ServiceInstance serviceInstance = instances.get(index);
    
    • 1
    • 2
    • 3

    如果想改变负载均衡的策略,例如想变成轮询策略,就需要来这里修改源代码,硬编码问题【开闭原则】

    提供了ribbon组件—该组件可以完成负载均衡。

    2. ribbon完成负载均衡

    ribbon是 Netflix 发布的一个负载均衡器,有助于控制 HTTP 和 TCP客户端行为。在 SpringCloud 中,nacos一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从nacos中读取到的服务信息,在调用服务节点提供的服务时,会合理(策略)的进行负载。

    在SpringCloud中可以将注册中心和Ribbon配合使用,Ribbon自动的从注册中心中获取服务提供者的列表信息,并基于内置的负载均衡算法,请求服务。

    Ribbon自动的从注册中心中获取服务提供者的 列表信息,并基于内置的负载均衡算法,请求服务

    2.1 如何使用ribbon

    不需要再引入任何依赖
    在这里插入图片描述
    只需要再RestTemplate获取的bean上添加一个LoadBalance注解
    在这里插入图片描述

    @LoadBalanced是告诉RestTemplate使用ribbon完成负载均衡。自动从注册中心拉取服务。使用内置的负载均衡策略完成服务的调用。

    修改controller代码
    在这里插入图片描述
    测试发现默认使用的是轮询策略。ribbon是否可以改变为其他策略。而ribbon中提供了很多策略。

    策路名策略描述实现说明
    BestAvailableRule选择─个最小的并发请求的server逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server
    RandomRule随机选择一个server在index上随机。选择index对应位置的server
    RoundRobinRule轮询方式轮询选择轮询index,选择index对应位置的server
    AvailabilityFilteringRule过滤掉那些因为一直连接失败的被标记为circuittripped的后端server ,并过滤掉那些高并发的的后端server (active connections超过配置的阈值)使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态
    WeightedResponseTimeRule根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas时,使用roubine策略选择server。
    RetryRule对选定的负载均衡策略机上重试机制。在一个配置时间段内当选择server不成功,则—直尝试使用subRule的方式选择一个可用的server
    ZoneAvoidanceRule复合判断server所在区域的性能和server的可用性选择server使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前—个判断判定一个zone的运行性能是否可用,别除不可用的zone (的所有server) , AvailabilityPredicate用于过滤掉连接数过多的Server。

    如何使用相应的策略:
    第一种方式:在配置类中定义一个bean
    这种方式是当前服务对所有服务都使用RandomRule策略

        @Bean
        public RandomRule randomRule(){
            return new RandomRule();
        }
    
    • 1
    • 2
    • 3
    • 4

    第二种方式:在yml中配置
    这种方式是当前服务对shop-product服务,使用RandomRule策略

    shop-product:  # 这里使用服务的名称
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #使用的的负载均衡策略
    
    • 1
    • 2
    • 3

    如果上面的策略不够用,你也可以自定义策略类。实现IRule接口完成自定义策略。
    在这里插入图片描述
    如果上面的策略不够用,你也可以自定义策略类。实现IRule接口完成自定义策略。

    2.2 自定义负载均衡策略

    不管任何一个负载均衡,都是IRule接口的子类。
    在这里插入图片描述
    我们自定义的规则类 也必须继承AbastractLoadBalancerRule类。

    需求:
    要求自定义的算法:依旧是轮询策略,但是每个服务器被调用5次后轮到下一个服务,即以前是每个服务被调用1次,现在是每个被调用5次 .

    自定义规则类—模拟原来有的类。

    package com.aaa.order.rule;
    
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancer;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.ILoadBalancer;
    import com.netflix.loadbalancer.Server;
    
    import java.util.List;
    
    /**
     * @program: qy156-shop-parent
     * @description:
     * @author: 闫克起2
     * @create: 2022-11-19 14:42
     **/
    public class MyRule extends AbstractLoadBalancerRule {
        @Override
        public void initWithNiwsConfig(IClientConfig iClientConfig) {
             //初始化方法 读取配置文件内容
        }
    
        //统计访问的次数
        private int total;
        //作为集群服务器下标
        private int index;
        @Override
        public Server choose(Object key) {
            //获取负载均衡选择器
            ILoadBalancer lb = getLoadBalancer();
            if (lb == null) {
                return null;
            }
            Server server = null;
    
            while (server == null) {
                if (Thread.interrupted()) {
                    return null;
                }
                //获取所有可用的服务器
                List<Server> upList = lb.getReachableServers();
                //获取所有的服务器。
                List<Server> allList = lb.getAllServers();
    
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }
    
                //判断该服务访问的次数是否>5次
                if(total<5){
                     server=upList.get(index);
                     total++;
                }else{
                     total=0;
                     index++;
                     index=index%upList.size();
                }
    
                if (server == null) {
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive()) {
                    return (server);
                }
    
                // Shouldn't actually happen.. but must be transient or a bug.
                server = null;
                Thread.yield();
            }
    
            return server;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    (2)创建一个配置类,该类用于创建上面的bean对象

    @Configuration
    public class RuleConfig {
    
        @Bean
        public MyRule myRule(){
            return new MyRule();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (3)ribbon使用上面自定义的规则
    在这里插入图片描述

    2.3 饿汉式加载

    ribbon默认为懒汉式加载,就是启动时不加载资源,第一次启动时加载,修改yml文件可以修改为饿汉式

    ribbon:
      eager-load:
        enabled: true # 开启饥饿加载
        clients: # 指定饥饿加载的服务名称
          - shop-order
    
    • 1
    • 2
    • 3
    • 4
    • 5

    远程调用组件-Openfeign

    我们上面服务与服务之间的调用,使用的为RestTemplate工具类,来完成相应的调用。但是RestTemplate这种模式不符合我们编程的习惯。

    dao----service----controller:在service类中注入dao对象,然后调用dao中的方法,并传入相关的参数,以及接受相关的返回类型。

    1. 概述

    OpenFeign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。

    Nacos很好的兼容了OpenFeign, Feign负载均衡默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负载均衡的效果。

    2. 如何使用openfeign组件

    (1)引入相关的依赖

    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2)创建feign接口

    value:调用远程微服务的名称
    @FeignClient(value = "shop-product")
    public interface ProductFeign {
        @GetMapping("/product/getById/{pid}")
        public Product getById(@PathVariable Long pid);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (3)开启feign注解的驱动
    在这里插入图片描述
    (4)使用feign接口

    package com.aaa.order.controller;
    
    import com.aaa.entity.Order;
    import com.aaa.entity.Product;
    import com.aaa.order.feign.ProductFeign;
    import com.aaa.order.service.IOrderService;
    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.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    import java.util.Random;
    
    /**
     * @program: qy156-shop-parent
     * @description:
     * @author: 闫克起2
     * @create: 2022-11-17 16:33
     **/
    @RestController
    @RequestMapping("/order")
    public class OrderController {
    
        @Autowired
        private IOrderService orderService;
    
        //spring容器会为该接口生成带来实现类。
        @Autowired
        private ProductFeign productFeign;
    
        @GetMapping("/saveOrder")
        public String saveOrder(Long pid,Integer num){
            Order order=new Order();
            //用户得信息可以从登录后获取--Session  redis  jwt
            order.setUid(1);
            order.setUsername("张恒");
            //为订单对象设置商品得信息
            order.setPid(pid);
            //就像调用本地方法一样
            Product product = productFeign.getById(pid);
    
            order.setPname(product.getPname());
            order.setPprice(product.getPprice());
            order.setNumber(num);
    
            orderService.save(order);
            return "成功";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    如果nacos单机出现故障,导致所有微服务服务注册和拉取相应的服务信息。从而导致整个项目无法使用。

    所以就需要给nacos搭建集群模式
    链接:xxx

  • 相关阅读:
    蓝牙技术|蓝牙Mesh在照明网络上的应用
    2. C++ 线程的使用
    Java面试题:@PostConstruct、init-method和afterPropertiesSet执行顺序?
    消息队列:原理与应用
    day-62 代码随想录算法训练营(19)图论 part 01
    .NET 使用配置文件
    14. v-model 是如何实现的, 语法糖实际是什么?
    Android学习笔记 29. Activity组件
    用DIV+CSS技术设计的网页与实现制作【体育文化】dreamweaver学生网页设计
    为什么OpenCV计算的帧率是错误的?
  • 原文地址:https://blog.csdn.net/qq_60969145/article/details/127970097