大体了解微服务概念之后,接下里使用一个简单案例,让你10分钟入门微服务开发。
为了方便讲解SpringCloud课程,我们以电商项目2个核心模块:商品模块、订单模块为例子,一一讲解SpringCloud组件的使用。
学习SpringCloud组件要诀:不求甚解
1>能解决啥问题
2>怎么解决(理解原理)
3>API调用(代码怎么写)--建议写3遍--【1遍抄全,2遍思考,3遍掌握】
4>总结,开口表述
5>类比传统的代码结构
微服务核心:将完整项目按功能分类拆分成n个子项目/子模块,这些子模块能对外提供对应的功能。我们称这些服务为微服务
落地到代码:单看子项目,每个子项目就是一个完整项目(springmvc项目)----记住没啥高大上的(战略上藐视,战术上重视)
商品微服务
对外提供查询商品列表接口
对外提供查询某个商品信息接口
订单微服务
对外提供创建订单接口
以下单为例子:客户向订单微服务发起一个下单的请求,在进行保存订单之前需要调用商品微服务查询商品的信息。
一般把调用方称为服务消费者,把被调用方称为服务提供者。
上例中,订单微服务就是服务消费者, 而商品微服务是服务提供者。
持久层: MyBatis-Plus
数据库: MySQL5.7
其他: SpringCloud Alibaba 技术栈,SpringCloud Hoxton.SR8
注意:往下学习前,希望你有mysql,mybatis,maven,java基础,原因:接下来的学习要用到
需求:使用微服务方式实现用户下单
分析:根据微服务规则,拆分成商品服务,订单服务。
--- shop-parent 父工程
--- shop-product-api 商品微服务api 【存放商品实体】
--- shop-product-server 商品微服务 【端口:808x】
--- shop-order-api 订单微服务api 【存放订单实体】
--- shop-order-server 订单微服务 【端口:809x】
shop-product-server:子项目-商品微服务,对外提供查询商品信息的接口
shop-order-server:子项目-订单微服务,对外提供创建订单的接口
shop-product-api / shop-order-api : 各自微服务依赖的实体类,为啥要拆开?答案是:解耦
创建shop-parent一个maven工程,目的用于管理项目最核心的依赖
1>创建maven项目shop-parent
2>在项目pom.xml文件中导入下面的依赖
- "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.0modelVersion>
- <groupId>com.langfeiyesgroupId>
- <artifactId>shop-parentartifactId>
- <version>1.0.0version>
- <packaging>pompackaging>
-
- <parent>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-parentartifactId>
- <version>2.3.2.RELEASEversion>
- parent>
-
- <properties>
- <java.version>1.8java.version>
- <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
- <spring-cloud.version>Hoxton.SR8spring-cloud.version>
- <spring-cloud-alibaba.version>2.2.3.RELEASEspring-cloud-alibaba.version>
- properties>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-dependenciesartifactId>
- <version>${spring-cloud.version}version>
- <type>pomtype>
- <scope>importscope>
- dependency>
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-alibaba-dependenciesartifactId>
- <version>${spring-cloud-alibaba.version}version>
- <type>pomtype>
- <scope>importscope>
- dependency>
- dependencies>
- dependencyManagement>
- project>
创建shop-product-api项目, 存放商品的实体类
0>建成之后代码结构
1>选择shop-parent,创建其子项目:shop-product-api
2>在项目pom.xml文件导入依赖
- "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>shop-parentartifactId>
- <groupId>com.langfeiyesgroupId>
- <version>1.0.0version>
- parent>
- <modelVersion>4.0.0modelVersion>
-
- <artifactId>shop-product-apiartifactId>
-
- <dependencies>
- <dependency>
- <groupId>com.baomidougroupId>
- <artifactId>mybatis-plus-boot-starterartifactId>
- <version>3.4.0version>
- dependency>
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- dependency>
- dependencies>
-
- project>
3>创建实体类
- //商品
- @Getter
- @Setter
- @ToString
- @TableName("t_product")
- public class Product implements Serializable {
- @TableId(type= IdType.AUTO)
- private Long id;//主键
- private String pname;//商品名称
- private Double pprice;//商品价格
- private Integer stock;//库存
- }
4>创建shop-product-server项目
5>在shop-product-server项目pom.xml文件中导入xml依赖
- "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>shop-parentartifactId>
- <groupId>com.langfeiyesgroupId>
- <version>1.0.0version>
- parent>
- <modelVersion>4.0.0modelVersion>
-
- <artifactId>shop-product-serverartifactId>
- <dependencies>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- dependency>
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>fastjsonartifactId>
- <version>1.2.56version>
- dependency>
-
- <dependency>
- <groupId>com.langfeiyesgroupId>
- <artifactId>shop-product-apiartifactId>
- <version>1.0.0version>
- dependency>
- dependencies>
- project>
6>在shop-product-server项目resources文件夹创建application.yml文件
- server:
- port: 8081
- spring:
- application:
- name: product-service
- datasource:
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql:///shop-product?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
- username: root
- password: admin
7>在数据库中创建shop-order的数据库
- CREATE TABLE `t_product` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
- `name` varchar(255) DEFAULT NULL COMMENT '商品名称',
- `price` double(10,2) DEFAULT NULL COMMENT '商品价格',
- `stock` int DEFAULT NULL COMMENT '库存',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
- INSERT INTO t_product VALUE(NULL,'小米','1000','5000');
- INSERT INTO t_product VALUE(NULL,'华为','2000','5000');
- INSERT INTO t_product VALUE(NULL,'苹果','3000','5000');
- INSERT INTO t_product VALUE(NULL,'OPPO','4000','5000');
8>创建ProductMapper
- package com.langfeiyes.mapper;
-
- import com.langfeiyes.domain.Product;
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
- public interface ProductMapper extends BaseMapper
{ - }
9>创建ProductService接口和实现类
- package com.langfeiyes.service;
-
- import com.langfeiyes.domain.Product;
- import com.baomidou.mybatisplus.extension.service.IService;
-
- public interface IProductService extends IService
{ - }
- package com.langfeiyes.service.impl;
-
- import com.langfeiyes.domain.Product;
- import com.langfeiyes.mapper.ProductMapper;
- import com.langfeiyes.service.IProductService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import org.springframework.stereotype.Service;
-
- @Service
- public class ProductServiceImpl extends ServiceImpl
implements IProductService { - }
10>创建Controller
- package com.langfeiyes.controller;
- @RestController
- @Slf4j
- public class ProductController {
- @Autowired
- private ProductService productService;
- //商品信息查询
- @RequestMapping("/product/{pid}")
- public Product findByPid(@PathVariable("pid") Long pid) {
- log.info("接下来要进行{}号商品信息的查询", pid);
- Product product = productService.findByPid(pid);
- log.info("商品信息查询成功,内容为{}", JSON.toJSONString(product));
- return product;
- }
- }
11>编写启动类ProductServer.java
- package com.langfeiyes;
-
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
-
- @SpringBootApplication
- @MapperScan("com.langfeiyes.mapper")
- public class ProductServer {
- public static void main(String[] args) {
- SpringApplication.run(ProductServer.class,args);
- }
- }
10.通过浏览器访问服务
创建订单服务跟商品服务一样,就省略相同结构
0>建成之后项目结构
1>创建shop-order-api项目,然后在pom.xml文件中添加下面内容
- "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>shop-parentartifactId>
- <groupId>com.langfeiyesgroupId>
- <version>1.0.0version>
- parent>
- <modelVersion>4.0.0modelVersion>
-
- <artifactId>shop-order-apiartifactId>
-
- <dependencies>
- <dependency>
- <groupId>com.baomidougroupId>
- <artifactId>mybatis-plus-boot-starterartifactId>
- <version>3.4.0version>
- dependency>
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- dependency>
- dependencies>
-
- project>
2>创建实体类
- package com.langfeiyes.domain;
-
- import com.baomidou.mybatisplus.annotation.IdType;
- import com.baomidou.mybatisplus.annotation.TableId;
- import lombok.Getter;
- import lombok.Setter;
-
- import java.io.Serializable;
-
- //订单
- @Getter
- @Setter
- @ToString
- @TableName("t_order")
- public class Order implements Serializable {
-
- @TableId(type = IdType.AUTO)
- private Long id;//订单id
- //用户
- private Long uid;//用户id
- private String username;//用户名
- //商品
- private Long pid;//商品id
- private String productName;//商品名称
- private Double productPrice;//商品单价
- //数量
- private Integer number;//购买数量
- }
3>创建shop-order-server项目,然后在pom.xml文件中添加下面内容
- "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>shop-parentartifactId>
- <groupId>com.langfeiyesgroupId>
- <version>1.0.0version>
- parent>
- <modelVersion>4.0.0modelVersion>
-
- <artifactId>shop-order-serverartifactId>
- <dependencies>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- dependency>
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>fastjsonartifactId>
- <version>1.2.56version>
- dependency>
- <dependency>
- <groupId>com.langfeiyesgroupId>
- <artifactId>shop-order-apiartifactId>
- <version>1.0.0version>
- dependency>
- dependencies>
- project>
4>编写配置文件application.yml
- server:
- port: 8091
- spring:
- application:
- name: order-service
- datasource:
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql:///shop-order?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
- username: root
- password: admin
5>在数据库中创建shop-order的数据库并创建t_order表
- CREATE TABLE `t_order` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
- `uid` bigint DEFAULT NULL COMMENT '用户id',
- `username` varchar(255) DEFAULT NULL COMMENT '用户名称',
- `pid` bigint DEFAULT NULL COMMENT '商品id',
- `product_name` varchar(255) DEFAULT NULL COMMENT '商品名称',
- `product_price` double(255,0) DEFAULT NULL COMMENT '商品单价',
- `number` int DEFAULT NULL COMMENT '购买数量',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
6>创建OrderMapper
- package com.langfeiyes.mapper;
-
- import com.langfeiyes.domain.Order;
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
- public interface OrderMapper extends BaseMapper
{ - }
7>创建OrderService接口和实现类
- package com.langfeiyes.service;
-
- import com.langfeiyes.domain.Order;
- import com.baomidou.mybatisplus.extension.service.IService;
-
- public interface IOrderService extends IService
{ - }
- package com.langfeiyes.service.impl;
-
- import com.langfeiyes.domain.Order;
- import com.langfeiyes.mapper.OrderMapper;
- import com.langfeiyes.service.IOrderService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import org.springframework.stereotype.Service;
-
- @Service
- public class OrderServiceImpl extends ServiceImpl
implements IOrderService { - }
8>创建Controller
- @RestController
- @RequestMapping("orders")
- public class OrderController {
- @Autowired
- private IOrderService orderService;
- @GetMapping("/save") //测试方便使用Get方式
- public Order order(Long pid, Long uid){
- return orderService.createOrder(pid, uid);
- }
- }
9>编写启动类OrderServer.java
- package com.langfeiyes;
-
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
-
- @SpringBootApplication
- @MapperScan("com.langfeiyes.mapper")
- public class OrderServer {
- public static void main(String[] args) {
- SpringApplication.run(OrderServer.class,args);
- }
- }
需求:定义createOrder方法,实现用户下订单
在shop-order-server项目的IOrderService 接口定义createOrder方法
- public interface IOrderService extends IService
{ - /**
- * 创建订单
- * @param pid
- * @param uid
- * @return
- */
- Order createOrder(Long pid, Long uid);
- }
OrderServiceImpl 实现类实现IOrderService 接口方法
- @Service
- public class OrderServiceImpl extends ServiceImpl
implements IOrderService { - @Override
- public Order createOrder(Long pid, Long uid) {
- Order order = new Order();
- //商品??
- Product product = null;
- order.setPid(pid);
- order.setProductName(product.getName());
- order.setProductPrice(product.getPrice());
-
- //用户
- order.setUid(1L);
- order.setUsername("dafei");
- order.setNumber(1);
- super.save(order);
- return order;
- }
- }
此时存在一个问题,Order-server服务创建订单操作需要配置商品信息,此时怎么办?
思考,谁能提供商品信息查询逻辑呢?答案:product-server, 问题来了,怎么调用?这里引入一个新问题:服务与服务间如何调用(交互)?
问题来了,怎么用java代码调用发起http接口调用嗯??答案是:RestTemplate
RestTempate 是SpringMVC提供专门用于访问http请求的工具类
1.在shop-order-server项目启动类上添加RestTemplate的bean配置
- @SpringBootApplication
- @MapperScan("com.langfeiyes.mapper")
- public class OrderServer {
- public static void main(String[] args) {
- SpringApplication.run(OrderServer.class,args);
- }
-
- @Bean
- public RestTemplate restTemplate(){
- return new RestTemplate();
- }
- }
2.在OrderServiceImpl中注入RestTemplate并实现远程调用
- @Service
- public class OrderServiceImpl extends ServiceImpl
implements IOrderService { -
- @Autowired
- private RestTemplate restTemplate;
-
- @Override
- public Order createOrder(Long pid, Long uid) {
- Order order = new Order();
- //商品
- //方案1:通过restTemplate方式
- String url = "http://localhost:8081/products/" + pid;
- Product product = restTemplate.getForObject(url, Product.class);
-
- order.setPid(pid);
- order.setProductName(product.getName());
- order.setProductPrice(product.getPrice());
-
- //用户
- order.setUid(1L);
- order.setUsername("dafei");
- order.setNumber(1);
- System.out.println(order);
- super.save(order);
- return order;
- }
- }
3>启动OrderServer服务器,访问: http://localhost:8091/orders/save?pid=1&uid=1
实现订单添加。
上面操作确实完成的服务间调用问题,但是代码很不优雅,存在着一定小瑕疵,比如:ip,端口变了呢?
一旦服务提供者(商品服务)地址变化,就需要手工修改代码
一旦是多个服务(商品服务)提供者,无法实现负载均衡功能
一旦服务变得越来越多,人工维护调用关系困难
那怎么办呢,请听下回分解~
看文字不过瘾可以切换视频版:SpringCloud Alibaba 极简入门