暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

微服务专栏(四):微服务项目的环境整合

修电脑的杂货店 2021-11-16
441


 完成工作的方法,是爱惜每一分钟。——达尔文


1、前言

上节主要是讲解了如何搭建空项目,本节主要是讲解如何在空项目基础上集成各种开发框架,变成一个完整的可直接开发和运行的项目。

前面章节我们也说过,网盘主要是以 SpringBoot+Dubbo 为主的微服务项目,那么如何集成这些这些框架呢?我们首先按照一个逐步的过程来搭建而不是一步到位。

第一步:首先搭建 netdisk-service-provier
 过程,并且只完成最基本的功能就是连接数据库、可以操作数据库即可。

第二步:创建 netdisk-web-perpc
 和 netdisk-web-manage
 这两个工程,并且只完成最基本的可以访问 Restfull 接口即可。

第三步:两个 controller
 工程和 service
 工程都集成 Dubbo,并且通过 Dubbo 远程调用接口

第四步:两个 controller
 工程和 service
 工程,都集成 Nacos
 配置中心

第五步:集成用户登录工程

本节我们需要完成下面这样一个架构,基于这样的架构开发人员可以在其上面直接开发业务代码。

架构简单介绍

  • netdisk-service-provider 工程

    • 底层主要是连接数据库并且做复杂的业务处理

    • 项目启动的时候,把 Dubbo 接口信息往 Zookeeper 注册中心进行保存

  • netdisk-web-perpc 和 netdisk-web-manage 是 controller 工程

    • 不处理复杂业务,一般只做简单的数据校验;

    • 项目启动的时候,从 Zookeeper 注册中心获取 Dubbo 接口信息并保存到本地

    • 它主要是远程调用 netdisk-service-provider 去完成业务处理,如果不是很清楚原理,可以阅读第三篇的 Dubbo 章节。

  • Nacos 是配置中心,主要是保存经常变动的配置信息

架构简单介绍

  • 这个架构图相比上面的架构图,主要是多了蓝色部分,分别是中间服务
    用户系统
    (回顾上一章内容)

  • 核心目的通过中间服务
    网盘系统
    用户系统
    给彻底解耦

工程结构如下所示:

netdisk
|-- netdisk-web-manage (后台controller工程)
|-- netdisk-web-perpc (个人controller工程)
|-- netdisk-service-api (服务api接口工程)
|-- netdisk-service-provider(服务工程)
|-- netdisk-user-api (用户api接口工程)
|-- netdisk-user-provider (用户中间项目工程)
|-- utils-filestore (工具-文件存储)
|-- utils-filesearch (工具-全文检索)
|-- utils-office (工具-文件转换)
|-- utils-filelogs (工具-日志采集)


2、基础环境集成


2.1、Api 接口工程的搭建

netdisk-service-api
|-- src/main/java
| |-- com.micro.disk.service (存放接口)
| |-- com.micro.disk.bean (存放实体)
|-- src/main/resources
|-- pom.xml

  • 其实 Api 接口工程是非常的简单的,不需要继承任何的框架,只需要定义好接口类和接口里面的方法即可。


2.2、service 工程环境的搭建

目标:service 独立运行并且可以对 MySQL 数据库进行操作

netdisk-service-provider
|-- src/main/java
|-- src/main/resources
| |-- application.properties
|-- pom.xml

第一步:在 pom.xml 导入 SpringBoot 坐标

<!--SpringBoot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--接口工程-->
<dependency>
<groupId>com.micro</groupId>
<artifactId>netdisk-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

第二步:在 src/main/resources/application.properties 配置 Tomcat 基本信息

#应用名称
spring.application.name=netdisk-service-web
#内置Tomcat配置
server.tomcat.uri-encoding=UTF-8
server.context-path=/${spring.application.name}
server.port=8013

第三步:集成 ORM 框架,首先在 pom.xml 导入 ORM 坐标

<!--Druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.18</version>
</dependency>
<!--MySQL驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jpa框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

第四步:集成 ORM 框架,在 src/main/resources/application.properties 配置 ORM 相关信息

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://ip:3306/micro-disk
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto=update

第五步:新建入口类

  • com.micro.DiskServiceApp.java

  • 这是 SpringBoot 工程启动入口类,只需要右击 run as 运行 main 函数即可

@SpringBootApplication
public class DiskServiceApp{
public static void main(String[] args) {
SpringApplication.run(DiskServiceApp.class, args);
}
}

好了,service 工程最基本的环境已经搭建完成,项目可以运行、并且可以对 MySQL 进行操作了。


2.3、controller 工程环境的搭建

目标:controller 工程可以正常对外提供 Restfull 接口,并且项目启动之后,通过浏览器访问接口

netdisk-web-perpc
|-- src/main/java
|-- src/main/resources
| |-- application.properties
|-- pom.xml

netdisk-web-manage
|-- src/main/java
|-- src/main/resources
| |-- application.properties
|-- pom.xml

注意的是 netdisk-web-perpc 和 netdisk-web-manage 的配置都如下所示

第一步:在 pom.xml 导入 SpringBoot 坐标

<!--SpringBoot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--接口工程-->
<dependency>
<groupId>com.micro</groupId>
<artifactId>netdisk-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

第二步:在 src/main/resources/application.properties 配置 Tomcat 基本信息

#应用名称(不同工程不同的名称)
spring.application.name=netdisk-web-perpc
#内置Tomcat配置
server.tomcat.uri-encoding=UTF-8
server.context-path=/${spring.application.name}
#应用端口(不同工程不同的端口)
server.port=8012

第三步:新建入口类

  • com.micro.DiskPerpcApp.java

  • 这是 SpringBoot 工程启动入口类,只需要右击 run as 运行 main 函数即可

@SpringBootApplication
public class DiskPerpcApp{
public static void main(String[] args) {
SpringApplication.run(DiskPerpcApp.class, args);
}
}

第四步:新建一个测试接口

@RestController
@RequestMapping("/test")
public class TestControler{
@RequestMapping("/sayHi")
public String sayHi(){
return "test";
}
}
//浏览器访问:http://ip:8012/netdisk-web-perpc/test/sayHi


2.4、集成 Dubbo

目标:controller 和 service 工程分别集成 Dubbo 框架,并且实现远程接口调用。

提示:首先需要预先安装好 Zookeeper

第一步:controller 和 service 工程分别在 pom.xml 导入坐标

<!-- dubbo -->
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!--Zookeeper-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.9</version>
</dependency>

第二步:controller 和 service 工程分别在 src/main/resources/application.properties 配置 dubbo 信息

#zk的地址
spring.dubbo.registry.address=zookeeper://192.168.1.8:2181
#自定义通讯端口(不同工程不同的端口)
spring.dubbo.protocol.port=28013
#服务名称
spring.dubbo.application.name=${spring.application.name}
#协议,默认就是dubbo,可以不写
spring.dubbo.protocol.name=dubbo
#远程通讯发送字节数,默认最大是8M
spring.dubbo.protocol.payload=2147483647
#扫描包名
spring.dubbo.scan=com.micro

第三步:controller 和 service 工程的启动入口类,分别加入启动 Dubbo 注解

//Dubbo配置类生效注解
@EnableDubboConfiguration
@SpringBootApplication
public class DiskPerpcApp{
public static void main(String[] args) {
SpringApplication.run(Perpc.class, args);
}
}

第四步:service 测试发布接口(netdisk-service-provider 工程)

//Dubbo注解
@Service(interfaceClass=TestService.class)
//Spring接口
@Component
public class TestServiceImpl implements TestService{
@Override
public String sayHi(String name){
return "你好:"+name;
}
}

第五步:controller 测试调用接口(netdisk-web-perpc 工程)

@RestController
@RequestMapping("/test")
public class TestControler{
//Dubbo注解
//check=false表示启动的时候是否需要检测service工程是否启动,如果true的时候则工程需要按照顺序启动否则会报错
@Reference(check = false)
private TestService testService;

@RequestMapping("/sayHi")
public String sayHi(String name){
return testService.sayHi(name);
}
}


2.5、集成 Nacos

目标:controller 和 service 工程分别集成 Nacos,并且可以实现从 Nacos 获取配置信息(刷新即生效);同时可以把 zk、mysql 的连接信息放到 Nacos 里面。

提示:首先需要预先安装好 Nacos

第一步:controller 和 service 工程分别在 pom.xml 导入 Nacos 坐标

<!-- nacos -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.1.1</version>
</dependency>

第二步:controller 和 service 工程分别在 src/main/resources/application.properties 配置 Nacos 连接信息

nacos.config.server-addr=192.168.1.8:8848

第三步:controller 和 service 工程的启动入口类,分别加入启动 Nacos 注解

//groupId跟Nacos后台的groupId对应
//dataId跟Nacos后台的dataId对应
//一般情况下,groupId项目的名称(比如:项目为xx电商),dataId跟具体工程名称一致即可
@NacosPropertySource(dataId = "disk-web-perpc",groupId="disk",autoRefreshed=true)
@EnableDubboConfiguration
@SpringBootApplication
public class DiskPerpcApp{
public static void main(String[] args) {
SpringApplication.run(Perpc.class, args);
}
}


2.6、集成用户登录

工程结构

netdisk-user-api
|-- src/main/java
| |-- com.micro.disk.user.bean
| | |-- Page.java
| | |-- SessionUserBean.java
| | |-- TreeJson.java
| | |-- UserBean.java
| |-- com.micro.disk.user.service
| | |-- UserService.java (接口)
|-- src/main/resources
|-- pom.xml

netdisk-user-provider
|-- src/main/java
|-- src/main/resources
| |-- application.properties
|-- pom.xml


第一步:netdisk-user-api 工程提供接口,具体如下

@Data
public class SessionUserBean implements Serializable{
private String id;
private String nickname;
private String username;
private String telephone;
private String token;
}

public interface UserService {
//用户列表:分页
public Page<UserBean> findUserPageList(Integer page,Integer limit,String username,String nickname);
//用户树
public List<TreeJson> findUserTree(String pid,String type);
//登录,需要生成token给客户端
public SessionUserBean login(String username, String password);
//根据token获取用户信息
public SessionUserBean getUserByToken(String token);
//登出
public void logout(String token);
}

第二步:netdisk-user-provider 集成 Dubbo 环境

  • 集成 Dubbo,跟上面布置一样,不重复

第三步:netdisk-user-web 集成 netdisk-user-api 接口工程,并且对接口进行实现

  • 接口的实现主要是通过 HttpClient 去调用真正的用户系统

  • 通过 @Service 注解对外暴露 Dubbo 接口给业务系统调用


3、开发环境优化

上面把 controller 工程和 service 工程的基本环境搭建完成了,但是此时还需要做进一步的配置。

思考:你觉得我们的工程还需要做哪些方面的工作,整个项目才算完善呢?

  • 经过上面的环境搭建,我们的工程确实可以跑起来了,也可以在上面进行开发了,但是还需要做以下基本工作,方便业务代码的开发。

  • 1)每个工程需要基于业务去进行分包,规范好什么样的包放什么样的类,否则项目会越来越乱的

  • 2)controller 工程是跟前端工程进行交互的,需要解决跨域问题、全局异常问题、使用拦截器进行安全认证、返回数据规范问题等。

第一步: 每个项目的业务分包

netdisk-service-provider
|-- src/main/java
| |-- com.micro
| | |-- DiskServiceApp.java (启动类)
| | |-- com.micro.service.impl(接口实现)
| | |-- com.micro.model (实体)
| | |-- com.micro.db.dao (存放dao接口)
| | |-- com.micro.db.jdbc (存放JdbcTemplate操作的类)
| | |-- com.micro.config (存放配置类)
| | |-- com.micro.utils (存放工具类)
| | |-- com.micro.lock (分布式锁)
|-- src/main/resources
| |-- application.properties
|-- pom.xml

  • 描述:这是 Service 工程的分包,主要区分 Model 实体、Service 实现类、dao 层

netdisk-web-perpc
|-- src/main/java
| |-- com.micro
| | |-- DiskPerpcApp.java (启动类)
| | |-- com.micro.controller (存放controller类)
| | |-- com.micro.mvc (核心讲解)
| | | |-- WebMvcConfig.java
| | | |-- LimitInterceptor.java
| | | |-- UserInfoUtils.java
| | | |-- GlobalException.java
|-- src/main/resources
| |-- application.properties
|-- pom.xml

  • 描述:这是 Controller 工程的分包,主要区分 controller 包、mvc 包(用来解决常见的 mvc 业务场景)

特殊说明:如果是单体工程的分包,则主要是以下两种模式,请参考下面

projectname
|-- src/main/java
| |-- com.micro.controller
| | |-- OrderController.java
| | |-- ProductController.java
| |-- com.micro.service
| | |-- OrderService.java
| | |-- ProductService.java
| |-- com.micro.model
| | |-- Order.java
| | |-- Product.java
|-- src/main/resources
| |-- application.properties
|-- pom.xml

  • 描述:按照规则分包,所有的业务都遵守这种规则

  • 优点:包结构比较简单、清晰

  • 缺点:所有的业务对应的类都混在一起了,后期项目升级为分布式架构的时候拆分就会比较麻烦。

projectname
|-- src/main/java
| |-- com.micro.modules
| | |-- order
| | | |-- controller
| | | | |-- OrderController.java
| | | |-- service
| | | | |-- OrderService.java
| | | |-- model
| | | | |-- Order.java
| | |-- product
| | | |-- controller
| | | | |-- ProductController.java
| | | |-- service
| | | | |-- ProductService.java
| | | |-- model
| | | | |-- Product.java
|-- src/main/resources
| |-- application.properties
|-- pom.xml

  • 描述:根据不同业务来进行分包

  • 优点:后期如果升级为分布式架构的时候,很容易就能剥离开来

  • 缺点:包结构比较复杂

第二步: controller 工程返回数据规范制定,比如说:

@RestController
@RequestMapping("/test")
public class TestController{
@Reference(check = false)
private TestService testService;

@RequestMapping("/findUsers")
public List<User> findUsers(){
return testService.findUsers();
}

@RequestMapping("/save")
public String save(User user){
testService.save(user);
return "添加成功";
}
}

  • 这种模式直接返回结果给前端,缺少响应状态码、响应状态信息,不能形成一种规范,因此我们需要制定一种简单的标准。

@Data
public class Result implements Serializable{
private Integer code;//响应编码(200成功,500失败)
private String msg;//响应信息(如:添加成功)
private Object data;//响应数据
}
public class ResultUtils {
public static Result success(String successMsg,Object data){
Result r=new Result();
r.setCode(200);
r.setMsg(successMsg);
r.setData(data);
return ar;
}
public static Result error(String errorMsg){
Result r=new Result();
r.setCode(500);
r.setMsg(errorMsg);
return r;
}
}

@RestController
@RequestMapping("/test")
public class TestController{
@Reference(check = false)
private TestService testService;

@RequestMapping("/findUsers")
public Result findUsers(){
try{
return ResultUtils.success("查询成功",testService.findUsers());
}catch(Exception e){
return ResultUtils.error("查询失败");
}
}

@RequestMapping("/save")
public Result save(User user){
try{
testService.save(user);
return ResultUtils.success("添加成功",null);
}catch(Exception e){
return ResultUtils.error("添加失败");
}
}
}

  • 这种模式,使用起来也非常的方便,并且前端也能获取请求的状态信息。

第三步: controller 工程解决前端 Ajax 请求跨域问题

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
//解决跨域问题
@Override
public void addCorsMappings(CorsRegistry cr) {
cr.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.maxAge(3600)
.allowedMethods("GET", "POST","OPTIONS");
}
}

第四步: controller 工程使用拦截器来拦截用户请求,并且对请求进行登录认证和权限认证

//自定义拦截器
@Component
public class LimitInterceptor implements HandlerInterceptor{
//这个是netdisk-user-api提供的接口
@Reference(check=false,timeout=120000)
private UserService userService;


@Override
public boolean preHandle(HttpServletRequest req,HttpServletResponse res,Object obj){

//1.获取token和判断token是否为非空
String token="";
token=request.getHeader("token");
if(StringUtils.isEmpty(token)){
token=request.getParameter("token");
}
if(StringUtils.isEmpty(token)){
throw new RuntimeException("token不能为空");
}

//2.调用接口对token进行认证
SessionUserBean ui=userService.getUserByToken(token);
if(ui==null){
throw new RuntimeException("token不存在");
}else{
//把用户信息放到request里面(!!!)
req.setAttribute("userInfo",ui);
}

return true;

}
}

//拦截器注册
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
@Autowired
private LimitInterceptor inter;

@Override
public void addInterceptors(InterceptorRegistry reg) {
reg.addInterceptor(inter).addPathPatterns("/**") //拦截所有的请求
}
}

第五步: controller 工程里面如何获取当前操作人的信息呢?

//封装从request获取用户信息的工具类
public class UserInfoUtils {
public static SessionUserBean getBean(HttpServletRequest req){
SessionUserBean userInfo=(SessionUserBean) req.getAttribute("userInfo");
return userInfo;
}
}

@RestController
@RequestMapping("/test")
public class TestController{
@RequestMapping("/findUsers")
public Result findUsers(HttpServletRequest req){
//获取用户信息
SessionUserBean user=UserInfoUtils.getBean(req);

String userId=user.getId();
String userName=user.getUsername();
}
}

第六步: controller 工程配置全局异常信息捕捉

@ControllerAdvice
public class GlobalException {
@ExceptionHandler(Exception.class) //异常类型
@ResponseBody //返回json格式
public Result defaultExceptionHandler(HttpServletRequest req,Exception e){
return ResultUtils.error(e.getMessage());
}
}

提示:service 工程它不是直接跟浏览器进行交互,因此不需要配置跨域和认证;那么 service 工程的数据返回需要封装成 Result
 实体吗?其实是不用的,因为你把它看成本地是 Service,那么本地 Service 会用封装 Result
 返回吗?其实可以在 controller 捕捉异常,或者在全局异常里面捕捉 service 返回异常信息即可。


4、小结

好了,本节内容就讲解到这里,可能代码量比较多,但是思路还是比较清晰,主要是一步步讲解如何集成各种基础框架和做基础的配置,让项目从抽象的架构落地,并且能够直接开发的完善的项目。如果同学基础比较薄弱,可以参考文档的思路来自己动手搭建一遍,这样更容易发现问题。

纸上得来终觉浅,绝知此事要coding...


文章转载自修电脑的杂货店,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论