首先,必须要说明的一点,微服务是一种工程架构,而SpringCloud是微服务的一种具体的技术实现。
单体架构
单体架构,最显而易见的的就是我们平时所开发的java应用。比如,我们写的web工程,然后打包成war包,发布到tomcat等服务器中。但是这种架构有很大的问题,最基本的是复杂性高。如果一个百万行级别的工程,那么可想开发难度和维护难度有多大。所以,我们的微服务架构就应运而生了。
微服务
什么是微服务
微服务目前来说,并没有一个严格的定义,我们以Matrin Fowler的博客为准。
微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
所以,微服务的特征总结如下:
- 每个服务可独立运行在自己的进程里。
- 一系列独立运行的微服务共同构建起整个系统。
- 每个服务为独立的业务开发,一个微服务只关注某个特定的功能,例如订单管理、用户管理等。
- 微服务之间通过一些轻量对的通信机制进行通信,例如通过RESTful API进行调用。
- 可以使用不同的语言与数据存储技术。
- 全自动的部署机制。
一个最基本的微服务架构实例如下(Uber的架构图):
不管是支付端,司机端,还是乘客端等,都是单独作为一个服务进行调用或者被调用,服务直接通过RESTful API进行调用。
使用SpringBoot实现微服务
SpringCloud是微服务的具体组件,而一般我们新建web项目的时候最主要使用SpringBoot进行开发,因为简单高效,可以实现零配置。
我们的业务逻辑很简单
1、商品微服务:通过商品id查询商品
2、订单微服务:创建订单时,通过调用商品的微服务进行查询商品数据。
对于商品微服务而言,商品微服务是服务的提供者,订单微服务是服务的消费者;对于订单微服务而已,订单微服务是服务的提供者,人是服务的消费者。
如图所示:
实现商品微服务
首先,我们需要新建一个父工程,名为example,然后在下面新建子模块,商品的模块名为item。
Item.java
然后我们创建一个实体Item:
package com.example.item.pojo;
public class Item {
private Long id;
private String title;
private String pic;
private String desc;
private Long price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPic() {
return pic;
}
public void setPic(String pic) {
this.pic = pic;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Long getPrice() {
return price;
}
public void setPrice(Long price) {
this.price = price;
}
@Override
public String toString() {
return "Item [id=" + id + ", title=" + title + ", pic=" + pic + ", desc=" + desc + ", price=" + price + "]";
}
public Item(Long id, String title, String pic, String desc, Long price) {
this.id = id;
this.title = title;
this.pic = pic;
this.desc = desc;
this.price = price;
}
public Item() {
}
}
ItemService.java
接着,我们编写ItemService,它主要实现商品的查询逻辑,为了演示方便,我们不连接数据库,而是做模拟实现。
package com.example.item.service;
import com.example.item.pojo.Item;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class ItemService {
private static final Map<Long, Item> MAP = new HashMap<Long, Item>();
static { // 准备一些静态数据
MAP.put(1L, new Item(1L, "商品标题1", "http://图片1", "商品描述1", 1000L));
MAP.put(2L, new Item(1L, "商品标题2", "http://图片2", "商品描述2", 2000L));
MAP.put(3L, new Item(1L, "商品标题3", "http://图片3", "商品描述3", 3000L));
MAP.put(4L, new Item(1L, "商品标题4", "http://图片4", "商品描述4", 4000L));
MAP.put(5L, new Item(1L, "商品标题5", "http://图片5", "商品描述5", 5000L));
MAP.put(6L, new Item(1L, "商品标题6", "http://图片6", "商品描述6", 6000L));
MAP.put(7L, new Item(1L, "商品标题7", "http://图片7", "商品描述7", 7000L));
MAP.put(8L, new Item(1L, "商品标题8", "http://图片8", "商品描述8", 8000L));
}
/**
* 模拟实现商品查询
*
* @param id
* @return
*/
public Item queryItemById(Long id) {
return MAP.get(id);
}
}
ItemController.java
最后,是需要编写ItemController,controller使用rest风格。
package com.example.item.controller;
import com.example.item.pojo.Item;
import com.example.item.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ItemController {
@Autowired
private ItemService itemService;
/**
* 对外提供接口服务,查询商品信息
*
* @param id
* @return
*/
@GetMapping(value = "item/{id}")
public Item queryItemById(@PathVariable("id") Long id) {
return this.itemService.queryItemById(id);
}
}
编写application.yml配置文件
SpringBoot项目支持yml和properties格式的配置文件,yml格式是YAML(Yet Another Markup Language)编写的格式,YAML和properties格式的文件是可以相互转化的。如:
等价于:
在当前工程中,我们配置项目的服务端口:
启动程序测试
启动程序就是启动ItemApplication很简单,我们在浏览器中输入地址如下,可以看到后端给我们返回的数据:
实现订单的微服务
首先,我们创建一个名为order的模块,如图所示:
Order.java
首先,需要创建Order实体:
package com.example.order.pojo;
import java.util.Date;
import java.util.List;
public class Order {
private String orderId;
private Long userId;
private Date createDate;
private Date updateDate;
private List<OrderDetail> orderDetails;
public Order() {
}
public Order(String orderId, Long userId, Date createDate, Date updateDate) {
this.orderId = orderId;
this.userId = userId;
this.createDate = createDate;
this.updateDate = updateDate;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
public List<OrderDetail> getOrderDetails() {
return orderDetails;
}
public void setOrderDetails(List<OrderDetail> orderDetails) {
this.orderDetails = orderDetails;
}
@Override
public String toString() {
return "Order [orderId=" + orderId + ", userId=" + userId
+ ", createDate=" + createDate + ", updateDate=" + updateDate
+ "]";
}
}
OrderDetail.java
创建订单详情,订单与订单详细是一对多的关系。
package com.example.order.pojo;
public class OrderDetail {
private String orderId;
private Item item;
public OrderDetail() {
}
public OrderDetail(String orderId, Item item) {
this.orderId = orderId;
this.item = item;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
@Override
public String toString() {
return "OrderDetail [orderId=" + orderId + ", item=" + item + "]";
}
}
随后,将商品微服务中的Item类拷贝到当前工程。
OrderServcie.java
编写OrderService.java,主要实现根据订单id查询订单的服务,为了方便,我们也伪造数据,不连接数据库。
package com.example.order.service;
import com.example.order.pojo.Item;
import com.example.order.pojo.Order;
import com.example.order.pojo.OrderDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class OrderService {
private static final Map<String, Order> MAP = new HashMap<String, Order>();
static {
// 构造测试数据
Order order = new Order();
order.setOrderId("59193738268961441");
order.setCreateDate(new Date());
order.setUpdateDate(order.getCreateDate());
order.setUserId(1L);
List<OrderDetail> orderDetails = new ArrayList<OrderDetail>();
Item item = new Item();// 此处并没有商品的数据,需要调用商品微服务获取
item.setId(1L);
orderDetails.add(new OrderDetail(order.getOrderId(), item));
item = new Item(); // 构造第二个商品数据
item.setId(2L);
orderDetails.add(new OrderDetail(order.getOrderId(), item));
order.setOrderDetails(orderDetails);
MAP.put(order.getOrderId(), order);
}
@Autowired
private ItemService itemService;
/**
* 根据订单id查询订单数据
*
* @param orderId
* @return
*/
public Order queryOrderById(String orderId) {
Order order = MAP.get(orderId);
if (null == order) {
return null;
}
List<OrderDetail> orderDetails = order.getOrderDetails();
for (OrderDetail orderDetail : orderDetails) {
// 通过商品微服务查询商品数据
Item item = this.itemService.queryItemById(orderDetail.getItem()
.getId());
if (null == item) {
continue;
}
orderDetail.setItem(item);
}
return order;
}
}
ItemService.java
这里的ItemService不做实现,主要是使用商品微服务中的方法。
package com.example.order.service;
import com.example.order.pojo.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Service
public class ItemService {
// @Value("${xushu.item.url}")
// private String itemUrl;
//Spring框架对RESTful方式的http请求做了封装,来简化操作
@Autowired
private RestTemplate restTemplate;
/ **
*调用商品的微服务提供的接口进行查询数据
* @param id
* @return
*/
public Item queryItemById(Long id) {
return this.restTemplate.getForObject("http://127.0.0.1:8081/item/"
+ id, Item.class);
}
}
OrderController.java
package com.example.order.controller;
import com.example.order.pojo.Order;
import com.example.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping(value = "order/{orderId}")
public Order queryOrderById(@PathVariable("orderId") String orderId) {
return this.orderService.queryOrderById(orderId);
}
}
修改程序入口
package com.example.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class OrderApplication {
@Bean // 向Spring容器中定义RestTemplate对象
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
然后修改application.yml文件,将端口改为8082,最后启动测试如图:
可以看到,我们的订单信息完全被查询出来了,而且商品信息也没有缺失。
总结
到此,我们一个基本的微服务架构就搭建完毕,但是我们也同时思考一个问题。刚才我们在订单微服务中ItemService,是采用硬编码的,这肯定不好。我们可以采用
服务注册的方式来解决它。