【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(二)Rest微服务工程搭建

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(二)Rest微服务工程搭建

1、简单架构示意图

两个模块,一个是服务提供者模块,另一个是服务消费者模块

2、创建Cloud-provider-payment8001 微服务提供者Module模块

2.1. 建Module

创建完成后回到父工程查看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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tigerhhzz.springcloud</groupId>
  <artifactId>springcloud2023</artifactId>
  <version>1.0-SNAPSHOT</version>
  <modules>
    <module>Cloud-provider-payment8001</module>
  </modules>
  
  <packaging>pom</packaging>
  <name>Maven</name>
  <!-- FIXME change it to the project's website -->
  <url>http://maven.apache.org/</url>
  <inceptionYear>2001</inceptionYear>
  <distributionManagement>
    <site>
      <id>website</id>
      <url>scp://webhost.company.com/www/website</url>
    </site>
  </distributionManagement>
  <!-- 统一管理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.13.2</junit.version>
    <log4j.version>1.2.17</log4j.version>
    <lombok.version>1.18.26</lombok.version>
    <mysql.version>8.0.32</mysql.version>
    <druid.version>1.2.16</druid.version>
    <mybatis.spring.boot.version>3.0.1</mybatis.spring.boot.version>
  </properties>
  <!-- 1、只是声明依赖,并不实际引入,子项目按需声明使用的依赖 -->
  <!-- 2、子项目可以继承父项目的 version 和 scope -->
  <!-- 3、子项目若指定了 version 和 scope,以子项目为准 -->
  <dependencyManagement>
    <dependencies>
      <!--spring boot 2.2.2-->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>3.0.5</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--spring cloud Hoxton.SR1-->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>2022.0.2</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--spring cloud alibaba 2.1.0.RELEASE-->
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2021.0.4.0</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>junit</artifactId>
        <version>${junit.version}</version>
      </dependency>
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <optional>true</optional>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <fork>true</fork>
          <addResources>true</addResources>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
2.2. 改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>springcloud2023</artifactId>
        <groupId>com.tigerhhzz.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>Cloud-provider-payment8001</artifactId>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <!--这个和web要写到一块-->
            <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>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
2.3. 写yml

编写application.yml配置文件

server:
  port: 8001
spring:
  application:
    name: cloud-provider-payment8001
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springcloud-tigerhhzz?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.dkf.springcloud.entities  # 所有Entity 别名类所在包

热部署:这里我使用的Jrebel插件。

2.4. 主启动
package com.tigerhhzz.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * @author Administrator
 */
@SpringBootApplication
public class PaymentMain8001 {
    public static void main(String[] args){
        SpringApplication.run(PaymentMain8001.class, args);
    }
}
2.5. 业务类

下面的常规操作:

  1. 建表SQL
create table `payment`(
    `id` bigint(20) not null auto_increment comment 'ID',
    `serial` varchar(200) default '',
    PRIMARY KEY (`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
select * from payment;

entities

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data   //set/get方法
@AllArgsConstructor //有参构造器
@NoArgsConstructor  //无参构造器
public class Payment implements Serializable {
  private long id;//数据库是bigint
  private String serial;
}

通用结果返回类实体:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//返回给前端的通用json数据串
@Data   //set/get方法
@AllArgsConstructor //有参构造器
@NoArgsConstructor  //无参构造器
public class CommonResult<T> {
  private Integer code;
  private String message;
  private T data; //泛型,对应类型的json数据
  //自定义两个参数的构造方法
  public CommonResult(Integer code, String message){
      this(code, message, null);
  }
}

dao

@Mapper // 是ibatis下面的注解 //@Repositoty有时候会有问题
public interface PaymentDao {
  
  int create(Payment payment);
  Payment getPaymentById(@Param("id") Long id);
}

resource下创建mapper文件夹,新建PaymentMapper.xml。在yml里有所有entity别名类所在包,所有payment不用写全类名

<?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.tigerhhzz.springcloud.dao.PaymentDao">
    <resultMap id="BaseResultMap" type="com.tigerhhzz.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <id 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>

@Param注解

service

public interface PaymentService {
  int create(Payment payment);
  Payment getPaymentById(@Param("id") Long id);
}
@Service
public class PaymentServiceImpl implements PaymentService {
  @Autowired
  private PaymentDao paymentDao;
  @Override
  public int create(Payment payment) {
      return paymentDao.create(payment);
  }
  @Override
  public Payment getPaymentById(Long id) {
      return paymentDao.getPaymentById(id);
  }
}

controller

@RestController   //必须是这个注解,因为是模拟前后端分离的restful风格的请求,要求每个方法返回 json
@Slf4j
public class PaymentController {
  @Resource
  private PaymentService paymentService;
  @PostMapping(value = "/payment/create")
  //      注意这里的 @RequestBody  是必须要写的,虽然 MVC可以自动封装参数成为对象,
  //      但是当消费者项目调用,它传参是 payment 整个实例对象传过来的, 即Json数据,因此需要写这个注解
  public CommonResult create(@RequestBody Payment payment){
      int result = paymentService.create(payment);
      log.info("****插入结果:" + result);
      if(result > 0){
          return new CommonResult(200, "插入数据库成功", result);
      }
      return new CommonResult(444, "插入数据库失败", null);
  }
  @GetMapping(value = "/payment/{id}")
  public CommonResult getPaymentById(@PathVariable("id")Long id){
      Payment result = paymentService.getPaymentById(id);
      log.info("****查询结果:" + result);
      if(result != null){
          return new CommonResult(200, "查询成功", result);
      }
      return new CommonResult(444, "没有对应id的记录", null);
  }
}

启动8001模块

对应POST方式的请求,要学会用POSTMAN工具

测试插入一条数据

微服务多了之后就使用run dashboard

不但编译有个别地方会报错,启动也会报错,但是测试两个接口都是没问题的,推测启动报错是因为引入了下面才会引入的jar包,目前不影响。

3、创建Cloud-consumer-order80 微服务消费者订单Module模块

3.1. 建Module

3.2. 改pom

修改本模块的pom文件如下:

3.3. 写yml

编写application.yml配置文件

<?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>springcloud2023</artifactId>
        <groupId>com.tigerhhzz.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>Cloud-consumer-order80</artifactId>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
3.4. 主启动

OrderMain80.java

package com.tigerhhzz.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OrderMain80{
    public static void main(String[] args){
        SpringApplication.run(OrderMain80.class,args);
    }
}
3.5. 业务类

entites包中的类也拷贝到本项目中

entities/CommonResult.java

entities/Payment.java

配置RestTemplate

ApplicationContextConfig 内容:

package com.tigerhhzz.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
 * @author tigerhhzz
 * @date 2023/4/8 22:52
 */
@Configuration
public class ApplicationContextConfig {
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
        /*
        RestTemplate提供了多种便捷访问远程http服务的方法,
        是一种简单便捷的访问restful服务模板类,是spring提供的用于rest服务的客户端模板工具集
        */
    }
}

controller

package com.tigerhhzz.springcloud.controller;
import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderController {
    //远程调用的 地址
    public static final String PAYMENY_URL = "http://localhost:8001";
    @Resource
    private RestTemplate restTemplate;
    @PostMapping("customer/payment/create")
    public CommonResult<Payment> create (Payment payment){
        return restTemplate.postForObject(PAYMENY_URL + "/payment/create",//请求地址
                                          payment,//请求参数
                                          CommonResult.class);//返回类型
    }
    @GetMapping("customer/payment/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id")Long id){
        return restTemplate.getForObject(PAYMENY_URL + "/payment/" + id,//请求地址
                                         CommonResult.class);//返回类型
    }
}

如果 runDashboard 控制台没有出来,右上角搜索 即可

运用spring cloud框架基于spring boot构建微服务,一般需要启动多个应用程序,在idea开发工具中,多个同时启动的应用

需要在RunDashboard运行仪表盘中可以更好的管理,但有时候idea中的RunDashboard窗口没有显示出来,也找不到直接的开启按钮

80调用8001服务测试成功:

4、工程重构

将两个微服务模块中公共的部分,例如实体类等放到新建的微服务公共模块中

上面 两个子项目,有多次重复的 导入 jar,和重复的 Entity 实体类。可以把 多余的部分,加入到一个独立的模块中,将这个模块打包,并提供给需要使用的 module

4.1 新建一个 cloud-api-commons 子模块

将 entities 包里面的实体类放到这个子模块中,也将 pom 文件中,重复导入的 jar包放到这个新建的 模块的 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>springcloud2023</artifactId>
        <groupId>com.tigerhhzz.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>Cloud-api-commons</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- 这个是新添加的,之前没用到,后面会用到。关于这个hutool 是个功能强大的工具包,官网:https://hutool.cn/ -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>
</project>
4.2 mvn跳过test,mvc clean,mvn install

将此项目打包 install 到 maven仓库。

将 提供者 和 消费者 两个项目中的 entities 包删除,并删除掉加入到 cloud-api-commons 模块的 依赖配置。

将 打包到 maven 仓库的 cloud-api-commons 模块,引入到 提供者 和 消费者的 pom 文件中,如下所示

<!-- 引入自己定义的api通用包 -->
      <dependency>
        <groupId>com.tigerhhzz.springcloud</groupId>
        <artifactId>Cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
        <scope>compile</scope>
      </dependency>

post测试插入数据成功

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
11天前
|
消息中间件 分布式计算 中间件
秀出天际!阿里甩出的988页分布式微服务架构进阶神仙手册我粉了
秀出天际!阿里甩出的988页分布式微服务架构进阶神仙手册我粉了
|
13天前
|
负载均衡 Java 网络架构
【SpringCloud】如何理解分布式、微服务、集群
【SpringCloud】如何理解分布式、微服务、集群
26 1
|
13天前
|
Java API Nacos
第十二章 Spring Cloud Alibaba Sentinel
第十二章 Spring Cloud Alibaba Sentinel
33 0
|
13天前
|
存储 前端开发 Java
第十一章 Spring Cloud Alibaba nacos配置中心
第十一章 Spring Cloud Alibaba nacos配置中心
30 0
|
13天前
|
消息中间件 SpringCloudAlibaba Java
第十章 SpringCloud Alibaba 之 Nacos discovery
第十章 SpringCloud Alibaba 之 Nacos discovery
11 1
|
13天前
|
NoSQL Java 关系型数据库
【Redis系列笔记】分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
144 2
|
13天前
|
存储 监控 NoSQL
【Redis】分布式锁及其他常见问题
【Redis】分布式锁及其他常见问题
45 0
|
13天前
|
NoSQL Java Redis
【Redis】Redis实现分布式锁
【Redis】Redis实现分布式锁
18 0
|
13天前
|
监控 NoSQL 算法
探秘Redis分布式锁:实战与注意事项
本文介绍了Redis分区容错中的分布式锁概念,包括利用Watch实现乐观锁和使用setnx防止库存超卖。乐观锁通过Watch命令监控键值变化,在事务中执行修改,若键值被改变则事务失败。Java代码示例展示了具体实现。setnx命令用于库存操作,确保无超卖,通过设置锁并检查库存来更新。文章还讨论了分布式锁存在的问题,如客户端阻塞、时钟漂移和单点故障,并提出了RedLock算法来提高可靠性。Redisson作为生产环境的分布式锁实现,提供了可重入锁、读写锁等高级功能。最后,文章对比了Redis、Zookeeper和etcd的分布式锁特性。
159 16
探秘Redis分布式锁:实战与注意事项
|
13天前
|
NoSQL Java 大数据
介绍redis分布式锁
分布式锁是解决多进程在分布式环境中争夺资源的问题,与本地锁相似但适用于不同进程。以Redis为例,通过`setIfAbsent`实现占锁,加锁同时设置过期时间避免死锁。然而,获取锁与设置过期时间非原子性可能导致并发问题,解决方案是使用`setIfAbsent`的超时参数。此外,释放锁前需验证归属,防止误删他人锁,可借助Lua脚本确保原子性。实际应用中还有锁续期、重试机制等复杂问题,现成解决方案如RedisLockRegistry和Redisson。