天天看点

SpringCloud(H版&alibaba)之基本框架篇SpringCloud(H版&alibaba)之基本框架篇什么是微服务SpringCloudSpringBoot和SpringCloud版本选择cloud组件的选择中文社区父工程Project构建父工程Pom文件微服务模块创建Rest微服务构建消费者订单模块工程重构

SpringCloud(H版&alibaba)之基本框架篇

什么是微服务

https://www.bilibili.com/video/BV18E411x7eT?p=14 视频内容

微服务和分布式架构的区别

https://blog.csdn.net/zhonglunsheng/article/details/83153451?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162042065216780255248036%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=162042065216780255248036&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-1-83153451.pc_search_result_no_baidu_js&utm_term=%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E5%92%8C%E5%88%86%E5%B8%83%E5%BC%8F%E6%9E%B6%E6%9E%84

微服务:

​ 提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级通信机制进行互相协作(通常是基于HTTP协议的RESTful API)。

每个服务都围绕着具体的基本业务进行构建,并且能够独立部署到生产环境、类生产环境等。另外,应当尽量避免统一的、集中式的服务管理机制。就具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。

分布式:

​ 分布式服务顾名思义服务是分散部署在不同的机器上的,一个服务可能负责几个功能,

生产环境下微服务肯定是分布式部署的,分布式部署的不一定是微服务架构,比如集群部署,它把相同应用非物质到不同的服务器上,但是逻辑上肯定还是单体应用。

SpringCloud

一站式微服务架构解决方案

服务的注册与发现

服务调用

服务熔断

负载均衡

服务降级

服务消息队列

配置中心管理

服务网关

服务监控

全链路追踪

自动化构建部署

服务定时任务调度操作

SpringBoot和SpringCloud版本选择

SpringBoot 全面升到2.0版本,当前最稳定版2.2.4,但是不见得最合适,因为要配合SpringCloud

SpringCloud 当前最新稳定的为H版

选型要看这两个之间的配合

H版必须配合SpringBoot 2.2.x

更详细的版本查看方法 https://start.spring.io/actuator/info

本次学习技术选型

​ cloud

​ Hoxton.SR1

​ boot

​ 2.2.2.RELEASE

​ cloud alibaba

​ 2.1.0.RELEASE

​ java

​ java8

​ Maven

​ 3.5及以上

​ Mysql

​ 5.7及以上

​ 题外话

​ 如果只用boot,就用最新,如果同时用boot和cloud,需要照顾cloud,由cloud决定boot版本

官网对于Cloud有推荐的搭配boot版本,不一定是最新的

cloud组件的选择

服务注册中心

Eureka 停止更新,停更不停用

Zookeeper

Consul

Nacos (强烈推荐)

服务调用

Ribbon (进入了维护状态)

LoadBalancer (更有前景,会取代ribbon)

服务调用2

Feign (挂了)

OpenFeign (推荐)

服务降级

Hystrix (停更)

resilience4j (国外更多用这个替代)

sentienl (国内推荐,alibaba的)

服务网关

Zuul (挂了)

Zuul2 (难产)

gateway (推荐)

服务的配置

Config (不推荐了)

Nacos (推荐)

服务总线

Bus (不推荐)

Nacos (推荐)

中文社区

https://www.bookstack.cn/read/spring-cloud-docs/docs-index.md

父工程Project构建

原则:约定 》配置》编码

IDEA新建project工作空间

总父工程

POM

微服务cloud创建步骤

1、New Project

​ Maven创建——》选site->apache.maven…site

2、聚合总父工程名字

3、Maven选版本

4、工程名字

5、字符编码

​ file encodings 选择 with NO BOM

​ default encoding for properties files: UTF-8

​ 后面记得勾选 transparent

6、注解生效激活

​ build --> Annotation processors

​ 记得勾选 Enable annotion processing 表示支持注解

7、java编译版本选8

​ compilier 选择 8

8、File Type过滤

​ Editor > > File Types >> ActionScript

​ 忽略 *.idea *.iml 等文件

父工程Pom文件

packaging 选 pom方式

<groupId>com.huawei.springcloud</groupId>
<artifactId>mscloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
           

统一jar包和版本号管理

<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>
    <log4j.version>1.2.17</log4j.version>
    <lombok.version>1.16.18</lombok.version>
    <mysql.version>5.1.47</mysql.version>
    <druid.version>1.1.16</druid.version>
    <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
  </properties>
           

本项目适用

<groupId>com.huawei.springcloud</groupId>
<artifactId>mscloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<!--统一jar包版本号管理-->
<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>
  <log4j.version>1.2.17</log4j.version>
  <lombok.version>1.16.18</lombok.version>
  <mysql.version>5.1.47</mysql.version>
  <druid.version>1.1.16</druid.version>
  <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>

<dependencyManagement>
  <dependencies>

    <dependency>
      <!--springboot 2.2.2-->
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.2.2.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Hoxton.SR1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>

    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
      <version>2.1.0.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>${druid.version}</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>${mybatis.spring.boot.version}</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>juint</artifactId>
      <version>${junit.version}</version>
    </dependency>

    <dependency>
      <groupId>com.github.kokorin.lombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
    </dependency>

  </dependencies>
</dependencyManagement>
           

dependencyManagement

父工程 pom中使用了后,子项目的会向上查找,应用父工程中的版本号,子项目中的不需要再写版本号

dependencyManagement里只是声明依赖,并不实际引入,因此子项目需要显示的声明需要用的依赖

微服务模块创建

1.建module

2.改POM

3.写YML

4.主启动

5.业务类

Rest微服务构建

构建一个支付模块

​ cloud-provider-payment8001 :微服务提供者支付Module模块

​ 建module,创建完毕后父工程Pom会有变化

子模块pom如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mscloud</artifactId>
        <groupId>com.huawei.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8001</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        
    </dependencies>

</project>
           

数据库

数据库的问题:

https://blog.csdn.net/yangbb123321/article/details/85621800?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162065620216780357257692%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162065620216780357257692&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-1-85621800.pc_search_result_no_baidu_js&utm_term=mysql+%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5+2003+ubuntu

2003问题解决

字符集选择

MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,为了节省空间,一般情况下使用utf8也就够了。

排序规则

utf8_general_ci 不区分大小写,这个你在注册用户名和邮箱的时候就要使用。
           

utf8_general_cs 区分大小写,如果用户名和邮箱用这个 就会造成不良后果

utf8_bin:字符串每个字符串用二进制数据编译存储。 区分大小写,而且可以存二进制的内容

utf8_general_ci校对速度快,但准确度稍差。

utf8_unicode_ci准确度高,但校对速度稍慢。

用一句话概况上面这段话:utf8_unicode_ci比较准确,utf8_general_ci速度比较快。通常情况下 utf8_general_ci的准确性就够我们用的了,在我看过很多程序源码后,发现它们大多数也用的是utf8_general_ci,所以新建数据 库时一般选用utf8_general_ci就可以了

原文链接:https://blog.csdn.net/weixin_42347415/article/details/113150081

写yml

放在resources下

文件名:application.yml 变绿色,有spring标志

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.huawei.springcloud.entities     # 所有Entities别名类所在包
  
           

主启动

@SpringBootApplication
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class, args);
    }
}
           

业务类

1.建表SQL

2.entities

​ 使用lombok 类型用包装类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private static final long serialVersionUID = 3589236030609749801L;
    private Long id;
    private String serial;
}
           

3.dao

推荐使用 @mapper

@Mapper
public interface PaymentDao {
    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") Long id);
}
           

mapper 文件的创建

resources 文件夹下创建 mapper

<?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.huawei.springcloud.dao.PaymentDao">

    <resultMap id="BaseResultMap" type="com.huawei.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial) values(#{serial});
    </insert>
    
    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        SELECT * FROM payment WHERE id=#{id};
    </select>

</mapper>
           

namespace对应Dao文件的位置

mapper下的xml对应yml文件中的mapperlocations

4.service

​ service 可以先写个接口,再写实现类,接口可以和dao的一致

impl 实现类

@Service
public class PaymentServiceImpl implements PaymentService {
    @Resource
    private PaymentDao paymentDao;

    @Override
    public int create(Payment payment) {
        return paymentDao.create(payment);
    }

    @Override
    public Payment getPaymentById(Long id) {
        return paymentDao.getPaymentById(id);
    }
}
           

5.controller

@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

    @PostMapping(value = "/payment/create")
    public CommonResult create(Payment payment) {
        int result = paymentService.create(payment);
        log.info("******插入结果:"+ result);
        if (result > 0){
            return new CommonResult(200,"插入数据库成功",result);
        } else {
            return new CommonResult(406,"插入数据库失败",null);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id) {
        Payment paymentById = paymentService.getPaymentById(id);
        if (paymentById != null) {
            return new CommonResult(200,"查询成功", paymentById);
        } else {
            return new CommonResult(406,"没有对应记录,查询id: " + id, null);
        }
    }

}
           

lombok有集成slf4j

注意Restful风格接口的注解

返回给前端的结果包装类 CommonResult

返回的结果的统一类别,前端用于获取json

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String  message;
    private T       data;

    public CommonResult(Integer code, String message) {
        this(code,message,null);
    }
}
           

更好一点的:

/**
 * REST接口封装统一返回数据工具类
 */
public class Result {
 
    /**
     * 响应状态码
     */
    private Integer code;
    /**
     * 响应成功与否
     */
    private boolean success;
    /**
     * 响应消息
     */
    private String msg;
    /**
     * 响应数据
     */
    private Object data;
 
    public Integer getCode() {
        return code;
    }
 
    public void setCode(Integer code) {
        this.code = code;
    }
 
    public boolean isSuccess() {
        return success;
    }
 
    public void setSuccess(boolean success) {
        this.success = success;
    }
 
    public String getMsg() {
        return msg;
    }
 
    public void setMsg(String msg) {
        this.msg = msg;
    }
 
    public Object getData() {
        return data;
    }
 
    public void setData(Object data) {
        this.data = data;
    }
 
    public Result() {
    }
 
    public Result(Integer code, boolean success, String msg) {
        this.code = code;
        this.success = success;
        this.msg = msg;
    }
 
    public Result(Integer code, boolean success, String msg, Object data) {
        this.code = code;
        this.success = success;
        this.msg = msg;
        this.data = data;
    }
 
    /**
     * 成功 返回默认成功信息
     *
     * @return
     */
    public static Result SUCCESS() {
        return new Result(1, true, "操作成功", null);
    }
 
    /**
     * 成功 返回(data数据)成功信息
     *
     * @param data
     * @return
     */
    public static Result SUCCESS(Object data) {
        return new Result(1, true, "操作成功", data);
    }
 
    /**
     * 成功 返回自定义(消息、data数据)成功信息
     *
     * @param msg
     * @param data
     * @return
     */
    public static Result SUCCESS(String msg, Object data) {
        return new Result(1, true, msg, data);
    }
 
    /**
     * 失败 返回默认失败信息
     *
     * @return
     */
    public static Result ERROR() {
        return new Result(-1, false, "操作失败", null);
    }
 
    /**
     * 失败 返回自定义(消息)失败信息
     *
     * @param msg
     * @return
     */
    public static Result ERROR(String msg) {
        return new Result(-1, false, msg, null);
    }
 
    /**
     * 失败 返回自定义(消息、状态码)失败信息
     *
     * @param code
     * @param msg
     * @return
     */
    public static Result ERROR(Integer code, String msg) {
        return new Result(code, false, msg, null);
    }
 
}
           

热部署Devtools

开发阶段使用,生产阶段必须去掉

1.第一步

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
           

加入依赖,在子模块里

2.添加插件 父工程pom里

<build>
	<finalName>你自己的工程名字</finalName>
	<plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <configuration>
            <fork>true</fork>
            <addResources>true</addResources>
          </configuration>
        </plugin>
</build>
           

3.开启自动编译选项

Settings —> Build 下 Compiler 的

选项里 连着的 A D B C四个全部打钩

4.第四步比较繁琐

在父工程的pom里, ctrl + shft + alt + / 四个快捷键

打开的选项里 点击 1.Registry…

勾选 compiler.automake.allow.when.app.running

和 actionSystem.assertFocusAccessFromEdt 两个

5.重启idea

消费者订单模块

还是那几步,1.建module 2. 改pom 3.写yml 4.主启动 5.业务类

pom主要的几个组件 spring-boot-starter-web spring-boot-start-actuator 必备

还有 spring-boot-starter-test lombok devtools 几个标配的开发用的类

写yml :

写个端口 就完事

主启动类,照着改

业务类:

​ 因为消费者订单模块就是用来调用别的模块,前面的支付模块,所以核心是怎么去跨服务调用,不是去数据库取数据,就不用service和dao了

RestTemplate

​ RestTemplate 用于调用别的服务接口

配置类

@Configuration
public class ApplicationContextConfig {

    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}
           

订单模块的接口,调用支付模块

@RestController
@Slf4j
public class OrderController {

    public static final String PAYMENT_URL = "http://localhost:8001";

    @Resource
    private RestTemplate restTemplate;

    @PostMapping("/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
    }
    
    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, CommonResult.class);
    }
}
           

浏览器输入 http://localhost/consumer/payment/get/1

获取结果 {“code”:200,“message”:“查询成功”,“data”:{“id”:1,“serial”:“xiba”}}

@RestController
@Slf4j
public class OrderController {

    public static final String PAYMENT_URL = "http://localhost:8001";

    @Resource
    private RestTemplate restTemplate;

    @PostMapping("/consumer/payment/create")
    public CommonResult<Payment> create(@RequestBody Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, CommonResult.class);
    }
}
           

@RequestBody注解的问题,

当PaymentController 和OrderController 都加上 @RequestBody注解时,浏览器测不了,因为是post,postman需要在body中输入参数,并且得是json格式

浏览器不支持post格式

当只有PaymentController加@RequestBody注解时,浏览器依旧测不了,postman此时可以不用body格式,用params也可以

当两个接口都不加@RequestBody时,浏览器测不了,postman可以插入,有记录但是为空,没有数据

因为PaymentController需要接受包装类,需要有这个注解

所以最好每个都加,方便别的接口调用,测试时用postman,然后用body来写参数,并用json格式

打开Run Dashboard

​ View-----> Tool Windows -----> 选择Run Dashboard

如果没有,就在项目的 .ideal 下打开 workspace 文件,加入如下内容

<component name="RunDashboard">
    <option name="configurationTypes">
      <set>
        <option value="SpringBootApplicationConfigurationType" />
      </set>
    </option>
    <option name="ruleStates">
      <list>
        <RuleState>
          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
        </RuleState>
        <RuleState>
          <option name="name" value="StatusDashboardGroupingRule" />
        </RuleState>
      </list>
    </option>
  </component>
           

工程重构

1.观察问题:系统中有重复的部分,(每个微服务里都有entities部分),需要重构

2、如果重构:

​ 将相同相似的部分提到一个公共的模块里面

​ cloud-api-commons

​ 不仅是实体类,工具类和相同的代码都可以放在这里面

通用模块

1.建子模块

2.改pom

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.1.0</version>
    </dependency>
</dependencies>
           

3.实体类,直接粘贴

4.maven clean install,注意,包名名必须完全一致,

然后将通用模块部分进行 maven clean install,这样就将通用模块安装进去了,

之后其他模块就可以直接引用了

<dependency>
    <groupId>com.huawei.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
           

或者

<dependency>
    <groupId>com.huawei.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>