精华内容
下载资源
问答
  • 多存储后端某些使用场景下,需要 Seafile 服务可以支持多个存储后端,例如:要将不同的文件类型存储到不同的存储后端。例如,普通文件可以存储到主存储(disks, SSD);文档文件可以存储到 “冷存储”(其他存储系统)。...

    多存储后端

    某些使用场景下,需要 Seafile 服务可以支持多个存储后端,例如:要将不同的文件类型存储到不同的存储后端。例如,普通文件可以存储到主存储(disks, SSD);文档文件可以存储到 “冷存储”(其他存储系统)。

    结合多个存储后端来扩展存储的可扩展性。例如,单独使用一个 NFS 挂载卷时可能会受到大小限制;单独使用一个 S3 后端存储时,当对象数量变得很大时,Ceph RGW 的性能可能会受到很大影响。

    在 Seafile 中,以资料库为单位,将数据分散存储到多个存储后端中。在同一个资料库中的所有数据将被存储到同一个存储后端。每个资料库和存储后端之间的映射关系存储在数据库中。根据使用情况选择不同的映射策略。

    为了使用该功能,您需要:在 seafile.conf 中定义存储后端。

    在 seahub_settings.py 中开启多存储后端功能,并选择一个映射策略。

    定义存储后端

    在 Seafile 中,每一个存储后端都由一个 "storage class" 代表。存储后端由以下信息定义:storage_id:用来定义存储后端的内部 ID 字符串,对用户不可见。例如:'primary storage'。

    name:用户可见的存储名称。

    is_default:定义该存储是否是默认的。如果这个存储后端允许用户使用,当用户不选择时,将默认采用该存储后端。

    commits:该存储后端中,用来存放 commit 对象的存储位置。它可以是任何 Seafile 支持的存储,比如:文件系统、S3 或者 ceph。

    fs:该存储后端中,用来存放 fs 对象的存储位置。它可以是任何 Seafile 支持的存储,比如:文件系统、S3 或者 ceph。

    blocks:该存储后端中,用来存放 block 对象的存储位置。它可以是任何 Seafile 支持的存储,比如:文件系统、S3 或者 ceph。

    commit, fs, 和 blocks 能够被存储在不同的存储中。这为定义存储后端提供了最为灵活的方式。

    在 Seafile 6.3 之前的版本中,不支持多个存储后端。您必须明确地启用这个新功能,并使用与以前定义存储后端不同的语法格式定义存储后端。

    首先,你必须在 seafile.conf 中启用这个功能:[storage]

    enable_storage_classes=true

    storage_classes_file=/opt/seafile_storage_classes.jsonenable_storage_classes:设置为 True,开启多存储后端功能;接下来你必须在下一个配置项指定的 JSON 文件中定义出这些存储后端。

    storage_classes_file:指定包含有存储后端定义的 JSON 文件的位置。

    JSON 文件是一个对象数组。每个对象定义一个存储后端。定义中的字段对应于我们需要为存储后端指定的信息。下面是一个例子:[

    {

    "storage_id":"hot_storage",

    "name":"Hot Storage",

    "is_default":true,

    "commits":{"backend":"s3","bucket":"seafile-commits","key":"ZjoJ8RPNDqP1vcdD60U4wAHwUQf2oJYqxN27oR09","key_id":"AKIAIOT3GCU5VGCCL44A"},

    "fs":{"backend":"s3","bucket":"seafile-fs","key":"ZjoJ8RPNDqP1vcdD60U4wAHwUQf2oJYqxN27oR09","key_id":"AKIAIOT3GCU5VGCCL44A"},

    "blocks":{"backend":"s3","bucket":"seafile-blocks","key":"ZjoJ8RPNDqP1vcdD60U4wAHwUQf2oJYqxN27oR09","key_id":"AKIAIOT3GCU5VGCCL44A"}

    },

    {

    "storage_id":"cold_storage",

    "name":"Cold Storage",

    "is_default":false,

    "fs":{"backend":"fs","dir":"/storage/seafile/seafile-data"},

    "commits":{"backend":"fs","dir":"/storage/seafile/seafile-data"},

    "blocks":{"backend":"fs","dir":"/storage/seafile/seaflle-data"}

    },

    {

    "storage_id":"swift_storage",

    "name":"Swift Storage",

    "fs":{"backend":"swift","tenant":"adminTenant","user_name":"admin","password":"openstack","container":"seafile-commits","auth_host":"192.168.56.31:5000","auth_ver":"v2.0"},

    "commits":{"backend":"swift","tenant":"adminTenant","user_name":"admin","password":"openstack","container":"seafile-fs","auth_host":"192.168.56.31:5000","auth_ver":"v2.0"},

    "blocks":{"backend":"swift","tenant":"adminTenant","user_name":"admin","password":"openstack","container":"seafile-blocks","auth_host":"192.168.56.31:5000","auth_ver":"v2.0","region":"RegionTwo"}

    }

    ]

    如上所示:commits,fs 和 blocks 信息的语法格式与 seafile.conf 中定义的 [commit_object_backend], [fs_object_backend] 和 [block_backend] 语法格式相似。

    如果你是用文件系统作为 commits,fs 或 blocks 的存储位置,您必须明确地提供"seafile-data"目录的路径。这些对象将会被存储在这个路径下的 storage/commits, storage/fs, storage/blocks 中。

    注意:一般的文件系统,S3 和 Swift 后端是支持的,但 Ceph/RADOS 目前还不支持。

    资料库映射策略

    资料库映射策略决定了资料库使用的存储后端。目前我们为3个不同的用例提供3个策略。资料库的存储后端被创建和存储在一个数据表中。如果之后再更改映射策略各资料库对应的存储后端将不会改变。

    选择映射策略之前,你需要在 seahub_settings.py 开启该功能:ENABLE_STORAGE_CLASSES=True

    用户选择

    该策略需要用户在创建新资料库的时候选择存储后端。用户可以选择定义在 JSON 文件中的任何一个存储后端。

    如果要使用该策略,需要在 seahub_settings.py 中添加以下配置:STORAGE_CLASS_MAPPING_POLICY='USER_SELECT'

    如果你开启了 STORAGE_CLASSES,但没有在 seahub_settings.py 中明确定义 STORAGE_CLASS_MAPPING_POLIICY,则默认使用“用户选择”策略。

    基于角色映射

    您还可以根据用户的角色配置用户可使用的存储后端。

    在 seahub_settings.py 中加一个新的配置项 storage_ids 到角色配置段中,给每一个角色分配存储后端。如果给某个角色只分配了一个存储后端,这个角色下的用户将不能选择其他存储后端给资料库使用。否则,如果分配了多个存储后端,用户在创建资料库时可以从中选择一个存储后端。如果没有为角色分配任何存储后端,将使用JSON文件中指定的默认后端。

    这里有一个简单的 seahub_settings.py 中的配置,使用该策略:ENABLE_STORAGE_CLASSES=True

    STORAGE_CLASS_MAPPING_POLICY='ROLE_BASED'

    ENABLED_ROLE_PERMISSIONS={

    'default':{

    'can_add_repo':True,

    'can_add_group':True,

    'can_view_org':True,

    'can_use_global_address_book':True,

    'can_generate_share_link':True,

    'can_generate_upload_link':True,

    'can_invite_guest':True,

    'can_connect_with_android_clients':True,

    'can_connect_with_ios_clients':True,

    'can_connect_with_desktop_clients':True,

    'storage_ids':['old_version_id','hot_storage','cold_storage','a_storage'],

    },

    'guest':{

    'can_add_repo':True,

    'can_add_group':False,

    'can_view_org':False,

    'can_use_global_address_book':False,

    'can_generate_share_link':False,

    'can_generate_upload_link':False,

    'can_invite_guest':False,

    'can_connect_with_android_clients':False,

    'can_connect_with_ios_clients':False,

    'can_connect_with_desktop_clients':False,

    'storage_ids':['hot_storage','cold_storage'],

    },

    }

    基于资料库 ID 的映射

    这个策略根据资料库的ID映射到存储后端。资料库的ID是UUID。这样,系统中的数据就可以在存储后端之间均匀地分配。

    注意,这个策略并不是设计成完整的分布式存储解决方案。它不处理资料库数据在各存储后端之间的自动迁移。如果需要向配置文件添加更多的存储后端,则现有资料库将保留在它们的原始存储中。新的资料库可以在新的存储后端之间分布。您仍然需要在开始时计划系统的总存储容量。

    要使用该策略,先在 seahub_settings.py 中添加以下配置项:STORAGE_CLASS_MAPPING_POLICY='REPO_ID_MAPPING'

    然后可以在 JSON 文件中的存储后端中添加 for_new_library 选项来存储新的资料库:[

    {

    "storage_id":"new_backend",

    "name":"New store",

    "for_new_library":true,

    "is_default":false,

    "fs":{"backend":"fs","dir":"/storage/seafile/new-data"},

    "commits":{"backend":"fs","dir":"/storage/seafile/new-data"},

    "blocks":{"backend":"fs","dir":"/storage/seafile/new-data"}

    }

    ]

    展开全文
  • Java 后端国际化设计方案设计需求 设计需求 国际化配置集中到数据库中进行管理,包含前端部分国际化 最好可动态添加国际化的语种 好用易用 高效

    前言

    代码就不放全了,还在公司上跑着呢,就放一点非核心代码,工具类封装之类的

    设计需求

    • 国际化配置集中到数据库中进行管理,包含前端部分国际化
    • 最好可动态添加国际化的语种
    • 好用易用
    • 高效

    设计思路

    • 利用自定义注解来启用国际化,拦截所有返回请求进行处理
    • 大数据量处理使用多线程并行处理
    • 国际化数据保存在 Redis 中视为热点数据
    • 使用手动刷新方式,保证无缝刷新缓存
    • 国际化部分数据以 Json 形式来保存,保证扩展性
    • 语种以配置的形式保存,必要可添加语种
    • 需要多语言切换的数据全部以占位符代替,通过自定义注解统一替换
    • 当前语言环境通过前端带在请求头里给后端,后端默认为英文

    数据库设计

    • type: 类型,非空
    • module: 模块,可为空
    • label: 标签,非空
    • langs: 国际化 Json String,非空
    • to_web: 是否返回前端,不返回的就只是后端使用,将数据切成两半

    其中 type.module.label 的组合为唯一标识
    在这里插入图片描述

    需要国际化翻译的数据保存形式
    由于后端部分为自动生成的,因此 label 使用 UUID()
    在这里插入图片描述

    后端返回数据部分,比如:异常提示这部分的翻译
    在这里插入图片描述

    功能设计

    用到的工具类

    JsonUtils.java

    自定义注解

    个人认为只需要一个启动开关即可,没必要做成那种一个个接口去加

    /**
     * 开启国际化注解
     */
    @Import(TranslationAspect.class)
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EnableTranslation {
    }
    

    使用
    在这里插入图片描述

    切面开发

    TranslationAspect

    /**
     * 国际化实现
     * @Author: linjinp
     * @Date: 2021/1/18 15:47
     */
    @Slf4j
    @Aspect
    public class TranslationAspect {
    
        // 默认语种
        private final static String DEFAULT_LANGUAGE = "en";
    
        // 切点
        // 指定拦截的包路径
        @Pointcut("execution(* com.xxx..*.*(..))")
        private void pointcut() {}
    
        @Around("pointcut()")
        private Object around(ProceedingJoinPoint pjp) throws Throwable {
    		// ...
    		// TODO
    		// ...
    		return ...
    	}
    

    从请求头获取当前语言环境

    在这里插入图片描述

    获取当前返回值的类型

    在这里插入图片描述

    /**
     * 获取列表中数据类型
     * @param obj
     * @return
     */
    private static Class getArrayListClass(Object obj) {
        // 判断空,判断类型是否为列表,判断是否有数据(无数据无法获取类型)
        if (obj != null && ArrayList.class.equals(obj.getClass()) && ((List) obj).size() > 0) {
            return ((List) obj).get(0).getClass();
        }
        return null;
    }
    
    /**
     * 获取数据类型
     * @param obj
     * @return
     */
    private static Class getClass(Object obj) {
        // 判断空,判断类型是否为列表,判断是否有数据(无数据无法获取类型)
        if (obj != null && !ArrayList.class.equals(obj.getClass())) {
            return obj.getClass();
        }
        return null;
    }
    

    将返回值转为 Json String 后,统一获取其中的占位符

    使用 StringBuilder 保存,防止大量的对象被创建
    在这里插入图片描述

    /**
    * 获取字符串中所有的变量参数
     * @param str 字符串/对象Json
     * @return
     */
    private static List<String> findParams(String str) {
        List<String> params = new ArrayList<>();
        // 转化为二进制
        char[] chars = str.toCharArray();
        // 找到标志的索引
        int findIndex = -1;
        for (int i = 0; i < chars.length; i ++) {
            // 判断 ${ 组合
            // i <= chars.length - 3 防越界,${A,假如以此结尾,$ 在 length - 3 位置
            if (i <= chars.length - 3 && chars[i] == '$' && chars[i + 1] == '{') {
                // 获取首个变量的下标索引
                findIndex = i + 2;
            }
            // 判断 } 且,已经存在索引下标,防止前面单独出现 } 的情况
            if (chars[i] == '}' && findIndex != -1) {
                // 添加变量
                params.add(new String(Arrays.copyOfRange(chars, findIndex, i)));
                // 重置标识
                findIndex = -1;
            }
        }
        return params;
    }
    

    替换返回值中所有的占位符为对应语言

    /**
     * 数据处理
     * @param lang 语言环境
     * @param data 返回数据
     * @param languages 语言包
     * @param params 需要替换的参数列表
     * @return
     */
    private static StringBuilder dataProcess(String lang, StringBuilder data, List<MultiLanguage> languages, List<String> params) {
        // 循环数据
        for (MultiLanguage language : languages) {
            // 有配置语言,非空对象,为后端使用的标签
            if (StringUtils.isNotBlank(language.getLangs()) && !"{}".equals(language.getLangs())) {
                for (String param : params) {
                    // 如果标签组合匹配
                    if (language.equalsCombination(param)) {
                        // 假如当前环境非默认语种,判断当前语种是否已经配置,如果没配置或为空,使用默认语种数据
                        if (!DEFAULT_LANGUAGE.equals(lang) && JsonUtils.toMap(language.getLangs()).containsKey(lang) && StringUtils.isNotBlank((String) JsonUtils.toMap(language.getLangs()).get(lang))) {
                            data.replace(0, data.length(), replaceRegex(data.toString(), param, StrUtil.nullToEmpty((String) JsonUtils.toMap(language.getLangs()).get(lang))));
                        } else {
                            data.replace(0, data.length(), replaceRegex(data.toString(), param, StrUtil.nullToEmpty((String) JsonUtils.toMap(language.getLangs()).get(DEFAULT_LANGUAGE))));
                        }
                    }
                }
            }
        }
        return data;
    }
    
    /**
     * 正则内容替换
     * @param source 数据
     * @param key 国际化标签
     * @param value 国际化对应值
     * @return
     */
    private static String replaceRegex(String source, String key, String value) {
        String regex = "\\$\\{"+key+"\\}";
        return source.replaceAll(regex, value);
    }
    

    最后要保证返回值的类型正确

    也是为了保证旧代码的兼容,比如你后来才加的国际化,这也是之前获取数据类型的原因
    在这里插入图片描述

    数据缓存

    构建线程池

    <!-- hutool -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.3.10</version>
    </dependency>
    
    /**
     * 线程池配置
     *
     * @Author: linjinp
     * @Date: 2020/9/29 10:54
     */
    @Slf4j
    @Component
    public class ExecutorConfig {
    
        public static ExecutorService executor;
    
        // 初始线程数量
        private final static int DEFAULT_NUM = 5;
    
        // 最大线程数
        private final static int MAX_NUM = 10;
    
        // 最大等待线程数
        private final static int MAX_WAITING = 100;
    
        @Bean
        public ExecutorService createExecutor() {
            this.executor = ExecutorBuilder.create()
                    // 默认初始化 5 个线程
                    .setCorePoolSize(DEFAULT_NUM)
                    // 最大线程数 10
                    .setMaxPoolSize(MAX_NUM)
                    // 最大等待线程数 100
                    .setWorkQueue(new LinkedBlockingQueue<>(MAX_WAITING))
                    .build();
            log.info("\n初始化线程池\n默认初始线程数:{}\n最大线程数:{}\n最大等待线程数:{}", DEFAULT_NUM, MAX_NUM, MAX_WAITING);
            return this.executor;
        }
    }
    

    数据缓存到 Redis

    刷新时将数据保存到 Redis 中

    这里使用 多线程 + 闭锁 的方式同步进行两方的处理,闭锁保证两个线程都完成后才继续执行,返回前端成功

    /**
     * 刷新国际化配置缓存
     * @return
     */
    @ApiOperation("刷新国际化配置缓存")
    @GetMapping(value = "/refresh")
    public ErrorMsg<Map<String, Object>> refresh() throws InterruptedException {
        // 利用闭锁保证两个线程都执行完毕后返回
        final CountDownLatch countDownLatch = new CountDownLatch(2);
    
        // 前端国际化数据,多线程处理
        ExecutorConfig.executor.execute(new Runnable(() -> {
            try {
            	// 构建前端所需的数据格式
                Map<String, Object> languageMap = buildLangToWeb();
                // 保存前端国际化部分数据 Map
                redisTemplate.opsForValue().set(RedisKeyConfig.LANGUAGE_ZONE, languageMap);
            } finally {
                countDownLatch.countDown();
            }
        }));
    
        // 后端国际化数据,多线程处理
        ExecutorConfig.executor.execute(new Runnable(() -> {
            try {
            	// 获取后端所需的数据列表
                List<MultiLanguage> languageList = buildLangToJava();
                // 保存后端国际化部分数据 List
                redisTemplate.opsForValue().set(RedisKeyConfig.LANGUAGE_JAVA, languageList);
            } finally {
                countDownLatch.countDown();
            }
        }));
        // 闭锁阻塞
        countDownLatch.await();
        return ErrorMsg.SUCCESS;
    }
    

    项目启动初始化国际化数据

    // 开启语言翻译
    @EnableTranslation
    public class AdminApplication {
        public static void main(String[] args) {
            SpringApplication.run(AdminApplication.class, args);
        }
    
        @Autowired
        private MultiLanguageController multiLanguageController;
    
        @Bean
        public CommandLineRunner runner() {
            return args -> {
                log.info("开始初始化国际化数据:{}", new Date());
                multiLanguageController.refresh();
                log.info("国际化初始化完成:{}", new Date());
            };
        }
    }
    

    效果展示

    中文返回
    在这里插入图片描述
    在这里插入图片描述
    英文返回
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 重新设计了状态后端的存储 统一keyState的savePoint的存储格式为二进制 FailureRateRestartBackoffTimeStrategy 允许比配置少重启一次 支持未对齐检查点的重新调整:从未对齐检查点恢复的时候支持改变作业的并行度 ...

    前言

    使用Flink版本 1.13 , 该版本对状态有所改变,版本变化

    • 删除 state.backend.async
    • 重新设计了状态后端的存储
    • 统一keyState的savePoint的存储格式为二进制
    • FailureRateRestartBackoffTimeStrategy 允许比配置少重启一次
    • 支持未对齐检查点的重新调整:从未对齐检查点恢复的时候支持改变作业的并行度


    什么是状态?

      对我们进行记住多个event的操作的时候,比如说窗口,那么我们称这种操作是有状态的,是需要缓存数据的。Flink 需要知道状态,以便使用检查点保存点使其容错 。简单的说就是Flink的一个存储单元。

      从 Flink 1.11 开始,检查点可以在对齐或不对齐的情况下进行。


      状态分为两种:键值状态(KeyState)和(OperatorState)非键值状态(算子状态)。



    状态和ck的关系

    State一般指一个具体的task/operator的状态。而Checkpoint则表示了一个Flink Job,在一个特定时刻的一份全局状态快照(state snapshots ),即包含了所有task/operator的状态。



    什么是状态后端

      Flink 提供了不同的状态后端,用于指定状态的存储方式和位置。默认情况下,配置文件flink-conf.yaml确定所有 Flink 作业的状态后端。

      对于流处理程序,Flink Job的状态后端决定了它的状态如何 存储在每个 TaskManager(TaskManager 的 Java 堆或(嵌入式)RocksDB)上。


    State 始终在本地访问(只是访问/使用),这有助于 Flink 应用程序实现高吞吐量和低延迟。您可以选择将状态保留在 JVM 堆上,或者如果它太大,则保留在有效组织的磁盘数据结构中,这取决于状态后端的配置。

    在这里插入图片描述


    官方提供了两种静态的(开箱即用)的状态后端:

    • HashMapStateBackend:保存数据在内部作为Java堆的对象。
    • EmbeddedRocksDBStateBackend:在RocksDB数据库中保存动态数据。



    通过状态快照的容错 #


    1.13版本Flink社区重新设计了状态后端的存储,以便于用户更好的理解状态后端的存储检查点的存储的分离。

    Flink详解Exactly-Once机制注意:该文写于1.12的flink版本,那个时候ck存储和状态存储在理解上还是绑定在一起,具体情况看本文的状态后端一节



    Keyed State

    如果要使用键控状态,则需要先分出键控流:dataStream.keyBy()产生一个键控流(KeyedStream),然后允许使用键控状态的操作。

    在这里插入图片描述

    在上图中:Keyed State 被进一步组织成所谓的Key Groups。Key Groups的数量与定义的并行度一样多。在运行并行实例中的一个并行算子的时候,使用到一个或多个Key Groups的keys。



    可用的KeydeState

    • ValueState<T>:保留一个可以更新和检索的值:设置update(T)T value()
    • ListState<T>:保留一个元素列表,可以追加、遍历Iterable和覆盖:add(T) , addAll(List) , Iterable Iterable<T> get() , update(List);
    • ReducingState<T>只保留一个值,但是这个值代表添加到State的所有值的聚合,故为reduce。
    • AggregatingState<IN, OUT>只保留一个值,但是这个值代表添加到State的所有值的聚合。与ReducingState不同的是,IN的元素可能会不一样。
    • MapState<UK, UV>:保留一个Map,可以增加(put(UK, UV),putAll(Map<UK, UV>)),检索(get(UK)),遍历(entries()->key()和value())和判空(isEmpty());

    所有类型的状态也有一个方法clear()可以清除当前活动键的状态,即输入元素的key。



    我们要记住两件事

    1、重要的是要记住,这些状态对象仅用于与状态接口。状态不一定存储(状态后端)在内部,但可能驻留在磁盘或其他地方。

    2、我们从状态(存储)获取的value,取决于event element流数据的key。流式数据是流动的。



    KeydeState的使用

    要获得状态句柄(使用State),我们必须创建一个StateDescriptorStateDescriptor包含了:

    • State的名称
    • 保存的值的类型
    • 可能的用户-指定的函数,例如ReduceFunction

    我们可以对于上面可用的KeyedState来创建对于的StateDescriptor。我们以官网的例子来示例:


    代码创建了一个统计每两个element的平均数的计数窗口,计数到2变求出平均值并清空ValueState:clear()。

    public class CountWindowAverage extends RichFlatMapFunction<Tuple2<Long, Long>, Tuple2<Long, Long>> {
    
        /**
         * The ValueState handle. The first field is the count, the second field a running sum.
         */
        private transient ValueState<Tuple2<Long, Long>> sum;
    
        @Override
        public void flatMap(Tuple2<Long, Long> input, Collector<Tuple2<Long, Long>> out) throws Exception {
    
            // access the state value
            Tuple2<Long, Long> currentSum = sum.value();
    
            // update the count
            currentSum.f0 += 1;
    
            // add the second field of the input value
            currentSum.f1 += input.f1;
    
            // update the state
            sum.update(currentSum);
    
            // if the count reaches 2, emit the average and clear the state
            if (currentSum.f0 >= 2) {
                out.collect(new Tuple2<>(input.f0, currentSum.f1 / currentSum.f0));
                sum.clear();
            }
        }
    
        @Override
        public void open(Configuration config) {
            ValueStateDescriptor<Tuple2<Long, Long>> descriptor =
                    new ValueStateDescriptor<>(
                            "average", // the state name
                            TypeInformation.of(new TypeHint<Tuple2<Long, Long>>() {}), // type information
                            Tuple2.of(0L, 0L)); // default value of the state, if nothing was set
            sum = getRuntimeContext().getState(descriptor);
        }
    }
    
    // this can be used in a streaming program like this (assuming we have a StreamExecutionEnvironment env)
    env.fromElements(Tuple2.of(1L, 3L), Tuple2.of(1L, 5L), Tuple2.of(1L, 7L), Tuple2.of(1L, 4L), Tuple2.of(1L, 2L))
            .keyBy(value -> value.f0)
            .flatMap(new CountWindowAverage())
            .print();
    
    // the printed output will be (1,4) and (1,5)
    // 输出两个是因为输入5个<6, 输出1,5是因为 /取整
    



    Keyed State的TTL(Time to live,只支持处理时间)

    当前仅支持与处理时间(Progress Time)相关的TTL!!!

    设置键值状态的过期时间,尽最大可能的清理存储值。这里我们需要配置一个StateTtlConfig对象:

    import org.apache.flink.api.common.state.StateTtlConfig;
    import org.apache.flink.api.common.state.ValueStateDescriptor;
    import org.apache.flink.api.common.time.Time;
    
    StateTtlConfig ttlConfig = StateTtlConfig
        .newBuilder(Time.seconds(1))
        .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
        .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
        .build();
        
    ValueStateDescriptor<String> stateDescriptor = new ValueStateDescriptor<>("text state", String.class);
    stateDescriptor.enableTimeToLive(ttlConfig);
    

    配置参数

    newBuilder:固定就是生存时间


    setUpdateType:表明了过期时间什么时候更新:在StateTtlConfig.UpdateType.下有:

    • OnCreateAndWrite - 仅在创建和写访问时
    • OnReadAndWrite - 也在读访问

    在State设置为可见情况下,是否还能访问过期的State的值:在StateTtlConfig.StateVisibility.下有:

    • NeverReturnExpired - 永远不会返回过期值
    • ReturnExpiredIfNotCleanedUp - 如果过期值未被清除仍然可用,则返回。

    默认情况下,过期State会被删除,设置过期不被删除策略,配置StateTtlConfig:

    StateTtlConfig.disableCleanupInBackground(); //此选项不适用于 RocksDB 状态后端的增量检查点。
    

    NeverReturnExpired处理隐私敏感数据的应用程序很有用。



    注意

    • TTL 配置不是检查点或保存点的一部分,而是 Flink 在当前运行的作业中如何处理它的一种方式。
    • 会增加状态存储的消耗;



    算子状态(Operator State )

      算子状态(or non-keyed state),是被绑定在单并行(并行度为1)的算子实例中的State(状态)的。一般来说它会被用在 Source 或 Sink 等算子上,用来保存流入数据的偏移量或对输出数据做缓存,以保证 Flink 应用的 Exactly-Once 语义。

      KafkaConsumer便是一个很好的例子来说明:KafkaConsumer的每一个并行消费实例都有一个算子状态,它存储着消费主题的分区们(partitions)偏移量们(offsets)的映射集合(Map)。



    广播状态

    待研习。



    状态后端

    StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    env.setStateBackend(...);
    

    Flink 提供了三种可用的状态后端用于在不同情况下进行状态的保存:

    • MemoryStateBackend
    • FsStateBackend
    • RocksDBStateBackend

    1.13版本Flink社区重新设计了状态后端的存储,以便于用户更好的理解状态后端的存储检查点的存储的分离。对于组合策略说明有官网链接可以查询。我们以下面的内存状态后端为例:



    MemoryStateBackend

    老版本设置

    env.setStateBackend(new MemoryStateBackend(DEFAULT_MAX_STATE_SIZE,false));
    

      false 代表关闭异步快照机制。很明显 MemoryStateBackend 适用于我们本地调试使用,来记录一些状态很小的 Job 状态信息。

      MemoryStateBackend相当于使用HashMapStateBackendJobManagerCheckpointStorage



    新版本配置

    代码配置

    StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    env.setStateBackend(new HashMapStateBackend());
    env.getCheckpointConfig().setCheckpointStorage(new JobManagerStateBackend());
    

    配置文件配置

    state.backend: hashmap
    
    # 默认为JobManagerCheckpointStorage
    state.checkpoint-storage: jobmanager
    



    FsStateBackend

      FsStateBackend 会把状态数据保存在 TaskManager 的内存中。CheckPoint 时,将状态快照写入到配置的文件系统目录中,少量的元数据信息存储到 JobManager 的内存中。

      FsStateBackend相当于使用HashMapStateBackendFileSystemCheckpointStorage


    代码配置

    StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    env.setStateBackend(new HashMapStateBackend());
    env.getCheckpointConfig().setCheckpointStorage("file:///checkpoint-dir");
    

    自定义化配置需要自定义实现FileSystemCheckpointStorage类:

    // 高级 FsStateBackend 配置,例如写缓冲区大小,可以通过手动实例化 FileSystemCheckpointStorage 对象来设置。
    env.getCheckpointConfig().setCheckpointStorage(new FileSystemCheckpointStorage("file:///checkpoint-dir"));
    

    配置文件配置

    state.backend: hashmap
    state.checkpoints.dir: file:///checkpoint-dir/
    
    # 默认为FileSystemCheckpointStorage
    state.checkpoint-storage: filesystem
    



    RocksDBStateBackend

      RocksDBStateBackend 将正在运行中的状态数据保存在 RocksDB 数据库中,RocksDB 数据库默认将数据存储在 TaskManager 运行节点的数据目录下。

    需要注意的是,RocksDBStateBackend 是唯一支持增量快照的状态后端。

      RocksDBStateBackend相当于使用EmbeddedRocksDBStateBackendFileSystemCheckpointStorage


    代码配置

    StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    env.setStateBackend(new EmbeddedRocksDBStateBackend());
    env.getCheckpointConfig().setCheckpointStorage("file:///checkpoint-dir");
    

    或者自定义化配置需要自定义实现FileSystemCheckpointStorage类:

    // 高级 FsStateBackend 配置,例如写缓冲区大小,可以通过手动实例化 FileSystemCheckpointStorage 对象来设置。
    env.getCheckpointConfig().setCheckpointStorage(new FileSystemCheckpointStorage("file:///checkpoint-dir"));
    

    配置文件配置

    state.backend: rocksdb
    state.checkpoints.dir: file:///checkpoint-dir/
    
    # 默认为FileSystemCheckpointStorage
    state.checkpoint-storage: filesystem
    



    备注

    Python DataStream API 仍然不支持 Operator 状态。 不支持广播状态。

    展开全文
  • 近期知乎上一个普通提问,却引发前端后端热烈讨论,相信不少学习和从业后端的朋友都有这个疑问,那么事实真的如此吗?学习数字IC后端该何去何从? 数字IC后端 真的不如前端设计和验证吗? 我是一名研一的学生,最近...

    近期知乎上一个普通提问,却引发前端后端热烈讨论,相信不少学习和从业后端的朋友都有这个疑问,那么事实真的如此吗?学习数字IC后端该何去何从?
    在这里插入图片描述

    数字IC后端
    真的不如前端设计和验证吗?

    我是一名研一的学生,最近找实习拿到了一家不错的外企的后端实习生offer。但是最近我却对数字后端这个方向有点犹豫了:
    

    一方面,我跟同学交流感觉他们都有点看不上数字后端,他们都想去做设计验证,他们对数字后端的印象就是加班多,很累,像是体力活,和设计验证相比真的是这样吗?希望数字ic的前辈们可以说说你们目前的工作状态,加班情况。

    另一方面,我了解到很多公司都会把后端外包出去,我看了近些年的招聘信息,发现大公司后端的岗位相对比较少,而小公司直接不招后端工程师(应该是都外包出去了),难道后端外包是一个趋势,以后渐渐的大公司也不需要这么多后端人员了,我们是不是以后只能去做外包?

    本来找到一个不错的实习我还挺高兴,可是现在我不得不对自己今后的职业发展重新考虑,希望设计、验证、后端的前辈们提供一些建议,我可以在对比后选择出一个可以坚定做下去的方向!‍
    在这里插入图片描述

    一些高薪的热门岗位,竞争十分激烈;对于应聘者的要求也是十分高的,想要提升面试技巧,可至>>>移知PC端官网,资料下载专区,搜索“面试资料”,看看往届知名公司面试真题,为面试做足准备。

    后端到底是什么?

    芯片后端设计,看似只是将网表中的晶体管摆放好。但并不是如同砖头砌墙那样简单粗暴。它是一门兼具形式美和工程实践需求的技术。形式美,直接来源于功能内容和需求,在后端设计的环节中,数以万计的标准单元如散乱的点点繁星,却在功能、时序等满足的前提下,寻求各个Block之间的依赖关系,进而使芯片内部之间呈现出和谐与稳定。

    数字芯片后端工程师主要工作就是接收数字前端提交的代码,最终交付一个完整的芯片布局布线结果。

    数字芯片后端设计环节一般都是芯片项目驱动。芯片也分很多种,大小不同,难度不同。简单按功能划分,主要类型有 WIFI芯片、BLE芯片、RFID芯片、音频芯片、传感器芯片、汽车电子、手机芯片等等。

    ——《来源:知乎》

    后端加班多?

    这个对大多数公司成立,因为芯片设计周期一般都要求尽量短,而后端设计是生产前的最后一步,因此前面的设计、验证、DFT或综合都没有后端这么强的时间约束,尤其是有些芯片需要赶上固定时间段的流片时间表,几乎没有什么延期的空间,这就导致整体上尤其流片前的时间压力较大。

    但这也不是对所有公司都成立,一些产品有一定壁垒的公司并不会把设计周期压得特别紧,甚至可能流片前几周就都修完了坐等流片的情况。

    虽然加班较多,但是个人感受没有多少公司能够达到长期996的强度,强制或者高强度加班的业界也就那几家,其他的都还好。

    后端门槛低?

    一定程度上成立。目前大厂校招虽然不论前端后端基本上都硕士起步,但是某些小公司还给本科生留了点门,社招的话除了某些特别看重出身的公司,后端基本不死卡学历,本科作为底线,技术到位基本上都有机会。

    除此之外,后端对于是否科班出身看的不重,我自己知道的基本上做后端的出身专业比较多样化:电子类、信息类、自动化类、物理类、化学类等工科专业都见过,侧面证明后端像入门并没有那么难。

    但是,芯片行业整体对学校比较看重,不少人把这个作为潜在衡量标准,后端也不例外,尤其是业内有几所学校影响力很大,其他学校的学生如果本校在行业内没什么知名度,那么必须要技术相对比较扎实才有可能拿到比较好的机会。

    后端技术含量低?

    说这话的一般有两类人:

    一类是没做过后端但是大概知道是做什么甚至懂点术语的;

    另一类是做过后端但项目经历都是小芯片、性能功耗要求不高且非先进工艺的。

    前者自认为懂点术语知道后端的工作内容就什么都敢说,但是要真让他讲点有深度的内容时才能看出,他连后端的门都找不到在哪,因此他们说的话基本上没什么参考价值,而且越是不懂的人说话胆子越大是我们这个世界的普遍现象,如果把他们的话真的当作意见来参考就跟听懂王的建议注射消毒液对抗COVID-19一个下场。

    那么对于做过后端的人来说,他们说后端没有技术含量在一定程度上是真的,因为复杂度不高的芯片真的已经不需要太多人工干预了,尤其是产品线和流程比较成熟,按部就班更新,加上对功耗、性能和面积没有强烈要求的芯片,在后端EDA工具已经相当强大的今天,真的是可以跑几遍就可以开始ECO,几轮下来修到差不多的干净的。

    即使是对与相对复杂的芯片,真正困难的设计也只占整体的一小部分,相当一部分模块可以只需很小的人工干预就能做到最后。

    但是,能做完这些项目就能代表你能独立做出这款芯片的全部后端吗?请问自己以下几个问题:芯片为什么做成现在的面积?如果有一款新的芯片你能合理定义它的面积吗?IO为什么摆成当前的位置?电源网络为什么设计成现在的模样,有什么约束?时序为什么signoff在当前的电压范围?时序signoff的corner是怎么决定的?不同corner的margin为什么加这么多?如果只给我一个综合网表和相应的SDC以及库文件,我能独立建立flow并实现满足所有signoff条件的GDSII吗?如果这些问题你都回答不了,那么你真的只是在跑流程。

    对于后端工程师,如果说你一直在做这样跑流程的工作,那么我劝你多去理解思考一下项目深层次的东西,尝试一下不同的项目类型,在高频、低功耗、复杂时钟、复杂电源和先进工艺方面至少精通一种,熟悉两三种,否则时间长了真的没有太大的竞争力。

    反之,如果你能在上述某一个或者几个领域做到精通,那么你已经有了自己的一定程度的壁垒,比多数人已经强很多了。

    后端未来可替代性高?

    这话说了很多年了,最近因为Jeff Dean的论文又有一帮人跟风再次提起。

    但是,这话虽然不好听,但是有一定的可能性,而且我认为相当一部分后端被更低成本的人力代替的可能性要高于被AI代替的可能性。

    即使在当前阶段,后端工具已经可以快速收敛大部分简单甚至中等难度的模块,根本用不上AI,而随着中国人力成本的提升,将这部分简单工作转移到成本更低的东南亚、印度的趋势是自然而然的选择。

    你可以说三哥十个有八个不靠谱,但是如果我给你成熟的flow和并培训固定形式的操作,配合自动化的数据采集分析脚本,仍然是有可操作性的。以欧美和日本公司之前经历的做法来看,最后留在国内的基本上是产品定义、核心模块的开发实现、flow开发维护、先进技术的探索开发和应用,其他的全都外包。

    中国未来很有可能也走这条路,所以再次强调,后端工程师应该建立自己的护城河,不要随波逐流。

    后端在企业里比前端地位低?

    以我个人的感觉看,确实后端团队整体比前端团队的地位低。

    很多公司的产品后端实现确实不难,因此老板不重视也是没办法的事。而更多的公司即使芯片比较复杂,在某些老板和不少前端心中后端仍然不如前端重要,这里的原因是多方位的,不做分析。

    建议应届生在比较offer的同时多做思考,不要被单纯的title所迷惑,多问问公司做的产品情况,技术含量高低再决定。

    后端天花板低?

    长期看来是的。

    有其他童鞋也说了,大厂中后端做到PMTS级别的可能性很低,前端虽然也很低,但是至少有这种案例。

    但我觉得看这个真的是想多了。芯片行业的人才培养速度相对是比较慢的,互联网中三年leader五年manager的情况基本不存在。

    个人认为一个原因是复杂芯片的设计需要多人分工协作,每人只负责一小部分,要想把每个部分都摸一遍都要好多年才行;

    另一个原因是随着芯片规模增大,项目周期越来越长,一个人得到全方位锻炼来独当一面所需要的时间也就越来越长。

    升到后端团队的manager之一其实对大多数人来说已经是烧高香了,在芯片行业30多岁还没有任何leader或者manager的title的人相当普遍。如果说你真的能力拔群,那么我相信不论前端后端都阻挡不住你的脚步。

    后端薪酬低?

    这个真心没有的事,尤其是最近几年,芯片行业薪酬看涨,年薪百万不容易,但是比大多数行业都强已经是事实了。但是如果只看钱,那还是去IT互联网或者金融吧。

    其实并没有真正完美得工作,也没有完美的行业。整体上后端有其自身特点,也不可避免有它的问题,希望各位在就业择业中多一份思考,少一分盲从。

    ——阎浮提《来源:知乎》

    数字后端设计入门说实话,对于一个行业相关专业的学生或者跳槽者来说,并不是特别难,问题是后端设计涉及到的知识相对来说较多,需要自己慢慢的学习,包括像一些基本的设计概念,时序修复的手段。还包括像脚本的编写,等等很多的工作。找个合适的途径学习,慢慢来。

    数字IC后端学习路线

    关于后端的话题各种网站不能再多了,但作为一个移知网站的资深自学者,希望以我的经历给你们一点启示或者帮助。
    我想大部分学者都是零基础的,所以一些必备的基础知识都没有的话,后面的学习是很困难的。这里既然是自学,那么对入门的书籍或者视频选择应当由浅入深。学完以下基本可以入门了,项目实践可以考虑找个大厂实习,会比自己自学进步更快。

    当然,如果着急入门,或者没有合适的实习机会,推荐移知的课程,有理论有实战还有老师答疑,一步到位!从零基础开始学习,打下扎实的基础,快速获得普通工程师需要2~3年才能得到知识和项目经验。

    有了能力钱慢慢就会来的,工作经验就是钱啊,说了这么多,还是希望你踏踏实实学习,理论基础很重要,有实践加持才能掌握更快!
    好了话不多说,进入干货环节。

    数字后端 — Synopsys流程体系

    第一阶段基础知识
    在这里插入图片描述

    Linux/Unix是除了window操作系统之外非常流行的操作系统。

    在IC设计领域,所有的开发任务几乎都是在Linux操作系统上完成,因此需要掌握Linux操作系统的使用。

    在Linux里面,我们经常用到的文本或者代码编辑器是VIM,VIM有很多命令和使用技巧,通过掌握这些命令和技巧,能够加快我们文本编辑以及代码编辑的效率,因此掌握VIM是IC学习的必备技能。

    第二阶段物理设计
    在这里插入图片描述
    IC Compiler(ICC)是 Synopsys 公司继 Astro 之后推出的另一款为集成电路设计师提供的深亚微米 P&R 工具,支持.18um到28nm工艺节点。

    ICC自推出以来,以其强大的算法引擎、稳定工作环境、简单易用的操作界面,被广泛应用于28nm以上各类芯片的后端设计流程中,是深亚微米芯片后端实现的首选。

    熟练掌握ICC的使用方法成为进行深亚微米工艺后端设计工作的前提。

    在这里插入图片描述

    IC Compiler II (ICC2) 是Synopsys推出的行业领先的布局布线解决方案,旨在支持FinFET 16nm以下先进工艺,消除前沿设计面临的性能、功耗、面积 (PPA) 和上市时间等紧迫压力。

    相比上一代经典布局布线工具ICC,ICC2在工具使用界面、设计流程、算法引擎等方便都做出了重大更新,熟练掌握ICC2的使用方法成为进行先进工艺后端设计工作的前提。

    在这里插入图片描述

    学了后端设计基本知识,还是不知如何做实际后端项目?

    不知如何读设计SPEC,并将其正确转化为后端设计约束?

    如何分析入口网表,SDC等文件的正确性?

    如何采用最佳的实现后端实现方案,达到最佳的PPA?

    所有一切的问题就是缺少实际项目经验!

    课程为项目导向,培养实际项目思维,强化实战,只要实战才能深刻理解后端设计知识。

    第三阶段签核验收(Signoff)

    在这里插入图片描述

    时序是目前高性能物理设计的核心问题,如何分析芯片的时序问题,并快速地实现时序收敛是物理设计师的核心任务。

    PrimeTime 静态时序分析工具是在时序、信号完整性等方面最受业内工程师信赖的金牌签核 (signoff) 工具。

    掌握静态时序分析模型的基础知识,熟练使用PrimeTime工具分析、解决芯片时序问题,是当今芯片设计工程师的必备技能。

    经过本次课程的学习,可以使没有时序经验的工程师直接上手进行芯片时序分析,对有一定经验的工程师也能带来一些有益的启发。

    在这里插入图片描述

    芯片特征尺寸缩小和频率升高,功耗逐渐成为芯片设计过程中的重要参数,并直接影响芯片电源网络设计。

    芯片电源网络的信号完整性又会对芯片性能和可靠性产生影响。如何精确分析芯片功耗和电源完整性,是尽早发现芯片设计问题降低芯片功耗的必要条件。

    PTPX 和 Redhawk 是在功耗、电源完整性等方面最受业内工程师信赖的金牌签核 (signoff) 工具。

    掌握功耗和电源分析的基础知识,熟练使用PTPX和redhawk工具分析,解决芯片设计问题是当今芯片设计工程师的必备技能。

    在这里插入图片描述

    时序/功耗分析全流程覆盖

    基础知识和进阶内容详细讲解

    详细的工具使用介绍,多年debug经验分享

    真实项目联系,理论实践结合

    大量视频、图片展示,关键步骤细微观察

    资深讲师讲解

    如果你想要快速入门,做数字IC后端,不妨不妨到移知官网学习这些课程,给自己一次蜕变的可能。后端东西还是挺多的,要讲的也太多。慢慢学吧。祝好运!

    展开全文
  • 后端服务器

    2021-08-06 01:31:33
    后端服务器概述在使用负载均衡服务前,你需要添加 QVM 实例作为负载均衡实例的后端服务器,用来接收负载均衡监听转发的请求。你可以在任意时刻增加或减少负载均衡实例的后端服务器数量,还可以在不同 QVM 实例之间...
  • 前端 vs 后端:哪一个适合你? 经常会有初学者来问我刚开始学习编程的时候应该学些什么?问这个问题就跟一个医学生询问应该专注研究哪个领域一样。根本没有一个标准答案。但我还是想给你提供一些指导,并就这个问题...
  • Vue - 与后端交互

    千次阅读 2021-02-05 00:18:43
    零:与后端交互 - ajax版本1 - 出现了跨域问题前端:index.htmlhtml>Vue与后端交互-出现了跨域问题加载数据letvm=newVue({el:'#box',data:{},methods:{handleClick(){$.ajax({url:'http://127.0.0.1:5000/',//...
  • 本文主要梳理到公司参与团队后端开发2年多来,总结开发过的项目中遇到的各种安全问题及应对方案。 目前我们后端团队使用的技术主要还是SpringBoot + Mysql + Redis这一套 ,暂未涉及到SpringBoot Cloud,后面内容...
  • HelloGitHub-嘉文这里是 HelloGitHub 推出的《讲解开源项目》系列,今天给大家带来一款开源免费的模拟后端 API 的工具:moco没学过后端开发的也能快速上手这个开源...
  • 前端和后端哪个工资更高呢? 一个大二学生问的,说老师在讲课时说,从工资来看,后端开发要比前端高,并且说,掌握公司核心技术的都是后端。他问事实是否真的如此,然后问该如何规划自己的工作。 这里整理的是个...
  • 点击上方“3D视觉工坊”,选择“星标”干货第一时间送达上篇文章讲解了Karto的前端是如何工作的.这篇文章将slam_karto中的后端优化部分的代码添加进lesson6中,我将带着大家一...
  • Python后端学习路线

    千次阅读 2021-12-08 10:42:08
    六、后端框架 七、安全技术 八、版本控制工具 九、Linux基础 十、测试技术 十一、分布式设计 十二、高并发 十三、高可用 十四、高性能 十五、工具使用 十六、监控与统计 十七、设计模式 十八、数据库 ...
  • Ruby on Rails还是Python或PHP?我们不会在本文中将这些语言相互比较,而是尝试回答这个问题:何时以及为何选择Java进行后端开发。 实际上,有几个参数取决于语言选择,例如语言能力、编码的难易程度、对不同操作...
  • 文章目录前端路由后端路由二者比较前端路由的两种模式hashhistroy二者比较 路由:路由是根据不同的 url 地址展示不同的内容或页面,是指分组从源到目的地时,决定端到端路径的网络范围的进程。 路由工作包含两个...
  • 网关没有可连接的后端服务器 内容精选换一换负载均衡器会定期向后端服务器发送请求以测试其运行状态,这些测试称为健康检查。通过健康检查来判断后端服务器是否可用。负载均衡器如果判断后端服务器健康检查异常,就...
  • SLAM后端概述

    2021-01-19 15:25:50
    后端概述状态估计的概率解释后端的概念后端要解决的问题问题的简单描述问题的数学表述 状态估计的概率解释 后端的概念   前端视觉里程计只能通过邻近的几张图片估计短暂时间内的运动轨迹和路标,这种方式为“渐进...
  • 后续每次请求都会将此token放在请求头中传递到后端服务,后端服务会有一个过滤器对token进行拦截校验,校验token的合法性以及token是否过期,如果token过期则会让前端跳转到登录页面重新登录。 因为token中一般会...
  • 常见的后端框架

    千次阅读 2020-12-19 04:25:17
    后端vs前端如果您是Web开发世界的新手,后端和前端开发之间的区别可能不那么明显,但是,了解两者之间的区别很重要。以下是前端开发人员与后端开发人员的一些区别。前端开发:前端开发人员在很大程度上负责用户所...
  • 后端埋点

    千次阅读 2021-01-22 17:00:38
    后端埋点 背景:客户端埋点 or 服务端埋点 埋点方式选择 埋点方式分为客户端埋点和服务端埋点两种。如何选取最合适的埋点方式,确保数据完整、准确、高效上报? 一、客户端埋点与服务端埋点结合 发挥各自优势 客户端...
  • 后端系统,后端系统是什么意思“后端系统”从宽泛的角度上讲是指向用户提供数据的服务器、超级服务器、群集系统、中程系统以及大型机。这些服务所在的位置通常称为服务器场或数据中心。客户机/服务器中的服务器称为...
  • 概述 后端是指用户看不见的东西,通常是与前端工程师进行数据交互及网站数据的保存和读取,相对来说后端涉及到的逻辑代码比前端要多的多,后端考虑的是底层业务逻辑的实现,平台的稳定性与性能等。后端开发(以java...
  • Java项目——后端笔记

    千次阅读 2021-03-09 22:38:40
    接手第一个后端任务就是写一个接口(根据课程id和学年学期查询教师信息),以供别的界面使用,对于代码还不熟,也有些看不懂的我来说让我改这个接口其实还是有难度的!刚开始有些不知所措,后来女神告诉我“就看一条线...
  • 后端图像上传

    2021-11-13 11:42:13
    VUE+Spring实现头像上传说明新的改变功能快捷键合理的创建标题,有...主体思路,前端写一个input标签,将input标签隐藏,将上传的图片传给后端后端接收后,保存在本地,将文件的地址存储到数据库。 新的改变 我们对Ma
  • 后端网关服务器隐藏ip 内容精选换一换确认虚拟机主网卡已经正确分配到IP地址。登录虚拟机内部。执行ifconfig命令或ip address查看网卡的IP信息。Windows虚拟机可以在命令行中执行ipconfig查看。登录虚拟机内部。执行...
  • # 后端实习日志

    2021-07-22 00:19:33
    后端实习日志 2021-07-21 报错: spring-boot-maven-plugin:2.5.2:repackage failed: Unable to find main class 改正: 有俩个main启动,删掉一个。 昨天只是完成了增删改查 师傅要求删的时候postman上...
  • 然而,在网络建设时,网络建设开发师常常为了选择哪一种网络开发语言而发愁,下面小编给您收集整理了一篇《后端开发语言哪一种比较好?》,对目前常见的后端语言进行简单的介绍,希望您能从中选择出一款您满意的后端...
  • 2021 Java后端学习路线

    万次阅读 多人点赞 2021-02-18 15:16:10
    2021 Java后端学习路线前言路线图详解计算机基础知识Java基础知识前端常用工具基本框架数据库缓存中间件网关消息队列搜索引擎容器RPC大数据 前言 最近在某乎收到很多提问,其中最多的是 自学java应该怎么学、java...
  • 想要快速地开发一个小程序,很多环节都需要注意,微信小程序php后端的开发估计很多人都还很陌生,但是这也是至关重要的,一起来看看开发实例详解吧。1.小程序相对于之前的WEB+PHP建站来说,个人理解为只是将web放到...
  • 以下错误不熟悉所导致... //后端 public async Task<IActionResult>User(user filter) { } 一:前端访问后端代码没反应 1.检查前端名字与后端是否相同 2.查看请求类型:前后 是否是一样的 Get或者Post请求

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,057,693
精华内容 423,077
关键字:

后端