学习微服务架构,不但要了解微服务中的基本概念和重要组件,更重要的是实践。本文将会以一个电商中的常见业务场景为例构建微服务。在本文中,主要使用最新的SpringCloud( version:Finchley.SR1)体系进行构建。
1 准备
在开始本文之前,需要以下预备知识:
-
熟悉Spring和SpringBoot
-
了解微服务
本文会使用SpringCloud中的一些组件进行开发:
-
Spring Cloud Netflix Eureka:注册中心
-
Spring Cloud Netflix Zuul:API网关
-
Spring Cloud OpenFeign: 服务调用工具
2 业务场景
为了更好的展示微服务,本文将以电商业务场景中的 创建订单 为例。现实系统中的下单非常复杂,这里会进行简化,主要是为了方便理解微服务。所以,这里假定,下单过程中的两个主要操作:
-
根据下单用户的id,查询当前用户的信息
-
根据下单的商品id,查询当前商品的信息
最终,根据这个业务场景,设计的微服务架构图如下:
按照业务场景,将不同的功能垂直划分成三个服务:
-
UserService
-
OrderService
-
ProductService
其中,OrderService会调用UserService和ProductService的相关服务,在调用过程中,并不是直接调用,而是通过Eureka去调用它们。外部调用不会直接调用具体的服务,全部都是通过Zuul网关进行调用,此处,Zuul也是通过Eureka去调用具体的服务。
3 构建项目
根据架构图,构建项目骨架。创建一个Maven项目,并且创建五个子模块,pom.xml:
1<?xml version="1.0" encoding="UTF-8"?> 2<project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 <parent> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-parent</artifactId> 9 <version>2.0.1.RELEASE</version> 10 </parent> 11 <groupId>com.no.one</groupId> 12 <artifactId>microservice-base</artifactId> 13 <packaging>pom</packaging> 14 <version>1.0-SNAPSHOT</version> 15 16 <properties> 17 <java.version>1.8</java.version> 18 </properties> 19 20 <dependencyManagement> 21 <dependencies> 22 <dependency> 23 <groupId>org.springframework.cloud</groupId> 24 <artifactId>spring-cloud-dependencies</artifactId> 25 <version>Finchley.SR1</version> 26 <type>pom</type> 27 <scope>import</scope> 28 </dependency> 29 </dependencies> 30 </dependencyManagement> 31 32 <modules> 33 <module>discovery-server</module> 34 <module>api-gateway-server</module> 35 <module>order-service</module> 36 <module>product-service</module> 37 <module>user-service</module> 38 </modules> 39 40 <build> 41 <plugins> 42 <plugin> 43 <groupId>org.springframework.boot</groupId> 44 <artifactId>spring-boot-maven-plugin</artifactId> 45 </plugin> 46 </plugins> 47 </build> 48 49</project>
在这个pom文件中,我们定义了所使用的SpringCloud以及SpringBoot相关库的版本。同时,在各个模块中,我们将使用 .properties
文件作为项目配置文件,而不使用 .yml
文件。 yml
文件虽然层次分明,但是基于缩进的语法,容易出问题。关于这个文件格式的选择,要考虑到个人习惯以及团队内的约定。
3.1 注册中心:Eureka
创建Eureka注册中心,首先要引入依赖,在配置文件 microservice-base\discovery-server\pom.xml
:
1<dependencies> 2 <dependency> 3 <groupId>org.springframework.cloud</groupId> 4 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> 5 </dependency> 6</dependencies>
然后创建主类, com.no.one.discovery.DiscoveryApplication
1@SpringBootApplication 2@EnableEurekaServer 3public class DiscoveryApplication { 4 public static void main(String[] args) { 5 SpringApplication.run(DiscoveryApplication.class, args); 6 } 7}
这是一个SpringBoot应用,关键就在于使用 @EnableEurekaServer
注解。然后,增加相关的配置:
microservice-base\discovery-server\src\main\resources\bootstrap.properties:
1spring.application.name=discovery-server
microservice-base\discovery-server\src\main\resources\application.properties:
1server.port=8761 2eureka.instance.hostname=localhost 3 4eureka.client.registerWithEureka=false 5eureka.client.fetchRegistry=false 6eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
这个配置文件中,配置了Eureka服务器的参数。
3.2 API网关: Zuul
引入Zuul的依赖,在配置文件 microservice-base\api-gateway-server\pom.xml
:
1<dependencies> 2 <dependency> 3 <groupId>org.springframework.cloud</groupId> 4 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>org.springframework.cloud</groupId> 8 <artifactId>spring-cloud-starter-netflix-zuul</artifactId> 9 </dependency> 10</dependencies>
注意,在Zuul网关中,调用上游服务时,是通过调用Eureka进行的,所以,此处引入eureka-client的依赖。
创建主类 com.no.one.gateway.GatewayApplication.java
1@SpringBootApplication 2@EnableDiscoveryClient 3@EnableZuulProxy 4public class GatewayApplication { 5 public static void main(String[] args) { 6 SpringApplication.run(GatewayApplication.class, args); 7 } 8}
这也是一个SpringBoot应用,关键就在于使用 @EnableZuulProxy
注解,该注解会启用反向代理,后面会看到相关的路由配置。同时,通过 @EnableDiscoveryClient
注解,集成对于Eureka的支持,后续,在配置路由时可以直接使用服务的名字进行。
然后,增加相关配置 microservice-base\api-gateway-server\src\main\resources\bootstrap.properties :
1spring.application.name=api-gateway-server
microservice-base\api-gateway-server\src\main\resources\application.properties:
1server.port=9002 2 3eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ 4 5zuul.prefix=/api 6zuul.ignoredServices='*' 7 8zuul.routes.user-service.path=/user-service/** 9zuul.routes.user-service.serviceId=user-service 10 11zuul.routes.product-service.path=/product-service/** 12zuul.routes.product-service.serviceId=product-service 13 14zuul.routes.order-service.path=/order-service/** 15zuul.routes.order-service.serviceId=order-service
在此处,除了Zuul网关的配置,还配置好相关的路由。
3.3 业务服务
完成基础组件,开始构建具体的业务服务。三个业务组件都是SpringBoot应用,构建方法类似。同时,为了简单起见,支持使用内存数据库,数据库的访问直接使用SpingDataJPA。以构建OrderService为例,首先,创建 microservice-base\order-service\pom.xml
文件,并引入依赖:
1<dependencies> 2 <!-- Spring Boot --> 3 <dependency> 4 <groupId>org.springframework.boot</groupId> 5 <artifactId>spring-boot-starter-data-jpa</artifactId> 6 </dependency> 7 <dependency> 8 <groupId>org.springframework.boot</groupId> 9 <artifactId>spring-boot-starter-web</artifactId> 10 </dependency> 11 <!-- Spring Cloud --> 12 <dependency> 13 <groupId>org.springframework.cloud</groupId> 14 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 15 </dependency> 16 <dependency> 17 <groupId>org.springframework.cloud</groupId> 18 <artifactId>spring-cloud-starter-openfeign</artifactId> 19 </dependency> 20 <!-- Third parties --> 21 <dependency> 22 <groupId>org.projectlombok</groupId> 23 <artifactId>lombok</artifactId> 24 </dependency> 25 <dependency> 26 <groupId>org.hsqldb</groupId> 27 <artifactId>hsqldb</artifactId> 28 </dependency> 29</dependencies>
主要的配置 microservice-base\order-service\src\main\resources\bootstrap.properties :
1spring.application.name=order-service
以上名字属性,作为服务名注册到Eureka中。
microservice-base\order-service\src\main\resources\application.properties :
1server.port = 9200 2eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
相关的主要 Java 类:
实体类 microservice-base\order-service\src\main\java\com\no\one\order\model\Order.java
:
1@Data 2@Entity 3@Table(name = "ms_order") 4public class Order { 5 @Id 6 @GeneratedValue(strategy = GenerationType.IDENTITY) 7 private Long id; 8 9 @Column(name = "product_name") 10 private String productName; 11 12 @Column(name = "user_name") 13 private String userName; 14 15 @Column(name = "price") 16 private BigDecimal price; 17 18 @Column(name ="created_time") 19 private Date createdTime; 20}
数据访问 microservice-base\order-service\src\main\java\com\no\one\order\repo\OrderRepo.java
:
1public interface OrderRepo extends JpaRepository<Order, Long> { 2}
业务接口 microservice-base\order-service\src\main\java\com\no\one\order\service\OrderService.java
1public interface OrderService { 2 Order createOrder(Long userId, Long productId); 3 List<Order> listOrder(); 4}
在实现业务接口之前,要实现对于ProductService和UserService的服务调用。此处,直接使用 Open Feign
,该库可以极大简化对于远程服务的调用,并且,使用SpringMVC里的各种常用注解。
调用ProductService, microservice-base\order-service\src\main\java\com\no\one\order\client\ProductClient.java
:
1@FeignClient("product-service") 2public interface ProductClient { 3 @RequestMapping(method = RequestMethod.GET, value = "/products/{id}") 4 ProductDto getProduct(@PathVariable("id") Long id); 5}
最后,实现 OrderService
中的业务接口 microservice-base\order-service\src\main\java\com\no\one\order\service\impl\OrderServiceImpl.java
1@Service 2@RequiredArgsConstructor 3public class OrderServiceImpl implements OrderService { 4 5 private final OrderRepo orderRepo; 6 private final UserClient userClient; 7 private final ProductClient productClient; 8 9 @Override 10 public Order createOrder(Long userId, Long productId) { 11 Order order = new Order(); 12 13 UserDto user = userClient.getUser(userId); 14 order.setUserName(user.getName()); 15 16 ProductDto product = productClient.getProduct(productId); 17 order.setProductName(product.getName()); 18 order.setPrice(product.getPrice()); 19 20 order.setCreatedTime(new Date()); 21 return orderRepo.save(order); 22 } 23 24 @Override 25 public List<Order> listOrder() { 26 return orderRepo.findAll(); 27 } 28}
在 createOrder
方法中,会调用两个远程的服务,然后,创建订单实体对象,最终数据保存到数据库中。
创建RestAPI, microservice-base\order-service\src\main\java\com\no\one\order\controller\OrderController.java
1@RequiredArgsConstructor 2@RestController 3@RequestMapping("/orders") 4public class OrderController { 5 6 private final OrderService orderService; 7 8 @PostMapping 9 public Order createOrder(@RequestBody CreateOrderRequest request){ 10 return orderService.createOrder(request.getUserId(), request.getProductId()); 11 } 12 13 @GetMapping 14 public List<Order> listOrder(){ 15 return orderService.listOrder(); 16 } 17 18 @Data 19 public static class CreateOrderRequest { 20 private Long userId; 21 private Long productId; 22 } 23}
ProductService和OrderService的构建类似,不过,我们会给这两个服务增加一些数据,他们会在项目启动时初始化到内存数据库中:
microservice-base\user-service\src\main\resources\data.sql
1INSERT INTO ms_user VALUES (1, 'Zeus'); 2INSERT INTO ms_user VALUES (2, 'Hera'); 3INSERT INTO ms_user VALUES (3, 'Hades');
microservice-base\product-service\src\main\resources\data.sql
1INSERT INTO ms_product VALUES (1, 'iPhone4', 99); 2INSERT INTO ms_product VALUES (2, 'iPhone6',999.99); 3INSERT INTO ms_product VALUES (3, 'iPhone8', 9999.98);
此时,我们可以借助一些RestAPI测试工具,测试创建订单的接口:
POST http://localhost:9002/api/order-service/orders
HTTP请求的请求体:
1{ 2 "userId":1, 3 "productId":1 4}
成功后,返回数据:
1{ 2 "id": 1, 3 "productName": "iPhone4", 4 "userName": "Zeus", 5 "price": 99, 6 "createdTime": "2018-08-27T12:17:02.406+0000" 7}
4 小结
本文结合一个电商中常见的创建订单这个业务场景,使用微服务架构进行构建。结合之前的微服务架构基础系列文章,理论联系实践,可以更好的理解微服务架构。当然,这是一个简单的例子,现实中的开发往往要更加复杂。
5 微服务架构基础系列
更多精彩内容,关注公众号 SeniorEngineer :
以上所述就是小编给大家介绍的《微服务架构基础之构建微服务》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 单体架构与微服务架构对比,为什么采用微服务架构
- Java架构书籍:微服务架构必读书单(附微服务架构模式进阶导图)
- 「微服务架构」微服务架构中的数据一致性
- 架构演进之「微服务架构」
- 微服务架构 VS 单体架构
- 从单体架构到微服务架构
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
编写可维护的JavaScript
扎卡斯 / 李晶、郭凯、张散集 / 人民邮电出版社 / 2013-4 / 55.00元
《编写可维护的JavaScript》向开发人员阐述了如何在团队开发中编写具备高可维护性的JavaScript代码,书中详细说明了作为团队一分子,应该怎么写JavaScript。《编写可维护的JavaScript》内容涵盖了编码风格、编程技巧、自动化、测试等几方面,既包括具体风格和原则的介绍,也包括示例和技巧说明,最后还介绍了如何通过自动化的工具和方法来实现一致的编程风格。 《编写可维护的Ja......一起来看看 《编写可维护的JavaScript》 这本书的介绍吧!