博客系统(SpringBoot项目)

文章目录

  • 一、项目开发的流程
  • 二、项目开发
    • 2.1 准备工作
    • 2.2 开发公共模块:把能写的先写了
      • 什么是公共模块
      • model层
      • mapper层
      • 定义统一返回结果
      • 统一异常处理
    • 2.2 博客列表页
    • 2.3 更改显示的时间
    • 2.4 博客详情页
    • 2.5 登录
      • Session式登录方法分析
      • 使用Token来实现登录
    • 2.6 强制登录
    • 2.7 获取用户信息和作者信息
    • 2.8 实现用户退出
    • 2.9 实现发布博客
    • 2.10 修改返回格式
    • 2.11 删除/编辑博客
      • 出现删除和编辑按钮
      • 编辑操作
      • 删除操作
  • 三、Token
    • 3.1 什么是Token + 与Session的区别 + 优缺点
    • 3.2 JWT令牌介绍
    • 3.3 如何使用JWT令牌实现Token
  • 四、有无状态
  • 五、加密/加盐
  • 六、部署服务
    • 6.1 部署的流程
    • 6.2 搭建Java部署环境
      • 安装jdk
      • 安装mysql
    • 6.3 多平台配置
    • 6.4 部署

一、项目开发的流程

  1. 产品经理定下来需求文档

  2. 了解需求,确认需求有无问题:如果需求文档有问题,提出来让产品经理去修改

  3. 方案设计:包括了【接口设计】、【数据库设计】、【架构图】、【流程图】等等

    • 文档设计:注意【方案设计】和【接口设计】都是要单独出一个文档的,方案设计里可以放一个【接口设计】的链接
      • 区别:方案设计是给后端开发团队内部人员看的,接口设计是给其他团队看的
      • 接口文档:告诉别人如何去使用该工具,需不需要引入包,接口是什么,每个字段都是什么含义,相当于一个对外的说明书
      • 方案设计
        在这里插入图片描述
  4. 开发

  5. 测试

  6. 联调:联动其他部门调试,也是一种形式的测试

  7. 提交测试:将结果提交给测试人员

  8. 上线

二、项目开发

2.1 准备工作

  1. 创建Spring Boot项目
  2. 修改Spring Boot配置文件内容
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Drivermybatis:mapper-locations: classpath:mapper/*Mapper.xmlconfiguration: # 配置打印 MyBatis 执行的 SQLlog-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true  #自动驼峰转换logging:file:name: logs/springboot.log
  1. 创建数据库
-- 建表SQL
create database if not exists java_blog_spring charset utf8mb4;
-- ⽤⼾表
DROP TABLE IF EXISTS java_blog_spring.user;
CREATE TABLE java_blog_spring.user(`id` INT NOT NULL AUTO_INCREMENT,`user_name` VARCHAR ( 128 ) NOT NULL,`password` VARCHAR ( 128 ) NOT NULL,`github_url` VARCHAR ( 128 ) NULL,`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),PRIMARY KEY ( id ));
-- 博客表
drop table if exists java_blog_spring.blog;
CREATE TABLE java_blog_spring.blog (`id` INT NOT NULL AUTO_INCREMENT,`title` VARCHAR(200) NULL,`content` TEXT NULL,`user_id` INT(11) NULL,`delete_flag` TINYINT(4) NULL DEFAULT 0,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),PRIMARY KEY (id))ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表';
-- 新增⽤⼾信息
insert into java_blog_spring.user (user_name, password,github_url)values("zhangs","123456", "xxx");
insert into java_blog_spring.user (user_name, password,github_url)values("lisi", "123456", "xxx");
insert into java_blog_spring.blog (title,content,user_id) values("第⼀篇博客","11",1);
insert into java_blog_spring.blog (title,content,user_id) values("第⼆篇博客","22",2);
  1. 添加前端代码

2.2 开发公共模块:把能写的先写了

什么是公共模块

在这里插入图片描述

model层

在这里插入图片描述

mapper层

  1. 写mapper时就需要对功能进行梳理了,看我们的需求需要哪一些数据库操作
    • 关于查询用户信息:根据用户名查询用户信息,比对密码是否正确
      • 为什么不建议通过用户名和密码同时去查:我们不建议让密码通过数据库的查询,而且密码可能会经过加密之类的处理,出现了SQL注入的情况也不太好处理
    • 可以通过单元测试来判断代码有无错误
@Mapper
public interface UserInfoMapper {@Select("select * from user where user_name = #{userName} and delete_flag = 0")UserInfo queryByName(String userName);@Select("select * from user where id = #{id} and delete_flag = 0")UserInfo queryById(Integer id);
}
@Mapper
public interface BlogInfoMapper {@Select("select * from blog where delete_flag = 0")List<BlogInfo> queryBlogList();@Select("select * from blog where id = #{id} and delete_flag = 0")BlogInfo queryById(Integer id);@Update("update blog set title = #{title}, content = #{content} where id  = #{id}")Integer updateBlog(BlogInfo blogInfo);@Update("update blog set delete_flag = 1 where id = #{id}")Integer deleteBlog(Integer id);@Insert("insert into blog(title, content, user_id) values (#{title}, #{content}, #{userId}")Integer insertBlog(BlogInfo blogInfo);
}

定义统一返回结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

统一异常处理

在这里插入图片描述

2.2 博客列表页

  1. 前端代码
    在这里插入图片描述
  2. 后端代码

Controller层

@RestController
@RequestMapping("/blog")
public class BlogController {@Autowiredprivate BlogService blogService;@RequestMapping("/queryBlogList")public List<BlogInfo> queryBlogList(){List<BlogInfo> res = blogService.queryBlogList();return res;}
}

Service层

@Service
public class BlogService {@Autowiredprivate BlogInfoMapper blogInfoMapper;public List<BlogInfo> queryBlogList(){List<BlogInfo> res = blogInfoMapper.queryBlogList();return res;}
}

Mapper层

@Mapper
public interface BlogInfoMapper {@Select("select * from blog where delete_flag = 0")List<BlogInfo> queryBlogList();
}

2.3 更改显示的时间

在这里插入图片描述

2.4 博客详情页

  1. 前端代码
    在这里插入图片描述
  2. 后端代码

Controller层

@RestController
@RequestMapping("/blog")
public class BlogController {@Autowiredprivate BlogService blogService;@RequestMapping("/queryBlogDetail")public BlogInfo queryBligDetail(Integer blogId){BlogInfo res = blogService.queryBlogDetail(blogId);return res;}}

Service层

@Service
public class BlogService {@Autowiredprivate BlogInfoMapper blogInfoMapper;public BlogInfo queryBlogDetail(Integer id){BlogInfo res = blogInfoMapper.queryById(id);return res;}
}

Mapper层

@Mapper
public interface BlogInfoMapper {@Select("select * from blog where id = #{id} and delete_flag = 0")BlogInfo queryById(Integer id);}

2.5 登录

Session式登录方法分析

  1. 传统思路
    • 前端输入账号、密码后,后端进行校验
      • 后端校验成功后,存储Session,并返回Cookie
    • 前端进行页面跳转,后续访问时,会携带Cookie(里面有sessionId)。后端通过sessionId去服务器里存储的Session里取值,判断用户是否登录
  2. 传统思路存在的问题
    • 修改代码后,需要重新登录
      • 因为Session存储在服务器内存中,所以当服务器重启后,Session就丢失了。用户如果正在使用程序,且遇到了服务器重启,此时如果要再想用,就需要重新登录,这很影响用户体验
    • 是单机部署:现在的服务大多不是单机部署,而是多机部署
      • 单机部署存在的问题:服务全部部署在一台机器上,当用户流量太大或者其他例如被黑客黑了的原因,导致这台机器挂了,那所有的服务就都无了

      • 关于多机部署
        在这里插入图片描述

      • 登录操作使用多机部署的情况

        • 情境
          (1)用户第一次请求被分配到了服务器1,Session此时存储在了服务器1

          (2)用户第二次请求,请求被分配到了服务器2,服务器2由于没有用户的Session,被认为没有登录,无法提供后续服务,要求再次登录。

        • 解决方法
          (1)Session存储在一个公用机器或者是缓存等地方,如Redis
          在这里插入图片描述

          (2)使用token(带有一定信息的字符串)

使用Token来实现登录

  1. 思路
    • 根据用户名和密码,验证密码是否正确
      • 如果密码正确,后端生成Token,并返回给前端(Token由客户端保存)
      • 前端存储时,可以把Token放在Cookie里,也可以放在本地存储里(浏览器给每个前端都保留了一个小空间,用来保存数据)
    • 后续访问时,Token一般会放在Http请求的header中(也可以作为一个参数),由后端校验Token的合法性
  2. Controller层
    • 思路
      • 检查参数
      • 查询用户是否存在,如果不存在,直接返回
      • 密码是否正确
      • 将用户的信息存储到token里,如用户名、密码等信息
@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public Result login(String userName, String password){if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){return Result.fail("用户名或密码为空");}UserInfo userInfo = userService.queryByName(userName);if (userInfo == null || userInfo.getId() < 0){return Result.fail("用户不存在");}if (!password.equals(userInfo.getPassword())){return Result.fail("密码错误");}//生成Token并返回,账户和密码都正确的情况Map<String, Object> claim = new HashMap<>();claim.put("id", userInfo.getId());claim.put("name", userInfo.getUserName());String token = JwtUtils.genToken(claim);return Result.success(token);}}
  1. 帮助生成token的代码
public class JwtUtils { //在utils包里private static final long expiration = 30 * 60 * 1000;private static final String secreString = "abJPV1XoZl11HrSHF0eSIfonkgMYwAk++RYhR5+i6RU=";private static final Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secreString));public static String genToken(Map<String ,Object> claim){String token = Jwts.builder().setClaims(claim).setExpiration(new Date(System.currentTimeMillis() + expiration)).signWith(key).compact();return token;}}
  1. Service层
@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;public UserInfo queryByName(String username){return userInfoMapper.queryByName(username);}
}
  1. mapper层
@Mapper
public interface UserInfoMapper {@Select("select * from user where user_name = #{userName} and delete_flag = 0")UserInfo queryByName(String userName);
}
  1. 效果
    在这里插入图片描述
  2. 前端代码
    • localStorage.setItem():存储到内存里
      在这里插入图片描述
      在这里插入图片描述

2.6 强制登录

  1. 关于重复登录的问题

    • 后端只是生成和验证Token,存储是交给前端的。此时,哪怕后端重启了,因为存储方不在后端,除非我们把前端存储的token删掉,否则用户不需要重复登录
    • 在多机环境下,Token可以正常工作
  2. 流程
    在这里插入图片描述

  3. 登录的拦截器
    在这里插入图片描述

  4. 让拦截器生效
    在这里插入图片描述

  5. 让前端在Header里面发送token
    在这里插入图片描述

  6. 前端未登录时的逻辑
    在这里插入图片描述

2.7 获取用户信息和作者信息

  1. 实现方式
    • 方式一:如果页面需要的信息较少,且是固定不变的,就可以把这些信息存储到token里,直接从token获取
      • 不建议这种方式,因为这点无法保证,比如用户信息就是会发生变化的,虽然一旦发生变化,我们只需要更改Token即可,但这里其实涉及到了服务边界的问题
    • 方式二:从token中获取用户ID,根据用户ID,获取用户信息
      • 如果用户信息较少,可以把信息存储在token里。此处我们在Token里面存储了id,可以通过id从数据库中查到用户的对应信息
  2. 后端代码

Controller层

@RequestMapping("/user")
@RestController
@Slf4j
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/getUserInfo")public UserInfo getUserInfo(HttpServletRequest request){String token = request.getHeader("user_token");Integer userId = JwtUtils.getUserIdFromToken(token);if (userId == null){return null;}return userService.queryById(userId);}@RequestMapping("/getAuthorInfo")public UserInfo getAuthorInfo(Integer blogId){return userService.getAuthorInfo(blogId);}
}

Service层

@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;public UserInfo queryById(Integer userId) {return userInfoMapper.queryById(userId);}public UserInfo getAuthorInfo(Integer blogId) {BlogInfo blogInfo = blogInfoMapper.queryById(blogId);if (blogInfo == null || blogInfo.getUserId() < 0){return null;}return userInfoMapper.queryById(blogInfo.getUserId());}
}

Mapper层

@Mapper
public interface UserInfoMapper {@Select("select * from user where id = #{id} and delete_flag = 0")UserInfo queryById(Integer id);
}

Utils层
解析传来的token(从里面获取id)

@Slf4j
public class JwtUtils {public static Claims parseToken(String token){JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();Claims claims = null;try{claims = build.parseClaimsJws(token).getBody();}catch (Exception e){log.info("解析token失败,token:" + token);}return claims;}public static Integer getUserIdFromToken(String token){Claims claims = parseToken(token);if (claims == null){return null;}return (Integer) claims.get("id");}}

测试后端
在这里插入图片描述

  1. 前端代码
    • 拿到blogId
      • 直接从query string里拿,location.href
      • 设置一个隐藏框,把blogId放进去
getUserInfo();
function getUserInfo() {$.ajax({type: "get",url: "/user/getUserInfosuccess: function(result){if (result.code == 200 && result.data != null){var user = result.data;$(".left .card h3").text(user.userName);$(".left .card a").after("href", user.githubUrl);}else{location.href = blog_login.html;}}});
}
getUserInfo();
function getUserInfo() {$.ajax({type: "get",url: "/user/getAuthorInfo" + location.search,success: function(result){if (result.code == 200 && result.data != null){var user = result.data;$(".left .card h3").text(user.userName);$(".left .card a").after("href", user.githubUrl);}else{location.href = blog_login.html;}}});
}

因为两个代码只改动了url,所以我们可以把这段代码提到 common.js 即可,后续直接传入 url 即可

在这里插入图片描述

2.8 实现用户退出

  1. 实现方式:把前端保存的token删除即可,注意因为很多页面都需要该功能,故直接放在common.js里,到时候直接引用即可
function  logout(){localStorage.removeItem("user_token");alert("注销成功");location.href = "blog_login.html";
}

2.9 实现发布博客

  1. 实现方式
    • editor.md:editor.md 是一个开源的页面 markdown 编辑器组件,使用时引入对应依赖就行了
  2. 后端代码

Controller层

@Slf4j
@RestController
@RequestMapping("/blog")
public class BlogController {@Autowiredprivate BlogService blogService;@RequestMapping("/publishBlog")public Result publishBlog(String title, String content, HttpServletRequest request){String token = request.getHeader("user_token");Integer userId = JwtUtils.getUserIdFromToken(token);if (userId == null || userId < 0){log.info("传来的用户id有误,为: " + userId);return Result.fail("用户未登录", false);}BlogInfo blogInfo = new BlogInfo();blogInfo.setUserId(userId);blogInfo.setTitle(title);blogInfo.setContent(content);blogService.insertBlog(blogInfo);return Result.success(true);}
}

Service层

@Service
public class BlogService {@Autowiredprivate BlogInfoMapper blogInfoMapper;public Integer insertBlog(BlogInfo blogInfo) {if (blogInfo == null){return 0;}return blogInfoMapper.insertBlog(blogInfo);}
}

Mapper层

@Insert("insert into blog(title, content, user_id) values (#{title}, #{content}, #{userId})")
Integer insertBlog(BlogInfo blogInfo);
  1. 前端代码
function submit() {$.ajax({type:"post",url: "/blog/publishBlog",data:{title:$("#title").val(),content:$("#content").val()},success:function(result){if(result.code==200 && result.data==true){location.href = "blog_list.html";}else {alert(result.error);}}});
}

2.10 修改返回格式

在这里插入图片描述

2.11 删除/编辑博客

出现删除和编辑按钮

  1. 实现效果:只有是作者本人(登录用户是作者)才能进行【编辑】和【删除】

    • 实现方式
      • 方式一:单独写一个用来判断 “当前登录用户是否等于作者” 的接口,返回true/false
        推荐,因为这符合接口的单一原则,让一个接口功能的维度更细一点。
      • 方式二:用一个变量标记当前登录用户是否为作者
        该方法不推荐,但实现起来比较简单
  2. 后端代码
    在这里插入图片描述

  3. 前端代码
    在这里插入图片描述

编辑操作

  1. 更新的时候,获取当前帖子的内容,方便修改
    在这里插入图片描述
  2. 编辑操作的后端代码
    在这里插入图片描述
  3. 编辑操作的前端代码
    在这里插入图片描述

删除操作

在这里插入图片描述

三、Token

3.1 什么是Token + 与Session的区别 + 优缺点

  1. 什么是Token
    • 是客户端进行访问时携带的身份标识,就像人的身份证一样,不能伪造(但是其他人能看到),不是加密
    • 令牌和token可以认为是一个东西
  2. Token与Session的区别
    • Session
      (1)走到哪都会用户都会携带身份证,酒店不会携带身份证,Session相当于把身份证放在了酒店,然后用户一直在该酒店里住

      (2)第一次入住时,用户把身份信息登记在酒店上,酒店会存储下来,往后用户再住的时候,酒店因为已经保存了我们的身份信息,可以直接入住,无需再登记

    • Token
      (1)用户不会在一个酒店上吊死,会去多个酒店入住,也因此不可能每个酒店都存有用户的身份信息

      (2)此时用户每到一个酒店,就会展示身份证,酒店看到身份信息后就知道我们是谁了

  3. 令牌的优缺点
    • 优点:解决了集群环境下的的认证问题 + 令牌是在用户这边存储的,减轻了服务器的存储压力
    • 缺点:需要自己生成、传递、校验

3.2 JWT令牌介绍

  1. 服务器 VS Token:服务器具备的功能就是校验身份信息,因为Token无法伪造,所以服务器需要有判断Token是否为真的能力
  2. 关于JWT令牌:Token的实现方式有很多,此处我们用开源的【JWT令牌】网址
  3. 存储位置:Cookie也是存储在客户端的一个信息,里面除了存Token,还存了些别的信息
  4. JWT介绍
    在这里插入图片描述

3.3 如何使用JWT令牌实现Token

  1. 引入依赖:这个依赖jdk8和17都能支持
<!--		jwt依赖-->
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --><version>0.11.5</version><scope>runtime</scope>
</dependency>
  1. 生成Token
    在这里插入图片描述

  2. 解析Token
    在这里插入图片描述
    在这里插入图片描述

  3. 其他
    在这里插入图片描述

四、有无状态

  1. 有状态 VS 无状态:和HTTP的有无状态是一样的
    • 有状态:当前状态与上一次的进度有关,是衔接过来的。如接力赛,下一棒的人要接过上一棒才能行动。
    • 无状态:当前状态与上一次的进度无关,从中间的任何一个状态上去都可以无缝衔接,如自由跑,可以选择从任何一个起点开始跑,不用管上一次是在哪里起跑的。
  2. 关于服务的有无状态:我们去做服务时,要尽可能保证服务是【无状态】的
    • 服务有状态:服务器重启后有记忆一些东西
    • 服务无状态:服务器重启后没有记忆一些东西
  3. 关于Session 和 Token:Session存储在服务器,服务器有记忆功能,是有状态的; Token存储在客户端,是无状态的

五、加密/加盐

  1. 什么是加密:我们需要对诸如“密码”、“身份证号”……等敏感信息加密,这样即使数据库被入侵了,别人也没办法知道数据是什么
  2. 如何加密:需要使用密码算法对保存的明文进行加密,使其变成密文
    在这里插入图片描述
    • 对称密码算法:加密是 y = f(x),解密就是 x = f(y),加密和解密是同一个,靠这一个算法就可以完成加密解密
    • 非对称密码算法:加密是 y = f(x),解密就是 x = m(y),加密和解密是不同的
      • 关于公钥和私钥:公钥类似于锁,通常存储在服务器上。私钥类似于钥匙,通常存储在客户端,一个锁会有很多把钥匙,钥匙可以不同。所以通常情况下,公钥是公开的,大家都可以拿到,但是没有正确

        一个文件通过锁进行了加密,别人是无法直接查看该文件的内容的,钥匙是在用户手中,且可以不一样,如小红和小王都拿了把钥匙,钥匙可以不同,但都能打开这把锁。

      • 关于安全性:没有非对称加密比对称加密更安全的说法,两者都是安全的,取决于实现方式

    • 摘要算法:不是字符串的编码方式,如果两个字符串加密后的密文一样,我们就认为明文是一样的
  3. Https用了对称加密和非对称加密两种手段
  4. 关于摘要算法之MD5加密

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 使用MD5加密解密
    • DigestUtils:import org.springframework.util.DigestUtils Spring中用来实现加密的一个工具
      在这里插入图片描述

六、部署服务

6.1 部署的流程

  1. 原理:把项目打包成一个Jar包,让它在远程的云服务器上运行
  2. 流程
    • 确保当前能运行Java程序:有jdk和mysql
    • 修改配置文件:修改 application.yml 配置文件,如修改数据库账号密码和日志保存位置
      • 使用多平台配置:不需要我们来回改application.yml,而是能根据【不同的环境/平台】去采用不同的配置文件
        • 不同的环境:当前是【开发环境】,部署到服务器上后为【线上环境/生产环境】,不同的环境上需要的配置是不同的,比如【开发环境】里需要打印SQL,但到【线上环境】后,就不需要打印了
    1. 打Jar包:IDEA上面可以用Maven来帮忙打Jar包
    2. 在Linux服务器上运行该项目

6.2 搭建Java部署环境

安装jdk

  1. centos 环境下
    在这里插入图片描述
  2. unbuntu 环境下
    在这里插入图片描述

安装mysql

  1. unbuntu 环境下安装

在这里插入图片描述

  1. mariaDB VS mysql
    • 相同点:两者相似,端口都是3306
    • 不同点:mariaDB会影响建表时间 以及 不支持数据库的DateTime类型
      在这里插入图片描述

6.3 多平台配置

  1. 使用场景:让每个平台都有自己专属的配置文件,省得我们在一个application.yml里改来改去
  2. 如何使用多平台配置
    在这里插入图片描述

6.4 部署

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/4628.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

软件设计师笔记-数据结构

数据结构 数据元素的集合及元素间的相互关系和构造方法。 线性表的存储结构 顺序存储链式存储 单链表节点 typedef struct node { int data; struct node *link; }NODE, *LinkList; 双向链表 每个节点有两个指针&#xff0c;分别指出直接前驱和直接后继。 循环链表 尾…

LangChain Ollama实战文献检索助手(一)环境配置和输入输出解析

挑选合适的模型 调用API需要花钱&#xff0c;因此在搭建阶段最佳的方法是利用Ollama部署本地CPU推理的轻量化大模型。大模型选择可以参照hugging face的榜单open-llm-leaderboard。 这里对我来说&#xff0c;要选择的模型需要满足 1.ollama上有的模型。 2.推理速度快&#xff…

在docker中搭建redis哨兵环境

文章目录 一、引言二、环境准备前提条件目录结构 三、配置文件1. 主节点配置文件 sentinel-master.conf2. 从节点配置文件3. 哨兵配置文件 sentinel.conf4. Docker Compose 文件 四、启动 Docker Compose五、验证哨兵机制1. 检查主节点状态2. 检查从节点状态3. 检查哨兵状态4. …

上线不出网机器

不出网机器介绍 上线不出网机器是我们常见的问题&#xff0c;如何在内网中实现不出网机器的上线呢&#xff0c;我们分为了如下的形式&#xff0c;根据之前所学的内容我们开始进行实验&#xff0c;常见的网络拓扑如下 情况分类 上线不出网机器一般是指B区域的电脑上线到CS工具或…

Modbus解析流程全面升级:体验全新核心与终极优化!

01 前言 本文章原文发表于我的微信公众号&#xff0c;请大家关注阅读&#xff0c;涉及的源代码等都在公众号&#xff0c;请搜索公众号&#xff1a; 智能家居NodeRed和HomeAssistant 即可关注。 02 全面改进的解析流程 前面发布过的Modbus解析流程在经过多个设备测试后发现存…

Python邮差:如何用代码精确投递商品快递费用的密信

目录 一、准备工作 二、编写API请求脚本 三、解析与处理快递费用数据 四、案例应用&#xff1a;模拟电商平台的快递费用计算 五、自动化邮件通知 六、总结 在电子商务的广阔天地里&#xff0c;精确计算并快速传递商品快递费用是一项至关重要的任务。作为Python邮差&#…

修改sql server 数据库的排序规则Chinese_PRC_CI_AS(字符集+排序)

文章目录 引言I 解决方案案例II 知识扩展排序规则SQL SERVER支持的所有排序规则引言 新增sql server 数据库实例的默认排序规则不支持中文存储,导致乱码 解决方案: 修改排序规则为Chinese_PRC_CI_AS 或者 Chinese_PRC_Stroke_CI_AS_WS或者Chinese_PRC_CI_AI_KS_WS 仅对新增…

七十页PPT展示智驾时代来临,国产汽车零部件厂商准备几何?

u 智能汽车车身架构主要可分为感知、决策控制、执行及通信四大板块&#xff0c;目前国产汽车零部件供应商在感知系统已取得较强的话语权&#xff0c;在决策控制系统、执行系统领域亦取得一定竞争力。 u 感知系统主要硬件包括激光雷达、毫米波雷达、摄像头等&#xff1b;其中&a…

Springboot 整合 Java DL4J 打造自然语言处理之智能写作助手

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

moffee

https://github.com/BMPixel/moffee Moffee&#xff1a;一键将Markdown转换为专业PPT&#xff0c;支持多主题与实时预览 文章目录 1-安装1.1-环境1.2-编码 2-使用2.1-语法 moffee 演示让 Markdown 准备好演示为什么选择 moffee&#xff1f;展示用 Markdown 设置样式媒体布局 1-…

玩转「HF/魔搭/魔乐」平台

模型下载 Hugging Face 下载到 GitHub CodeSpace CodeSpace创建环境&#xff1a; # 安装transformers pip install transformers4.38 pip install sentencepiece0.1.99 pip install einops0.8.0 pip install protobuf5.27.2 pip install accelerate0.33.0下载internlm2_5-7b…

运维高可用架构设计

一、硬件 1、服务器 2、网络架构 二、软件 1、基础组件 组件名称 高可用方式 最少节点数 负载均衡(Tenginx) corsyncpacemaker互为主备 多组集群通过DNS轮循实现一个大集群 2DNS主从集群2RabbitMQ原生HA镜像集群3Zookeeper原生分布式集群3Kafka原生分布式集群3ES原生分布式集…

DICOM标准:MR图像模块属性详解——磁共振成像(MR)在DICOM中的应用

目录 引言 磁共振成像&#xff08;MR&#xff09; 一、MR图像模块 二、MR图像属性描述 1、图像类型 (Image Type) 2、抽样每个象素 (Sampling per Pixel) 3、光度插值 (Photometric Interpretation) 4、位分配 (Bits Allocated) 结论 引言 数字成像和通信在医学&#xff08…

SpringBoot在线教育系统:多语言支持

5系统详细实现 5.1 普通管理员管理 管理员可以对普通管理员账号信息进行添加修改删除操作。具体界面的展示如图5.1所示。 图5.1 普通管理员管理界面 5.2 课程管理员管理 管理员可以对课程管理员进行添加修改删除操作。具体界面如图5.2所示。 图5.2 课程管理员管理界面 5.3 …

Cursor和GitHub Copilot之间的竞争

大家好&#xff0c;今天我们要聊聊一个在开发者圈子里引起热议的话题&#xff1a;GitHub Copilot和Cursor之间的竞争&#xff0c;以及Copilot最近宣布的新功能&#xff0c;这可能会改变我们对编程辅助工具的看法。 GitHub Copilot将支持来自Anthropic、Google和OpenAI的模型&am…

Python酷库之旅-第三方库Pandas(181)

目录 一、用法精讲 836、pandas.api.types.is_file_like函数 836-1、语法 836-2、参数 836-3、功能 836-4、返回值 836-5、说明 836-6、用法 836-6-1、数据准备 836-6-2、代码示例 836-6-3、结果输出 837、pandas.api.types.is_list_like函数 837-1、语法 837-2、…

软件测试必会:cookie、session和token的区别~

今天就来说说session、cookie、token这三者之间的关系&#xff01;最近这仨玩意搞得头有点大&#x1f923; 01、为什么会有它们三个 我们都知道 HTTP 协议是无状态的&#xff0c;所谓的无状态就是客户端每次想要与服务端通信&#xff0c;都必须重新与服务端链接&#xff0c;意…

Vue3+vite 加载优化

公司项目&#xff0c;技术栈&#xff1a;vue3viteelementPLusecharts。首屏加载有点慢&#xff0c;针对这个做了一些优化措施&#xff0c;记录一下。之前写过关于vue2版本的优化&#xff0c;有兴趣的可以了解下 定位问题 f12打开控制台&#xff0c;然后Network看下那些包占比大…

Nvidia突袭AI江湖!悄悄发布新模型,完爆OpenAI和Anthropic?

你以为Nvidia只会造芯片&#xff1f;太天真了&#xff01;这家GPU巨头刚刚在AI语言模型领域上演了一出惊天逆袭&#xff0c;让OpenAI和Anthropic都措手不及。 没有轰轰烈烈的发布会&#xff0c;没有铺天盖地的宣传&#xff0c;Nvidia就这么静悄悄地在Hugging Face平台上扔出了一…

【Unity Shader】Special Effects(十)Change 变换(UI)

源码:[点我获取源码] 索引 Change 变换思路分析变换进度噪声纹理闪烁闪烁时机闪烁颜色闪烁动画Change 变换 变换的效果为图像间的切换带来动感过程,使用动画播放器: 思路分析 首先,从原始图像变换到目标图像是一个从0到1的过程,这个过程我们命名为变换进度(0为完全显…