1.分布式架构简介
1.1.分布式架构
SOA:Service Oriented Architecture 面向服务的架构,其中包含多个功能,服务之间通过相互依赖最终提供一些列的功能,一个功能,一个服务,各个服务之间通过网络调用。 微服务:将⼀个⼤的单体应⽤进⾏细粒度的服务化拆分,每个拆分出来的服务各⾃独⽴打包部署,各个服务之间 通过⽹络调⽤。
优点:
- 易开发、理解和维护
- 独立的部署和启动
缺点:
- 分布式系统->分布式事务
- 需要管理多个服务->服务治理
1.2.常见的微服务架构解决方案
- ServiceComb
- 华为内部的CSE框架,一个微服务的开源解决方案,文档不多,通信领域比较强。
- dubbo
- zookeeper+
- dubbo+springmvc/springboot
- 官⽅地址:http://dubbo.apache.org/#!/?lang=zh-cn
- 通信方式:rpc
- 注册中心:zookeeper/redis/nacos
- 配置中心:diamond/nacos
SpringCloud
- 全家桶+轻松嵌⼊第三⽅组件(Netflix 奈⻜)
- 官⽹:https://spring.io/projects/spring-cloud
- 通信⽅式:http restful
- 注册中⼼:eruka
- 配置中⼼:config
- 断路器:hystrix
- ⽹关:zuul/gateway
- 分布式追踪系统:sleuth+zipkin
Spring Cloud Alibaba
- 全家桶+阿⾥⽣态多个组件组合+SpringCloud⽀持
- 官⽹ https://spring.io/projects/spring-cloud-alibaba
- 通信⽅式:http restful
- 注册中⼼:nacos
- 配置中⼼:nacos
- 断路器:sentinel
- ⽹关:gateway
- 分布式追踪系统:sleuth+zipkin
1.3.分布式系统核心组件图
2.AlibabaCloud架构环境准备
2.1.创建maven聚合项目
(1)版本说明
- Spring5以上
- SpringBoot2.x以上
- AlibabaCloud 版本 2.2.x https://spring.io/projects/spring-cloud-alibaba#learn
- SpirngCloud版本 Hoxton https://spring.io/projects/spring-cloud
(2)创建父工程项目ali-cloud,删除src目录
<!-- 一般来说父级项目的packaging都为pom,packaging默认类型jar类型--> <packaging>pom</packaging> <properties> <!--JDK版本,如果是jdk8则这里是 1.8--> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.boot.version>2.3.3.RELEASE</spring.boot.version> <spring.cloud.version>Hoxton.SR8</spring.cloud.version> <alibaba.cloud.version>2.2.1.RELEASE</alibaba.cloud.version> <mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.version> <lombok.version>1.18.16</lombok.version> <commons.lang3.version>3.9</commons.lang3.version> <commons.codec.version>1.15</commons.codec.version> <springfox.boot.starter.version>3.0.0</springfox.boot.starter.version> <docker.image.prefix>xdclass-cloud</docker.image.prefix> <!--跳过单元测试--> <skipTests>true</skipTests> </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>${spring.boot.version}</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>${spring.cloud.version}</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>${alibaba.cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--mybatis plus和springboot整合--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatisplus.boot.starter.version}</version> </dependency> <!--https://mvnrepository.com/artifact/org.projectlombok/lombok/1.18.16--> <!--scope=provided,说明它只在编译阶段生效,不需要打入包中, Lombok在编译期将带Lombok注解的Java文件正确编译为完整的Class文件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <!--<scope>provided</scope>--> </dependency> <!--接口文档依赖--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>${springfox.boot.starter.version}</version> </dependency> </dependencies> </dependencyManagement> <!-- 代码库 --> <repositories> <repository> <id>maven-ali</id> <url>http://maven.aliyun.com/nexus/content/groups/public//</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>fail</checksumPolicy> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>public</id> <name>aliyun nexus</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> <!--module不用添加打包版本信息--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> <configuration> <fork>true</fork> <addResources>true</addResources> </configuration> </plugin> </plugins> </build>
(3)父工程下创建常量类
<dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--项目中添加 spring-boot-starter--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--数据库连接--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--MybatisPlus驱动--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <!-- 代码自动生成依赖 begin 上线后不需要--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> <!--<scope>provided</scope>--> </dependency> <!-- velocity --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> <!--<scope>provided</scope>--> </dependency> <!-- 代码自动生成依赖 end--> <!--swagger ui接口文档依赖--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> </dependencies>
(4)创建order-service、video-service、user-service
<dependencies> <dependency> <groupId>net.xdclass</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
2.2.配置MyBatis
(1)分别在order-service、user-service、video-service的yml文件中加入mybatis配置
server: port: 8001 #(端口号) spring: application: name: user-service #(服务名称) #数据库配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.10.88:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai #(配置数据库地址) username: root password: 123456 #配置plus打印sql日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
(2)测试Mybatis
- VideoServiceImpl
@Service public class VideoServiceImpl implements VideoService { @Autowired private VideoMapper videoMapper; @Override public Video findById(int id) { return videoMapper.findById(id); } }
- VideoMapper
public interface VideoMapper { @Select("select * from video where id = #{id}") Video findById(int id); }
- VideoController
@RestController @RequestMapping("/api/v1/video") public class VideoController { @Autowired private VideoService videoService; @GetMapping("/find_video/{video_id}") public JsonData findVideo(@PathVariable("video_id") int id){ System.out.println(id); Video video = videoService.findById(id); return JsonData.buildSuccess(video); } }
- 测试
3.RestTemplate服务间调用
(1)订单微服务主类注入RestTemplate
@SpringBootApplication @MapperScan("net.xdclass.mapper") public class OrderApplication { public static void main(String[] args){ SpringApplication.run(OrderApplication.class); } @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
(2)Controller中调用
@RestController @RequestMapping("/api/v1/order") public class OrderController { @Autowired private RestTemplate restTemplate; @GetMapping("/save/{video_id}") public JsonData save(@PathVariable("video_id") int id){ Video video = restTemplate.getForObject("http://localhost:7001/api/v1/video/find_video/" + id, Video.class); System.out.println("video:"+video); VideoOrder videoOrder = new VideoOrder(); videoOrder.setCreateTime(new Date()); videoOrder.setVideoImg(video.getCoverImg()); videoOrder.setVideoImg(video.getCoverImg()); videoOrder.setVideoTitle(video.getTitle()); return JsonData.buildSuccess(videoOrder); } }
3.Nacos服务治理
3.1.服务注册中心简介
(1)什么是注册中心
服务注册:服务提供者provider,启动的时候想注册中心上报自己的网络信息. 服务发现:服务消费者consumer,启动的时候想注册中心上报自己的网络信息,拉取provider的相关网络信息. 核心:服务管理,注册中心有个注册表,心跳机制动态维护,服务实例在启动时注册到服务表,关闭时注销.
(2)为什么要用
微服务应用和机器越来越多,调用方需要知道接口的网络地址,如果靠配置文件的方式去控制网络地址,对于动态新增机器,维护带来很大问题,主流的注册中心:zookeeper、Eureka、consul、etcd、Nacos。
(3)nacos的特性
3.2.Nacos注册中心实战
(1)安装部署Nacos
#上传Nacos压缩包到服务器,解压 unzip nacos-server-1.3.2.zip #配置jdk、maven环境,解压jdk、maven安装包 #jdk JAVA_HOME=/usr/local/jdk8 export JAVA_HOME CLASSPATH=.:$JAVA_HOME/lib export CLASSPATH PATH=$PATH:$JAVA_HOME/bin:$CLASSPATH export PATH #maven MAVEN_HOME=/usr/local/maven export MAVEN_HOME PATH=$PATH:$MAVEN_HOME/bin export PATH
(2)启动Nacos
cd /usr/local/nacos/bin ./startup.sh #启动后会发现启动不成功,因为nacos默认是以集群方式启动的,要修改启动方式
#需修改配置文件 export MODE="standalone"
(3)开放防火墙端口,nacos默认端口8848,重启防火墙
firewall-cmd --add-port=8848/tcp --permanent firewall-cmd --reload
(4)Nacos访问页面,默认账号密码为nacos
3.3.微服务配置Nacos
(1)yml文件中配置服务注册
spring: application: name: order-service cloud: nacos: discovery: server-addr: 192.168.10.88:8848 #nacos地址
(2)主类配置@EnableDiscoveryClient注解
@EnableDiscoveryClient
(3)验证服务注册
3.5.微服务间服务调用案例
@RestController @RequestMapping("/api/v1/order") public class OrderController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @GetMapping("/save/{video_id}") public JsonData save(@PathVariable("video_id") int id){ // Video video = restTemplate.getForObject("http://localhost:7001/api/v1/video/find_video/" + id, Video.class); //list获取服务的元数据信息,包括IP,端口,服务名称等等 List<ServiceInstance> list = discoveryClient.getInstances("video-service"); ServiceInstance serviceInstance = list.get(0); Video video = restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+ "/api/v1/video/find_video?id="+id,Video.class); System.out.println("video:"+video); VideoOrder videoOrder = new VideoOrder(); videoOrder.setCreateTime(new Date()); videoOrder.setVideoImg(video.getCoverImg()); videoOrder.setVideoImg(video.getCoverImg()); videoOrder.setVideoTitle(video.getTitle()); return JsonData.buildSuccess(videoOrder); }
4.Ribbon负载均衡策略
4.1.什么是Ribbon
Ribbon是⼀个客户端负载均衡⼯具,通过Spring Cloud封装,可以轻松和AlibabaCloud整合。
订单微服务开启Ribbon,添加注解@LoadBalanced @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate();}
controller中要修改成服务名称调用,ribbon是根据服务名称调用的
4.2.Ribbon源码分析
(1)大体流程
- 首先请求通过注册中心获取provider的列表
- 通过一定的策略选择一个节点
- 在返回给RestTemplate进行调用
(2)Ribbon的作用主要在于第二步选择节点
- 首先请求进来先进入注解
- 点进LoadBalancerClient接口
点进LoadBalancerClient的实现类RibbonLoadBalancerClient,只有这一个实现类
- 主要看getServer方法是选择负载均衡策略的方法
- loadBalancer默认传的是DynamicServerListLoadBalancer,但是DynamicServerListLoadBalancer没有chooseServer方法,所以掉用的是父类BaseLoadBalancer的。
- 默认BaseLoadBalancer定义的rule就是RoundRobinRule规则
(3)RibbonLoadBalancerClient继承体系
4.3.Ribbon负载均衡策略调整
(1)Ribbon支持的负载均衡策略
策略类 | 命名 | 描述 |
RandomRule | 随机策略 | 随机选择Server |
RoundRobinRule | 轮询策略 | 按照顺序选择server(默认) |
RetryRule | 重试策略 | 当选择server不成功,短期内尝试选择一个可用的server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤一直失败并被标记为circuit tripped的server,过滤掉那些高并发连接的server |
WeightedResponseTimeRule | 响应时间加权重策略 | 根据server的响应时间分配权重,以响应时间作为权重,响应时间越短的服务器被选中的概率越⼤,综合了各种因素,⽐如:⽹络,磁盘,io等,都直接影响响应时间 |
ZoneAvoidanceRule | 区域权重策略 | 综合所在区域的性能,和server的可用性,轮询选择server |
(2)配置yml
#使用随机负载均衡策略 video-service: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
(3)测试、分别启动三个VideoServer:9001、9006、9007
策略选择:
- 如果每个机器配置一样,则建议不修改策略(推荐)
- 如果部分机器配置强,则可以改为WeightedResponseTimeRule