单体架构 VS 微服务架构
1. 单体架构
一个典型的单体应用就是将所有的业务场景的表示层、业务逻辑层和数据访问层放在一个工程中,最终经过编译、打包,部署在一台服务器上。
以电商系统为例,单体架构设计方案如下图所示:
单体架构部署简单,可以直接部署在一个服务器上,并且技术单一,不需要复杂的技术栈,用人成本低。但由于一个进程包含了所有的业务逻辑,因此系统可伸缩性差,由于涉及到的启动模块过多,也可能会导致系统的启动时间过长、可用性差等问题。
2. 微服务架构
微服务这一概念出现于2012年,微服务的基本思想在于考虑围绕着业务领域组件来创建应用,这些应用可独立地进行开发、管理和加速。在分散的组件中使用微服务云架构和平台,使部署、管理和服务功能交付变得更加简单。
微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法每一个服务运行在自己的进程中服务间通信采用的轻量级通信机制通(常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署这些服务公用一个最小型的集中式的管理服务可用不同的语言开发,使用不同的数据存储技术。
在微服务架构中一个微服务只会关注一个特定的业务功能,所以它业务清晰、代码量少,易于开发和维护,各个微服务间技术栈不受限,可以按需收缩,可根据需求,实现细粒度的扩展。
微服务架构下电商系统的设计图如下:
电商系统微服务实战详解
Spring Cloud 致力于提供微服务开发的一站式解决方案。它包含开发分布式应用微服务的必需组件,依托 Spring Cloud Alibaba,只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
1. 创建服务注册中心
1) 创建 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) 修改启动类
在Application类上添加注解@EnableEurekaServer 声明注册中心
4) 配置属性文件
server.port=6868
eureka.instance.hostname=localhost
#表示是否将自己注册到 Eureka Server,默认为 true。
# 由于当前这个应用就是 Eureka Server,故而设为 false。
eureka.client.registerWithEureka=false。
#表示是否从 Eureka Server 获取注册信息,默认为 true。
# 因为这是一个单点的 Eureka Server,不需要同步其他的 Eureka Server 节点的数据,故而设为false。
eureka.client.fetchRegistry=false。
#设置与 Eureka Server 交互的地址,查询服务和注册服务都需要依赖这个地址。
#默认是 http://localhost:8761/eureka;多个地址可使用,分隔。
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.po
rt}/eureka/。
5) 启动服务注册中心
启动工程,打开浏览器访问:http://localhost:6868,界面如下:
2. 创建服务提供者
1) 创建 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
#指定注册中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:6868/eureka/
#服务的名称
spring.application.name=product-service
4) 修改启动类
在Application类上添加注解@EnableEurekaClient
5) 创建模型类
Product
public class Product {
private Long id; //商品 ID
private String title; //商品名称
private String pic; //商品图片地址
private Long price; //商品价格
6) 创建 Product 的 service 层
@Service
public class ProductService {
//模拟商品数据
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,"洗衣机",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) 创建 Product 的 controller 层
@RestController
@RequestMapping("/api/v1/product")
public class ProductController {
@Autowired
private ProductService productService;
//获取所有商品列表
@RequestMapping("list")
public Object list(){
return productService.listProduct();
//根据 id 获取商品详情
@RequestMapping("find/{id}")
public Object findById(@PathVariable("id") int id) {
return productService.findById(id);
}
8) 启动 Product 工程
启动 Product 工程后,服务已经注册到 EurekaServer
访问 http://localhost:8081/api/v1/product/find/1,可以得到某商品的详情
访问 http://localhost:8081/api/v1/product/list,可以得到所有商品的列表
3. 创建服务消费者
1) 创建 Order 工程
步骤与创建服务提供者工程相同
2) Pom 文件
同 Product 工程 pom.xml 文件配置
3) 配置文件
server.port=8082
#服务的名称
spring.application.name=order-service
#指定注册中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:6868/eureka/
4) 修改启动类
在 Application 类上添加注解@ EnableDiscoveryClient,表明标注类是消费者,同时加入restTemplate 以消费相关的服务
5) 创建模型类 Order 及 OrderDetail
复制 Product 工程中的 Product 模型到本工程
创建 OrderDetail 类
public class OrderDetail {
private String orderId;
private Product product;
创建 Order 类
public class Order {
private String id;
private Date createDate;
private Date updateDate;
private int userId;
private List<OrderDetail> orderDetails;
6) 创建 Order 的 service 层
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
//模拟订单集合数据
public static final Map<String,Order> ORDER_MAP=new HashMap<String, Order>();
static {
//模拟一个订单
Order order=new Order();
order.setId("202001010110101");
order.setCreateDate(new Date());
order.setUpdateDate(order.getCreateDate());
order.setUserId(1);
//模拟订单中的订单项 OrderDetail
List<OrderDetail> orderDetails = new ArrayList<>();
//向 OrderDetail 中添加一个商品
Product product=new Product();
product.setId(1);
orderDetails.add(new OrderDetail(order.getId(),product));
//向 OrderDetail 中添加另一个商品
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 服务地址
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) 创建 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) 启动 Order 工程
启动 Order 工程后,服务已经注册到 EurekaServer
访问 http://localhost:8082/order/202001010110101,可以得到订单详情信息
至此,我们已经完成了最简单的电商系统微服务架构实现,更多微服务集群及高可用框架配置可查阅中培相关课程。