zoukankan      html  css  js  c++  java
  • AlibabaCloud 核⼼组件服务治理 Nacos 实战

    使用 Maven 聚合工程创建微服务架构

    maven 聚合工程 xdclass-cloud,包含以下四个子项目

    • xdclass-common
    • xdclass-video-service
    • xdclass-user-service
    • xdclass-order-service

    创建聚合工程(记得删除聚合工程 src 目录)

    <modelVersion>4.0.0</modelVersion>
    
    <groupId>net.xdclass</groupId>
    <artifactId>xdclass-cloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <modules>
        <module>xdclass-common</module>
        <module>xdclass-order-service</module>
        <module>xdclass-user-service</module>
        <module>xdclass-video-service</module>
    </modules>
    
    <!-- 一般来说父级项目的packaging都为pom,packaging默认类型jar类型-->
    <packaging>pom</packaging>
    
    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencyManagement>
        <dependencies>
            <!--https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/2.3.3.RELEASE-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.3.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
    
            <!--https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies/Hoxton.SR8-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
    
            <!--https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-dependencies/2.2.1.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
    
        </dependencies>
    </dependencyManagement>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.5.RELEASE</version>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
    聚合工程pom.xml

    四个子项目都是普通的 Maven 项目,xdclass-common 是公共项目,用来存放实体类等其他作用

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>net.xdclass</groupId>
            <artifactId>xdclass-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    子项目pom.xml

    基本的项目结构就是这样,我们来打通 Mybatis 连接 Mysql 数据库

    创建 common 包实体类

    public class User {
        private Integer id;
        private String name;
        private String pwd;
        private String headImg;
        private String phone;
        private Date createTime;
        private String wechat;
        //省略getter/setter方法
    }
    ​
    public class Video {
        private Integer id;
        private String title;
        private String summary;
        private String coverImg;
        private Integer  price;
        private Date createTime;
        private Double point;
        //省略getter/setter方法
    }
    ​
    public class VideoOrder {
        private Integer id;
        private String outTradeNo;
        private Integer state;
        private Date createTime;
        private  Integer totalFee;
        private Integer videoId;
        private String videoTitle;
        private String videoImg;
        private Integer userId;
        //省略getter/setter方法
    }
    实体类

    Maven 项目创建时没有像 SpringBoot 创建时已经创建好包结构,需要自己手动创建

    xdclass-video-service 项目 pom 文件新增依赖(子项目没有指明依赖版本号就是使用父项目一样的版本)

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    xdclass-video-service项目pom文件

    application.yml

    server:
      port: 9000
    
    spring:
      application:
        name: xdclass-video-service
    
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/cloud_video?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
        username: root
        password: 123456
    
    
    mybatis:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        map-underscore-to-camel-case: true
    application.yml

    VideoApplication.java

    @SpringBootApplication
    @MapperScan("net.xdclass.dao")
    public class VideoApplication {
        public static void main(String [] args){
            SpringApplication.run(VideoApplication.class,args);
        }
    }
    VideoApplication.java

    VideoMapper.java

    @Repository
    public interface VideoMapper {
        @Select("select * from video where id=#{videoId}")
        Video findById(@Param("videoId") int videoId);
    }
    VideoMapper.java

    服务直接怎么调用

    RPC:

    • 远程过程调用,像调用本地服务(方法)一样调用服务器的服务
    • 支持同步、异步调用
    • 客户端和服务器之间建立 TCP 连接,可以一次建立一个,也可以多个调用复用一次链接
    • RPC 数据包小
      • protobuf
      • thrift
    • RPC:编解码,序列化,链接,丢包,协议

    Rest(Http):

    • http 请求,支持多种协议和功能
    • 开发方便成本低
    • http 数据包大
    • java 开发:RestTemplate 或者 HttpClient

    我们看一个 RestTemplate 的例子

    OrderController.java

    @RestController
    @RequestMapping("api/v1/video_order")
    public class OrderController {
        @Autowired
        private RestTemplate restTemplate;
    
        @RequestMapping("/save")
        public Object save(int videoId) {
    
            Video video = restTemplate.getForObject("http://localhost:9000/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
    
            VideoOrder videoOrder = new VideoOrder();
            videoOrder.setVideoId(video.getId());
            videoOrder.setVideoTitle(video.getTitle());
            videoOrder.setCreateTime(new Date());
            return videoOrder;
        }
    }
    OrderController.java

    OrderApplication.java

    @SpringBootApplication
    public class OrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderApplication.class, args);
        }
    
        @Bean
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    OrderApplication.java

    application.yml

    server:
      port: 8000
    
    spring:
      application:
        name: xdclass-order-service
    
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/cloud_order?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: xdclass.net
    
    # 控制台输出sql、下划线转驼峰
    mybatis:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        map-underscore-to-camel-case: true
    application.yml

    存在的问题:

    • 服务之间的 IP 信息写死
    • 服务之间无法提供负载均衡
    • 多个服务直接关系调用维护复杂

    既然存在这些问题,解决问题的方案也呼之欲出:注册中心

    注册中心

    什么是注册中心(服务治理)

    • 服务注册:服务提供者 provider,启动的时候向注册中心上报自己的网络信息
    • 服务发现:服务消费者 consumer,启动的时候向注册中心上报自己的网络信息,拉取 provider 的相关网络信息
    • 核心:服务管理,是有个服务注册表,心跳机制动态维护,服务实例在启动时注册到服务注册表,并在关闭时注销。

    为什么要用

    • 微服务应用和机器越来越多,调用方需要知道接口的网络地址,如果靠配置文件的方式去控制网络地址,对于动态新增机器,维护带来很大问题

    主流的注册中心:zookeeper、Eureka、consul、etcd、Nacos

    • AlibabaCloud 搭配最好的是 Nacos,且服务的注册发现之外,还支持动态配置服务

    nacos下载和启动官网上有很清楚的介绍

    • 访问 localhost:8848/nacos
    • 默认账号密码 nacos/nacos

    通过注册中心解决第一个问题:服务之间的 IP 信息写死

    视频服务集成Nacos

    添加依赖

    <!--添加nacos客户端-->
    <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    nacos依赖

    配置 Nacos 地址

    server:
      port: 9000
    ​
    spring:
      application:
        name: xdclass-video-service
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    Nacos地址

    启动类增加注解

    @SpringBootApplication
    @EnableDiscoveryClient
    public class OrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderApplication.class, args);
        }
    
        @Bean
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }

    订单服务集成和用户服务集成 Nacos 都是一样的套路

    服务之间的调用,这样可以获得所有服务集合,我们可以访问任意一个服务

    @RestController
    @RequestMapping("api/v1/video_order")
    public class OrderController {
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private DiscoveryClient discoveryClient;
    
        @RequestMapping("/save")
        public Object save(int videoId) {
    
            //Video video = restTemplate.getForObject("http://localhost:9000/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
    
            List<ServiceInstance> list = discoveryClient.getInstances("xdclass-video-service");
            ServiceInstance serviceInstance = list.get(0);
            Video video = restTemplate.getForObject("http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() +
                    "/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
    
            VideoOrder videoOrder = new VideoOrder();
            videoOrder.setVideoId(video.getId());
            videoOrder.setVideoTitle(video.getTitle());
            videoOrder.setCreateTime(new Date());
            return videoOrder;
        }
    }

    第一个问题解决了,现在看看第二个问题

    负载均衡

    什么是负载均衡(Load Balance)

    • 分布式系统中一个非常重要的概念,当访问的服务具有多个实例时,需要根据某种“均衡”的策略决定请求发往哪个节点,这就是所谓的负载均衡,
    • 原理是将数据流量分摊到多个服务器执行,减轻每台服务器的压力,从而提高了数据的吞吐量

    软硬件角度负载均衡的种类

    • 通过硬件来进行解决,常见的硬件有 NetScaler、F5、Radware 和 Array 等商用的负载均衡器,但比较昂贵的
    • 通过软件来进行解决,常见的软件有 LVS、Nginx 等,它们是基于 Linux 系统并且开源的负载均衡策略

    从端的角度负载均衡有两种

    • 服务端负载均衡
    • 客户端负载均衡

    常见的负载均衡策略(看组件的支持情况)

    • 节点轮询
      • 每个请求按顺序分配到不同的后端服务器
    • weight 权重配置
      • weight 和访问比率成正比,数字越大,分配得到的流量越高
    • 固定分发
      • 根据请求按访问 ip 的 hash 结果分配,这样每个用户就可以固定访问一个后端服务器
    • 随机选择、最短响应时间等等

    集成 Ribbon 实现负载均衡

    什么是 Ribbon?

    • 一个客户端负载均衡工具,通过 SpringCloud 封装,可以轻松和 AlibabaCloud 整合

    订单服务增加 @LoadBalanced 注解

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    调用方式改造,注意红色字体

    @RestController
    @RequestMapping("api/v1/video_order")
    public class OrderController {
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private DiscoveryClient discoveryClient;
    
        @RequestMapping("/save")
        public Object save(int videoId) {
    
            //Video video = restTemplate.getForObject("http://localhost:9000/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
    
            //List<ServiceInstance> list = discoveryClient.getInstances("xdclass-video-service");
            //ServiceInstance serviceInstance = list.get(0);
            //Video video = restTemplate.getForObject("http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() +"/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
    
            Video video = restTemplate.getForObject("http://xdclass-video-service/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
    
            VideoOrder videoOrder = new VideoOrder();
            videoOrder.setVideoId(video.getId());
            videoOrder.setVideoTitle(video.getTitle());
            videoOrder.setCreateTime(new Date());
            return videoOrder;
        }
    }

    自定义 Ribbon 负载均衡策略

    分析源码得知 Ribbon 支持多种负载均衡策略

    Ribbon 支持的负载均衡策略介绍

    • RandomRule(随机策略):随机选择server
    • RoundRobinRule(轮询策略):按照顺序选择 server(默认)
    • RetryRule(重试策略):当选择 server 不成功,短期内尝试选择一个可用的server
    • AvailabilityFilteringRule(可用过滤策略):过滤掉一直失败并被标记为 circuit tripped 的 server,过滤掉那些高并发链接的 server(active connections 超过配置的阈值)
    • WeightedResponseTimeRule(响应时间加权重策略):根据 server 的响应时间分配权重,以响应时间作为权重,响应时间越短的服务器被选中的概率越大,综合了各种因素,比如:网络,磁盘,io 等,都直接影响响应时间
    • ZoneAvoidanceRule(区域权重策略):综合判断 server 所在区域的性能,和 server 的可用性,轮询选择server

    负载均衡策略调整

    订单服务增加配置
    ​
    xdclass-video-service:
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

    策略选择: 

    1. 如果每个机器配置一样,则建议不修改策略 (推荐) 
    2. 如果部分机器配置强,则可以改为WeightedResponseTimeRule

    现在解决第三个问题:多个服务直接关系调用维护复杂

    Feign 介绍

    原先 Ribbon 代码存在的问题:不规范,风格不统一,维护性比较差

    什么是Feign:

    • SpringCloud 提供的伪 HTTP 客户端(本质还是用 HTTP),封装了 HTTP 调用流程,更适合面向接口化
    • 让用 Java 接口注解的方式调用 HTTP 请求
    • 不用像 Ribbon 中通过封装 HTTP 请求报文的方式调用,Feign 默认集成了Ribbon

    官方文档(版本 2.2.5)

    Nacos 支持 Feign,可以直接集成实现负载均衡的效果 

    使用 Feign 步骤

    加入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    openfeign依赖

    启动类增加注解 @EnableFeignClients

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    public class OrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderApplication.class, args);
        }
    
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }

    订单服务增加接口,服务名称记得和 nacos 保持一样

    @FeignClient(name="xdclass-video-service")
    public interface VideoService {
    
        @GetMapping(value = "/api/v1/video/find_by_id")
        Video findById(@RequestParam("videoId") int videoId);
    }

    调用方进行改造

    @RestController
    @RequestMapping("api/v1/video_order")
    public class OrderController {
    
        @Autowired
        private VideoService videoService;
    
        @RequestMapping("/save")
        public Object save(int videoId) {
    
            Video video = videoService.findById(videoId);
    
            VideoOrder videoOrder = new VideoOrder();
            videoOrder.setVideoId(video.getId());
            videoOrder.setVideoTitle(video.getTitle());
            videoOrder.setCreateTime(new Date());
            return videoOrder;
        }
    }

    订单服务项目结构

    POST 方式提交怎么做?

    //订单服务service
    @PostMapping(value = "/api/v1/video/save")
    Video saveVideo(@RequestBody Video video);
    ​
    ​//视频服务controller
    @PostMapping("save")
    public Object save(@RequestBody Video video){
      System.out.println(video.getTitle());
      return  video;
    }

    注意:

    • 路径需要一致
    • Http 方法必须对应
    • 使用方、提供方需要用 @RequestBody,@PostMapping

    Ribbon 和 Feign 两个的区别和选择

    • 选择Feign
    • 默认集成了Ribbon
    • 写起来更加思路清晰和方便
    • 采用注解方式进行配置,配置熔断等方式方便

    CAP理论知识

    CAP 定理:指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition Tolerance(分区容错性),三者不可同时获得。

    • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(所有节点在同一时间的数据完全一致,越多节点,数据同步越耗时)
    • 可用性(A):负载过大后,集群整体是否还能响应客户端的读写请求。(服务一直可用,而且是正常响应时间)
    • 分区容错性(P):分区容忍性,就是高可用性,一个节点崩了,并不影响其它的节点。(100个节点,挂了几个,不影响服务,越多机器越好)

    CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡。

    原因:

    • CA 满足的情况下,P 不能满足的原因:数据同步(C)需要时间,也要正常的时间内响应(A),那么机器数量就要少,所以P就不满足。
    • CP 满足的情况下,A 不能满足的原因:数据同步(C)需要时间,,机器数量也多(P),但是同步数据需要时间,所以不能再正常时间内响应,所以A就不满足。
    • AP 满足的情况下,C不能满足的原因:机器数量也多(P),正常的时间内响应(A),那么数据就不能及时同步到其他节点,所以C不满足。

    注册中心选择:

    • Zookeeper:CP 设计,保证了一致性,集群搭建的时候,某个节点失效,则会进行选举行的 leader,或者半数以上节点不可用,则无法提供服务,因此可用性没法满足。
    • Eureka:AP 原则,无主从节点,一个节点挂了,自动切换其他节点可以使用,去中心化。

    结论:

    分布式系统中P,肯定要满足,所以只能在 C 和 A 中二选一。没有最好的选择,最好的选择是根据业务场景来进行架构设计,如果要求一致性,则选择 Zookeeper,如金融行业;如果要去可用性,则 Eureka,如电商系统。

     NacosEurekaConsulZookeeper
    一致性协议 CP+AP AP CP CP
    健康检查 TCP/HTTP/MYSQL/Client Beat 心跳 TCP/HTTP/gRPC/Cmd Keep Alive
    雪崩保护
    访问协议 HTTP/DNS HTTP HTTP/DNS TCP
    SpringCloud集成 支持 支持 支持 支持

    BASE理论

    什么是 BASE 理论

    • CAP 中的一致性和可用性进行一个权衡的结果,核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性,来自 ebay 的架构师提出

    Basically Available(基本可用)

    • 假设系统,出现了不可预知的故障,但还是能用,可能会有性能或者功能上的影响

    Soft state(软状态)

    • 允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时

    Eventually consistent(最终一致性)

    • 系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值
  • 相关阅读:
    格律詩
    React获取视频时长
    ant 入门级详解
    OpenShift证书批准及查询证书过期时间 wang
    kubeadm快速部署kubernetes集群(v1.22.3) wang
    OpenShift中SDN核心知识点总结 wang
    kubeadm快速部署kubernetes集群(v1.22.3)(二) wang
    Prometheus Operator使用ServiceMonitor自定义监控 wang
    Prometheus Operator配置k8s服务自动发现 wang
    Ceph RBD Mirroring wang
  • 原文地址:https://www.cnblogs.com/jwen1994/p/13952688.html
Copyright © 2011-2022 走看看