Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,
如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,
通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
微服务中的相关概念:
服务注册与发现:
服务注册:服务实例将自身服务信息注册到注册中心。这部分服务信息包括服务所在主机IP和提供服务的Port,以及暴露服务自身状态以及访问协议等信息。
服务发现:服务实例请求注册中心获取所依赖服务信息。服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务。
负载均衡:
负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。
熔断:
熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,
上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
链路追踪:
随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,
这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。
因此,就需要对一次请求涉及的多个服务链路进行日志记录,性能监控即链路追踪
API网关:
随着微服务的不断增多,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,
如果让客户端直接与各个微服务通信可能出现:
客户端需要调用不同的url地址,增加难度
在一定的场景下,存在跨域请求的问题
每个微服务都需要进行单独的身份认证
针对这些问题,API网关顺势而生。
API网关直面意思是将所有API调用统一接入到API网关层,由网关层统一接入和输出。一个网关的基本功能有:
统一接入、安全防护、协议适配、流量管控、长短链接支持、容错能力。
有了网关之后,各个API服务提供团队可以专注于自己的的业务逻辑处理,而API网关更专注于安全、流量、路由等问题。
SpringCloud中的核心组件:
Spring Cloud的本质是在 Spring Boot 的基础上,增加了一堆微服务相关的规范,并对应用上下文(Application Context)进行了功能增强。
既然 Spring Cloud 是规范,那么就需要去实现,目前Spring Cloud 规范已由 Spring官方Spring Cloud Netflix,Spring Cloud Alibaba等实现。
通过组件化的方式,Spring Cloud将这些实现整合到一起构成全家桶式的微服务技术栈。
Spring Cloud Netflix组件:
Spring Cloud Alibaba组件:
Spring Cloud原生及其他组件:
注册中心负责服务的注册与发现,很好将各服务连接起来
断路器负责监控服务之间的调用情况,连续多次失败进行熔断保护。
API网关负责转发所有对外的请求和服务
配置中心提供了统一的配置信息管理服务,可以实时的通知各个服务获取最新的配置信息
链路追踪技术可以将所有的请求数据记录下来,方便我们进行后续分析
各个组件又提供了功能完善的dashboard监控平台,可以方便的监控各组件的运行状况
快速入门:
使用微服务架构的分布式系统,微服务之间通过网络通信。我们通过服务提供者与服务消费者来描述微服务间的调用关系。
服务提供者:服务的被调用方,提供调用接口的一方
服务消费者:服务的调用方,依赖于其他服务的一方
以电商系统中常见的用户下单为例,用户向订单微服务发起一个购买的请求。在进行保存订单之前需要调用商品微服务查询当前商品库存,单价等信息。
在这种场景下,订单微服务就是一个服务消费者,商品微服务就是一个服务提供者
数据库表:
商品表
CREATE TABLE `tb_product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `product_name` varchar(40) DEFAULT NULL COMMENT '名称', `status` int(2) DEFAULT NULL COMMENT '状态', `price` decimal(10,2) DEFAULT NULL COMMENT '单价', `product_desc` varchar(255) DEFAULT NULL COMMENT '描述', `caption` varchar(255) DEFAULT NULL COMMENT '标题', `inventory` int(11) DEFAULT NULL COMMENT '库存', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
订单表
CREATE TABLE `tb_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) DEFAULT NULL COMMENT '用户id', `product_id` int(11) DEFAULT NULL COMMENT '商品id', `number` int(11) DEFAULT NULL COMMENT '数量', `price` decimal(10,2) DEFAULT NULL COMMENT '单价', `amount` decimal(10,2) DEFAULT NULL COMMENT '总额', `product_name` varchar(40) DEFAULT NULL COMMENT '商品名', `username` varchar(40) DEFAULT NULL COMMENT '用户名', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
搭建环境:
1.创建父工程并引入坐标
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
2.创建微服务工程模块
创建公共模块 common_service ,用于存放公共的实体类和工具类
创建商品微服务模块 product_service
创建订单微服务模块 order_service
3.工程的依赖结构
搭建公共模块:
1.在common_service创建实体类
/** * 商品实体类 */ @Data @Entity @Table(name="tb_product") public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String productName; private Integer status; private BigDecimal price; private String productDesc; private String caption; private Integer inventory; }
搭建商品微服务:
1.在product_service的pom文件中引入坐标
<dependency> <groupId>com.fgy</groupId> <artifactId>common_service</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency
2.编写dao接口
/** * 商品持久层接口 */ public interface ProductDao extends JpaRepository<Product,Long>, JpaSpecificationExecutor<Product> { }
3.编写service层
/** * 商品业务层接口 */ public interface ProductService { /** * 根据id查询 * @return */ Product findById(Long id); /** * 保存 */ 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 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); } }
4.编写web层
/** * 商品控制层 */ @RestController // 使用rest风格 @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @GetMapping(value = "/{id}") public Product findById(@PathVariable Long id) { Product product = productService.findById(id); return product; } @PostMapping public String save(@RequestBody Product product) { productService.save(product); return "保存成功"; } }
5.配置启动类
@SpringBootApplication @EntityScan("com.fgy.common.domain") public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class,args); } }
6.配置yml文件
server: port: 9001 spring: application: name: service-product #服务名称 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf8 username: root password: root jpa: database: MySQL show-sql: true open-in-view: true
7.在启动类启动测试
访问:http://localhost:9001/product/1 返回商品信息的json字符串,则商品微服务可正常访问
服务调用:
前文已经编写了基础的微服务,在用户下单时需要调用商品微服务获取商品数据。那应该怎么做呢?
商品微服务提供了供别人调用的HTTP接口。所以可以在下定单的时候使用http请求的相关工具类完成,如常见的HttpClient,OkHttp,当然也可以使用Spring提供的RestTemplate
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方法介绍:
搭建订单微服务,调用商品微服务:
1.在order_service的pom文件中引入坐标
<dependency> <groupId>com.fgy</groupId> <artifactId>common_service</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency>
2.编写web层
/** * 订单控制层 */ @RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; /** * 通过订单系统,调用商品服务根据id查询商品信息 * @param id * @return */ @GetMapping("/buy/{id}") public Product findById(@PathVariable("id") Long id) { return restTemplate.getForObject("http://localhost:9001/product/" + id, Product.class); } }
3.配置yml文件
server: port: 9002 spring: application: name: order-product #服务名称 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf8 username: root password: root jpa: database: MySQL show-sql: true open-in-view: true
4.配置启动类
@SpringBootApplication @EntityScan("com.fgy.common.domain") public class OrderApplication { // 配置RestTemplate交给spring管理 @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(OrderApplication.class,args); } }
服务注册与发现Eureka:https://www.cnblogs.com/roadlandscape/p/12491040.html