作者:fyupeng
技术专栏:☞ https://github.com/fyupeng
项目地址:☞ https://github.com/fyupeng/distributed-blog-system-api
项目预览地址:☞ 博客第四天
留给读者
对于Redis
缓存是干嘛用的,我直接开门见山,我用到了Java热搜,也就是使用它实现了一个Redis
排名。
关于Redis的客户端连接工具,我使用的是RDM
(Redis DataBase Manager
),叫Redis
数据库管理工具。
主要实现用到的有以下四个字段:
关于这四个字段的设计,我想大家可以先去看看我推荐的这篇 Java热搜,或许对你项目的提升有点帮助呢!
当然,这时到后期才会用到,现在暂时不需要使用到,为了给项目有更高的加分项,于是有了新的创意
这个项目使用服务分离,也就是把暴露接口的服务作为代理服务,只暴露接口,真实服务需要通过RPC
请求协议获取
有点类似Spring Cloud
——微服务,只不过微服务我是自己基于Netty
手写的,也就是手写了一个分布式的博客系统,这听起来是不是很牛掰。
我相信,就算是Netty
零基础小白,也可以轻易驾驭我的RPC
框架,有兴趣可以传送过去学习
传送门:基于 Nacos
的 RPC 分布式微服务框架
因为我使用了Springboot
来集成RPC
,所以对于每个真实服务,都需要提供数据源、持久层对象、Mapper
对象和Service
对象,然而如果每个类中固定创建,而不自动注入依赖来创建,将会导致每次请求都会创建,就算可以声明为static
类,但这是很不规范的。
所以就想到了Spring
的容器,而且避免麻烦,就用了SpringBoot
的简单配置,虽然Springboot
是提供Web
应用的框架,但我只需要使用到它的容器注解,所以这里有一个问题待解决,先留给大家思考
除此之外,Springboot
每次启动还会占用一个端口号,但我的RPC
服务本身使用套接字绑定端口,这里又有一个问题待解决
大家稍安勿躁,我这个问题想了几天,最后还是解决了,也是在八月份解决的。
下面我将分为几个简明的步骤来介绍
1. 升级 Springboot 为 2.0
1.1 代理服务模块依赖
commons
模块
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<!-- 排除springboot默认的logback日志框架 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<!-- 排除springboot 默认的commons logging 实现(版本低,出现方法找不到问题) -->
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-jcl</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 与 logback 整合 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!--引入log4j日志依赖,目的是使用 jcl-over-slf4j来重写 commons logging 的实现-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入spring aop 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 资源配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- apache 工具类 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<!-- swagger2 配置 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- 引入 redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</exclusion>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
</dependencies>
1.2 真实服务模块依赖
commons
模块
<dependencies>
<!--整合mongodb-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-mongodb</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<!-- 排除springboot默认的logback日志框架 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<!-- 排除springboot 默认的commons logging 实现(版本低,出现方法找不到问题) -->
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-jcl</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 与 logback 整合 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!--引入log4j日志依赖,目的是使用 jcl-over-slf4j来重写 commons logging 的实现-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入spring aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 资源配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 阿里开源数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<!-- <version>1.2.4</version>-->
<version>2.0.2</version>
</dependency>
<!--pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
<!-- apache 工具类 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 引入 redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</exclusion>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
</dependencies>
其中,两个模块的依赖不同的地方在于,代理依赖不需要做持久层数据的持久化操作,只需调用真实服务实现的方法即可,因此真实服务比代理服务多了MySql
和MongoDB
和数据源Druid
,当然Redis
也包含,只不过它会用在代理层中做过滤。
这样一来,代理服务就不需要编写Service
和Mapper
这一层了,直接可以删除Mapper
的依赖和相关的Mapper
接口和配置文件、application.properties
配置信息
2. 分离服务
分离服务,其实说白就是,对于登录注册模块作为一个服务模块,文章管理作为一个服务模块,标签分类管理作为一个服务模块,这样就可以把请求分流出去,通俗来说就是把请求注册登录的请求到端口号8080,文章管理请求到8081,标签分类请求到8082,而设置端口号的服务可以放到不同的服务器上,目的是为了分布式服务,就算其中一台挂了也不影响其他服务,而且一种服务还可以克隆多台服务,作为备份机,或者负载均衡。
2.1 效果图
- 接口
- 测试Rpc接口
- 测试结果响应
- 包校验日志
- 注册服务
- 接收结果
- 长连接机制
2.2 代理服务
- 配置文件
application.properties
配置信息如下
############################################################
#
# Server Port
#
############################################################
server.port=8080
############################################################
# Server - tomcat
############################################################
# Tomcat URI Encoding
server.tomcat.uri-encoding=UTF-8
pagehelper.helperDialect=mysql
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
# springboot 1.5
#spring.http.multipart.maxFileSize=150Mb
#spring.http.multipart.maxRequestSize=1000Mb
# springboot 2.0
spring.servlet.multipart.max-file-size=150MB
spring.servlet.multipart.max-request-size=1000MB
############################################################
#
# Redis
#
############################################################
# Redis default use dataBase
#spring.redis.database=0
## Redis Host
spring.redis.host=localhost
## Redis Port
spring.redis.port=6379
# Redis password
spring.redis.password=root
#spring.redis.pool.max-active=300
spring.redis.jedis.pool.max-active=300
#spring.redis.pool.max-wait=10000
spring.redis.jedis.pool.max-wait=10000
#spring.redis.pool.maxIdle=300
spring.redis.jedis.pool.max-idle=300
#spring.redis.pool.minIdle=6
spring.redis.jedis.pool.min-idle=6
spring.redis.timeout=0
- 测试接口
写到api
模块中
@ApiOperation(value = "testRpc", notes = "测试Rpc微服务的接口")
@PostMapping(value = "/testRpc")
public BlogJSONResult testRpc(String message) {
HelloService proxy = rpcClientProxy.getProxy(HelloService.class);
BlogJSONResult result = proxy.sayHello(message);
return result;
}
- 服务接口
写到service
模块中
package com.fyupeng.service;
import com.fyupeng.utils.BlogJSONResult;
/**
* @Auther: fyp
* @Date: 2022/8/12
* @Description: 测试Rpc的第一个接口
* @Package: PACKAGE_NAME
* @Version: 1.0
*/
public interface HelloService {
BlogJSONResult sayHello(String message);
}
2.3 真实服务
- 配置文件
application.properties
配置信息如下
############################################################
#
# Close Port And provide bean definition ioc
#
############################################################
# 不开放 端口 占用,并且允许 Bean 重写
spring.main.web-application-type=none
spring.main.allow-bean-definition-overriding=true
############################################################
#
# DataSource Pool - druid
#
############################################################
spring.datasource.url=jdbc:mysql:/localhost:3306/fyupeng_blog?useUnicode=true&characterEncoding=utf8&useJDBCComplliantTimezoneShift=true\
&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true
spring.datasource.druid.stat-view-servlet.allow=true
############################################################
#
# mybatis
#
############################################################
# mybatis
mybatis.type-aliases-package=com.fyupeng.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
# Mapper
mapper.mappers=com.fyupeng.utils.MyMapper
mapper.not-empty=false
mapper.identity=MYSQL
# page pagination
pagehelper.helperDialect=mysql
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
# springboot 1.5
#spring.http.multipart.maxFileSize=150Mb
#spring.http.multipart.maxRequestSize=1000Mb
# springboot 2.0
spring.servlet.multipart.max-file-size=150MB
spring.servlet.multipart.max-request-size=1000MB
############################################################
#
# Redis
#
############################################################
# Redis default use dataBase
#spring.redis.database=0
## Redis Host
spring.redis.host=localhost
## Redis Port
spring.redis.port=6379
# Redis password
spring.redis.password=root
#spring.redis.pool.max-active=300
spring.redis.jedis.pool.max-active=300
#spring.redis.pool.max-wait=10000
spring.redis.jedis.pool.max-wait=10000
#spring.redis.pool.maxIdle=300
spring.redis.jedis.pool.max-idle=300
#spring.redis.pool.minIdle=6
spring.redis.jedis.pool.min-idle=6
spring.redis.timeout=0
# mongodb
#spring.data.mongodb.host=localhost
#spring.data.mongodb.port=27017
#spring.data.mongodb.database=fyupeng-blog
##spring.data.mongodb.authentication-database=admin
##spring.data.mongodb.username=root
##spring.data.mongodb.password=root
# 保证 能够正确 注入依赖
#spring.main.allow-bean-definition-overriding=true
- 服务启动
package com.fyupeng;
import com.zhkucst.anotion.ServiceScan;
import com.zhkucst.enums.SerializerCode;
import com.zhkucst.net.netty.server.NettyServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import tk.mybatis.spring.annotation.MapperScan;
/**
* @Auther: fyp
* @Date: 2022/8/13
* @Description:
* @Package: com.fyupeng
* @Version: 1.0
*/
@Slf4j
@ServiceScan
@SpringBootApplication
@EnableScheduling
@MapperScan(basePackages = "com.fyupeng.mapper")
@ComponentScan(basePackages = {"com.fyupeng", "org.n3r.idworker"})
public class RegisterAndLoginServer implements CommandLineRunner {
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(RegisterAndLoginServer.class, args);
}
@Override
public void run(String... args) throws Exception {
//这里也可以添加一些业务处理方法,比如一些初始化参数等
while(true){
try {
NettyServer nettyServer = new NettyServer("127.0.0.1", 8081, SerializerCode.KRYO.getCode());
nettyServer.start();
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
log.error("RegisterAndLoginService is died: {}",e);
}
}
}
}
- 服务接口
切记,必须与代理服务中的服务接口保持一致
package com.fyupeng.service;
import com.fyupeng.utils.BlogJSONResult;
/**
* @Auther: fyp
* @Date: 2022/8/12
* @Description: 测试Rpc的第一个接口
* @Package: PACKAGE_NAME
* @Version: 1.0
*/
public interface HelloService {
BlogJSONResult sayHello(String message);
}
- 真实服务
package com.fyupeng.service.impl;
import com.fyupeng.service.HelloService;
import com.fyupeng.utils.BlogJSONResult;
import com.zhkucst.anotion.Service;
/**
* @Auther: fyp
* @Date: 2022/8/13
* @Description: 测试HelloService实现类
* @Package: com.fyupeng.service.impl
* @Version: 1.0
*/
@Service
public class HelloServiceImpl implements HelloService {
@Override
public BlogJSONResult sayHello(String message) {
return BlogJSONResult.ok("这里是服务端:已收到你发送的消息\'" + message + "\'");
}
}