四、微服务案例搭建
使用微服务架构的分布式系统,微服务之间通过网络通信,我们通过服务提供者与服务消费者来描述微服务间的调用关系。
服务提供者:服务的被调用方,提供调用接口的一方
服务消费者:服务的调用方,依赖于其他服务的一方
我们以电商系统中常见的用户下单为例,用户向订单微服务发起一个购买的请求。在进行保存订单之前需要调用商品微服务查询当前商品库存,单价等信息。在这种场景下,订单微服务就是一个服务消费者,商品微服务就是一个服务提供者。
1、搭建环境
创建父工程
在IDEA中创建父工程并引入坐标
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <modules> <module>service-product</module> <module>service-order</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.uncle</groupId> <artifactId>micro-service-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>micro-service-demo</name> <description>micro-service-demo</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
创建微服务工程模块
2、搭建商品微服务
编写实体类
@Data @Entity @Table(name="tb_product") public class Product { @Id private Long id; private String productName; private Integer status; private BigDecimal price; private String productDesc; private String caption; }
这里使用了lombok简化实体类的开发
Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率
编写dao接口
public interface ProductDao extends JpaRepository<Product,Long> , JpaSpecificationExecutor<Product> {}
编写service层
public interface ProductService { //根据id查询 Product findById(Long id); //查询全部 List findAll(); //保存 void save(Product product); //更新 void update(Product product); //删除 void delete(Long id); }
@Service public class ProductServiceImpl implements ProductService { @Autowired private ProductDao productDao; @Override public Product findById(Long id) { return productDao.findById(id).get(); } @Override public List findAll() { return productDao.findAll(); } @Override public void save(Product product) { productDao.save(product); } @Override public void update(Product product) { productDao.save(product); } @Override public void delete(Long id) { productDao.deleteById(id); } }
编写web层
@RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @GetMapping public List findAll() { return productService.findAll(); } @GetMapping("/{id}") public Product findById(@PathVariable Long id) { return productService.findById(id); } @PostMapping public String save(@RequestBody Product product) { productService.save(product); return "保存成功"; } @PutMapping("/{id}") public String update(@RequestBody Product product) { productService.update(product); return "修改成功"; } @DeleteMapping("/{id}") public String delete(Long id) { productService.delete(id); return "删除成功"; } }
controller中使用的@GetMapping是一个组合注解,相当与@RequestMapping(method=“get”)。
类似的注解还有@PostMapping,@PutMapping,@DeleteMapping
配置启动类
package com.uncle.product; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 启动类 * * @author sjx */ @SpringBootApplication @EntityScan("") public class ProductApplication { /** * main 方法 * @param args:参数列表 */ public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } }
配置yml文件
server: port: 9002 spring: application: name: shop-service-product datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8 username: root password: 111111 jpa: database: MySQL show-sql: true open-in-view: true
3、搭建订单微服务
编写实体类
@Data @Entity @Table(name="tb_order") public class Order { @Id private Long id; private String productName; private Integer status; private BigDecimal price; private String orderDesc; private String caption; }
这里使用了lombok简化实体类的开发
Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率
编写dao接口
public interface OrdertDao extends JpaRepository<Order,Long> , JpaSpecificationExecutor<Order> {}
编写service层
public interface OrderService { //根据id查询 Order findById(Long id); //查询全部 List findAll(); //保存 void save(Order order); //更新 void update(Order order); //删除 void delete(Long id); }
@Service public class OrderServiceImpl implements OrderService { @Autowired private OrderDao productDao; @Override public Order findById(Long id) { return productDao.findById(id).get(); } @Override public List findAll() { return productDao.findAll(); } @Override public void save(Product product) { productDao.save(product); } @Override public void update(Product product) { productDao.save(product); } @Override public void delete(Long id) { productDao.deleteById(id); } }
编写web层
@RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @GetMapping public List findAll() { return productService.findAll(); } @GetMapping("/{id}") public Product findById(@PathVariable Long id) { return productService.findById(id); } @PostMapping public String save(@RequestBody Product product) { productService.save(product); return "保存成功"; } @PutMapping("/{id}") public String update(@RequestBody Product product) { productService.update(product); return "修改成功"; } @DeleteMapping("/{id}") public String delete(Long id) { productService.delete(id); return "删除成功"; } }
controller中使用的@GetMapping是一个组合注解,相当与@RequestMapping(method=“get”)。
类似的注解还有@PostMapping,@PutMapping,@DeleteMapping
配置启动类
package com.uncle.order; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 启动类 * * @author sjx */ @SpringBootApplication @EntityScan("") public class OrderApplication { /** * main 方法 * @param args:参数列表 */ public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
配置yml文件
server: port: 9001 spring: application: name: shop-service-order datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8 username: root password: 111111 jpa: database: MySQL show-sql: true open-in-view: true
4、服务调用
RestTemplate介绍
Spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与许多其他Spring 模板类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。
RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。
考虑到RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()、getForObject()、postForObject()、put()和delete()等方法。
RestTemplate方法介绍
通过RestTemplate调用微服务
配置RestTemplate
//配置RestTemplate交给spring管理 @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); }
编写下订单方法
@PostMapping("/{id}") public String order(Integer num) { //通过restTemplate调用商品微服务 Product object = restTemplate.getForObject("http://127.0.0.1:9002/product/1", Product.class); System.out.println(object); return "操作成功"; }
硬编码存在的问题
至此已经可以通过RestTemplate调用商品微服务的RESTFul API接口。但是我们把提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:
- 应用场景有局限
- 无法动态调整
那么应该怎么解决呢,就需要通过注册中心动态的对服务注册和服务发现