單體架構(gòu) VS 微服務(wù)架構(gòu)
1. 單體架構(gòu)
一個(gè)典型的單體應(yīng)用就是將所有的業(yè)務(wù)場(chǎng)景的表示層、業(yè)務(wù)邏輯層和數(shù)據(jù)訪問(wèn)層放在一個(gè)工程中,最終經(jīng)過(guò)編譯、打包,部署在一臺(tái)服務(wù)器上。
以電商系統(tǒng)為例,單體架構(gòu)設(shè)計(jì)方案如下圖所示:
單體架構(gòu)部署簡(jiǎn)單,可以直接部署在一個(gè)服務(wù)器上,并且技術(shù)單一,不需要復(fù)雜的技術(shù)棧,用人成本低。但由于一個(gè)進(jìn)程包含了所有的業(yè)務(wù)邏輯,因此系統(tǒng)可伸縮性差,由于涉及到的啟動(dòng)模塊過(guò)多,也可能會(huì)導(dǎo)致系統(tǒng)的啟動(dòng)時(shí)間過(guò)長(zhǎng)、可用性差等問(wèn)題。
2. 微服務(wù)架構(gòu)
微服務(wù)這一概念出現(xiàn)于2012年,微服務(wù)的基本思想在于考慮圍繞著業(yè)務(wù)領(lǐng)域組件來(lái)創(chuàng)建應(yīng)用,這些應(yīng)用可獨(dú)立地進(jìn)行開發(fā)、管理和加速。在分散的組件中使用微服務(wù)云架構(gòu)和平臺(tái),使部署、管理和服務(wù)功能交付變得更加簡(jiǎn)單。
微服務(wù)架構(gòu)風(fēng)格是一種將一個(gè)單一應(yīng)用程序開發(fā)為一組小型服務(wù)的方法每一個(gè)服務(wù)運(yùn)行在自己的進(jìn)程中服務(wù)間通信采用的輕量級(jí)通信機(jī)制通(常用HTTP資源API)。這些服務(wù)圍繞業(yè)務(wù)能力構(gòu)建并且可通過(guò)全自動(dòng)部署機(jī)制獨(dú)立部署這些服務(wù)公用一個(gè)最小型的集中式的管理服務(wù)可用不同的語(yǔ)言開發(fā),使用不同的數(shù)據(jù)存儲(chǔ)技術(shù)。
在微服務(wù)架構(gòu)中一個(gè)微服務(wù)只會(huì)關(guān)注一個(gè)特定的業(yè)務(wù)功能,所以它業(yè)務(wù)清晰、代碼量少,易于開發(fā)和維護(hù),各個(gè)微服務(wù)間技術(shù)棧不受限,可以按需收縮,可根據(jù)需求,實(shí)現(xiàn)細(xì)粒度的擴(kuò)展。
微服務(wù)架構(gòu)下電商系統(tǒng)的設(shè)計(jì)圖如下:
電商系統(tǒng)微服務(wù)實(shí)戰(zhàn)詳解
Spring Cloud 致力于提供微服務(wù)開發(fā)的一站式解決方案。它包含開發(fā)分布式應(yīng)用微服務(wù)的必需組件,依托 Spring Cloud Alibaba,只需要添加一些注解和少量配置,就可以將 Spring Cloud 應(yīng)用接入阿里微服務(wù)解決方案,通過(guò)阿里中間件來(lái)迅速搭建分布式應(yīng)用系統(tǒng)。
1. 創(chuàng)建服務(wù)注冊(cè)中心
1) 創(chuàng)建 EurekaServer 工程
2) Pom 文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
</dependencies>
</dependencyManagement>
3) 修改啟動(dòng)類
在Application類上添加注解@EnableEurekaServer 聲明注冊(cè)中心
4) 配置屬性文件
server.port=6868
eureka.instance.hostname=localhost
#表示是否將自己注冊(cè)到 Eureka Server,默認(rèn)為 true。
# 由于當(dāng)前這個(gè)應(yīng)用就是 Eureka Server,故而設(shè)為 false。
eureka.client.registerWithEureka=false。
#表示是否從 Eureka Server 獲取注冊(cè)信息,默認(rèn)為 true。
# 因?yàn)檫@是一個(gè)單點(diǎn)的 Eureka Server,不需要同步其他的 Eureka Server 節(jié)點(diǎn)的數(shù)據(jù),故而設(shè)為false。
eureka.client.fetchRegistry=false。
#設(shè)置與 Eureka Server 交互的地址,查詢服務(wù)和注冊(cè)服務(wù)都需要依賴這個(gè)地址。
#默認(rèn)是 http://localhost:8761/eureka;多個(gè)地址可使用,分隔。
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.po
rt}/eureka/。
5) 啟動(dòng)服務(wù)注冊(cè)中心
啟動(dòng)工程,打開瀏覽器訪問(wèn):http://localhost:6868,界面如下:
2. 創(chuàng)建服務(wù)提供者
1) 創(chuàng)建 Product 工程
2) Pom 文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
</dependencies>
</dependencyManagement>
3) 配置文件
server.port=8081
#指定注冊(cè)中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:6868/eureka/
#服務(wù)的名稱
spring.application.name=product-service
4) 修改啟動(dòng)類
在Application類上添加注解@EnableEurekaClient
5) 創(chuàng)建模型類
Product
public class Product {
private Long id; //商品 ID
private String title; //商品名稱
private String pic; //商品圖片地址
private Long price; //商品價(jià)格
6) 創(chuàng)建 Product 的 service 層
@Service
public class ProductService {
//模擬商品數(shù)據(jù)
private static final Map<Integer, Product> PRODUCT_MAP = new HashMap<>();
static {
Product p1 = new Product(1,"iphonex",9999, 10);
Product p2 = new Product(2,"冰箱",5342, 19);
Product p3 = new Product(3,"洗衣機(jī)",523, 90);
Product p4 = new Product(4,"電話",64345, 150);
Product p5 = new Product(5,"汽車",2345, 140);
Product p6 = new Product(6,"椅子",253, 20);
Product p7 = new Product(7,"java 編程思想",2341, 10);
PRODUCT_MAP.put(p1.getId(),p1);
PRODUCT_MAP.put(p2.getId(),p2);
PRODUCT_MAP.put(p3.getId(),p3);
PRODUCT_MAP.put(p4.getId(),p4);
PRODUCT_MAP.put(p5.getId(),p5);
PRODUCT_MAP.put(p6.getId(),p6);
PRODUCT_MAP.put(p7.getId(),p7);
//獲取商品列表
public List<Product> listProduct() {
Collection<Product> collection = PRODUCT_MAP.values();
List<Product> list = new ArrayList<>(collection);
return list;
//獲取商品詳情
public Product findById(int id) {
return PRODUCT_MAP.get(id);
7) 創(chuàng)建 Product 的 controller 層
@RestController
@RequestMapping("/api/v1/product")
public class ProductController {
@Autowired
private ProductService productService;
//獲取所有商品列表
@RequestMapping("list")
public Object list(){
return productService.listProduct();
//根據(jù) id 獲取商品詳情
@RequestMapping("find/{id}")
public Object findById(@PathVariable("id") int id) {
return productService.findById(id);
}
8) 啟動(dòng) Product 工程
啟動(dòng) Product 工程后,服務(wù)已經(jīng)注冊(cè)到 EurekaServer
訪問(wèn) http://localhost:8081/api/v1/product/find/1,可以得到某商品的詳情
訪問(wèn) http://localhost:8081/api/v1/product/list,可以得到所有商品的列表
3. 創(chuàng)建服務(wù)消費(fèi)者
1) 創(chuàng)建 Order 工程
步驟與創(chuàng)建服務(wù)提供者工程相同
2) Pom 文件
同 Product 工程 pom.xml 文件配置
3) 配置文件
server.port=8082
#服務(wù)的名稱
spring.application.name=order-service
#指定注冊(cè)中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:6868/eureka/
4) 修改啟動(dòng)類
在 Application 類上添加注解@ EnableDiscoveryClient,表明標(biāo)注類是消費(fèi)者,同時(shí)加入restTemplate 以消費(fèi)相關(guān)的服務(wù)
5) 創(chuàng)建模型類 Order 及 OrderDetail
復(fù)制 Product 工程中的 Product 模型到本工程
創(chuàng)建 OrderDetail 類
public class OrderDetail {
private String orderId;
private Product product;
創(chuàng)建 Order 類
public class Order {
private String id;
private Date createDate;
private Date updateDate;
private int userId;
private List<OrderDetail> orderDetails;
6) 創(chuàng)建 Order 的 service 層
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
//模擬訂單集合數(shù)據(jù)
public static final Map<String,Order> ORDER_MAP=new HashMap<String, Order>();
static {
//模擬一個(gè)訂單
Order order=new Order();
order.setId("202001010110101");
order.setCreateDate(new Date());
order.setUpdateDate(order.getCreateDate());
order.setUserId(1);
//模擬訂單中的訂單項(xiàng) OrderDetail
List<OrderDetail> orderDetails = new ArrayList<>();
//向 OrderDetail 中添加一個(gè)商品
Product product=new Product();
product.setId(1);
orderDetails.add(new OrderDetail(order.getId(),product));
//向 OrderDetail 中添加另一個(gè)商品
product=new Product();
product.setId(2);
orderDetails.add(new OrderDetail(order.getId(),product));
//將 OrderDetail 添加到訂單
order.setOrderDetails(orderDetails);
//將模擬的訂單添加到訂單集合中
ORDER_MAP.put(order.getId(),order);
public Order queryOrderById(String id){
Order order=ORDER_MAP.get(id);
//獲取 Product 服務(wù)地址
String urlStr="http://product-service/api/v1/product/find/";
int productId=-1;
for (OrderDetail orderDetail:order.getOrderDetails()){
productId=orderDetail.getProduct().getId();
Product pro=restTemplate.getForObject(urlStr+productId,Product.class);
orderDetail.setProduct(pro);
return order;
}
7) 創(chuàng)建 Order 的 controller 層
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping(value = "order/{orderId}")
public Order queryOrderById(@PathVariable("orderId") String orderId){
return orderService.queryOrderById(orderId);
}
8) 啟動(dòng) Order 工程
啟動(dòng) Order 工程后,服務(wù)已經(jīng)注冊(cè)到 EurekaServer
訪問(wèn) http://localhost:8082/order/202001010110101,可以得到訂單詳情信息
至此,我們已經(jīng)完成了最簡(jiǎn)單的電商系統(tǒng)微服務(wù)架構(gòu)實(shí)現(xiàn),更多微服務(wù)集群及高可用框架配置可查閱中培相關(guān)課程。