不用写几行代码的springcloud(一)—— 什么是SpringCloud、从零搭建一个微服务工程
零、标题为什么是不用写几行代码的springcloud因为Springcloud的几乎不需要在springboot项目基础上增加什么新的代码一、为什么会诞生springcloud为什么会诞生springcloud因为springboot微服务变多了就需要管理问题。面试题springboot和springcloud的区别怎么会有这么zhizhang的面试题springboot是用来构建微服务项目的springcloud是用来治理微服务的先有springboot再有springcloud。二、微服务架构的4个核心问题服务很多客户端该怎么访问服务之间是如何通信的这么多服务如何治理服务挂了怎么办解决方案springcloud不是一门技术也不是一个框架而是一个生态或者说一套理论或者理解成一个接口。围绕着这个生态有很多解决方案或者说接口的实现。下面介绍三套解决方案各有优缺点。解决方案一spring cloud NetFlix一站式解决方案就是上面四个问题都能解决用的很多因为比较成熟虽然已经停止维护了1api网关zuul组件2Feign3Eureka4Hystrix解决方案二Apache Dubbo Zookeeper不是一站式的就是只能解决上面四个问题里的一部分其他部分要靠整合其他组件去解决半自动化1api网关没有所以要整合第三方组件比如zuul2DubboDubbo的定义是一款RPC框架所以Dubbo主要用来解决上面第二个核心问题3Zookeeper也没有需要整合第三方组件比如Hystrix4Spring cloud Alibaba 一站式解决方案,2019年孵化出来解决方案三【最常用】Spring cloud Alibaba 一站式解决方案,2019年孵化出来1服务注册与发现nacos2流量控制与熔断sentinel3消息队列RocketMQ4分布式通信Dubbo。feign是HTTP架构dubbo是RPC架构5Seata 转账交易的事务一致性解决方案其他三、手把手教你从零搭建一个微服务项目new一个干净的maven项目作为总项目把原有的src文件夹删了为什么new一个maven项目而不是new一个springboot项目呢因为springboot项目会自带pom.xml包不利于我们从0进行包的管理。maven项目的pom.xml里没有包。然后进行总pom文件的配置以及整个微服务依赖的管理。在父pom文件里继承父pom文件VS在dependencyManagement里引入就是继承类和实现接口的关系继承只能继承一个类实现可以实现多个接口。同理子pom文件只能继承一个父pom文件但是可以在dependencyManagement引入多个pom文件。PS注意dependencyManagement只在父pom文件里用。你想一下如果在子pom文件里用dependencyManagement引入别的pom文件版本号就别人写死的了那我们怎么进行不同模块之间依赖版本的统一?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd !--xmlns: 指定这份pom.xml文件的行业标准来自Apache公司-- !--xmlns:xsi指定-- !--xsi:schemaLocation 指定参考模版位置-- modelVersion4.0.0/modelVersion !--maven遵循的版本是4.0.0同上面http://maven.apache.org/pom/4.0.0-- groupIdorg.example/groupId artifactIdspring-cloud-study/artifactId version1.0-SNAPSHOT/version modules modulespring-cloud-model/module modulespring-cloud-provider-dept-8001/module /modules !--打包方式pom-- packagingpom/packaging properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target junit.version4.12/junit.version log4j.version1.2.17/log4j.version lombok.version1.18.12/lombok.version /properties !--这是一个总工程-- !--dependencyManagement只是管理标签里面包含的依赖并不会被导入你刷新一下maven就能看出来-- dependencyManagement dependencies !--springcloud的依赖文件 注意这里是把一整个文件给引入进来了 引入这个以后子pom文件就可以用这个文件里的依赖了-- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-dependencies/artifactId version2021.0.3/version typepom/type scopeimport/scope /dependency !--springboot的依赖文件 注意这里是把一整个文件给引入进来了 引入这个以后子pom文件就可以用这个文件里的依赖了-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version2.7.14/version typepom/type scopeimport/scope /dependency !--数据库 版本要和mysql的版本匹配起来mysql是5开头的话这个也要以5开头如果是8开头。。-- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.47/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.1.10/version /dependency !--springboot的starter-- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version1.3.2/version /dependency !--junit单元测试-- dependency groupIdjunit/groupId artifactIdjunit/artifactId version${junit.version}/version /dependency !--日志SL4j是一个接口可以对接log4j也可以对接logback-- dependency groupIdlog4j/groupId artifactIdlog4j/artifactId version${log4j.version}/version /dependency dependency groupIdch.qos.logback/groupId artifactIdlogback-core/artifactId version1.3.2/version /dependency !--lombok之前总结说以插件的方式使用但是安装插件以后还是没法用Data等注释 所以导入一下依赖-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version${lombok.version}/version /dependency /dependencies /dependencyManagement !--静态资源过滤-- build resources resource directorysrc/main/java/directory includes include**/*.properties/include include**/*.xml/include include**/*.yml/include /includes filteringfalse/filtering /resource resource directorysrc/main/resources/directory includes include**/*.properties/include include**/*.xml/include include**/*.yml/include /includes filteringfalse/filtering /resource /resources /build /projectPS: denpendencyMangement里只是管理依赖并不会导入。实践一下你刷新一下maven并不会导入任何依赖。如果后面需要静态资源过滤的话再补充下面的配置!--静态资源过滤-- build resources resource directorysrc/main/java/directory includes include**/*.properties/include include**/*.xml/include include**/*.yml/include /includes filteringfalse/filtering /resource resource directorysrc/main/resources/directory includes include**/*.properties/include include**/*.xml/include include**/*.yml/include /includes filteringfalse/filtering /resource /resources /build3.1 数据库表3.2 新建spring-cloud-model模块然后新建微服务的第一个module专门用于存放实体类在大的pom文件里就会出现modules标签然后配置这个模块的pom文件。需要什么依赖先在父pom文件的dependencyManagment里导入并指定版本号然后再来子pom文件的dependencies里导入子pom里不需要指定版本号。这样做的原因是如果有4个子模块都用到了junit依赖如果各自在各自的pom文件里指定版本号的话可能在一个项目中出现4个不一样版本的junit依赖所以我们要在父pom里进行版本的统一管理。删掉test文件夹然后在main文件夹里新建“com.kuang.springcloud.pojo”这样一个目录在pojo下新建Dept.java实体类package com.kuang.springcloud.pojo; import javafx.beans.NamedArg; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.io.Serializable; Data NoArgsConstructor Accessors(chain true) //实现链式编程 public class Dept implements Serializable { //所有实体类首先要实现序列化 private Long deptNo; private String deptName; private String db_source; //因为deptNo是自动生成的 public Dept(String deptName){ this.deptName deptName; } }3.3 新建服务提供者spring-cloud-provider-8001模块1、写pom文件。这是第二个模块了因为能用到第一个模块所以在pom文件的dependencies里把第一个模块当做依赖引入。然后其他用到的依赖就正常引入不做展示了。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd parent artifactIdspring-cloud-study/artifactId groupIdorg.example/groupId version1.0-SNAPSHOT/version /parent modelVersion4.0.0/modelVersion artifactIdspring-cloud-provider-dept-8001/artifactId dependencies !--① 因为要用到这个微服务的实体类-- dependency !--groupId是maven坐标的其中一个在我们new project的时候指定的 那时候默认是org.example如果你有公司了可以写公司名-- groupIdorg.example/groupId artifactIdspring-cloud-model/artifactId version1.0-SNAPSHOT/version /dependency !--②一些基本的依赖-- dependency groupIdjunit/groupId artifactIdjunit/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId /dependency dependency groupIdch.qos.logback/groupId artifactIdlogback-core/artifactId /dependency !--③springboot相关-- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-test/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !--跟Tomcat没有区别另一个应用启动器-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jetty/artifactId /dependency !--④热部署工具-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-devtools/artifactId /dependency /dependencies /project2、然后写服务提供者模块的配置文件在该模块的src/resources下新建一个application.ymlserver: port: 8001 #mybatis配置 mybatis: # 别名 type-aliases-package: com.kuang.springcloud.pojo # sql.xml的位置 mapper-locations: classpath:mapper/*.xml configuration: # 开启二级缓存。虽然默认是开启的但还要显式开启一下 cache-enabled: true spring: application: # 给应用起个名字springcloud服务的注册与发现用的都是这个名字 name: springcloud-provider datasource: driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/db01?useUnicodetruecharacterEncodingutf8 username: root password: 1234563、新建一个dao层然后写XxxMapper.java接口用到的实体类是前面那个模块的spring-cloud-model模块的我们通过pom文件引入了写完mapper接口写xml文件。package com.kuang.springcloud.dao; import com.kuang.springcloud.pojo.Dept; import org.apache.ibatis.annotations.Mapper; import java.util.List; Mapper public interface DeptMapper { Boolean add(Dept dept); Dept queryOne(int deptno); ListDept list(); }?xml version1.0 encodingUTF8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.kuang.springcloud.dao.DeptMapper insert idadd parameterTypecom.kuang.springcloud.pojo.Dept insert into dept(deptname,db_source) values (#{deptName},#{db_source}) /insert select idqueryOne resultTypecom.kuang.springcloud.pojo.Dept select * from dept where deptno #{deptno} /select select idlist resultTypecom.kuang.springcloud.pojo.Dept select * from dept /select /mapper4、然后新建service层写service层。package com.kuang.springcloud.service; import com.kuang.springcloud.pojo.Dept; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Service; import java.util.List; Service public interface IDeptService { Boolean add(Dept dept); Dept queryOne(int deptno); ListDept list(); }package com.kuang.springcloud.service.impl; import com.kuang.springcloud.dao.DeptMapper; import com.kuang.springcloud.pojo.Dept; import com.kuang.springcloud.service.IDeptService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; Service public class DeptServiceImpl implements IDeptService { Autowired DeptMapper deptMapper; Override public Boolean add(Dept dept) { return deptMapper.add(dept); } Override public Dept queryOne(int deptno) { return deptMapper.queryOne(deptno); } Override public ListDept list() { return deptMapper.list(); } }5、然后新建controller层写controller。package com.kuang.springcloud.controller; import com.kuang.springcloud.pojo.Dept; import com.kuang.springcloud.service.impl.DeptServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(dept) public class DeptController { Autowired DeptServiceImpl deptService; PostMapping(/add) public Boolean add(RequestBody Dept dept) { return deptService.add(dept); } GetMapping(/get/{deptno}) public Dept queryOne(PathVariable int deptno) { return deptService.queryOne(deptno); } GetMapping(/list) public ListDept list() { return deptService.list(); } }6、然后写主启动类。package com.kuang.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class DeptApplication { public static void main(String[] args) { SpringApplication.run(DeptApplication.class,args); } }然后运行3.4 新建服务消费者spring-cloud-consumer-dept-80模块package com.kuang.springcloud.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; Configuration public class ConfigBean { Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }消费者的消费行为没有自己的service层和dao层直接在controller层调用别人的。but我们这里只是演示所以功能比较单一因为实际上一个模块肯定既是消费者又是生产者肯定有自己的service层和dao层的所以前面写的是“消费者的消费行为没有自己的service层和dao层”而不是整个消费者PS:“只有在学习的时候才分消费者和生产者实际中每个微服务都兼具生产者和消费者既要给其他微服务提供接口又要调用其他微服务。”package com.kuang.springcloud.controller; import com.kuang.springcloud.pojo.Dept; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; 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 public class DeptConsumerController { Autowired private RestTemplate restTemplate; RequestMapping(/consumer/dept/add) public Boolean add(RequestBody Dept dept){ //post请求就用postForObjectget请求就用getForObject return restTemplate.postForObject(http://localhost:8001/dept/add,dept,Boolean.class); } RequestMapping(/consumer/dept/get/{id}) public Dept get(PathVariable(id) Long id){ return restTemplate.getForObject(http://localhost:8001/dept/get/id,Dept.class); } RequestMapping(/consumer/dept/list) public ListDept list(){ return restTemplate.getForObject(http://localhost:8001/dept/list,List.class); } }主启动类.javapackage com.kuang.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class DeptConsumerApplication { public static void main(String[] args) { SpringApplication.run(DeptConsumerApplication.class,args); } }pom.xml?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd parent artifactIdspring-cloud-study/artifactId groupIdorg.example/groupId version1.0-SNAPSHOT/version /parent modelVersion4.0.0/modelVersion artifactIdspring-cloud-comsumer-dept-80/artifactId dependencies !--用到了生产者的模块就在这引入-- dependency groupIdorg.example/groupId artifactIdspring-cloud-provider-dept-8001/artifactId version1.0-SNAPSHOT/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !--热部署-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-devtools/artifactId /dependency /dependencies /projectapplication.ymlserver: port: 80 # 因为消费者模块在pom里引入了生产者模块: # dependency # groupIdorg.example/groupId # artifactIdspring-cloud-provider-dept-8001/artifactId # version1.0-SNAPSHOT/version # /dependency # 而生产者模块用到了数据库,如果不在消费者这里配置数据库会报找不到datasource的错误 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db01?useUnicodetruecharacterEncodingutf8 username: root password: 123456同时开启生产者和消费者的服务消费者发起一个新增数据的请求