什么是微服务
微小的服务,没有明确边界,通常情况下微服务只做一件事情
分布式架构VS微服务架构
单体架构:运行整个服务
分布式架构:将服务拆分,一般倾向于服务的分散化,解决的是压力的缓解
微服务架构:将服务进行更细粒度的拆分,一般为垂直拆分,即一台服务器只做一项任务,侧重于能力的分散化
微服务架构的优势和挑战
优势
易开发和维护,每个微服务体量小,业务清晰,开发维护成本低
容错性高,一个服务发生故障,可以让故障被隔离在单个服务中,不影响整体服务
扩展性好,每个服务单独运行,可以自由增减服务
技术选型灵活,每个微服务都是单独的团队构建,可以根据业务特点选择合适的技术栈,甚至语言
挑战
服务依赖,随着微服务数量变多,服务治理变得困难
运维成本,一个微服务架构由多个团队共同完成,需要更多的编译,部署,运行,甚至不同的语言和环境,也需要集群来处理故障转移,对于运维人员而言挑战巨大
服务监控,在单体服务架构中,我们很容易对服务进行监控,但是微服务架构则更加困难
负载均衡,微服务中的服务实例数量庞大,因此需要有效的负载均衡机制来管理请求流量和保证高可用性
SpringCloud
SpringCloud就是分布式微服务架构的一站式解决方案
springcloud把一些比较优秀的解决微服务架构中的常用问题的开源框架基于SpringBoot框架进行整合,并基于SpringBoot风格对这些组件进行了封装。
SpringCloud实现对比
SpringCloud工程搭建
采用父子工程的搭建方式
首先通过SpringBoot创建父工程,只保留pom.xml文件
配置文件需要根据个人进行修改,这里提供适配作者的配置文件,仅供参考
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.spring</groupId> <artifactId>springcloud-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <!-- 子工程 --> <modules> <module>order-service</module> <module>product-service</module> </modules> <!-- 父工程 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.6</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <java.version>17</java.version> <mybatis.version>3.0.3</mybatis.version> <mysql.version>8.0.33</mysql.version> <spring-cloud.version>2022.0.3</spring-cloud.version> </properties> <!-- 依赖管理 --> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter-test</artifactId> <version>${mybatis.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> </project>随后通过new module的方式创建子工程
在子工程中配置一系列配置文件
pom.xml:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.spring</groupId> <artifactId>springcloud-demo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>product-service</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/**</include> </includes> </resource> </resources> </build> </project>application.yml:
server: port: 你自己的端口号,注意,每个子工程的端口号应保持不同 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false username: 数据库用户名 password: 数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*Mapper.xml configuration: # 配置打印 MyBatis 执行的 SQL log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true #自动驼峰转换随后按照图片,在子工程中搭建出SpringBoot的架构:
这样,我们就搭建好了SpringCloud的简单架构。
简单的微服务实现
现在我们创建了两个基于SpringBoot的微服务8080: order-service和9090: product-service
我们的目标是前端请求order-service微服务,然后order-service微服务调用product-service微服务,获取product的详细信息,最后由order-service传回包括product的order详细信息。
首先,先完成基本的SpringBoot搭建,此处以pruduct-service为例:
product类
package com.spring.product.model; import lombok.Data; import java.util.Date; @Data public class ProductInfo { private Integer id; private String productName; private Integer state; private Date createTime; private Date updateTime; }Controller
package com.spring.product.controller; import com.spring.product.model.ProductInfo; import com.spring.product.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @RequestMapping("/selectProductInfoByProductId") public ProductInfo selectProductInfoByProductId(Integer productId) { return productService.selectProductInfoById(productId); } }Service
package com.spring.product.service; import com.spring.product.mapper.ProductMapper; import com.spring.product.model.ProductInfo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Slf4j @Service public class ProductService { @Autowired private ProductMapper productMapper; public ProductInfo selectProductInfoById(Integer productId) { return productMapper.selectProductInfoById(productId); } }Mapper
package com.spring.product.mapper; import com.spring.product.model.ProductInfo; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; @Mapper public interface ProductMapper { @Select("select * from product_detail where id = #{productId}") ProductInfo selectProductInfoById(Integer productId); }同理搭建order-service。
第二步,创建RestTemplate类,然后通过order-service调用product-service的接口,实现数据的获取
RestTemplate类
package com.spring.order.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class BeanConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }order-service修改
@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private RestTemplate restTemplate; public OrderInfo selectOrderInfoById(Integer orderId) { OrderInfo orderInfo = orderMapper.selectOrderInfoById(orderId); //获取product-service的URL String url="http://127.0.0.1:9090/product/selectProductInfoByProductIdproductId="+orderInfo.getProductId(); //通过restTemplate调用product-service接口,获得product信息 ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class); //填充信息 orderInfo.setProductInfo(productInfo); return orderInfo; } }这样就完成了前端请求order-service——order-service调用product-service并返回的流程了。
问题
1. URL是写死的,如果IP地址发生变化,就需要修改代码
2. 如果多机部署,如何处理?
3. 返回结果如何公用?
4. 接口对外开放,有一定风险
上述问题我们将在接下来的文章中使用Eureka等服务注册中心解决