手写一个博客平台 ~第四天

作者:fyupeng
技术专栏:☞ https://github.com/fyupeng
项目地址:☞ https://github.com/fyupeng/distributed-blog-system-api
项目预览地址:☞ 博客第四天


留给读者

对于Redis缓存是干嘛用的,我直接开门见山,我用到了Java热搜,也就是使用它实现了一个Redis排名。

关于Redis的客户端连接工具,我使用的是RDMRedis DataBase Manager),叫Redis数据库管理工具。

主要实现用到的有以下四个字段:

image-20220730141940684

关于这四个字段的设计,我想大家可以先去看看我推荐的这篇 Java热搜,或许对你项目的提升有点帮助呢!

当然,这时到后期才会用到,现在暂时不需要使用到,为了给项目有更高的加分项,于是有了新的创意

这个项目使用服务分离,也就是把暴露接口的服务作为代理服务,只暴露接口,真实服务需要通过RPC请求协议获取

有点类似Spring Cloud——微服务,只不过微服务我是自己基于Netty手写的,也就是手写了一个分布式的博客系统,这听起来是不是很牛掰。

我相信,就算是Netty零基础小白,也可以轻易驾驭我的RPC框架,有兴趣可以传送过去学习

传送门:基于 NacosRPC 分布式微服务框架

因为我使用了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>

其中,两个模块的依赖不同的地方在于,代理依赖不需要做持久层数据的持久化操作,只需调用真实服务实现的方法即可,因此真实服务比代理服务多了MySqlMongoDB和数据源Druid,当然Redis也包含,只不过它会用在代理层中做过滤。

这样一来,代理服务就不需要编写ServiceMapper这一层了,直接可以删除Mapper的依赖和相关的Mapper接口和配置文件、application.properties配置信息

2. 分离服务

分离服务,其实说白就是,对于登录注册模块作为一个服务模块,文章管理作为一个服务模块,标签分类管理作为一个服务模块,这样就可以把请求分流出去,通俗来说就是把请求注册登录的请求到端口号8080,文章管理请求到8081,标签分类请求到8082,而设置端口号的服务可以放到不同的服务器上,目的是为了分布式服务,就算其中一台挂了也不影响其他服务,而且一种服务还可以克隆多台服务,作为备份机,或者负载均衡。

2.1 效果图
  • 接口

image-20220816220949829

  • 测试Rpc接口

image-20220816221014858

  • 测试结果响应

image-20220816221048756

  • 包校验日志

image-20220816223524562

  • 注册服务

image-20220816223620013

  • 接收结果

image-20220816223732240

  • 长连接机制

image-20220816223755320

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 + "\'");
    }
}
2.4 图示流程

image-20220816232124108

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嗝屁小孩纸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值