SpringCloud(一)基础入门

SpringCloud(一)基础入门

木子李 238 2021-11-25

SpringCloud(一)基础入门

1. 什么是SpringCloud

1.1 SpringBoot介绍

基于springboot提供的一套微服务解决方案,包裹服务注册于发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。

利用springboot的开发便利性,巧妙的简化了分布式系统基础设施的开发,springcloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用springboot的开发风格做到一键启动和部署。

springcloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。

1.2. SpringCloud和SpringBoot的关系

  • SpringBoot专注于快速方便的开发单个个体微服务。 -jar
  • SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、微代理、事务总线、全局锁、决策竞争、分布式会话等等集成服务。
  • SpringBoot可以离开SpringCloud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系。

1.3. Dubbo和SpringCloud技术选型

1.3.1 分布式 + 服务治理Dubbo

目前成熟的互联网架构:应用服务化拆分 + 消息中间件

1.3.2 Dubbo 和 SpringCloud对比

功能DubboSpring
服务注册中心ZookeeperSpring Cloud Netflix Eureka
服务调用方式RPCREST API
服务监控Dubbo-monitorSpring Boot Admin
断路器不完善Spring Cloud Netflix Hystrix
服务网关Spring Cloud Netflix Zuul
分布式配置Spring Cloud Config
服务跟踪Spring Cloud Sleuth
消息总线Spring Cloud Bus
数据流Spring Cloud Stream
批量任务Spring Cloud Task

最大区别:SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式

严格来说,这两种各有优势,虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免不了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。

品牌机和主装机的区别

很明显,SpringCloud的功能比Dubbo更强大,涵盖面更广,而且作为Spring的明星项目,它也能够与SpringFramework、SpringBoot、SpringData、SpringBatch等其他Spring项目完美融合,这些对于微服务而言是至关重要的,使用Dubbo构建的微服务架构就像组装电脑,各环节的选择自由度很高,但是最终结果很有可能因为一条内存质量不行就点不亮了,总是让人不怎么放心,但是如果你是一个高手,那么这些都不是问题;而SpringCLoud就像品牌机,在SpringSource的整合下,做了大量的兼容性测试,保证了机器拥有更高的稳定性,但是如果要使用非原装组件外的东西,就需要对其基础有足够的了解。

社区支持与更新力度

最为重要的是,Dubbo停止了5年左右的更新,虽然2017年7月项目重启了,对于技术发展的新需求,需要有开发者自行拓展升级(比如当当网弄出DubboX),这对于很多想要采用微服务架构的中小软件组织,显然是不太合适的,中小公司没有这么强大的技术能力去修改Dubbo源码+周边的一整套解决方案,并不是每个公司都有阿里的大牛+真实的线上生成环境测试过。

总结

曾风靡国内开源RPC服务框架Dubbo在重启维护后,令许多用户为之雀跃,但同时,也迎来了一些质疑的声音,互联网技术发展迅速,Dubbo是否能够跟上时代?Dubbo与SpringCloud相比又有何优势和劣势?是否有相关举措保证Dubbo的后续更新频率?这些都是目前未知的。

2. Rest学习环境搭建:服务提供者

  • 新建Maven项目:springcloud,切记Packageing是pom模式
  • 主要是定义POM文件,将后续各个子模块公用的jar包等统一提取出来,类似一个抽象父类
  • 父级pom.xml
<!--打包方式pom-->
<packaging>pom</packaging>

<!--版本号-->
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <junit.version>4.12</junit.version>
    <lombok.version>1.18.12</lombok.version>
    <log4j.version>1.2.17</log4j.version>
</properties>

<dependencyManagement>
    <dependencies>
        <!--SpringCloud依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--SpringBoot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.4.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <!--数据连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--springboot启动器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!--junit测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
        <!--log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <!--日志门面-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
</dependencyManagement>
  • 新建一个module:springcloud-api
  • 配置pom.xml
<!--当前的Module自己需要的依赖,如果父依赖中已经配置了版本,就不用写了-->
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
  • 创建数据库:db01
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept`  (
  `d_no` bigint(20) NOT NULL AUTO_INCREMENT,
  `d_name` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `db_source` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`d_no`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '部门表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES (1, '开发部', 'blog_test');
INSERT INTO `dept` VALUES (2, '人事部', 'blog_test');
INSERT INTO `dept` VALUES (3, '财务部', 'blog_test');
INSERT INTO `dept` VALUES (4, '市场部', 'blog_test');
INSERT INTO `dept` VALUES (5, '运维部', 'blog_test');

SET FOREIGN_KEY_CHECKS = 1;
  • 创建pojo文件,添加Dept.java
@Data
@NoArgsConstructor
@Accessors(chain = true) // 链式写法
public class Dept implements Serializable {

    // Dept 实体类, orm 类表关系映射

    /**
     * 主键
     */
    private Long deptNo;

    /**
     * 部门名
     */
    private String deptName;

    /**
     * 数据来源
     * 同一个信息可能存在不同的数据库
     */
    private String dbSource;

    public Dept(String deptName) {
        this.deptName = deptName;
    }

    /*
    链式写法样例,持续向下赋值
    Dept dept = new Dept();
    dept.setDNo(11).setDName('ssss');
     */
}
  • 新建module:springcloud-provider-dept-8001
  • pom.xml配置
<dependencies>
    <!--需要拿到实体类,要配置api-module-->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>
    <!--logback-->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
    </dependency>
    <!--springboot启动器-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test</artifactId>
    </dependency>
    <!--web启动器-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--jetty,等价tomcat-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
    <!--热部署工具-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>
  • 配置application.yml
server:
  port: 8001

# mybatis配置
mybatis:
  type-aliases-package: com.example.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

# spring配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root
  • mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <setting name="cacheEnabled"             value="true" />  <!-- 全局映射器启用缓存 -->
        <setting name="useGeneratedKeys"         value="true" />  <!-- 允许 JDBC 支持自动生成主键 -->
        <setting name="defaultExecutorType"      value="REUSE" /> <!-- 配置默认的执行器 -->
        <setting name="logImpl"                  value="SLF4J" /> <!-- 指定 MyBatis 所用日志的具体实现 -->
        <!-- <setting name="mapUnderscoreToCamelCase" value="true"/>  驼峰式命名 -->
    </settings>

</configuration>
  • 创建DeptMapper.java
@Mapper
public interface DeptMapper {

    /**
     * 添加一个部门
     * @param dept 部门实体
     * @return 添加结果
     */
    boolean addDepartment(Dept dept);

    /**
     * 通过id查部门
     * @param id 部门id
     * @return 部门信息
     */
    Dept queryDeptById(Long id);

    /**
     * 查询所有部门
     * @return 部门列表
     */
    List<Dept> queryAllDept();
}
  • 创建DeptMapper.xml映射文件
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.springcloud.mapper.DeptMapper">

    <resultMap id="deptResult" type="com.example.springcloud.pojo.Dept">
        <result column="d_no" property="deptNo"/>
        <result column="d_name" property="deptName"/>
        <result column="db_source" property="dbSource"/>
    </resultMap>

    <sql id="selectDept">
        select
            d_no,
            d_name,
            db_source
        from dept
    </sql>

    <insert id="addDepartment" parameterType="com.example.springcloud.pojo.Dept">
        insert into db01.dept (d_name, db_source)
            value (#{dName}, DATABASE())
    </insert>

    <select id="queryDeptById" parameterType="Long" resultMap="deptResult">
        <include refid="selectDept"/>
        where d_no = #{id}
    </select>

    <select id="queryAllDept" resultMap="deptResult">
        <include refid="selectDept"/>
    </select>
</mapper>
  • 创建DeptService.java
public interface DeptService {

    /**
     * 添加一个部门
     * @param dept 部门实体
     * @return 添加结果
     */
    boolean addDepartment(Dept dept);

    /**
     * 通过id查部门
     * @param id 部门id
     * @return 部门信息
     */
    Dept queryDeptById(Long id);

    /**
     * 查询所有部门
     * @return 部门列表
     */
    List<Dept> queryAllDept();
}
  • 创建DeptServiceImpl.java
@Service
public class DeptServiceImpl implements DeptService{

    @Autowired
    private DeptMapper deptMapper;

    /**
     * 添加一个部门
     *
     * @param dept 部门实体
     * @return 添加结果
     */
    @Override
    public boolean addDepartment(Dept dept) {
        return deptMapper.addDepartment(dept);
    }

    /**
     * 通过id查部门
     *
     * @param id 部门id
     * @return 部门信息
     */
    @Override
    public Dept queryDeptById(Long id) {
        return deptMapper.queryDeptById(id);
    }

    /**
     * 查询所有部门
     *
     * @return 部门列表
     */
    @Override
    public List<Dept> queryAllDept() {
        return deptMapper.queryAllDept();
    }
}
  • 创建DeptController.java
@RestController
@RequestMapping("/dept")
public class DeptController {

    /**
     * 注入服务
     */
    @Autowired
    private DeptService deptService;

    /**
     * 添加部门
     * @param dept 部门对象
     * @return 添加结果
     */
    @PostMapping("/add")
    public Boolean addDept(@RequestBody Dept dept) {
        return deptService.addDepartment(dept);
    }

    /**
     * 通过部门id查询部门
     * @param id 部门id
     * @return 部门信息
     */
    @GetMapping("/get/{id}")
    public Dept getDept(@PathVariable("id") Long id) {
        return deptService.queryDeptById(id);
    }

    /**
     * 查询所有部门
     * @return 部门列表
     */
    @GetMapping("/list")
    public List<Dept> queryAll() {
        return deptService.queryAllDept();
    }
}
  • 创建SpringBoot启动类:DeptProvider
@SpringBootApplication
public class DeptProvider {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider.class,args);
    }
}
  • 启动项目
  • 访问地址
    • GET:http://localhost:8001/dept/get/1
    • GET:http://localhost:8001/dept/list
    • POST:http://localhost:8001/dept/add
      • 参数:deptName

总结:服务提供者和正常的SpringBoot项目一样

3.Rest学习环境搭建:服务消费者

  • 创建新module:springcloud-consumer-dept-80
  • 配置pom.xml依赖
<!--实体类+web-->
<dependencies>
    <!--实体类-->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--web依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>
  • 配置application.yml
server:
  port: 80
  • 创建ConfigBean
@Configuration
public class ConfigBean {

    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
  • 创建DeptConsumerController
@RestController
@RequestMapping("/consumer/dept")
public class DeptConsumerController {

    /**
     * 理解:消费者不应该有service层
     * RestTemplate 供我们直接调用就可以了!注册到Spring中
     * (url, 实体:Map, Class<T> responseType)
     * 什么方式拿,去哪拿,拿什么类型东西
     * 提供多种便捷访问远程http服务的方法
     * 简单的restful服务模板
     */
    @Autowired
    private RestTemplate restTemplate;

    private static final String REST_URL_PREFIX = "http://localhost:8001/dept";

    /**
     * 客户端添加一个部门
     * @param dept 部门对象
     * @return 添加结果
     */
    @RequestMapping("/add")
    public boolean add(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX + "/add", dept, Boolean.class);
    }

    /**
     * 通过部门id获取部门信息
     * @param id 部门id
     * @return 部门信息
     */
    @RequestMapping("get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX + "/get/" + id, Dept.class);
    }

    /**
     * 查询所有部门
     * @return 部门列表
     */
    @RequestMapping("/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/list", List.class);
    }
}

总结:相对于消费者来说,他不需要进行业务处理,只需要知道服务接口是怎样的,通过RestTemplate 进行接口调用就好,需要注册到Spring中

(url, 实体:Map, Class responseType)

通过什么方式获取,去哪获取,获取什么类型

提供多种便捷访问远程http服务的方法

简单的restful服务模板