?一.前期準備
1.1開發環境安裝
?Mysql安裝版本8.0即可
1.2分析需求

1.3服務拆分原則
1.3.1單?職責原則
組織團隊也是, ?個?專注做?件事情的效率遠高于同時關注多件事情
?比如電商系統:
1.3.2服務自治

1.3.3單向依賴
- 循環依賴: A -> B -> C ->A
- 雙向依賴: A -> B, B ->?A
如果?些場景確實無法避免循環依賴或者雙向依賴, 可以考慮使用消息隊列等其他方式來實現
補充:
1.4設置需求
- 訂單列表
- 商品信息
二.項目搭建
Spring Cloud 是基于SpringBoot搭建的, 所以Spring Cloud 版本與SpringBoot版本有關?
?該項?中使?的SpringBoot 版本為 3.1.6, 對應的Spring Cloud版本應該為2022.0.x, 選擇任?就可以
2.1數據準備
?根據服務自治原則, 每個服務都應有自己獨立的數據庫
-- 訂單服務-- 建庫
create database if not exists cloud_order charset utf8mb4;use cloud_order;
-- 訂單表
DROP TABLE IF EXISTS order_detail;
CREATE TABLE order_detail (`id` INT NOT NULL AUTO_INCREMENT COMMENT '訂單id',`user_id` BIGINT ( 20 ) NOT NULL COMMENT '用戶ID',`product_id` BIGINT ( 20 ) NULL COMMENT '產品id',`num` INT ( 10 ) NULL DEFAULT 0 COMMENT '下單數量',`price` BIGINT ( 20 ) NOT NULL COMMENT '實付款',`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( id )) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '訂單表';-- 數據初始化
insert into order_detail (user_id,product_id,num,price)
values
(2001, 1001,1,99), (2002, 1002,1,30), (2001, 1003,1,40),
(2003, 1004,3,58), (2004, 1005,7,85), (2005, 1006,7,94);
產品服務數據庫信息:?
-- 產品服務
create database if not exists cloud_product charset utf8mb4;-- 產品表
use cloud_product;
DROP TABLE IF EXISTS product_detail;
CREATE TABLE product_detail (`id` INT NOT NULL AUTO_INCREMENT COMMENT '產品id',`product_name` varchar ( 128 ) NULL COMMENT '產品名稱',`product_price` BIGINT ( 20 ) NOT NULL COMMENT '產品價格',`state` TINYINT ( 4 ) NULL DEFAULT 0 COMMENT '產品狀態 0-有效 1-下架',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( id )) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '產品表';-- 數據初始化
insert into product_detail (id, product_name,product_price,state)
values
(1001,"T恤", 101, 0), (1002, "短袖",30, 0), (1003, "短褲",44, 0),
(1004, "衛衣",58, 0), (1005, "馬甲",98, 0),(1006,"羽絨服", 101, 0),
(1007, "沖鋒衣",30, 0), (1008, "襪子",44, 0), (1009, "鞋子",58, 0),
(10010, "毛衣",98, 0);
2.2工程搭建
?項目創建目前有兩種:
- 使用JavaEE的方式,使用IDEA分別創建兩個項目,一個項目一個窗口(麻煩)
- 采用父子工程的方式搭建(推薦)
1.創建父工程?
2.刪除src文件(因為不需要編寫代碼)
3.完善pom.xml文件信息
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.6</version><relativePath/> <!-- lookup parent from repository --></parent><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><java.version>17</java.version><mybatis.version>3.0.3</mybatis.version><mysql.version>8.0.33</mysql.version><spring-cloud.version>2022.0.3</spring-cloud.version></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.version}</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql.version}</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter-test</artifactId><version>${mybatis.version}</version><scope>test</scope></dependency></dependencies></dependencyManagement>
補充:?
pom文件DependencyManagement 和 Dependencies區別:?
dependencies :將所依賴的jar直接加到項?中. ?項?也會繼承該依賴?
4.創建兩個子項目(order-service , product-service)
??
?
?此時父項目的pom文件就會多出modul
5.給order.pom文件和 product.pom文件添加必要的依賴
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins><resources><resource><directory>src/main/resources</directory><filtering>true</filtering><includes><include>**/**</include></includes></resource></resources>
</build>
?2.3編寫order服務
?1.給order子項目創建order包 - 編寫啟動類
package Order;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class,args);}
}
2.編寫application.yml配置文件
開始編寫業務代碼
訂單服務:
- 根據訂單ID,返回訂單詳情
3.先寫實體類
package order.model;import lombok.Data;import java.util.Date;@Data
public class OrderInfo {private Integer id;private Integer userId;private Integer productId;private Integer num;private Integer price;private Integer deleteFlag;private Date createTime;private Date updateTime;
}
4.編寫mapper接口?
package order.mapper;import order.model.OrderInfo;
import org.apache.ibatis.annotations.Select;public interface OrderMapper {@Select("select * from order_detail where id = #{orderId}")OrderInfo selectOrderById(Integer orderId);//先聲明方法
}
5,編寫service Service主要也是從mapper接口調用訂單的信息
package order.service;import order.mapper.OrderMapper;
import order.model.OrderInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;//注入這個接口public OrderInfo selectOrderById(Integer orderId){return orderMapper.selectOrderById(orderId);}
}
6.編寫controller 接口
package order.controller;import order.model.OrderInfo;
import order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("order")
@RestController
public class OrderController {@Autowiredprivate OrderService orderService;@RequestMapping("/{orderId}")public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){return orderService.selectOrderById(orderId);}
}
啟動項目失敗,
原因:忘記在mapper接口添加@Mapper注解
?7.項目啟動成功耶耶
2.4編寫product服務
給product子項目也跟上述操作類似(代碼就不粘貼了)
1.編寫啟動類
?2.配置yml文件(注意修改端口號成9090,數據庫名也要修改)
開始編寫業務代碼:
- 根據商品ID,返回商品信息
?1.編寫實體類信息
2.編寫返回商品信息接口
?
3.編寫service層
4.編寫controller控制層
?5.啟動項目成功
?2.5遠程調用
實現兩個子項目進行交互,因為訂單信息里面肯定需要商品信息嘛
根據訂單查詢訂單信息時,根據訂單里產品ID,獲取產品的詳細信息
實現思路: order-service服務向product-service服務發送?個http請求, 把得到的返回結果, 和訂單結果融合在?起, 返回給調用方.
實現方式: 采用Spring 提供的RestTemplate?
1.在將product的實體類復制到order的實體類,并在order實體類添加product類信息
2.創建config包 -? 創建HTTP對象 -?定義RestTemplate
3.在servicec層使用http對象調用 ?
package order.service;import order.mapper.OrderMapper;
import order.model.OrderInfo;
import order.model.ProductInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;//注入這個接口@Autowiredprivate RestTemplate restTemplate;public OrderInfo selectOrderById(Integer orderId){OrderInfo orderInfo = orderMapper.selectOrderById(orderId);String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();ProductInfo productInfo = restTemplate.getForObject(url,ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
}
4.兩個項目都要啟動,然后調用order路徑就可以返回product的信息了
三.RestTemplate介紹
?3.1什么是REST?
?REST(Representational State Transfer), 表現層資源狀態轉移.
可以把 REST 想象成一個大家都遵循的規則手冊,讓不同的軟件、系統之間能夠順暢地 “交流” 和 “合作”,就像人們說同一種語言能更好地溝通一樣。
這里面主要有三個概念:
- 資源: ?絡上的所有事物(文字,圖片,視頻等等)都可以抽象為資源, 每個資源都有?個唯?的資源標識符(URI)
- 表現層: 資源的表現形式, ?如?本作為資源, 可以?txt格式表現, 也可以通過HTML, XML, JSON等格式來表現, 甚?以?進制的格式表現.
- 狀態轉移: 訪問URI, 也就是客?端和服務器的交互過程. 客?端?到的?段,只能是HTTP協議. 這個過程中, 可能會涉及到數據狀態的變化. ?如對數據的增刪改查, 都是狀態的轉移
?REST 是?種設計?格, 指資源在?絡中以某種表現形式進?狀態轉移
3.2什么是RESTful??
?RESTful 風格大致有以下?個主要特征:
- 資源: 資源可以是?個圖?, ?頻, 視頻或者JSON格式等?絡上的?個實體, 除了?些?進制的資源外普通的?本資源更多以JSON為載體、?向??的?組數據(通常從數據庫中查詢?得到)
- 統?接?: 對資源的操作. ?如獲取, 創建, 修改和刪除. 這些操作正好對應HTTP協議提供的GET、POST、PUT和DELETE?法. 換??知,如果使?RESTful?格的接?, 從接?上你可能只能定位其資源,但是?法知曉它具體進?了什么操作,需要具體了解其發?了什么操作動作要從其HTTP請求?法類型上進?判斷
這些內容都是通過HTTP協議來呈現的. 所以RESTful是基于HTTP協議的,RestTemplate 是Spring提供, 封裝HTTP調用, 并強制使用RESTful風格. 它會處理HTTP連接和關閉,只需要使用者提供資源的地址和參數即可。
3.3RESTful實踐
?RESTful API 缺點:
- 操作?式繁瑣,?RESTful API通常根據GET, POST, PUT, DELETE 來區分對資源的操作動作. 但是HTTP Method 并不可直接?到, 需要通過抓包等?具才能觀察. 如果把動作放在URL上反?更加直觀, 更利于團隊的理解和交流.
- ?些瀏覽器對GET, POST之外的請求?持不太友好, 需要額外處理.
- 過分強調資源. ?實際業務需求可能?較復雜, 并不能單純使?增刪改查就能滿?需求, 強?使?RESTful API會增加開發難度和成本
3.4總結?
REST:
想象有一個超級大的 “信息超市”,里面有各種各樣的 “商品”,這些 “商品” 就是資源。比如說有水果類的資源(像蘋果、香蕉),電器類的資源(像電視、冰箱),REST 就是這個 “信息超市” 里大家都遵守的一套規則,有了它,不管是誰來超市(不管是哪種客戶端),也不管超市是誰開的(不管是哪個服務器),大家都能按照統一的方式順暢地交易 “商品”(交換信息)。?
RESTful:
如果說 REST 是規則,那 RESTful 就是嚴格遵守這個規則的 “好超市”。這個 “好超市” 里的每一個 “商品” 都有清晰準確的標簽(唯一的 URI),工作人員和顧客交流時也完全按照規定的方式來(使用標準 HTTP 方法)。
RESTful 實踐:
資源標識:
給每一本書都分配一個獨一無二的編號,在網絡里就是 URI。比如/books/1
?代表編號為 1 的書,這就像給書貼上了專屬標簽,方便大家查找。
HTTP 方法使用:
- GET:你在網頁上輸入
/books/1
?并發送請求,就像你在書店里跟工作人員說 “我想看看編號為 1 的書的信息”,服務器會返回這本書的詳細信息,如書名、作者、價格等。 - POST:你在網頁上填寫新書的信息并提交,就像你要把一本新書放到書店里賣。服務器接收到這個請求后,會創建一個新的圖書資源。
- PUT:你發現編號為 1 的書價格寫錯了,于是修改價格后再次提交,這就像你在書店里把書的價格標簽換了。服務器會根據你提供的新信息更新這本書的資源。
- DELETE:你覺得編號為 1 的書不再需要了,發送一個刪除請求,就像你把這本書從書店的貨架上拿走了。服務器會把對應的圖書資源刪除。
現在假設有一個在線書店,我們把它當作一個遵守 REST 規則的 “信息超市” 來實踐。
通過這些方式,在線書店就能高效地管理圖書信息,并且能和不同的用戶(客戶端)進行良好的交互,這就是 RESTful 實踐.
四.項目存在問題?
- 遠程調?時, URL的IP和端?號是寫死的(http://127.0.0.1:9090/product/), 如果更換IP, 需要修改代碼
- 調??如何可以不依賴服務提供?的IP? 如果多機部署, 如何分攤壓??
- 遠程調?時, URL非常容易寫錯, ?且復?性不?, 如何優雅的實現遠程調?
- 所有的服務都可以調用該接?, 是否有風險?
?微服務架構還面臨很多問題, 接下來我們學習如何使用Spring Cloud 來解決這些問題