Saturday, May 15, 2021

V2EX - 技术

V2EX - 技术


请教一个 hexo 部署的问题,或许是 git 的问题

Posted: 15 May 2021 04:26 AM PDT

我有一个在自己服务器上的部署完成的 blog,之前的正常推送都是没有问题的。
我本地的 hexo g 也是正常结果,hexo d -g 和 hexo d 也都是正常结果。
但是网页没有更新。

我检查了服务器的 blog 文件夹,没有新的文章。
然而我从本地克隆服务器的 blog.git ,blog 文件夹里面是有新文章的。

目前的解决方案是我把克隆下来的 blog 文件夹覆盖了服务器的 blog 文件夹,新增的文章就出现了。

现在的问题就是我不知道是哪个环节的问题,所以来请教一下各位大佬。

研究生学网安出路怎么样?

Posted: 15 May 2021 04:11 AM PDT

最近研究生选方向,感觉 ai 太火也太卷了,感觉中科院信工所信息安全国重挺不错的,就想知道网安这个专业怎么样?

Win 10 硬盘读写几乎没有,但活动时间 100%

Posted: 15 May 2021 03:53 AM PDT

破旧老电脑,就最近半个月才有的现象(更新 20H2 几天后)。每次开机之后就会保持这个状态很长一段时间(一个小时只多不少)。排查过索引服务、磁盘碎片清理计划等可能的问题,但都没有解决。 image.png

会员系统会员合并逻辑设计疑问

Posted: 15 May 2021 03:48 AM PDT

对于会员系统,存在会员合并的逻辑,可能由于用户手动合并或者系统因为用户补充了部分信息可以判定为同一个会员时就会触发合并。
假设现在系统中有两个会员:
userId1 和 userId2
那么对于其他业务系统,比如订单,微服务方式设计
可能因为历史存在了以下数据:
order1 -> userId1
order2 -> userId1
order3 -> userId2

那么会员系统合并之后,业务系统应该如何操作,现在想到两种操作
1. 逐个去 update 订单归属的会员
2. order 用 in (userId)的方式进行关联

第一种方案对于原始数据进行了操作,繁琐且感觉不一定可靠,业务微服务得监听会员合并事件,如果操作的时候遗漏了就麻烦了
第二种方案需要 其他的业务微服务比如 order 去理解会员合并的逻辑,感觉逻辑不太干净

求问各位还有其他更好的思路吗

使用 golang 的 unsafe 操作结构体私有属性

Posted: 15 May 2021 03:23 AM PDT

开篇之前,咱们先考虑一个问题,golang 中如何访问其他包的一个公有结构的私有属性,如下:

user 包

package user  type Info struct { 	name string 	age int }  func NewUser(name string, age int) Info { 	return Info{ 		name: name, 		age:  age, 	} } 

main 包

package main  import ( 	"grpcTest/grpcCodeRead/littlecases/unsafe/user" #  倒入 user 包 )  func main() { 	u := user.NewUser("wei.wei", 18)  	u.name = "wweeii" 	u.age = 18 } 

如上,我们在 main 包中调用了 user 包的公有函数 NewUser,创建了对象 u,想在 main 中通过 u.name = "wweeii"u.age = 18 来修改对象 u 的 name 和 age 属性,是做不到了,运行 go run main.go 编译是会报错的 :

# command-line-arguments ./main.go:10:3: u.name undefined (cannot refer to unexported field or method name) ./main.go:11:3: u.age undefined (cannot refer to unexported field or method age) 

我们能想到的一个可行的方法如下: user package

package user  type Info struct { 	name string 	age int }  func NewUser(name string, age int) Info { 	return Info{ 		name: name, 		age:  age, 	} }  func (i *Info) NameSetter(name string) { 	i.name = name }  func (i *Info) NameGetter()string { 	return i.name }  func (i *Info) AgeSetter(age int) { 	i.age = age }  func (i *Info) AgeGetter() int { 	return i.age } 

main package

package main  import ( 	"fmt" 	"grpcTest/grpcCodeRead/littlecases/unsafe/user" )  func main() { 	u := user.NewUser("wei.wei", 18)  	//u.name = "wweeii" 	//u.age = 18 	u.NameSetter("wweeii") 	u.AgeSetter(20)  	fmt.Println(u) } 

在 user 包中添加公有的 getter 和 setter 方法,来访问私有的属性。

但是如果 user 包没有提供访问私有变量的方法呢?我们怎么才能读取到对象 u 的 name 和 age 属性,这里就可以用到 golang 中提供的 unsafe 包。

如下:user 包不变:

package user  type Info struct { 	name string 	age int }  func NewUser(name string, age int) Info { 	return Info{ 		name: name, 		age:  age, 	} } 

main 包改成:

package main  import ( 	"fmt" 	"grpcTest/grpcCodeRead/littlecases/unsafe/user" 	"unsafe" )  func main() { 	u := user.NewUser("wei.wei", 18)  	pName := (*string)(unsafe.Pointer(&u)) 	fmt.Println(*pName)  	pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&u)) + unsafe.Sizeof(string("")))) 	fmt.Println(*pAge) } 

测试运行 go run main.go 就可以访问对象 u 的私有属性 name 和 age 了。

当然看到这里,大家估计还是一头雾水,没关系,不用明白上面代码是怎么做到的,那是因为咱们还不知道 unsafe 是什么,更不知道上面用到的 unsafe.Pointer 、unsafe.Sizeof 、uintptr 是什么,先往后看,等了解了 unsafe 后再来看这段代码,咱们就能明白了。

unsafe

官方文档: https://golang.org/pkg/unsafe

unsafe 是 golang 提供的一个包,通过这个包可以实现不同类型指针之间的转化,可以实现对指针的计算,来访问变量的属性。

unsafe 包是一种不安全的包,它能绕过编译器检查,直接快速的访问和修改一些变量,从它的命名也能看出设计者是希望谨慎使用它的,至少这个包名导致咱们在使用它的时候,会让人产生不舒服的感觉。

unsafe 提供了两个类型和三个函数:

type ArbitraryType int  type Pointer *ArbitraryType  func Sizeof(x ArbitraryType) uintptr  func Offsetof(x ArbitraryType) uintptr  func Alignof(x ArbitraryType) uintptr 

ArbitraryType 是一个 int 类型的重定义,从字面看是任意类型,golang 中任意类型都可以赋值给 ArbitraryType,Sizeof 、Offsetof 、Alignof 三个方法的形参是 (x ArbitraryType),也就是这三个函数可以接受任意的一个类型,并返回一个 uintptr 类型的值。

Pointer 是一个 *ArbitraryType 的重定义,unsafe.Pointer(*x) 可以将 *x 指针转为 unsafe.Pointer 类型。

uintptr 是内置的类型,可以理解为可以参与计算的指针地址。

  • unsafe.Sizeof Sizeof 可以接受任意类型,返回该类型在当前操作系统上占用的字节数,这个函数的返回值和系统是相关的,比如一个 int 型在 32 位操作系统上返回 4,在 64 位操作系统上返回 8,在我的 64 位电脑上返回如下:
 fmt.Println(unsafe.Sizeof(string("")))  // 返回:16  fmt.Println(unsafe.Sizeof(int(0)))    // 返回:8  fmt.Println(unsafe.Sizeof(user.Info{})) // 返回 24 

看完上面的例子大家想想 unsafe.Sizeof(string("Hi")) 返回值是多少?没错这里返回的是 16,因为 string 这种类型在 64 位操作系统上站 16 个字节,和参数中是几个字符没有关系。

  • unsafe.Alignof 和 unsafe.Offsetof

看如下例子:

package main  import ( "fmt" "unsafe" )  type XTest struct { a bool b int16 c []int }  func main() { x := XTest{} fmt.Println(unsafe.Alignof(x.a)) fmt.Println(unsafe.Alignof(x.b)) fmt.Println(unsafe.Alignof(x.c))  fmt.Println("--")  fmt.Println(unsafe.Offsetof(x.a)) fmt.Println(unsafe.Offsetof(x.b)) fmt.Println(unsafe.Offsetof(x.c)) } 

执行 go run main.go 后输入如下:

1 2 8 -- 0 2 8 

unsafe.Alignof 返回的是类型的对齐方式,unsafe.Offsetof 返回的是属性相对于结构体开头的偏移量。

看了上面的简介,相信大家一定还是思绪万千,甚至还有些小小的思维混乱,这里我们总结下 Pointer 和 uintptr 的使用,相信掌握了如下的规律,稍加琢磨就能知道掌握 unsafe 包怎么使用:

  1. 任何的指针类型 T 都可以转为一个 Pointer 类型 ,转化的方式是 unsafe.Pointer(T)
  2. 任何一个 Pointer 类型都可以转为 uintptr 类型
  3. 任何一个 uintptr 类型都可以转为一个 Pointer 类型,Pointer 类型可以转为指针类型 T
  4. uintptr 可以参与计算,Pointer 类型不能参与计算

看完上面的 4 个点,大家使用 unsafe 进行指针计算,脑子里一定有了如下的计算路线:

指针 T -> unsafe.Pointer -> uintptr -> 做加减计算 -> unsafe.Pointer -> T

有 C 语言基础的同学一定有通过指针来遍历数组的经历,这里的 uintptr 就是可以看作是一个和 c 中指针相同的东西,是可以计算的指针,现在我们再解释下上面的代码:

u := user.NewUser("wei.wei", 18) pName := (*string)(unsafe.Pointer(&u)) fmt.Println(*pName)  pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&u)) + unsafe.Sizeof(string("")))) fmt.Println(*pAge) 

这里的 (*string)(unsafe.Pointer(&u)) 应该是可以理解的,就是拿到了指向对象 u 地址头部的一个指针,这个指针指向的正好是第一个 string 类型的变量,所以 *pName 就是 "wei.wei"。

pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&u)) + unsafe.Sizeof(string("")))) 这一句中 unsafe.Pointer(&u) 指向的对象 u 的头部,u 的第一元素是 string 类型,第二个元素是一个 int 类型,在 u 的指针头部,加一个 string 类型的 unsafe.Sizeof,也就是加上代码中的 unsafe.Sizeof(string("")) 就正好是一个指向到第二个元素 age 的头部的指针,因为 unsafe.Pointer(&u) 返回的是一个不可计算的类型,所以使用 uintptr 先转为一个可计算的 uintptr 类型,而 unsafe.Sizeof(string("")) 返回的是一个可以计算的 uintptr 类型,这两者相加就得到了指向 age 元素的指针 pAge 所以 *pAge 就是 18

240 计划中的前端框架开源了,给故事一个小结。

Posted: 15 May 2021 03:17 AM PDT

前文:https://v2ex.com/t/713954 240 计划:https://zhuanlan.zhihu.com/p/264954202

240 的进展: https://zhuanlan.zhihu.com/p/370441727

都 2021 了,为什么还要写前端框架:

  1. 我想使用 vue3 形式的 reactive data 。相比 rxjs 为代表的 event steam based reative,data reactive 更符合直觉描述。当然 rxjs 能保障一致性,对此有高要求的的场景仍首选推荐。
  2. 我想用 function 来写组件而不是 template,但同时又框架能实现原子粒度的局部更新,而不是像 react 一样重新 render 计算。
  3. 组件的复用问题仍然是前端的重要问题之一。组织越大,要支持的场景越多,对组件的维护者来说负担越重。同时对使用者来说,寻求维护者支持的链路往往太长,想要自己动手又要深陷到组件库的语言、打包配置等实现细节之中。我想要在框架层面提供"扩展"和"覆写"组件 feature 的机制,能同时解放两个角色。
  4. 想要把样式和结构分离,想用 design pattern 去实现样式而不是手动对着设计稿调。
  5. ...

还有太多改善的想法,不如直接看官网吧:

https://axii.js.org/

除了框架本身我也完善了一部分生态:

  • axii-components: 基础组件库
  • axii-mdx: 用来写文档
  • axii-x6: 整和 X6 来画图
  • devtool: 调试工具,还有待完善

我还需要很多帮助来做好开源工作,感兴趣的小伙伴请联系我,谢谢。

如何快速便捷的查看 android 系统类的类、方法参数?

Posted: 15 May 2021 02:45 AM PDT

最近在写 xposed 插件,在大部分手机上都没问题,但是在小米手机上死活找不到相关方法。猜测是小米对 android 源码魔改太多,方法入参或者返回变了,导致 hook 不生效。因此想看看小米系统底层的函数定义。以前都是直接看 android 源码,但是对于小米这种编译好的系统怎么看他的 android 系统类定义呢?

比如这个函数
https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/input/InputManagerService.java#218

我有什么办法看看在小米 rom 上面的定义呢?

你们在 vercel 托管的网页能访问么?

Posted: 15 May 2021 01:19 AM PDT

今天早上想打开,发现拒绝连接

使用域名访问云服务,能不能做到伪装成使用 IP 访问

Posted: 15 May 2021 12:20 AM PDT

RT,能不能在用域名访问云服务时伪装,使得云服务器端检测为使用 IP 访问(未备案无法放行)

waynboot 商城发布啦,整合了 Redis、RabbitMQ、ElasticSearch 等常用中间件, 根据生产环境开发经验而来不断完善、优化、改进中

Posted: 15 May 2021 12:06 AM PDT

waynboot-mall 项目

觉得有用的铁子们给个 star 就行了,求求你们啦😘😍

waynboot-mall 是一套全部开源的微商城项目,包含一个运营后台、h5 商城和后台接口。 实现了一个商城所需的首页展示、商品分类、商品详情、sku 详情、商品搜索、加入购物车、结算下单、订单状态流转、商品评论等一系列功能。 技术上基于 Springboot2.0,整合了 Redis 、RabbitMQ 、ElasticSearch 等常用中间件, 贴近生产环境实际经验开发而来不断完善、优化、改进中。

后台接口项目
运营后台项目
h5 商城项目

waynboot-mall 接口项目

  1. 商城接口代码清晰、注释完善、模块拆分合理
  2. 使用 Spring-Security 进行访问权限控制
  3. 使用 jwt 进行接口授权验证
  4. ORM 层使用 Mybatis Plus 提升开发效率
  5. 添加全局异常处理器,统一异常处理
  6. 添加 https 配置代码,支持 https 访问
  7. 集成七牛云存储配置,上传文件至七牛
  8. 集成常用邮箱配置,方便发送邮件
  9. 集成 druid 连接池,进行 sql 监控
  10. 集成 swagger,管理接口文档
  11. 添加策略模式使用示例,优化首页金刚区跳转逻辑
  12. 拆分出通用的数据访问模块,统一 redis & elastic 配置与访问
  13. 使用 elasticsearch-rest-high-level-client 客户端对 elasticsearch 进行操作
  14. 支持商品数据同步 elasticsearch 操作以及 elasticsearch 商品搜索
  15. RabbitMQ 生产者发送消息采用异步 confirm 模式,消费者消费消息时需手动确认
  16. 下单处理过程引入 rabbitMQ,异步生成订单记录,提高系统下单处理能力
  17. ...

商城难点整理

1. 库存扣减操作是在下单操作扣减还是在支付成功时扣减?( ps:扣减库存使用乐观锁机制 where goods_num - num >= 0

  1. 下单时扣减,这个方案属于实时扣减,当有大量下单请求时,由于订单数小于请求数,会发生下单失败,但是无法防止短时间大量恶意请求占用库存, 造成普通用户无法下单
  2. 支付成功扣减,这个方案可以预防恶意请求占用库存,但是会存在多个请求同时下单后,在支付回调中扣减库存失败,导致订单还是下单失败并且还要退还订单金额(这种请求就是订单数超过了库存数,无法发货,影响用户体验)
  3. 还是下单时扣减,但是对于未支付订单设置一个超时过期机制,比如下单时库存减一,生成订单后,对于未在 15 分钟内完成支付的订单, 自动取消超期未支付订单并将库存加一,该方案基本满足了大部分使用场景
  4. 针对大流量下单场景,比如一分钟内五十万次下单请求,可以通过设置虚拟库存的方式减少下单接口对数据库的访问。具体来说就是把商品实际库存保存到 redis 中, 下单时配合 lua 脚本原子的 get 和 decr 商品库存数量(这一步就拦截了大部分请求),执行成功后在扣减实际库存

2. 首页商品展示接口利用多线程技术进行查询优化,将多个 sql 语句的排队查询变成异步查询,接口时长只跟查询时长最大的 sql 查询挂钩

# 1. 通过创建子线程继承 Callable 接口 Callable<List<Banner>> bannerCall = () -> iBannerService.list(new QueryWrapper<Banner>().eq("status", 0).orderByAsc("sort")); # 2. 传入 Callable 的任务给 FutureTask FutureTask<List<Banner>> bannerTask = new FutureTask<>(bannerCall); # 3. 放入线程池执行 threadPoolTaskExecutor.submit(bannerTask); # 4. 最后可以在外部通过 FutureTask 的 get 方法异步获取执行结果  List<Banner> list = bannerTask.get() 

3. ElasticSearch查询操作,查询包含搜索关键字并且是上架中的商品,在根据指定字段进行排序,最后分页返回

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); MatchQueryBuilder matchFiler = QueryBuilders.matchQuery("isOnSale", true); MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("name", keyword); MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery("keyword", keyword); boolQueryBuilder.filter(matchFiler).should(matchQuery).should(matchPhraseQueryBuilder).minimumShouldMatch(1); searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS)); // 按是否新品排序 if (isNew) {      searchSourceBuilder.sort(new FieldSortBuilder("isNew").order(SortOrder.DESC)); } // 按是否热品排序 if (isHot) {     searchSourceBuilder.sort(new FieldSortBuilder("isHot").order(SortOrder.DESC)); } // 按价格高低排序 if (isPrice) {     searchSourceBuilder.sort(new FieldSortBuilder("retailPrice").order("asc".equals(orderBy) ? SortOrder.ASC : SortOrder.DESC)); } // 按销量排序 if (isSales) {     searchSourceBuilder.sort(new FieldSortBuilder("sales").order(SortOrder.DESC)); } // 筛选新品 if (filterNew) {     MatchQueryBuilder filterQuery = QueryBuilders.matchQuery("isNew", true);     boolQueryBuilder.filter(filterQuery); } // 筛选热品 if (filterHot) {     MatchQueryBuilder filterQuery = QueryBuilders.matchQuery("isHot", true);     boolQueryBuilder.filter(filterQuery); }  searchSourceBuilder.query(boolQueryBuilder); searchSourceBuilder.from((int) (page.getCurrent() - 1) * (int) page.getSize()); searchSourceBuilder.size((int) page.getSize()); List<JSONObject> list = elasticDocument.search("goods", searchSourceBuilder, JSONObject.class); 

4. 订单编号生成规则:秒级时间戳 + 加密用户 ID + 今日第几次下单

  1. 秒级时间戳:时间递增保证唯一性
  2. 加密用户 ID:加密处理,返回用户 ID6 位数字,可以防并发访问,同一秒用户不会产生 2 个订单
  3. 今日第几次下单:便于运营查询处理用户当日订单

5. 下单流程处理过程,通过 rabbitMQ 异步生成订单,提高系统下单处理能力

  1. 用户点击提交订单按钮,后台生成订单编号和订单金额跳转到订单支付页面,并发送 rabbitMQ 消息(包含订单编号等信息)
  2. 订单消费者接受到订单消息后生成订单记录(未支付)
  3. 用户点击支付按钮时,前端根据订单编号轮询订单信息查询接口,如果订单编号记录已经入库则进行后续支付操作,如果订单编号未入库则返回错误信息(订单异常)
  4. 用户支付完成后在回调通知里更新订单状态为已支付

6. 金刚区跳转使用策略模式

# 1. 定义金刚位跳转策略接口 public interface DiamondJumpType {      List<Goods> getGoods(Page<Goods> page, Diamond diamond);      Integer getType(); }  # 2. 定义策略实现类,并使用 @Component 注解注入 spring @Component public class CategoryStrategy implements DiamondJumpType {      @Autowired     private GoodsMapper goodsMapper;      @Override     public List<Goods> getGoods(Page<Goods> page, Diamond diamond) {         List<Long> cateList = Arrays.asList(diamond.getValueId());         return goodsMapper.selectGoodsListPageByl2CateId(page, cateList).getRecords();     }      @Override     public Integer getType() {         return JumpTypeEnum.CATEGORY.getType();     } } @Component public class ColumnStrategy implements DiamondJumpType {      @Autowired     private IColumnGoodsRelationService iColumnGoodsRelationService;      @Autowired     private IGoodsService iGoodsService;      @Override     public List<Goods> getGoods(Page<Goods> page, Diamond diamond) {         List<ColumnGoodsRelation> goodsRelationList = iColumnGoodsRelationService.list(new QueryWrapper<ColumnGoodsRelation>()                 .eq("column_id", diamond.getValueId()));         List<Long> goodsIdList = goodsRelationList.stream().map(ColumnGoodsRelation::getGoodsId).collect(Collectors.toList());         Page<Goods> goodsPage = iGoodsService.page(page, new QueryWrapper<Goods>().in("id", goodsIdList).eq("is_on_sale", true));         return goodsPage.getRecords();     }      @Override     public Integer getType() {         return JumpTypeEnum.COLUMN.getType();     } }  # 3. 定义策略上下文,通过构造器注入 spring,定义 map 属性,通过 key 获取对应策略实现类 @Component public class DiamondJumpContext {      private Map<Integer, DiamondJumpType> map = new HashMap<>();      /**      * 由 spring 自动注入 DiamondJumpType 子类      *      * @param diamondJumpTypes 金刚位跳转类型集合      */     public DiamondJumpContext(List<DiamondJumpType> diamondJumpTypes) {         for (DiamondJumpType diamondJumpType : diamondJumpTypes) {             map.put(diamondJumpType.getType(), diamondJumpType);         }     }      public DiamondJumpType getInstance(Integer jumpType) {         return map.get(jumpType);     } }  # 4.使用 @Autowired private DiamondJumpContext diamondJumpContext;  @Test public void test(){     DiamondJumpType diamondJumpType = diamondJumpContext.getInstance(1); }  
  • todo

文件目录

|-- waynboot-admin-api             // 运营后台 api 模块,提供后台项目 api 接口 |-- waynboot-common                // 通用模块,包含项目核心基础类 |-- waynboot-data                  // 数据模块,通用中间件数据访问 |   |-- waynboot-data-redis        // redis 访问配置模块 |   |-- waynboot-data-elastic      // elastic 访问配置模块 |-- waynboot-generator             // 代码生成模块 |-- waynboot-message-consumer      // 消费者模块,处理订单消息和邮件消息 |-- waynboot-message-core          // 消费者核心模块,队列、交换机配置 |-- waynboot-mobile-api            // h5 商城 api 模块,提供 h5 商城 api 接口 |-- pom.xml                        // maven 父项目依赖,定义子项目依赖版本 |-- ... 

开发部署

# 1. 克隆项目 git clone git@github.com:wayn111/waynboot-mall.git  # 2. 导入项目依赖 将 waynboot-mall 目录用 idea 打开,导入 maven 依赖  # 3. 安装 Mysql8.0+、Redis3.0+、RabbitMQ3.0+、ElasticSearch7.0+到本地  # 4. 导入 sql 文件 在项目根目录下,找到`wayn_shop_*.sql`文件,新建 mysql 数据库 wayn_shop,导入其中  # 5. 修改 Mysql 、Redis 、RabbitMQ 、Elasticsearch 连接配置 修改`application-dev.yml`以及`application.yml`文件中数据连接配置相关信息  # 6. 启动项目 后台 api:     进入 waynboot-admin-api 子项目,找到 AdminApplication 文件,右键`run AdminApplication`,启动后台项目 h5 商城 api:     进入 waynboot-mobile-api 子项目,找到 MobileApplication 文件,右键`run MobileApplication`,启动 h5 商城项目 

在线体验

  • 注册一个账号
  • 然后登陆

演示地址: http://www.wayn.ltd

演示图

商城登陆 商城注册
商城首页 商城搜索
搜索结果展示 金刚位跳转
商品分类 商品详情
商品 sku 选择 购物车查看
确认下单 选择支付方式
商城我的页面 我的订单列表
添加商品评论 查看商品评论
后台登陆 后台首页
后台会员管理 后台评论管理
后台地址管理 后台添加商品
后台商品管理 后台 banner 管理
后台订单管理 后台分类管理
后台金刚区管理 后台栏目管理

waynboot-mall 交流群

QQ 群:waynboot-mall 交流群 有问题可以先提 issue😁

todo

  • 支持多店铺
  • 订单详情页面
  • 秒杀专区
  • 优惠卷使用
  • 团购下单
  • ...

感谢

国内的云服务商现在是 DoT 都不允许建立了吗?

Posted: 15 May 2021 12:05 AM PDT

今天上午大概 10 点突然 DNS 挂了,压根没往这个方向想,调了半天试了一下 openssl s_client -connect x.x.x.x:853,直接看到了熟悉的 write:errno=10054

在服务器上 openssl s_client -connect 127.0.0.1:853 是握手失败,因为没发 CN 算是正常的

所以现在是国内自建的 DoT 也不成了?

Histogram(Prometheus)指标类型如何设置 bucket 呢?

Posted: 14 May 2021 10:30 PM PDT

在求服务的 P95/P99 的时候,如何更加精准地使用 histogram 来估计耗时呢?

histogram_quantile 假定了 bucket 是线性分布,那么是不是设置 bucket 也是需要线性增长的。

比如服务请求耗时在大约 50ms,那我的 bucket 设置成 [10, 20, 30, 40, 50, 60] 是不是就能较为精确的计算出耗时?请问有什么最佳实践吗?

解决 Windows 在后台疯狂读写硬盘的问题。

Posted: 14 May 2021 10:22 PM PDT

每次 PC 进入待机状态,屏幕暗了后,硬盘灯就开始不断闪烁,一闪要闪很长时间。

然后只要鼠标一定移动,读盘马上就停止了。和我在玩躲猫猫一样。

以前以为是系统在建立索引,结果并不是,把文件索引关了,还是一样读盘。今天有幸终于给我逮个现场,是一个叫 defragsvc 的后台服务,默认是开启的。

顺藤摸瓜,查到[附件]->[系统工具]->[磁盘碎片整理程序], 把[计划任务]取消后,世界终于清静了。

IPv6 测速节点搭建教程

Posted: 14 May 2021 09:26 PM PDT

本教程是搭建 IPv6.stream 测速节点的经验记录,对其他应用可能也有参考价值

本文使用 Apache 作为网络服务器,Nginx 版见 这里

如使用 Docker 可能需要更高配置,本教程暂不涉及

VPS 准备

最低硬件要求:
128MB RAM 、1 GB 剩余空间、IPv6 支持

操作系统以 Debian 10 为例

网络准备

部分服务商可能配置有系统或者服务商级防火墙,应使其允许 80 、443 入站

由于存在 OpenVZ 机型,为保证测速结果的可比性,本站测速点默认不开启 BBR

测试 IPv4 连通性

在 VPS 上执行:

ping -4 ip.sb 

带 IPv4 的机型通常可以连通(含 NAT )
仅 IPv6 的机型通常不通

如果不通,不支持 IPv6 的在线安装可能会出现问题,可配置 DNS64 解决:

echo -e "nameserver 2001:67c:2b0::4\nnameserver 2001:67c:2b0::6\nnameserver 127.0.0.1" > /etc/resolv.conf 

此命令会覆盖原有的配置

测试 IPv6 连通性

在 VPS 上执行:

ping -6 ip.sb 

应当可以连通,否则会影响测速程序正常运行

个别服务商的 DHCPv6 配置存在问题,会获得意外的 IPv6 地址,导致服务器无法出站
如遇到此情况,设网卡为 eth0,则可在 /etc/sysctl.conf 中增加:

net.ipv6.conf.eth0.autoconf=0 net.ipv6.conf.eth0.accept_ra=0 net.ipv6.conf.eth0.use_tempaddr=0 

从而禁止意外的自动地址配置

域名

以下是本文示例使用的域名和地址,操作时请记得替换:

示例域名 example.ipv6.stream
示例 IP 地址 2001:db8::1

如果是自己持有域名,在 DNS 提供商处设置 example.ipv6.stream 的 AAAA 记录为 2001:db8::1

如果想使用本站域名,但还未解析,可先在本地计算机的 hosts 增加一条记录用于后续配置:

2001:db8::1 example.ipv6.stream 

提出申请、解析完成后建议删除此记录

HTTP 配置

安装教程所需的软件

apt update   apt install php libapache2-mod-php unzip curl vim 

speedtest-x

下载 speedtest-x 代码并解压缩

cd /var/www/ wget https://github.com/BadApple9/speedtest-x/archive/refs/heads/master.zip unzip master.zip mv speedtest-x-master speedtest rm master.zip 

授予 Apache ( www-data )测速记录所在目录的读写权限

chown www-data:www-data  /var/www/speedtest/backend/ 

个性化

本站的配置文件 /var/www/speedtest/backend/config.php 如下:

<?php  /**  * 最多保存多少条测试记录  */ const MAX_LOG_COUNT = 150;  /**  * IP 运营商解析服务:(1) ip.sb | (2) ipinfo.io (如果 1 解析 ip 异常,请切换成 2 )  */ const IP_SERVICE = 'ip.sb';  /**  * 是否允许同一 IP 记录多条测速结果  */ const SAME_IP_MULTI_LOGS = true; 

Apache

新建文件 /etc/apache2/sites-available/speedtest.conf
写入:

<VirtualHost *:80>     ServerName example.ipv6.stream     DocumentRoot /var/www/speedtest     ErrorLog ${APACHE_LOG_DIR}/error.log     CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost> 

启用网站

a2ensite speedtest.conf 

如果希望记录的时间与北京时间保持一致,可在 /etc/php/7.3/apache2/php.ini 中配置时区:

date.timezone = "PRC" 

(大约位于第 956 行,去掉注释)

重启 Apache

systemctl restart apache2 

如无报错,打开域名 http://example.ipv6.stream 应能显示出测速页面
检查上下行测速、记录是否正常工作

至此 HTTP 部分已经配置完成。

HTTPS 配置

该部分需要域名已经完成解析

本教程使用 acme.sh 申请,cerbot 可参考 Nginx 教程

acme.sh

使用前可先参考 官方文档

获取并安装

curl  https://get.acme.sh | sh 

退出并重新登陆终端

签署证书

acme.sh --issue  -d example.ipv6.stream   --apache 

新建证书目录并安装

mkdir /var/ssl/ acme.sh --install-cert -d example.ipv6.stream \ --cert-file      /var/ssl/cert.pem  \ --key-file      /var/ssl/key.pem  \ --fullchain-file /var/ssl/fullchain.pem \ --reloadcmd     "service apache2 force-reload" 

Apache

启用 SSL

a2enmod ssl 

新建文件 /etc/apache2/sites-available/speedtest-ssl.conf

写入:

<VirtualHost *:443>     ServerName example.ipv6.stream     DocumentRoot /var/www/speedtest     ErrorLog ${APACHE_LOG_DIR}/error.log     CustomLog ${APACHE_LOG_DIR}/access.log combined     SSLEngine on     SSLCertificateFile /var/ssl/cert.pem     SSLCertificateKeyFile /var/ssl/key.pem     SSLCertificateChainFile /var/ssl/fullchain.pem </VirtualHost> 

启用新配置

a2ensite speedtest-ssl.conf 

重启 Apache

systemctl restart apache2 

如无报错,打开域名 https://example.ipv6.stream 应能显示出测速页面

至此 HTTPS 部分已经配置完成。

配置完成后

可提交 Issue 加入 IPv6.stream 列表。

写的 github 开源项目,目的是分享一些有用的 Java 技术栈,大家给点建议

Posted: 14 May 2021 08:29 PM PDT

老哥们,真心求个建议!

Posted: 14 May 2021 08:00 PM PDT

背景

和对象目前异地,她大学毕业一直留在成都。前不久去见了父母,在广东那边。顺便感受了下当地得风土人情。她爸妈在她很小得时候就来到广东这边,但是一直到现在都没安家置业。上次沟通中,一直没安家多少有些思乡情愫,之前也没想着买房投资啥的,反正就是没打算买房。后来房价暴涨,不是想不想的问题,想买也没能力上车了。然后她很多亲戚都在那边,办厂的,打工不少。然后呢,对象去了几次广州后和成都一对比感觉广州怎么说呢,人潮汹涌,感觉很压抑,特别时上班高峰时段。

纠结

  1. 成都 or 广州 ?

  2. 有必要去广州奋斗 5,6 年然后回去么?

大家见过能独立构架与实现数据库等复杂系统的大牛么,讲讲你们身边的故事。

Posted: 14 May 2021 07:31 PM PDT

如题,周末无事闲聊,大家畅所欲言。

我先说, 在百度参与过偏基础架构的工作,同事们为了晋升就直接造出类 HDFS 、ETCD 之类的轮子,虽然说细节上没有尽善尽美,可能也存在一些硬伤,但这种照着论文干工程的能力,我至今记忆犹新。

iOS 上有没有 adb client?

Posted: 14 May 2021 06:49 PM PDT

这里的 adb client 指的是"pc 端通过 adb 连接安卓手机"时的 pc 端的角色

Swift /Sqlite: 请问: 用 Swift 语言, 如何写 SQL 查询, 把分散在两个数据库文件中的 sqlite 表的数据查询出来?

Posted: 14 May 2021 03:39 PM PDT

小妹请教各位帅锅:

我在 android/kotlin 下能用如下代码写 Sql 查询, 把分散在两个文件中的 sqlite 表的数据查询出来:

val db = SQLiteDatabase.openOrCreateDatabase("文件 1.sqlite",null,null);
db.execSQL("ATTACH DATABASE '"+"文件 2.sqlite"+"' AS db2;");

var selectQuery ="SELECT db1.id, db2.name FROM db1 JOIN db1.id ON db2.id "

val cursor = db.rawQuery(selectQuery,null)

请问上述代码在 Swift 中怎么写?
用 Swift 语言, 如何写 SQL 查询, 把分散在两个数据库文件中的 sqlite 表的数据查询出来?

请帮小妹一把, 谢谢!

Google fi 收不到短信怎么回事

Posted: 14 May 2021 03:26 PM PDT

可以打电话跟接听,可以上网但是收不到短信,短信是按照 google fi 的要求设置,奇了怪了,卡已经用了一年多了,去年还能收到短信,以前有发邮件过来说我长期异地漫游,要停用我,不过停用这事已经解决了。还有现在是不是新卡不能激活了,我还有两张带 10 刀话费的 google fi,很久没研究这个了,有老哥知道为什么收不到短信吗,再问下,永久的 gv 号是不是不需要保号也不会被回收,里面有话费但是我很久没登录使用了

在大厂写代码干了多年,现在想申请香港/美国硕士,能用工作经验申请吗?

Posted: 13 May 2021 07:18 PM PDT

在大厂干了很多年, 现在想申请香港硕士,或者美国那边的硕士 能用工作经验申请吗?

因为不是计算机专业出身,而且绩点也不高

No comments:

Post a Comment