作者标题

Autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et dolore feugait.

Author Archive by joson

ETH框架概览

Ethereum 2.0(Eth2.0)主要由 执行层(Execution Layer, EL)共识层(Consensus Layer, CL) 组成。它在以太坊 1.0 的基础上引入了权益证明(PoS)机制,并通过 信标链(Beacon Chain)分片(Sharding,未来版本) 进行扩展。

1. Ethereum 2.0 的核心组件

(1)执行层(Execution Layer, EL)

执行层负责:

• 处理 智能合约

• 处理 交易

• 执行 EVM(以太坊虚拟机)

• 维护 账户余额和状态

主流的 执行层客户端(EL Clients):

客户端语言说明
Geth(Go-Ethereum)Go以太坊最流行的执行层客户端,官方推荐
NethermindC#适用于高性能环境,如企业级应用
BesuJava适用于企业应用,可用于许可链
ErigonGo轻量级、性能优化的以太坊客户端

(2)共识层(Consensus Layer, CL)

共识层负责:

• 运行 PoS 共识机制

• 维护 信标链(Beacon Chain)

• 管理 验证者(Validators)

• 处理 区块验证

主流的 共识层客户端(CL Clients):

客户端语言说明
PrysmGo最流行的 Eth2.0 共识客户端,适合大规模部署
TekuJava适用于企业级应用,ConsenSys 开发
LighthouseRust高效、资源占用低,适合轻量级部署
NimbusNim低资源消耗,适用于 IoT 和移动设备

(3)信标链(Beacon Chain)

• 信标链是 Ethereum 2.0 的核心,它管理 PoS 共识机制,并协调网络中的所有验证者。

• 它存储验证者的质押信息,并负责区块提议、验证和最终确定。

(4)验证者(Validators)

• 在 Ethereum 2.0 的 PoS 机制中,验证者 取代了矿工的角色,他们负责提议和验证新区块。

• 质押 32 ETH 可成为验证者,运行 共识层客户端(如 Prysm、Teku) 以执行验证工作。

(5)质押合约(Deposit Contract)

• 质押合约位于以太坊 1.0 执行层(EL),用于将 ETH 存入 信标链,以成为验证者。

• 质押合约地址:官方存款合约(Ethereum Staking Deposit Contract)

2. Ethereum 2.0 的关键网络

Ethereum 2.0 目前支持多个测试网和主网,主要包括:

网络说明主要用途
Mainnet(主网)以太坊正式网络生产环境
Sepolia主要测试网(PoS)合约测试、验证者测试
HoleskyStaking 主要测试网质押和 PoS 相关测试
Mekong(实验性)实验性测试网开发者实验
Goerli旧版测试网(不推荐)过渡到 Sepolia 和 Holesky

3. Ethereum 2.0 重要升级

Ethereum 2.0 是逐步演进的,目前已完成以下主要升级:

升级名称说明状态
The Merge(合并)以太坊从 PoW 迁移到 PoS已完成(2022 年 9 月)
Shanghai(上海升级)支持 staked ETH 取回已完成(2023 年 4 月)
Dencun(即将升级)引入 Proto-Danksharding计划 2024 年上线
Sharding(分片)扩展以太坊吞吐量预计 2025+

4. 部署 Ethereum 2.0 私链

如果你想部署 以太坊 2.0 私链,你可以:

1. 选择 Geth(执行层)+ Prysm/Teku(共识层)

2. 创建一个自定义的 genesis.json(创世区块文件)

3. 初始化并启动信标链和执行层

4. 如果使用 PoS,需要部署质押合约

总结

Ethereum 2.0 采用 PoS 机制,用 信标链 替代 PoW 挖矿。

执行层(EL) 负责智能合约和交易(Geth、Nethermind)。

共识层(CL) 负责 PoS 机制(Prysm、Teku、Lighthouse)。

• 以太坊主网使用 Mainnet,开发测试通常使用 Sepolia 或 Holesky

• 未来 Sharding(分片) 将进一步扩展 Ethereum 2.0 的性能。

如果你需要在 生产环境 部署 Ethereum 2.0 私链,请告诉我你的具体需求,我可以提供更详细的 部署指南! 🚀

JAVA21 虚线程 使用TaskDecorator装饰的方法

public class DxVirtualThreadTaskExecutor extends TaskExecutorAdapter {

    public DxVirtualThreadTaskExecutor() {
        super(Executors.newVirtualThreadPerTaskExecutor());
    }

}
DxVirtualThreadTaskExecutor executor = new DxVirtualThreadTaskExecutor();
executor.setTaskDecorator( decorator );
executor.execute(()->{

});

CompletableFuture 结合 java21 虚线程

try( ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor() ) {
            List<CompletableFuture<Void>> f = list.stream().map(item ->
                    CompletableFuture.runAsync(() -> {
                        try {
                            processOrder(item);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }, executorService)
            ).toList();
            CompletableFuture.allOf(f.toArray(new CompletableFuture[0])).join();
}

使用 Executors.newVirtualThreadPerTaskExecutor() 作为 CompletableFuture.runAsync的 Executor 即可。对于老代码无痛更改

rocketMQ-封装库 使用

一,引入本地依赖, 注意自己的电脑是否配置有本地库,没配置问一下别人

     <!--<dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.3.0</version>
     </dependency>-->  这个就不要了,关于 rocketmq的一切依赖都可以删掉,只需要保留以下本地库

        <dependency>
            <groupId>org.dxstudio</groupId>
            <artifactId>dx-rocketmq-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

二,配置文件增加以下内容

environment: local  #这里各个环境要使用不同的环境标记,叫什么不重要,目前统一 local | dev | pron | prod
rocketmq:
  consumer:
    # 一次拉取消息最大值,注意是拉取消息的最大值而非消费最大值
    pull-batch-size: 10
  producer:
    group: dx-dump-group
    # 发送消息超时时间,默认3000
    sendMessageTimeout: 3000
    # 发送消息失败重试次数,默认2
    retryTimesWhenSendFailed: 2
    # 异步消息重试此处,默认2
    retryTimesWhenSendAsyncFailed: 2
    # 消息最大长度,默认1024 * 1024 * 4(默认4M)
    maxMessageSize: 4096
    # 压缩消息阈值,默认4k(1024 * 4)
    compressMessageBodyThreshold: 4096
    # 是否在内部发送失败时重试另一个broker,默认false
    retryNextServer: false
  name-server: 192.168.222.10:9876

三,main上添加注解

为了自动注入三方库中的类。因为在配置中做了 注解拦截和修改,用于自动隔离环境,自动更改你的侦听器里的group和topic

@MapperScan("org.dxstudio.dump.core.mapper")
@EnableAsync
@SpringBootApplication
@ComponentScan("org.dxstudio.*")  //加上这个
public class DumpApplication {

    public static void main(String[] args) {
        SpringApplication.run(DumpApplication.class, args);
    }

}

四,发布消息

已经使用项目组的规则做了封装,为保持统一,虽然没有二次封装 rocketMQTemplate,也请使用封装好的 topic 和message 来

@Autowired
RocketMQTemplate rocketMQTemplate;

private SendResult sendMsg(int taskId ){
        //第一个参数是标签,我们约定为事件类型,
        //第二个参数是消息的key(用于在mq后台查询),这里如果是订单 直接订单号,其他使用ID即可
        //第三个参数是你要发送的消息体,范型,爱传什么传什么
        DxMQTpl<Integer> t = new DxMQTpl<>("task", String.valueOf( taskId ), taskId);

        //没有再二次封装 rocketMQTemplate, 所以可以网上搜索这个的发送方法即可,没有特别情况一般就用 syncSend
        SendResult result = rocketMQTemplate.syncSend( t.topic(), t.message() );
        log.info("发送消息 {}", result) ;
        return result;

}

五,消费消息

消费消息已经封装好了处理方法,继承DxMQMessageHandler<T>、并实现 RocketMQListener< DxMQMessage<T> > 即可。

@RocketMQMessageListener 这个是 rocketMQTemplate 的注释,可网上查资料,一般就用我下面的即可,改成自己要订阅的频道

@Slf4j
@RocketMQMessageListener(consumerGroup = "dx-dump-group",
        topic = "dx-dump",
        selectorExpression = "task",
        selectorType = SelectorType.TAG,
        consumeThreadMax = 1,
        consumeThreadNumber = 1)
public class Consumer extends DxMQMessageHandler<Long> implements RocketMQListener< DxMQMessage<Long> > {
    @Autowired
    TaskScanService taskScanService;
    
    //这里固定写 执行超类方法即可
    @Override
    public void onMessage( DxMQMessage<Long> msg) {
        super.dispatchMessage( msg );
    }
    
    //这里是自己真正要处理的逻辑,有问题毫无犹豫抛出异常即可
    @Override
    protected void handleMessage(DxMQMessage<Long> message) throws Exception {
        log.info("处理消息 {}", message);
//        throw new RuntimeException("测试一下异常");
    }
    
    //这里是异常时要做的事情,比如标记异常。 
    //这里必须是重试都失败了才会最后触发,比如关闭 isRetry 和 throwException。 throwException开启会触发rocketmq的自动重发
    @Override
    protected void handleMaxRetriesExceeded(DxMQMessage<Long> message) {
        log.error("消费失败,后续处理,比如标记异常");
    }
    

    //过滤方法,返回true不会执行handleMessage。 用于限制重复的消息消费(rocketmq不会保证不重复),比如订单已经处理过了,在这里个方法里检查好,就不应该再继续处理
    @Override
    protected boolean filter(DxMQMessage<Long> message) {
        return super.filter(message);
    }
    
    //如果要自己修改重试规则(mq的重试是一直会有),开启true,则每次发生异常,是由代码控制 重新发消息。这样自己可以控制重试多少次(5秒间隔),达到最大次数后触发handleMaxRetriesExceeded
    //这个要看自己的业务,允许异常的业务 用这个,不允许异常的,使用mq的重试
    @Override
    protected boolean isRetry() {
        return true;
    }

    @Override
    protected boolean throwException() {
        return true;
    }
}

六,附上rocketmq的重发机制

系统工程教程 —— 仿谷歌超大图片局部加载实现

为了跟公司的人演示一个软件从思考到开发的过程,我花了一天的时间,实现了这部分功能

先看效果: https://php.joson.cc/imagemap/

第一步,搞清原理

什么是局部加载大图,如果有一张体积超大的图,一次性传送给浏览器那几乎是一次糟糕的体验。
1,就算一次传给用户,用户浏览器迫于分辨率和窗口所见局限,也有可能看不清楚。特别是需要看清图上的文字。
2,用户要等很久才能看到图片长啥样。
3,服务器带宽浪费。

如果能只传送用户可见区域的部分图片给用户,等用户拖动时再加载其他部分,这将给用户一个非常有意思的体验。就像我们经常使用的百度地图,地图是非常大的图片,如果百度服务器一次性将地图传给用户,那就不可想象。

那么,我们只要知道用户正在请求图片的哪部分,我们将图片的那部分传过去就好了,其实我们可以用一些简单的数学计算,加上服务器上的GD处理,很容易办到。但如果这样,那么用户的每次请求,服务器都将进行大量的实时演算,消耗巨大的资源来处理图片,这将是可怕的。那么我们很容易想到,预先让服务器把图片的每个缩放等级处理好,再缓存下来。下次直接读取磁盘的缓存则可以解决。

我们可以通过简单的计算,把图片拆分成一个网格。再利用前端JS动态读取每个格子的图片即可。

第二步,理清系统工作流程

第三步,逐个模块实现

其他模块就不用说了,都很简单,重点在图形处理与客户端显示部分。首先我们确立缩放等级的公式,定为 块尺寸 *  2^level,这个公式的缩放可以得到比较好的结果。

在服务器上,我们首先通过图片的尺寸,计算出它的最大 缩放等级 ceil(  sqrt( width / 块尺寸 ) ) , 再把每个缩放等级按照 块尺寸 分割成图则可。如下图(4个缩放等级):

(程序将每个缩放等级的图,自动分发在相对应的文件夹)

(再在每个文件夹里,自动把图拆分成块储存)

现在,只需要前端能准确知道,当前屏幕正在显示哪几块碎片,就可以将他们读取出来。 

(放大后加载缩放等级更高的局部)

(半秒后,所见区域的其他部分被加载了)

springboot+modelMapper+mybatis的枚举和JSON处理

一,依赖

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.48</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.2.0</version>

二,枚举使用

(1)定义枚举

public enum StoreTypeEnum {

    OSS(0,"阿里OSS服务"),
    S3(1,"亚马逊S3服务");

    StoreTypeEnum( int code, String des ){
        this.code = code;
        this.des = des;
    }

    @EnumValue
    private final int code;

    private final String des; //有jsonvalue注释,转换值为des,否则为枚举名

}

(2)接收参数中使用和验证枚举

@Data
public class StoreConfigParam {

    @NotEmpty(message = "键名不能为空")
    private String storeKey;

    @NotEmpty(message = "描述不能为空")
    private String des;

    @NotNull(message = "配置的类型不能为空")
    private StoreTypeEnum storeType;

    private JSONObject storeMeta;

}

(3)数据库实体类中定义枚举

@TableName(autoResultMap = true)
@Data
public class StoreConfig {

    @TableId
    private String storeKey;
    private String des;
    private StoreTypeEnum storeType;

    @TableField( typeHandler = Fastjson2TypeHandler.class)
    private JSONObject storeMeta;

}

(4)使用枚举

public class StoreConfigHttp {

    @Autowired
    private StoreConfigMapper storeConfigMapper;

    @Autowired
    private ModelMapper modelMapper;

    @PostMapping("/store-config/add")
    public ResultRes add(@Validated @RequestBody StoreConfigParam param){
        StoreConfig storeConfig = modelMapper.map( param, StoreConfig.class );
        storeConfigMapper.insert( storeConfig );
        return ResultRes.success();

    }

    @PostMapping("/store-config/update")
    public ResultRes update(@Validated @RequestBody StoreConfigParam param){
        StoreConfig storeConfig = modelMapper.map( param, StoreConfig.class );
        storeConfig.setStoreType(StoreTypeEnum.S3); //这里故意多写一行演示设置枚举值,实际上 modelMapper可以自动帮我买转换
        storeConfigMapper.updateById( storeConfig);
        return ResultRes.success();
    }
}

使用modelMapper讲param转换成endity时,会自动转换枚举值,非常方便。查询时,也会将数据库中存的code(整型)自动转为枚举名(字符串类型)。避免接收或返回给前端无意义的状态数字

三,JSON使用

(1)关于Mysql的JSON字段说明

https://www.cnblogs.com/ivictor/p/16221712.html

(2)使用modelMapper自动转换模型

modelMapper不能默认自动转换json高级类型,但它提供自定义converter方法。我们只需要在配置中加入以下代码。这里有2种方法,JSONObject转string,存数据库也行,JSONObject转JSONObject也行(听起来很奇怪,但即使完全相同的类型,modelMapper确实不能识别),Mybatis可以完成JSONObject的存储

@Configuration
public class ModelMapperConfig {

    @Bean
    public ModelMapper modelMapper(){

        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setFullTypeMatchingRequired(true);
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
         
        //从JSONObject类型转为 entidy 的JSONObject类型
        Converter< JSONObject, JSONObject > converter = new Converter<JSONObject, JSONObject>() {
            @Override
            public JSONObject convert(MappingContext<JSONObject, JSONObject> mappingContext) {
                return mappingContext.getSource();
            }
        };

        modelMapper.addConverter(converter);
        return modelMapper;

    }

}

(3)mybatis的自适应

entidy模型,必须开启注释@TableName(autoResultMap = true),并在json字段注释@tableField,指定使用typeHandler为json。mybatis默认提供了fastjson\fastjson2\jackson等的typehandler,也可以利用typehandler机制自己实现

@TableName(autoResultMap = true) //必须加这个选项
@Data
public class StoreConfig {

    @TableId
    private String storeKey;
    private String des;
    private StoreTypeEnum storeType;

    @TableField( typeHandler = Fastjson2TypeHandler.class) //mybatis 自带的TypeHandler可以处理json
    private JSONObject storeMeta;

}

完毕后,即可以使用JSONObject插入、更新字段,查询出内容后也会自动转换成JSON格式输出

{
    "code": 0,
    "message": null,
    "data": {
        "records": [
            {
                "storeKey": "tes2",
                "des": "这是一个测试配置da",
                "storeType": "S3",
                "storeMeta": {
                    "key": "wawa444"
                }
            },
   }
}

APItable客户端

看见vika官方的客户端,还要收费才能获得,而且简直糊弄人。哈哈,按照他的尿性,半小时给我开源版的APItable也来它一套。

顺便把它所有的外链都给禁止了。LOGO、名字也改掉~~盗版盗全套~~

项目地址:https://joson.cc:8402/joson/apitable_desktop