006 SpringCloud 学习笔记2-----SpringCloud基础入门
1.SpringCloud概述
微服务是一种架构方式,最终肯定需要技术架构去实施。
微服务的实现方式很多,但是最火的莫过于Spring Cloud了。
SpringCloud优点:
- 后台硬:作为Spring家族的一员,有整个Spring全家桶靠山,背景十分强大。
- 技术强:Spring作为Java领域的前辈,可以说是功力深厚。有强力的技术团队支撑,一般人还真比不了
- 群众基础好:可以说大多数程序员的成长都伴随着Spring框架,试问:现在有几家公司开发不用Spring?SpringCloud与Spring的各个框架无缝整合,对大家来说一切都是熟悉的配方,熟悉的味道。
- 使用方便:相信大家都体会到了SpringBoot给我们开发带来的便利,而SpringCloud完全支持SpringBoot的开发,用很少的配置就能完成微服务框架的搭建
(1)简介
Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。
SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:
- Eureka:服务治理组件,包含服务注册中心,服务注册与发现机制的实现。(服务治理,服务注册/发现)
- Zuul:网关组件,提供智能路由,访问过滤功能
- Ribbon:客户端负载均衡的服务调用组件(客户端负载)
- Feign:服务调用,给予Ribbon和Hystrix的声明式服务调用组件 (声明式服务调用)
- Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力。(熔断、断路器,容错)
架构图:
2.微服务场景模拟案例
首先,我们需要模拟一个服务调用的场景,搭建两个工程:lucky-service-provider(服务提供方)和lucky-service-consumer(服务调用方)。方便后面学习微服务架构
服务提供方:使用mybatis操作数据库,实现对数据的增删改查;并对外提供rest接口服务。
服务消费方:使用restTemplate远程调用服务提供方的rest接口服务,获取数据。
(1)服务提供者
我们新建一个项目:lucky-service-provider,对外提供根据id查询用户的服务。
<1>Spring脚手架(Spring Initializr)创建工程
最终生成的项目结构:
(2)代码编写
<1>配置
属性文件,这里我们采用了yaml语法,而不是properties:
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot
username: root
password: plj824
mybatis:
type-aliases-package: lucky.service.domain
<2>实体类
package lucky.service.domain; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; /** * 数据库表users对应的实体类 * 注意:Users这个类中添加了@Table、@Id、@GeneratedValue等注解 * 这些注解的使用需要在pom文件中添加mapper的依赖 */ @Table(name="users") public class Users { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; // 主 键 private String username; // 用户名 private String password; // 密 码 private String name; // 姓 名 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", name='" + name + '\'' + '}'; } }
<3>UsersMapper
package lucky.service.mapper; import lucky.service.domain.Users; public interface UsersMapper extends tk.mybatis.mapper.common.Mapper<Users>{ }
注意:mapper接口类需要在springboot引导类----LuckyServiceProviderApplication.java类中添加注解@MapperScan
package lucky.service; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import tk.mybatis.spring.annotation.MapperScan; //声明该类是一个springboot引导类:springboot应用的入口 @SpringBootApplication @MapperScan("lucky.service.mapper") //mapper接口的包扫描 public class LuckyServiceProviderApplication { public static void main(String[] args) { SpringApplication.run(LuckyServiceProviderApplication.class, args); } }
<4>UsersService.java
package lucky.service; import lucky.domain.Users; import lucky.mapper.UsersMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service public class UsersService { @Autowired private UsersMapper usersMapper; public Users queryUsersById(Integer id){ return this.usersMapper.selectByPrimaryKey(id); } @Transactional public void deleteUserById(Long id){ this.usersMapper.deleteByPrimaryKey(id); } public List<Users> queryAllUsers() { return this.usersMapper.selectAll(); } }
<5>UsersController.java
package lucky.controller; import lucky.domain.Users; import lucky.service.UsersService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller @RequestMapping(path = "/users") public class UsersController { @Autowired private UsersService usersService; @RequestMapping(path = "/query") @ResponseBody public String queryUsers(){ return "hello users"; } @RequestMapping(path = "/queryUsersById") @ResponseBody public Users queryUsersById(@RequestParam("id") Integer id){ return this.usersService.queryUsersById(id); } /** * 查询所有用户,并在前端显示 * @param model model对象用来向前端传递数据 * @return 返回视图名称 */ @RequestMapping(path = "/queryAllUsers") public String queryAllUsers(Model model){ //1.查询所有用户 List<Users> users = this.usersService.queryAllUsers(); //2.放入模型 model.addAttribute("users",users); //3.返回模板名称(就是classpath:/templates/目录下的html文件名) return "users"; } }
<6>测试效果
(3)服务调用者
搭建lucky-service-consumer服务消费方工程。
<1>Spring脚手架(Spring Initializr)创建工程(在同一个工程project下创建不同的模块)
(4)代码编写
package lucky.service.luckyserviceconsumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class LuckyServiceConsumerApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(LuckyServiceConsumerApplication.class, args); } }
<2>编写配置(application.yml):
server:
port: 8080
<3>编写UserController:
package lucky.service.controller; import lucky.service.domain.Users; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RestTemplate; @Controller @RequestMapping(path = "/consumer/user") public class UserController { @Autowired private RestTemplate restTemplate; @RequestMapping(path = "/queryUsersById") @ResponseBody public Users queryUserById(@RequestParam("id") Integer id){ return this.restTemplate.getForObject("http://localhost:8081/users/queryUsersById?id="+id,Users.class); } }
<4>实体类
package lucky.service.domain; import java.io.Serializable; /** * 数据库表users对应的实体类 */ public class Users implements Serializable { private Integer id; // 主 键 private String username; // 用户名 private String password; // 密 码 private String name; // 姓 名 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", name='" + name + '\'' + '}'; } }
<5>测试效果
3.微服务场景模拟案例存在的问题
- 在consumer中,我们把url地址硬编码到了代码中,不方便后期维护
- consumer需要记忆provider的地址,如果出现变更,可能得不到通知,地址将失效
- consumer不清楚provider的状态,服务宕机也不知道
- provider只有1台服务,不具备高可用性
- 即便provider形成集群,consumer还需自己实现负载均衡
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)