基本案例
第 1 节 案例说明
本部分我们按照普通⽅式模拟⼀个微服务之间的调⽤(后续我们将⼀步步使⽤ Spring Cloud的组件对案例进⾏改造)。
拉勾App⾥有这样⼀个功能:“⾯试直通⻋”,当求职⽤户开启了⾯试直通⻋之后,会根据企业客户的招聘岗位需求进⾏双向匹配。其中有⼀个操作是:为企业⽤户开启⼀个定时任务,根据企业录⼊的⽤⼈条件,每⽇匹配⼀定数量的应聘者“投递”到企业的资源池中去,那么系统在将匹配到的应聘者投递到资源池的时候需要先检查:此时应聘者默认简历的状态(公开/隐藏),如果此时默认简历的状态已经被应聘者设置为“隐藏”,那么不再执⾏“投递”操作。 “⾃动投递功能”在“⾃动投递微服务”中,“简历状态查询功能”在“简历微服务”中,那么就涉及到“⾃动投递微服务”调⽤“简历微服务”查询简历。在这种场景下,“⾃动投递微服务”就是⼀个服务消费者,“简历微服务”就是⼀个服务提供者。
第 2 节 案例数据库环境准备
简历基本信息表 r_resume
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for r_resume
-- ----------------------------
DROP TABLE IF EXISTS `r_resume`;
CREATE TABLE `r_resume` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sex` varchar(10) DEFAULT NULL COMMENT '性别',
`birthday` varchar(30) DEFAULT NULL COMMENT '出生日期',
`work_year` varchar(100) DEFAULT NULL COMMENT '工作年限',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号码',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`status` varchar(80) DEFAULT NULL COMMENT '目前状态',
`resumeName` varchar(500) DEFAULT NULL COMMENT '简历名称',
`name` varchar(40) DEFAULT NULL,
`createTime` datetime DEFAULT NULL COMMENT '创建日期',
`headPic` varchar(100) DEFAULT NULL COMMENT '头像',
`isDel` int(2) DEFAULT NULL COMMENT '是否删除 默认值0-未删除 1-已删除',
`updateTime` datetime DEFAULT NULL COMMENT '简历更新时间',
`userId` int(11) DEFAULT NULL COMMENT '用户ID',
`isDefault` int(2) DEFAULT NULL COMMENT '是否为默认简历 0-默认 1-非默认',
`highestEducation` varchar(20) DEFAULT '' COMMENT '最高学历',
`deliverNearByConfirm` int(2) DEFAULT '0' COMMENT '投递附件简历确认 0-需要确认 1-不需要确认',
`refuseCount` int(11) NOT NULL DEFAULT '0' COMMENT '简历被拒绝次数',
`markCanInterviewCount` int(11) NOT NULL DEFAULT '0' COMMENT '被标记为可面试次数',
`haveNoticeInterCount` int(11) NOT NULL DEFAULT '0' COMMENT '已通知面试次数',
`oneWord` varchar(100) DEFAULT '' COMMENT '一句话介绍自己',
`liveCity` varchar(100) DEFAULT '' COMMENT '居住城市',
`resumeScore` int(3) DEFAULT NULL COMMENT '简历得分',
`userIdentity` int(1) DEFAULT '0' COMMENT '用户身份1-学生 2-工人',
`isOpenResume` int(1) DEFAULT '3' COMMENT '人才搜索-开放简历 0-关闭,1-打开,2-简历未达到投放标准被动关闭 3-从未设置过开放简历',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2195388 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
第 3 节 案例⼯程环境准备
我们基于SpringBoot来构造⼯程环境,我们的⼯程模块关系如下所示:
- 建立一个父工程 lagou-parent
- 建立一个公共模块 lagou-service-common 项目提供数据模型,公共类,组件
- 建立一个简历微服务模块 lagou-service-resume 项目完成CRUD,提供API接口
- 建立一个自动投递微服务模块 lagou-service-autodeliver 通过接口调用简历微服务模块
整个项目的结构如下图所示:
3.1 构建lagou-parent父工程
<?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">
....
<!--⽗⼯程打包⽅式为pom-->
<packaging>pom</packaging>
<!--spring boot ⽗启动器依赖-->
<!--spring boot 父启动器依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<!--web依赖-->
<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>
<!--lombok⼯具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<!-- Actuator可以帮助你监控和管理Spring Boot应⽤-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<!--打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2 公共服务
-
新建maven项目名为:lagou-service-common
-
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
- 新建简历的pojo
@Data
@Entity
@Table(name="r_resume")
public class Resume {
@Id
private Long id; // 主键
private String sex; // 性别
private String birthday; // ⽣⽇
...
}
3.3 简历微服务
此项目针对简历进行查询操作,并且提供查询接口,查询某个简历的开放状态
新建模块 lagou-service-resume,分别创建常规的CRUD操作,建立Dao,Service,Controller三层,外加一个启动程序
ResumeDao
public interface ResumeDao extends JpaRepository<Resume,Long> {
}
ResumeService
public interface ResumeService {
Resume findDefaultResumeByUserId(Long userId);
}
ResumeServiceImpl
@Service
public class ResumeServiceImpl implements ResumeService {
@Autowired
private ResumeDao resumeDao;
@Override
public Resume findDefaultResumeByUserId(Long userId) {
Resume resume=new Resume();
resume.setUserId(userId);
resume.setIsDefault(1);
Example<? extends Resume> example=Example.of(resume);
return resumeDao.findOne(example).get();
}
}
ResumeController
@RestController
@RequestMapping("/resume")
public class ResumeController {
@Autowired
private ResumeService resumeService;
//http://localhost:8080/resume/openstate/1545133
@GetMapping("/openstate/{userId}")
public Integer findDefaultResumeState(@PathVariable Long userId){
return resumeService.findDefaultResumeByUserId(userId).getIsOpenResume();
}
}
LagouResumeApplication
@SpringBootApplication
@EntityScan("com.lagou.edu.pojo")
public class LagouResumeApplication {
public static void main(String[] args) {
SpringApplication.run(LagouResumeApplication.class, args);
}
}
application.yml文件
server:
port: 8080
spring:
application:
name: lagou-service-resume
datasource:
driver-class-name: com.mysql.jdbc.Driver
#url: jdbc:mysql://localhost:3306/lagou?useUnicode=true&characterEncoding=utf8
url: jdbc:mysql://localhost:3306/lagou?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: root
jpa:
database: MySQL
show-sql: true
hibernate:
naming:
physical-strategy:
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl #避免将驼峰命名转换为下划线命名
3.4 投递服务
投递服务就是简单的调用简历微服务的接口,完成查询
AutodeliverApplicaton
@SpringBootApplication
public class AutodeliverApplicaton {
public static void main(String[] args) {
SpringApplication.run(AutodeliverApplicaton.class,args);
}
//使用Resttemple进行远程调用,先对注入该对象
@Bean
public RestTemplate getRestTemplete(){
return new RestTemplate();
}
}
AutoDeliverController
@RestController
@RequestMapping("/autodeliver")
public class AutoDeliverController {
@Autowired
private RestTemplate restTemplate;
//http://localhost:8090/autodeliver/checkState/1545133
@GetMapping("/checkState/{userId}")
public Integer findResumeOpenState(@PathVariable Long userId){
//调用远程服务--> 简历微服务
//直接拿到结果
Integer forObject = restTemplate.getForObject("http://localhost:8080/resume/openstate/" + userId, Integer.class);
return forObject;
}
}
application.yml
server:
port: 8090
第 4 节 案例代码问题分析
我们在⾃动投递微服务中使⽤RestTemplate调⽤简历微服务的简历状态接⼝时(Restful API 接⼝)。在微服务分布式集群环境下会存在什么问题呢?怎么解决?
存在的问题:
- 1)在服务消费者中,我们把url地址硬编码到代码中,不⽅便后期维护。
- 2)服务提供者只有⼀个服务,即便服务提供者形成集群,服务消费者还需要⾃⼰实现负载均衡。
- 3)在服务消费者中,不清楚服务提供者的状态。
- 4)服务消费者调⽤服务提供者时候,如果出现故障能否及时发现不向⽤户抛出异常⻚⾯?
- 5)RestTemplate这种请求调⽤⽅式是否还有优化空间?能不能类似于Dubbo那样玩?
- 6)这么多的微服务统⼀认证如何实现?
- 7)配置⽂件每次都修改好多个很麻烦!?
- 8)....
上述分析出的问题,其实就是微服务架构中必然⾯临的⼀些问题:
- 1)服务管理:⾃动注册与发现、状态监管
- 2)服务负载均衡
- 3)熔断
- 4)远程过程调⽤
- 5)⽹关拦截、路由转发
- 6)统⼀认证
- 7)集中式配置管理,配置信息实时⾃动更新
总结 相信看到这里,大家都有所共鸣。这不就是我们平常的CRUD么?然后维护一个别人写好的接口地址,根据业务来进行调用,别人蹦了,自己也就蹦了。稍微好一点的,还能做点日志记录,微信推送。企业内部业务出了问题也没太大关系,挂了就挂了。是时候彻底改变这种无力回天的现象了
基本演示代码 github.com/langkemaoxi…