微服务架构概述
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相协作(通常是基于HTTP协议的RESTful API)。每个服务都围绕着具本业务进行构建,并且能够独立的部署到生产环境,类生产环境等。另外,应当尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言,工具对其进行构建。
微服务架构是一堆小服务的集合
每一个服务都可以作为一个单独的进程来运行
微服务的建模是围绕业务逻辑进行的
服务是独立部署的
微服务是去中心化的,也就是说是分布式的
SpringCloud
SpringCloud是分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶。SpringCloud已成为微服务开发的主流技术栈。
SpringCloud版本目前最新为H版
SpringCloud和SpringBoot的版本选择
此次项目的技术版本
SpringCloud各种组件的停更升级替换
项目环境搭建 父工程项目搭建 搭建一个Maven项目,作为Maven的父工程项目。删除src目录
添加项目依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 <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 > <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 > <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 > 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 >
支付模块构建 在父工程中新建支付Modulecloud-provider-payment8001
,同样是Maven项目
pom 添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <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.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency > <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 >
配置文件 新建编写配置文件application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 server: port: 8001 spring: application: name: cloud-payment-service datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: kylin mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.kylin.entities
主启动类 创建com.kylin
包在包下创建SpringBoot启动类PaymentMain8001
1 2 3 4 5 6 @SpringBootApplication public class PaymentMain8001 { public static void main (String[] args) { SpringApplication.run(PaymentMain8001.class, args); } }
数据库 1 2 3 4 5 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
自己插入数据
entities Payment
1 2 3 4 5 6 7 @Data @AllArgsConstructor @NoArgsConstructor public class Payment implements Serializable { private Long id; private String serial; }
CommonResult
1 2 3 4 5 6 7 8 9 10 11 12 13 @Data @AllArgsConstructor @NoArgsConstructor public class CommonResult <T > { private Integer code; private String message; private T data; public CommonResult (Integer code,String message) { this (code,message,null ); } }
Dao PaymentDao
1 2 3 4 5 6 7 8 @Mapper public interface PaymentDao { int create (Payment payment) ; Payment getPaymentId (@Param("id") Long id) ; }
PaymentMapper 分别按id查询和插入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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.kylin.dao.PaymentDao" > <insert id ="create" parameterType ="payment" useGeneratedKeys ="true" keyColumn ="id" > insert into payment(serial) values (#{serial}) </insert > <resultMap id ="BaseResultMap" type ="payment" > <id column ="id" property ="id" jdbcType ="BIGINT" /> <id column ="serial" property ="serial" jdbcType ="VARCHAR" /> </resultMap > <select id ="getPaymentId" resultMap ="BaseResultMap" parameterType ="Long" > select * from payment where id = #{id} </select > </mapper >
Service PaymentService
1 2 3 4 5 6 public interface PaymentService { int create (Payment payment) ; Payment getPaymentId (Long id) ; }
PaymentService实现类PaymentServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Service public class PaymentServiceImpl implements PaymentService { @Resource PaymentDao paymentDao; @Override public int create (Payment payment) { return paymentDao.create(payment); } @Override public Payment getPaymentId (Long id) { return paymentDao.getPaymentId(id); } }
Controller PaymentController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @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(444 ,"插入数据库失败" ,null ); } } @GetMapping(value = "/payment/get/{id}") public CommonResult getPaymentById (@PathVariable("id") Long id) { Payment payment = paymentService.getPaymentById(id); log.info("*****查询结果:" +payment); if (payment!=null ){ return new CommonResult(200 ,"查询成功" ,payment); }else { return new CommonResult(444 ,"没有对应记录,查询ID:" +id,null ); } } }
测试 在主启动类中启动项目进行测试查询操作http://localhost:8001/payment/get/31
由于插入操作是发送的post请求,浏览器默认不支持地址栏发送post请求所以使用Postman发送post请求
发生post请求成功
查看数据库
全部测试成功,代码无误~
消费者订单模块构建 创建cloud-consumer-order80
模块
pom 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <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 >
配置文件
主启动类
创建com.kylin
包在包下创建主启动类OrderMain80
1 2 3 4 5 6 7 @SpringBootApplication public class OrderMain80 { public static void main (String[] args) { SpringApplication.run(OrderMain80.class,args); } }
entities 将支付模块cloud-provider-payment8001
中的entities包拷贝
Controller 编写OrderController,由于要使用消费者模块调用支付模块。我们这里使用Resttemplate发送请求调用。
传统情况下在java代码里访问restful服务,一般使用Apache
的HttpClient
。不过此种方法使用起来太过繁琐。spring提供了一种简单便捷的模板类来进行操作,这就是RestTemplate
。RestTemplate
提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
使用前将其注入到Spring容器中,使用RestTemplate访问restful接口非常的简单,(url,requestMap,ResponseBean.class)
这三个参数分别代表REST请求地址,请求参数,HTTP响应转换被转换的对象类型。
创建config包,编写ApplicationContextConfig
在下面注入RestTemplate
1 2 3 4 5 6 7 8 @Configuration public class ApplicationContextConfig { @Bean public RestTemplate getRestTemplate () { return new RestTemplate(); } }
此时在继续编写OrderController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @RestController @Slf4j public class OrderController { public static final String PAYMENT_URL = "http://localhost:8001" ; @Resource RestTemplate restTemplate; @GetMapping("/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); } }
测试 再启动OrderMain80
和PaymentMain80
此时idea会弹出一个提示是否启用Services
选择启用,方便管理多个服务之间的启动关闭。启动成功后发送请求查看效果
发现插入数据为空!
由于RestTemplate.postForObject发送的是Json格式的字符,所以需要@RequestBody注解接收。
重启项目查看效果,成功!
工程重构 通过构建项目的基础环境,我们发现我们的项目中存在重复的部分。也就是我们的的entities
我们将他抽取成一个模块cloud-api-commons
,新建一个模块
pom 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <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 >
将entities
下的类复制过来
接着将这个模块clean,install
接着分别将我们之前的两个项目中的entities
包删除
分别修改项目的pom.xml引入cloud-api-commons
,坐标修改为自己的,记得导入依赖。
1 2 3 4 5 <dependency > <groupId > com.kylin</groupId > <artifactId > cloud-api-commons</artifactId > <version > 1.0-SNAPSHOT</version > </dependency >
测试一下功能是否正常,一切正常!
SpringCloud学习的基础环境搭建完成!