maven 聚合工程 xdclass-cloud,包含以下四个子项目
创建聚合工程(记得删除聚合工程 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>
四个子项目都是普通的 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>
基本的项目结构就是这样,我们来打通 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>
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
VideoApplication.java
@SpringBootApplication @MapperScan("net.xdclass.dao") public class VideoApplication { public static void main(String [] args){ SpringApplication.run(VideoApplication.class,args); } }
VideoMapper.java
@Repository public interface VideoMapper { @Select("select * from video where id=#{videoId}") Video findById(@Param("videoId") int videoId); }
RPC:
Rest(Http):
我们看一个 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; } }
OrderApplication.java
@SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } }
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
存在的问题:
既然存在这些问题,解决问题的方案也呼之欲出:注册中心
什么是注册中心(服务治理)
为什么要用
主流的注册中心:zookeeper、Eureka、consul、etcd、Nacos
nacos下载和启动官网上有很清楚的介绍
通过注册中心解决第一个问题:服务之间的 IP 信息写死
视频服务集成Nacos
添加依赖
<!--添加nacos客户端--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
配置 Nacos 地址
server: port: 9000 ? spring: application: name: xdclass-video-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848
启动类增加注解
@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)
软硬件角度负载均衡的种类
从端的角度负载均衡有两种
常见的负载均衡策略(看组件的支持情况)
什么是 Ribbon?
订单服务增加 @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 支持的负载均衡策略介绍
负载均衡策略调整
订单服务增加配置
?
xdclass-video-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
策略选择:
现在解决第三个问题:多个服务直接关系调用维护复杂
原先 Ribbon 代码存在的问题:不规范,风格不统一,维护性比较差
什么是Feign:
官方文档(版本 2.2.5)
Nacos 支持 Feign,可以直接集成实现负载均衡的效果
使用 Feign 步骤
加入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
启动类增加注解 @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; }
注意:
Ribbon 和 Feign 两个的区别和选择
CAP 定理:指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition Tolerance(分区容错性),三者不可同时获得。
CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡。
原因:
注册中心选择:
结论:
分布式系统中P,肯定要满足,所以只能在 C 和 A 中二选一。没有最好的选择,最好的选择是根据业务场景来进行架构设计,如果要求一致性,则选择 Zookeeper,如金融行业;如果要去可用性,则 Eureka,如电商系统。
Nacos | Eureka | Consul | Zookeeper | |
---|---|---|---|---|
一致性协议 | CP+AP | AP | CP | CP |
健康检查 | TCP/HTTP/MYSQL/Client Beat | 心跳 | TCP/HTTP/gRPC/Cmd | Keep Alive |
雪崩保护 | 有 | 有 | 无 | 无 |
访问协议 | HTTP/DNS | HTTP | HTTP/DNS | TCP |
SpringCloud集成 | 支持 | 支持 | 支持 | 支持 |
什么是 BASE 理论
Basically Available(基本可用)
Soft state(软状态)
Eventually consistent(最终一致性)
AlibabaCloud 核?组件服务治理 Nacos 实战
原文:https://www.cnblogs.com/jwen1994/p/13952688.html