钱塘江上潮信来 今日方知我是我.|

Appletree24

园龄:2年10个月粉丝:25关注:0

2022-09-02 23:00阅读: 164评论: 0推荐: 0

湘潭大学新生匿名问答网站——解湘 项目总结

湘潭大学新生匿名问答网站——解湘 项目总结

一.开发进度

温馨提示:左下角有音乐播放器

解湘

56nFX.jpg

项目首页

kOMOK.png

大一暑假过半,7月29日建立本地工程文件

kO8Lk.png

其中项目在github上经历七次push(第八次为修改配置文件,防止数据库泄露),但在本地修改次数远远大于七次。

仓库地址Appletree24/Sky31Welcome (github.com)

后端开发均为我一人完成,前端开发由他人负责。除此之外,特感谢三翼设计部门设计出此次项目的UI界面

kjOMI.png

实际应用接口数量为31个,采用APIFOX进行团队接口管理。部分后端实现接口实际因前端功能不需没有加入。

二.项目大纲

此项目立项起,我就很明确这是个传统的论坛类项目,砍去一些如上传图片用户信息修改等不必要功能。这是一个很好的练习基于Java进行Web开发的机会,不计所谓回报,我投身到了这次的开发中。

kj6Sm.png

项目技术栈总览如上图所示。

其中因技术不牢固原因,Spring Security框架最终并未能加入项目进行权限管理。考虑使用Shiro,但因假期繁忙,最终放弃。

其中邮件验证功能因避免网站使用的复杂性,最终删去。

kjhN7.png

C/S之间使用Nginx进行反向代理,因图片功能删去,未配置CDN加速。

三.项目中遇到的问题&收获

1.Base64编码后长度必须为4的倍数

​ 起因为项目采用Base64对用户密码进行加盐处理,但因方式不当。导致服务端频繁报错,无法正常使用。

2.Mybatis-plus导致Mysql数据库主键出现负数

​ 测试期间发现ORM框架生成的数据在Mysql中出现了id为负数的情况。如下图所示

kjCMi.png

​ MP-plus有五种主键ID生成策略:

  • AUTO,配合数据库设置自增主键,可以实现主键的自动增长,类型为nmber;

  • INPUT,由用户输入;

  • NONE,不设置,等同于INPUT;

  • ASSIGN_ID,只有当用户未输入时,采用雪花算法生成一个适用于分布式环境的全局唯一主键,类型可以是String和number;

  • ASSIGN_UUID,只有当用户未输入时,生成一个String类型的主键,但不保证全局唯一;

​ 其中默认为ASSIGN_ID,即使用雪花算法进行生成。这也就导致对应生成的主键应该使用Long类型进行存储,且设置主键类型为bigint。也可在Java代码中设置@TableId注解

3.跨域问题

​ 老生长谈的一个问题了。本次跨域代码如下:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//设置放行哪些原始域 SpringBoot2.4.4下低版本使用.allowedOrigins("*")
.allowedOriginPatterns("*")
//放行哪些请求方式
.allowedMethods("GET", "POST", "PUT", "DELETE")
//.allowedMethods("*") //或者放行全部
//放行哪些原始请求头部信息
.allowedHeaders("*")
//暴露哪些原始请求头部信息
.exposedHeaders("*");
}
}

​ 如此常见的一个问题,并不可能在开发前后端分离的项目时未进行考虑。但本次遇到的问题是,发现前端也要在vite中配置有关跨域的内容才能使cookie正常出现。

4.响应头中set-cookie字段

  1. set-cookie是一个函数,由服务器向浏览器发出响应
  2. cookie是服务器发送给浏览器的变量
  3. 浏览器向服务器发送请求(put,get,post,delect方法),服务器会使用set-cookie()方法向本地的浏览器发送cookie,存在客户端主机中的一个文件下

set-cookie响应头可以设置如下属性:

属性 意义
NAME=VALUE 赋予 Cookie 的名称和其值(必需项)
expires=DATE Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止)
path=PATH 将服务器上的文件目录作为Cookie的适用对象(若不指定则默认为文档所在的文件目录)
domain=域名 作为 Cookie 适用对象的域名 (若不指定则默认为创建 Cookie的服务器的域名)
Secure 仅在 HTTPS 安全通信时才会发送 Cookie
HttpOnly 加以限制, 使 Cookie 不能被 JavaScript 脚本访问

max-age
设置cookie的相对有效期。expire和max-age通常仅设置一个即可。比如设置max-age为1000,浏览器在添加cookie时,会自动设置它的expire为当前时间加上1000秒,作为过期时间。
如果不设置expire,又没有设置max-age,则表示会话结束后过期。
对于大部分浏览器而言,关闭所有浏览器窗口意味着会话结束。

5.用户无法发送emoji表情

​ Mysql中的utf8支持一个字符最多3个字节,但emoji表情为4个字节,所以需要切换为utf8mb4即可

6.敏感词过滤器

​ 使用前缀树算法实现敏感词过滤器。

@Component
public class SensitiveFilter {
private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);
private static final String REPLACEMENT = "***";
private TrieNode rootNode = new TrieNode();
@PostConstruct
public void init() {
try (
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
) {
String keyword;
while ((keyword = reader.readLine()) != null) {
this.addKeyWord(keyword);
}
} catch (IOException e) {
logger.error("敏感词加载失败: " + e.getMessage());
}
}
public String filter(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
TrieNode tempNode = rootNode;
int begin = 0;
int position = 0;
StringBuilder sb = new StringBuilder();
while (begin < text.length()) {
char c = text.charAt(position);
if (isSymbol(c)) {
if (tempNode == rootNode) {
sb.append(c);
begin++;
}
position++;
continue;
}
tempNode = tempNode.getSubNode(c);
if (tempNode == null) {
sb.append(text.charAt(begin));
position = ++begin;
tempNode = rootNode;
} else if (tempNode.isEnd()) {
sb.append(REPLACEMENT);
begin = ++position;
tempNode = rootNode;
} else {
if (position < text.length() - 1) {
position++;
} else {
sb.append(text.charAt(begin));
position = ++begin;
tempNode = rootNode;
}
}
}
sb.append(text.substring(begin));
return sb.toString();
}
private boolean isSymbol(Character c) {
return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
}
private void addKeyWord(String keyword) {
TrieNode tempNode = rootNode;
for (int i = 0; i < keyword.length(); i++) {
char c = keyword.charAt(i);
TrieNode subNode = tempNode.getSubNode(c);
if (subNode == null) {
subNode = new TrieNode();
tempNode.addSubNode(c, subNode);
}
tempNode = subNode;
if (i == keyword.length() - 1) {
tempNode.setEnd(true);
}
}
}
private class TrieNode {
private boolean isEnd = false;
private Map<Character, TrieNode> setNodes = new HashMap<>();
public boolean isEnd() {
return isEnd;
}
public void setEnd(boolean end) {
isEnd = end;
}
public void addSubNode(Character c, TrieNode node) {
setNodes.put(c, node);
}
public TrieNode getSubNode(Character c) {
return setNodes.get(c);
}
}
}

7.切实使用如Redis、Kafka等技术

​ 此前只是学习过Redis、Kafka等技术,但离专业课太远,而又没有合适的项目,于是乎一直悬浮在空中,对技术一知半解。

​ 此项目中的点赞功能、查看个人信息功能、登陆凭证存储功能、消息提醒功能均使用了Redis,也即当对象需要被频繁的访问时,我们可以使用Redis解决问题。

​ 此项目中的消息提醒使用Kafka消息队列进行开发,在学习Kafka的过程中,再次巩固了设计模式中的发布-订阅模式。在此抛出一个疑问,能否使用定期查询数据库的形式来实现消息提醒呢?

8.SSL证书配置

​ 如果要使用HTTPS,那么需要配置SSL证书,在腾讯云中可白嫖免费的证书。此次采用nginx形式进行配置,只需在nginx对应配置文件中加入固定内容,并将证书文件放在置顶目录下即可,十分简单。

server {
listen 443 ssl;
server_name question.tsky31.cn;
ssl_certificate question.tsky31.cn_bundle.crt;
ssl_certificate_key question.tsky31.cn.key;
ssl_session_timeout 5m;
#请按照以下协议配置
ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2;
#请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
client_max_body_size 1000m;
}
server {
listen 80;
server_name question.tsky31.cn;
return 301 https://$host$request_uri;
}

9.域名配置流程

​ 首先要申请一个域名,例如本次的tsky31.cn,如今域名一般都在云服务提供商的后台可以进行管理,以腾讯云为例,在申请到域名后,进行对应域名的DNS解析管理,添加进项目所需要的域名,例如question.tsky31.cn,完成解析

​ 之后在服务器中进行nginx的配置即可。

10.统一接口形式

​ 在接口的返回部分,最好是可以统一形式,采用创建一个类的方式来达到目的。其中进行重载,以适应不同的返回情况

package com.sky31.domain;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> {
/**
* 状态码
*/
private Integer code;
/**
* 提示信息,如果有错误时,前端可以获取该字段进行提示
*/
private String msg;
/**
* 查询到的结果数据,
*/
private T data;
public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
}

11.tomcat日志查看

​ 关于tomcat的日志,启动部分的成功与否,以及报错信息,存放在tomcat目录下/logs中的catalina.xxxx-xx-xx.log文件中,可以使用cat命令查看;而成功启动后的信息,如请求接口时终端的输出,则存放在目录中的catalina.out文件中,可以使用tail -f的命令进行查看。

12.内网穿透

​ 因此次服务器环境较为特殊,所以跟着教程学习了内网穿透,使用了工具frp。

13.拦截器配置

​ 关于SpringBoot的拦截器,可建一名为interceptor的软件包,在包下进行拦截器的编写。使拦截器类实现HandlerInterceptor这个接口,选择性重写其中的perHandlepostHandleafterCompletion方法。

​ 之后新建配置类,实现WebMvcConfigurer接口,在其中的addInterceptors方法中注册编写的拦截器类即可。其中拦截器注册类拥有增加排除路径的方法。

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Autowired
private MessageInterceptor messageInterceptor;
@Autowired
private DataInterceptor dataInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.excludePathPatterns();
registry.addInterceptor(messageInterceptor)
.excludePathPatterns();
registry.addInterceptor(dataInterceptor)
.excludePathPatterns();
}
}

14.UV与DAU的统计

​ 利用拦截器的特性。我们可以将uv与dau的统计功能使用redis+拦截器进行实现。因preHandle方法会在请求到达dispatcherServlet前进行拦截,所以我们可以在其中获得用户的ip地址,根据ip进行UV的统计,按照年份天数等标准构建redis中的key,进而存放到redis中。

​ 而DAU则是获取当前用户的信息,例如可以从token中提取等,判空后进行记录。

15.用户的保存

​ 因许多接口都需要获取当前用户的身份,但每次都要用HttpServletRequest中获取,十分麻烦。如果想要在service、dao层中使用,就需要从controller层层传递。所以我们可以创建一个实用类来解决。

​ 使用方式就是利用ThreadLocal类进行保存,将用户信息保存在线程中。浏览器每一次请求就是启动了一个线程,当请求结束,我们将用户的信息销毁即可

​ 实现方式:

  • 我们需要创建一个ThreadLocal类,创建一个ThreadLocal对象,设置ThreadLocal的set,remove,get方法
  • 定义一个登录的拦截器类,实现HandlerInterceptor ,重写 preHandle() 和afterCompletion()方法 ,preHandle ()方法把登录信息写入ThreadLocal,afterCompletion()方法清除登录信息
  • 我们需要设置一些配置信息,创建一个类实现 WebMvcConfigurer ,重写addInterceptors()方法,创建一个登录拦截器类的对象,给他添加到配置中,我们就实现了ThreadLocal保存用户信息
//HostHolder.java
@Component
public class HostHolder {
private ThreadLocal<User> users=new ThreadLocal<>();
public void setUser(User user){
users.set(user);
}
public User getUser(){
return users.get();
}
public void clear(){
users.remove();
}
}
//在拦截器中的preHandle进行用户信息的获取
@Component
public class DataInterceptor implements HandlerInterceptor {
public DataInterceptor() {
}
@Autowired
private DataService dataService;
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//统计UV
String ip = request.getRemoteHost();
dataService.recordUV(ip);
//统计DAU
User user = hostHolder.getUser();
if (user!=null){
dataService.recordDAU(user.getId());
}
return true;
}
}
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
//此方法是获取登录信息,登录方式不一样获取方法不一样,用户信息保存用的UserInfoVO,里边具体信息自己定义即可
UserInfoVO userInfo = getUserInfo(request);
ThreadLocalUser.set(userInfo);
return true;
}
@Override
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
ThreadLocalUser.clear();
}
}

16.环境配置

​ 本项目使用到了Redis、ElasticSearch、Kafka等,其中不可避免地在服务器环境搭建时会遇到各种各样的报错,这里进行一个总结。

​ Redis:

  • 一定要为服务器的redis设置密码,有不怀好意之人扫到6379端口后会放置挖矿病毒或是清空redis
  • redis的配置中要关闭保护模式,且redis默认无法远程连接,要更改bind设置。可选择注释掉
  • 在启动redis时,要使用上级目录中配置好的config文件,且可以使用-d参数后台运行

​ Kafka:

  • 在运行Kafka前,要先运行zookeeper,可以将这两个设置为系统的服务。并且有先后级关系
  • Kafka运行前,一定要在配置文件中进行集群的配置

​ ElasticSearch:

17.maven

​ 本项目构建工具选择了maven,关于pom.xml不再进行赘述。

​ 在服务端进行maven打包时,可以使用mvn clean package -Dmaven.test.skip=true来跳过测试类进行打包

18.Linux命令

​ 记录一些项目部署中经常使用的命令:

  • ps -ef |grep tomcat 在运行的进程中查找tomcat 之后可利用pid进行kill
  • kill -9 pid
  • tail -f $filename

19.统一处理报错

​ 当项目规模逐渐增大时,报错的处理就成为了一个问题。我们可以使用一个类来统一处理报错

@ControllerAdvice(annotations = Controller.class)
public class MyControllerAdvice {
private static final Logger logger= LoggerFactory.getLogger(MyControllerAdvice.class);
@ExceptionHandler(Exception.class)
@ResponseBody
public void handlerException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.error("服务器发生异常"+e.getMessage());
for (StackTraceElement element:e.getStackTrace()){
logger.error(element.toString());
}
String requestHeader = request.getHeader("x-requested-with");
if (requestHeader.equals("XMLHttpRequest")){
response.setContentType("application/plain;charset=utf-8");
PrintWriter writer=response.getWriter();
writer.write(Md5AndJsonUtil.getJSONString(1,"服务器异常"));
}else{
response.sendRedirect(request.getContextPath()+"/error");
}
}
}

20.git

​ 起初打算采用团队形式进行开发。但因特殊原因,后端实际情况仅我一人。但仓库是已经配好的。这次学习到了使用.gitignore文件进行非上传文件的设置,只上传必要的文件,使其他用户方便使用。

HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

21.application.yaml

spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/****?characterEncoding=utf-8&userSSL=false
username: root
password:
redis:
host: localhost
port: 6379
password:
database: 0
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: test-consumer-group
enable-auto-commit: true
auto-commit-interval: 3000
task:
execution:
pool:
core-size: 5
max-size: 15
queue-capacity: 100
scheduling:
pool:
size: 5
elasticsearch:
uris: localhost:9200
mybatis-plus:
global-config:
db-config:
id-type: none
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2022.9.6 补

22.chmod

​ 近期因新生返校,并且由于不是所有用户都会具备校园网内网环境的问题,我需要将项目部署运行在生产机上。虽说早有心理准备生产机的环境配置会遇到与之前不一样的问题,但还是没想到有这么多。从chmod开始一一罗列如下:

​ 首先因生产机安全考虑,我们并不能拥有root用户,而是以开放了一定权限的普通用户作为操作者。在使用sudo rz -E命令上传文件后,默认权限为644状态,这就会导致几乎所有的环境部署都无法实现了,我们使用chmod 755 -R $directoryname递归修改文件夹下的文件权限,这样才可以使得如nginx代理访问静态资源报403部分框架无法正常拉起目录内文件导致无法启动等问题不再发生

23.elasticsearch-head报错406

​ 首先分析406,开头是4,问题发生在客户端部分,着重考虑是否是客户端出了问题。406为客户端无法解析服务端发送回的数据。那么思路应该为服务端发送了什么特殊类型的数据,导致客户端无法成功解析。于是乎向着ES框架的配置文件进行搜索。

{"error":"Content-Type header [application/x-www-form-urlencoded] is not supported","status":406}报错内容如上

​ 查找资料后发现需要修改ES中的_site/vendor.js文件,修改内容如下:

  • 首先docker exec -it $container's hashcode or name /bin/bash进入容器内部

  • apt-get update更新apt,防止安装失败

  • apt-get install vim安装vim

  • vim /_site/vendor.js修改文件

  • 修改 vendor.js 共有两处,重启head插件
    vi _site/vendor.js

    6886行
    contentType: "application/x-www-form-urlencoded
    改成
    contentType: “application/json;charset=UTF-8”
    7573行
    var inspectData = s.contentType === “application/x-www-form-urlencoded” &&
    修改为
    var inspectData = s.contentType === “application/json;charset=UTF-8” &&

24.elasticsearch删除索引内数据,但不删除索引结构

​ 因为换到了新机器上,但是为了不出现大问题,就将测试机配置好的ES移动到了生产机,那么至少要解决的问题就是清空掉在测试机上存储的问题内容,以免影响实际的搜索功能

​ 因为安装了es-head,所以可以在网页端进行操作

​ 起初并未在docker拉取插件的镜像,所以想着能否使用apifox这类工具,但可惜自己的服务器很容易,但不知为何学校特殊网络环境下的服务器就连接不上了。无奈只得启用插件在web端进行解决

curl -X POST总感觉也可,但还没学习到如何发送json

​ 总之最后的解决方案如下

  • 在网页端输入自己要删除的索引,最终应该是这种形式——$indexname/_delete_by_query
{
"query":{
"match_all":{
}
}
}

25.MVC设计模式

​ 这个没啥好写的,就是重新温习了一下MVC设计模式罢了。但是挺有意义,写在这里吧

26.Nginx+docker

​ 因为这次的环境部署都是我负责,所以我又要去学习了nginx和docker这两样东西。

​ 一个意外是以往很多届成员写的nginx配置竟然存在一些问题,导致部署的时候耽误了一些时间。

​ 这次也是终于明白了Nginx为什么叫反向代理,它应该如何配置文件。docker里的image和container到底是什么关系,人们都说docker香,那么到底好在哪里?

27.注意外部包完整性

​ 有这么一个问题。因为另外有一个非常小的项目是他人开发,我负责部署,但是我用的是2.7.2的springboot,而他用的是2.7.3,导致他的项目在服务端tomcat运行时找不到2.7.3的外部包,于是便无法运行,只能访问静态资源,接口无法调用。

28.maven安装

​ 其实在maven的使用过程中,服务器也遇到很多配置问题,但是好像解决的太顺利了,现在不记得了……

​ 索性贴一个安装教程吧 CentOS7——安装配置Maven(Apache Maven)_Hern(宋兆恒)的博客-CSDN博客_centos maven安装与配置

29.Tomcat 404

​ 遇到一个很恼人的问题,Tomcat里的日志文件启动是没有报错的,因为404导致也无法分析.out文件,所以就一直在猜到底是哪里的问题

​ 网上很容易可以搜到一些常见的解决方式,我这次碰见的是自己一个疏忽忘记掉的。

​ 记得检查一下项目文件中有没有配置serverletinitializer这个类

/**
* @AUTHOR Zzh
* @DATE 2022/8/8
* @TIME 23:27
*/
public class CommunityServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Sky31WelcomeApplication.class);
}
}

三.写在最后

​ 虽然写了很多代码,加起来删删改改的也得有几千行了。如果加一些小功能,希望能冲着1W行的目标写一下。给学校部门写这个网站是没有所谓的什么钱或者分的,有时聊天也是笑着说搬砖罢了。有时对于参与本次项目的所有人(真正付出了的人,混子另当别论吧)感到可惜,虽然技术确实锻炼到了,但没有物质上的回报还是令人觉得五味杂陈。还是对组内的小伙伴们再次致谢。

​ 关于用户问题,因为这个项目是学校老师要求写的,虽然大家都知道竞品很多,但是也没办法。(甚至前几天还和同学赌用户会不会超过100

​ 写这个项目感觉很累,复盘时思考一个原因也是因为学生组织的人数稀缺,而稀缺的人中搞技术的又参差不齐,有些时候甚至除了你没人能干活,导致开发之外的部署、日常运维都要你来干。这就很不合理了,鲜有人意识到吧……

​ 对于工作的话,我是希望要么你就别开始,要么开始就立马结束。项目编写的时候经常一天从早到晚都在电脑前坐着,如果父母不带我出去转转,可能一天就没有什么其他活动了。还是要注意身体啊

​ 本次项目的一大遗憾是因工时原因,最终没有使用SpringSecurity,会在之后研究加入

​ 项目起初想过使用Go来编写,但最终因框架不熟悉而放弃。希望下一个项目能用Go

​ 本次项目一定程度上参考了牛客社区

​ 在19年来看,这样一个项目可以被拿来当作简历上的项目来宣传,但在如今,这样的项目也早已烂大街了。

​ 这样也算是写过一个完整的Web开发项目,也在实验室写过了数据处理类的项目。十分圆满,总算能让浮躁的心静下来一点,之后还是着手408的学习吧。

​ 在项目的编写过程中,遇到很多毛躁的时刻,有时因服务器环境配置需要推翻重来,有时因数据结构的复杂不知如何下手。导致有时自言自语说了很多负能量的话,感谢和我合作的同学以及父母的鼓励

​ 目前打算使用Go手写一个RPC框架,或是尝试在Windows平台下使用DLL进行多语言合作编写一个程序,不再懊恼于大一学了很多语言的语法,却没有深入过这一点。

​ 2022.9.2 深夜

本文作者:Appletree24

本文链接:https://www.cnblogs.com/appletree24/p/16651618.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Appletree24  阅读(164)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 愛錠 (Edit ver.) LiSA
愛錠 (Edit ver.) - LiSA
00:00 / 00:00
An audio error has occurred.

話してしまえば 思い出

隠してしまえば 幸せ

時間がすべてを奪ってく

What do you think?do you think?

愛してしまえば 地獄で

離れてしまえば 孤独だ

もう戻れない

ああ、想う 想うほど 絡まる愛錠

この手を繋ぐ鎖のように

ただ目の前の明日を信じられるのならば

それだけでいい

今そっと手を伸ばした

もたれてしまえば 2倍で

壊してしまえば それぞれ

時間このまま過ぎ去って

What do you think?do you think?

願ってしまえば 欲しくて

叶ってしまえば 足りない

知っている

もう戻れはしない

あの日には

今夜すべてを置き去りに

独りで逃げ出したって

きっと私はいつまでも

後悔に縛られたまま

ずっときょうを恨みながら

罪責と悪夢に魘されるのでしょう

解けないわ

鉄と鉄が擦れ合い

指と指の温もりも

今ここに貴方を感じられるカギ

ああ、想う 想うほど 絡まる愛錠

この手を繋ぐ鎖のように

ただ目の前の明日を信じられるのならば

それだけでいいよ 今は

ああ、巡る 巡るほど 絡まる愛錠

切れない絆 確かめながら

どんな見えない明日も貴方が傍にいるのなら

それだけでいい

強く確かな愛情