基于grpc从零开始搭建一个准生产分布式应用(3) - GRPC实现

本章开始会进入GRPC子专题,先实现前面章节中提到的例子。然后就使用的知识点展开全面的描述。本章代码任务:1、实现一个简单的GRPC服务;2、实现GRPC拦截器。

本章的代码承接上一章的代码进行迭代。因模块间存在相互依赖关系,读者一定先按笔者讲述的顺序操作,否则最后程序可能由于依赖问题导致不能运行;

一、准备工作

因为本专题定位于准生产环境,所以我们在代码上也会按规范严格要求一下,本章涉及的准备工作就是在common模块优先定义一些辅助工具类。因代码比较多,详细可查看:

  • ​​基于grpc从零开始搭建一个准生产分布式应用(8) - 01 - 附:GRPC公共库源码​​
  • ​​基于grpc从零开始搭建一个准生产分布式应用(7) - 01 - 附:GRPC拦截器源码​​

二、API接口定义

修改【base-grpc-framework-api】模块,我们用proto方式来实现,首先要修改pom.xml文件引入必要的依赖,再定义一个.proto文件。proto的使用在下一章节会详细介绍,本章了解下必要的知识点即可。

2.1、pom.xml文件修改

这块没有需要注意的地方,用下列内容覆盖上一章中的源码即可。源码中的dependency全部用provided来修饰的原因是,在系统运行时会用到编译后的源码,这里的依赖只是用于辅助编译用。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>base-grpc-framework-parent</artifactId>
        <groupId>com.zd</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>base-grpc-framework-api</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
            <scope>provided</scope>
        </dependency>

    </dependencies>
    <build>
        <!--取得本地系统信息的插件-->
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>${maven.kr.motd}</version>
            </extension>
        </extensions>

        <plugins>
            <!--grpc插件,用于IDEA集成使用-->
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>${maven.org.xolstice}</version>
                <configuration>
                    <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.java.version}:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal><!--对应插件的complie命令-->
                            <goal>compile-custom</goal><!--对应插件的complie-custom命令,需要本地配置插件-->
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2.2、systemlog.proto文件定义

文件路径:src/main/proto/sysrecord.proto,一个经验就是,定义入参时要用包装类型方便参数验证,如下面的的CreateSysRecordRequest定义。返回结果用简单类型防止空指针异常,如下面的ListSysRecordResponse定义。

syntax = "proto3";

package com.zd.baseframework.core.api.sysrecord;

import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";

option java_package = "com.zd.baseframework.core.api.systemlog";
option java_outer_classname = "SystemLogProto";
option java_multiple_files = true;

service ISystemLogService{

  //创建
  rpc CreateSystemLog (CreateSystemLogRequest) returns (SystemLogOperatorResponse);

  //查询
  rpc ListSystemLogByCondition (ListSystemLogRequest) returns (ListSystemLogResponse);

}
//请求参数
message CreateSystemLogRequest{
  google.protobuf.StringValue biz_id = 1;
  google.protobuf.Int64Value user_id = 2;
  google.protobuf.StringValue code = 4;
  google.protobuf.StringValue custom_code = 5;
}

message ListSystemLogRequest{
  google.protobuf.StringValue biz_id = 1;
  google.protobuf.Int64Value user_id = 2;
  google.protobuf.Int32Value code = 3;
}

//返回结果
message  SystemLogOperatorResponse{
  optional int32 status = 1;
  optional string message = 2;
}

message  ListSystemLogResponse{
  repeated SystemLogDto data = 1;
}

message SystemLogDto {
  int64 id = 1;
  string biz_id = 2;
  int64 user_id = 3;
  string track_uid = 4;
  string code = 5;
  string custom_code = 6;
  int32 state = 7;
  google.protobuf.Timestamp code_name = 8;
  google.protobuf.Timestamp utime = 9;
}

2.3、proto文件编译

把.proto文件编译成java代码,在idea中的操作过程共分为三步:

  1. 在pom.xml中配置proto插件;
  2. 执行protobuf:complie生成接口;
  3. 执行protobuf:complile-custom生成实体类;

先确定已经按2.2节内容正确配置了protobuf-maven-plugin插件后,然后先后双击下图中红框的内容:

最后打开源码的target文件夹,会看到生成的Java源码文件,如下图所示,grpc-java文件夹中存储的是接口定义即proto文件中service标签中定义的内容,java文件中存储的是message标签定义的内容,至此api模块的所有修改就完成了:

三、GPRC实现-服务端

本章我们会传承上一章节的代码,需改动下图中标红的模块。服务端的实现稍复杂一些,同样也要改一些pom配置,本次我们会实现两个接口,同学们可以了解下grpc本地调用的实现方法,同样grpc详细的内容也会在后续章节详细展开。

  • application模块:配置配置参数;
  • core模块:实现一个grpc和一个controler服务,同时暴露两种协议的接口,熟悉grpc的本地调用;

3.1、pom.xml文件修改

修改【base-grpc-framework-core】模块,这里没有太多需要注意的内容,复制即可;下面源码中多了lombok和mapstruft的内容,暂时保留吧,后面也会用到了。

<?xml versinotallow="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>base-grpc-framework-parent</artifactId>
        <groupId>com.zd</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>base-grpc-framework-core</artifactId>

    <properties>
        <lombok.mapstruct.binding.version>0.1.0</lombok.mapstruct.binding.version>
    </properties>

    <dependencies>
        <!--grpc依赖-->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
        </dependency>

        <!--gprc依赖实现本地grpc调用-->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
        </dependency>

        <!--项目依赖-->
        <dependency>
            <groupId>com.zd</groupId>
            <artifactId>base-grpc-framework-common</artifactId>
            <version>${project.parent.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.zd</groupId>
            <artifactId>base-grpc-framework-api</artifactId>
            <version>${project.parent.version}</version>
        </dependency>

        <!--nacos相关-->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-config-spring-boot-starter</artifactId>
        </dependency>


        <!--===========以下全是工具类和工具框架=========================-->

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>compile</scope>
        </dependency>

        <!--hutool工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <!-- 使用log4j2,性能更高 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
    </dependencies>

    <build>

        <plugins>
            <!--处理lombok和mapstruft 加载顺序的问题 防止在生成的实体没有属性-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>

                        <!-- additional annotation processor required as of Lombok 1.18.16 -->
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>${lombok.mapstruct.binding.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.2、Gprc服务实现

修改【base-grpc-framework-core】模块,类路径:com.zd.baseframework.core.core.sysrecord.api.impl.ISysRecordServiceApiImpl ,代码实现如下,这里我们只实现一个创建接口,本类创建后还需要在application.yml中进行一些配置,后面一起来讲;

下面源码唯一注意的是一定要标上@GrpcService注解,对于源码中没有找到的类全在common包中,可先按附录方式创建好必要的辅助类。

如果发现找不到API中的类,可点击右侧MAVEN窗口刷新工程。

@GrpcService
@Slf4j
public class SystemLogServiceApiImpl extends ISystemLogServiceGrpc.ISystemLogServiceImplBase {

    @Override
    public void createSystemLog(CreateSystemLogRequest request, StreamObserver<SystemLogOperatorResponse> responseObserver)  {
        String bizId = request.hasBizId()?request.getBizId().getValue():null;
        Long userId  = request.hasUserId()?request.getUserId().getValue():null;
        String code = request.hasCode()?request.getCode().getValue():null;
        String customCode = request.hasCustomCode()?request.getCustomCode().getValue():null;

        String trackLog = LogGenerator.trackLog();

        try{
            //打印原始入参
            log.info(trackLog
                    + " grpcReqParam=" + JsonFormat.printer().omittingInsignificantWhitespace().print(request)
            );

            //入参验证
            if(StrUtil.isEmpty(bizId)
                    || null==userId
                    || StrUtil.isEmpty(code) ){
                throw new AppException("非法参数");
            }

            SysRecordOperatorResponse response  = SysRecordOperatorResponse.newBuilder()
                    .setStatus(ResponseConst.SUCCESS)
                    .setMessage(ResponseConst.Msg.SUCCESS)
                    .build();
            responseObserver.onNext(response);
            responseObserver.onCompleted();
            responseObserver.onCompleted();

        }catch (AppException e) {
            log.error(e.getMessage());
            responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException());
        }
        catch(Exception e){
            log.error(e.getMessage());
            responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asException());
        }
    }

    @Override
    public void listSystemLogByCondition(ListSystemLogRequest request, StreamObserver<ListSystemLogResponse> responseObserver) {

        String trackLog = LogGenerator.trackLog();

        try{
            //打印原始入参
            log.info(trackLog
                    + " grpcReqParam=" + JsonFormat.printer().omittingInsignificantWhitespace().print(request)
            );


            ListSystemLogResponse response = ListSystemLogResponse.newBuilder().build();
            responseObserver.onNext(response);
            responseObserver.onCompleted();

        }catch (AppException e) {
            log.error(e.getMessage());
            responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException());
        }
        catch(Exception e){
            log.error(e.getMessage());
            responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asException());
        }
    }
}

3.3、Controller接口实现

修改【base-grpc-framework-core】模块,实现本地grpc调用,本类创建后还需要在application.yml中进行一些配置,后面一起来讲;类路径:com.zd.baseframework.core.controller.core.SystemLogController.java。代码如下:

注意@GrpcClient("inProcess")和@RestController注解不要忘记。

@Slf4j
@RestController
@RequestMapping("/systemlog")
public class SystemLogController {

    @GrpcClient("inProcess")
    private ISystemLogServiceGrpc.ISystemLogServiceBlockingStub iSysRecordServiceBlockingStub;

    @GetMapping("/v1/create_systemlog")
    public BaseResponse createSystemLog(
            @RequestParam(value="biz_id") String bizId,
            @RequestParam(value="user_id") Long userId,
            @RequestParam(value="code") String code,
            @RequestParam(value="custom_code", required=false) String customCode){
        try{
            //构建请求参数
            CreateSystemLogRequest createSysRecordRequest  = CreateSystemLogRequest.newBuilder()
                    .setBizId(StringValue.of(bizId))
                    .setUserId(Int64Value.of(userId))
                    .setCode(StringValue.of(code))
                    .setCustomCode(StringValue.of(customCode))
                    .build();

            //正常此处需要判断response返回的state值
            SystemLogOperatorResponse response = iSysRecordServiceBlockingStub.createSystemLog(createSysRecordRequest);

            return BaseResponse.success(null);
        }catch(StatusRuntimeException e){
            log.error("ErrorCode : " + e.getStatus().getCode());
            return BaseResponse.error(e.getMessage());
        }catch(Exception e){
            e.printStackTrace();
            return BaseResponse.error(e.getMessage());
        }
    }
    
    @PostMapping("/v1/list_systemlog")
    public BaseResponse listSystemLog(@RequestBody SystemLogQueryRequest systemLogRequest){
        try{
            //构建查询参数
            ListSystemLogResponse listSystemLogResponse = iSysRecordServiceBlockingStub.listSystemLogByCondition(null);
            return BaseResponse.success(vos);

        }catch(StatusRuntimeException e){
            log.error("ErrorCode : " + e.getStatus().getCode());
            return BaseResponse.error(e.getMessage());
        }catch(Exception e){
            e.printStackTrace();
            return BaseResponse.error(e.getMessage());
        }
    }

}

3.4、服务端配置

修改【base-grpc-framework-application】模块,在这个专题中,与应用相关的配置全在base-grpc-framework-application模块的application.yml中配置,因为是本地运行,所以这里我们只修改application-dev.yml。修改后的源码如下:

# http配置
server:
  compression:
    enabled: true
    mime-types: application/json,application/octet-stream

# spring配置
spring:
  application:
    name: GrpcFramework-Server-APP

# grpc Server配置
grpc:
  server:
    port: 9898   #发布远程访问地址
    in-process-name: native  #发布本地访问地址
  client:
    inProcess:
      address: in-process:native #配置内部访问服务名称

上面配置注意grpc.server.in-process-name和grpc.client.inprocess.adderss一定要一样,而且要与controller中的@GrpcClient("inProcess")配置一样,其中grpc.client.inprocess.adderss后面内容in-process:native中的in-process:是固定的写法,不能更改。

3.5、GRPC服务端测试

启动BaseFrameworkApplication.java,启动时不要忘记配置环境变量为dev,配置方式可参考笔者的上一章文章​​https://blog.51cto.com/arch/5378945​​。

3.5.1、grpc测试

可先安装grpcurl工具,笔者使用的是mac系统,安装方式如下,其它系统可网上查询:

brew install grpcui  #安装
grpcui -plaintext 127.0.0.1:9898  #运行

执行上面运行命令后,会自动打开浏览器,显示如下图所示,测试方式可自行操作下:

3.5.2、controller测试

在浏览器中输入 ​​http://localhost:8080/swagger-ui.html​​  ,下图中红框外的两个controller是笔者测试用的,可忽略。红框内的内容就是上面小节写的controller。展开后就可进行测试了:

四、GRPC服务端拦截器实现

这个拦截器主要是实现了日志打印功能,通用track的方式来跟踪每一次访问,详细设计思路可参考笔者写的另一篇文章的描述 ​​https://blog.51cto.com/arch/5295170​​。

完整的类源码如下所下图所示,源码可以查看:​​基于grpc从零开始搭建一个准生产分布式应用(0) - 07 - 附:GRPC拦截器源码​​。

五、GRPC实现-客户端

修改【base-grpc-framework-client】模块。client端不是太重点,这里只是为了说明一下调用和配置方式,笔者的代码没有配置启动类,感兴趣的同学可按第3章的内容自行配置springboot来启动。也可跳过此章,了解下内容即可。完整代码如下图所示:

5.1、修改pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>base-grpc-framework-parent</artifactId>
        <groupId>com.zd</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>base-grpc-framework-client</artifactId>

    <properties>
        <lombok.mapstruct.binding.version>0.1.0</lombok.mapstruct.binding.version>
    </properties>

    <dependencies>
        <!--grpc依赖-->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
        </dependency>

        <!--项目依赖-->
        <dependency>
            <groupId>com.zd</groupId>
            <artifactId>base-grpc-framework-common</artifactId>
            <version>${project.parent.version}</version>
        </dependency>
        <dependency>
            <groupId>com.zd</groupId>
            <artifactId>base-grpc-framework-api</artifactId>
            <version>${project.parent.version}</version>
        </dependency>

        <!--===========以下全是工具类和工具框架=========================-->
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>compile</scope>
        </dependency>

        <!--hutool工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <!-- 使用log4j2,性能更高 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
    </dependencies>

</project>

5.2、Controller实现

同3.3节的实现一样,只是@GrpcClient("local-grpc-server")中的内容不一样,可参考5.3节的配置。

@Slf4j
@RestController
@RequestMapping("/systemlog")
public class SystemLogController {

    @GrpcClient("local-grpc-server")
    private ISystemLogServiceGrpc.ISystemLogServiceBlockingStub iSysRecordServiceBlockingStub;

    @GetMapping("/v1/create_systemlog")
    public BaseResponse listWorkFlowByStatus(
            @RequestParam(value="biz_id") String bizId,
            @RequestParam(value="user_id") Long userId,
            @RequestParam(value="code") String code,
            @RequestParam(value="custom_code", required=false) String customCode){
        try{
            //构建查询参数
            CreateSystemLogRequest createSysRecordRequest  = CreateSystemLogRequest.newBuilder()
                    .setBizId(StringValue.of(bizId))
                    .setUserId(Int64Value.of(userId))
                    .setCode(StringValue.of(code))
                    .setCustomCode(StringValue.of(customCode))
                    .build();

            SystemLogOperatorResponse response = iSysRecordServiceBlockingStub.createSystemLog(createSysRecordRequest);

            return BaseResponse.success(null);
        }catch(StatusRuntimeException e){
            log.error("ErrorCode : " + e.getStatus().getCode());
            return BaseResponse.error(e.getMessage());
        }catch(Exception e){
            e.printStackTrace();
            return BaseResponse.error(e.getMessage());
        }
    }
}

5.3、客户端配置

在base-grpc-framework-client】新建一个yml文件。

server:
  port: 8089
spring:
  application:
    name: GrpcFramework-Client-APP
grpc:
  client:
    local-grpc-server:
      address: 'static://127.0.0.1:9898'
      enableKeepAlive: true
      keepAliveWithoutCalls: true

下一章节会先讲述proto的内容。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】二叉树篇| 纲领思路02+刷题

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; 是瑶瑶子啦每日一言&#x1f33c;: 所谓自由&#xff0c;不是随心所欲&#xff0c;而是自我主宰。——康德 目录 一、前言二、刷题1、翻转二叉树 2、二叉树的层序遍历✨3、 二…

2023国赛数学建模D题思路分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 全国大学生数学建模…

【Java】一只小菜坤的编程题之旅【3】

文章目录 1丶判定是否互为字符重排2、杨辉三角3丶某公司的1个面试题&#xff08;字符串包含问题&#xff09; 1丶判定是否互为字符重排 这个题我们用一个非常简单的思想就能实现&#xff0c;我们先将字符串转换为字符数组&#xff0c;然后对字符数组进行排序&#xff0c;然后再…

安卓如何卸载应用

卸载系统应用 首先需要打开手机的开发者选项&#xff0c;启动usb调试。 第二步需要在电脑上安装adb命令&#xff0c;喜欢的话还可以将它加入系统path。如果不知道怎么安装&#xff0c;可以从这里下载免安装版本。 第三步将手机与电脑用数据线连接&#xff0c;注意是数据线&a…

阿里云服务器部署Drupal网站教程基于CentOS系统

阿里云百科分享如何在CentOS 7操作系统的ECS实例上搭建Drupal电子商务网站。Drupal是使用PHP语言编写的开源内容管理框架&#xff08;CMF&#xff09;&#xff0c;它由内容管理系统&#xff08;CMS&#xff09;和PHP开发框架&#xff08;Framework&#xff09;共同构成。它用于…

安装Jenkins

一、什么是Jenkins Jenkins是一个开源软件项目&#xff0c;是基于Java开发的。我们可以利用Jenkins来实现持续集成的功能。 因为Jenkins是基于Java开发的&#xff0c;所以在安装Jenkins之前首先需要安装Java的JDK。 二、安装Jenkins 在Windows平台上面安装Jenkins共有两种方式…

gSpan算法执行步骤详解示例

目录 1. 问题描述2. gSpan算法步骤2.1 数据预处理2.2 深度递归挖掘频繁子图2.2.1 获取所有的频繁边2.2.2 深度递归挖掘频繁子图 参考文献 1. 问题描述 gSpan 是一款图规则挖掘算法&#xff0c;目标是从现有的图集中挖掘频繁子图。如下图中包含三个图&#xff1a; 其中圆圈为顶…

13-把矩阵看作是对系统的描述

探索矩阵乘法&#xff1a;更深刻的理解与应用视角 &#x1f9e9;&#x1f50d; 引言 &#x1f4d6; 在我们进一步探讨矩阵乘法之前&#xff0c;让我们从不同的角度来理解什么是矩阵&#xff0c;以及如何将矩阵视为一个系统。我们之前已经介绍了矩阵的基本概念和运算&#xff…

SpringBoot案例-部门管理-新增

根据页面原型&#xff0c;明确需求 页面原型 需求 阅读接口文档 接口文档链接如下&#xff1a; 【腾讯文档】SpringBoot案例所需文档 https://docs.qq.com/doc/DUkRiTWVaUmFVck9N 思路分析 前端在输入要新增的部门名称后&#xff0c;会以JSON格式将数据传入至后端&#xf…

idea如何开启远程调试

一&#xff1a;打包需要部署的jar包上传到服务器 二&#xff1a;服务器&#xff08;开启远程调试接口&#xff09; nohup java -jar -Xdebug -Xrunjdwp:transportdt_socket,servery,suspendn,address8453 xxx.jar > xxx.log 2>&1 & 三&#xff1a; idea配置rem…

电脑开机出现Boot Device怎么办?

开机出现Boot Device这个问题很常见&#xff0c;有时还会出现No Boot Device的问题&#xff0c;虽然多了一个单词&#xff0c;但意思是相同的&#xff0c;这些问题说明你的系统盘出现了问题&#xff0c;或者是引导出现了问题。这该如何解决呢&#xff1f; 方法1. 检查主板或硬盘…

如何定位线上CPU飙高的问题

1.问题情景 我们的接口卡死&#xff0c;CPU飙高到打不开的网页 2.问题定位 2.1 top指令 通过top命令找到CPU耗用最厉害的那个进程的PID 直接输入top Linux下的100%代表一个核心&#xff0c;如果是八核&#xff0c;最高可以到800%&#xff0c;这样才算满 然后通过PID找到CP…

Redis原理简述

Redis原理简述 Redis 有哪些特性 1. 特性 key-value 型内存数据库单线程——原子化操作支持lua脚本发布与订阅可持久化逐出与过期……2. 持久化 RDB:经过压缩的二进制文件;fork子进程进行操作AOF:保存所有写命令;先写缓存再同步至AOF文件;文件过大时会触发AOF重写3. 过期…

Smart HTML Elements 16.1 Crack

Smart HTML Elements 是一个现代 Vanilla JS 和 ES6 库以及下一代前端框架。企业级 Web 组件包括辅助功能&#xff08;WAI-ARIA、第 508 节/WCAG 合规性&#xff09;、本地化、从右到左键盘导航和主题。与 Angular、ReactJS、Vue.js、Bootstrap、Meteor 和任何其他框架集成。 智…

深入了解 Vue 3 组件间通信机制

什么是组件&#xff1f; 在 Vue3 中&#xff0c;组件是构建应用界面的核心概念之一。组件可以看作是可复用、自包含和可组合的代码块&#xff0c;用于封装 UI 元素和相应的行为逻辑。 通俗来说就是&#xff0c;组件&#xff08;Component&#xff09;是一种对数据和方法的简单…

c语言经典例题讲解(输出菱形,喝汽水问题)

目录 一、输出菱形 二、喝汽水问题 方法1&#xff1a;一步一步来 方法二&#xff1a;直接套公式 一、输出菱形 输出类似于下图的菱形&#xff1a; 通过分析&#xff1a;1、先分为上下两部分输出 2.在输出前先输出空格 3.找规律进行输出 可知&#xff0c;可令上半部分lin…

Vue3 + Ts + Vite 封装一套企业级axiso全流程

前期回顾 从零搭建 Vue3 VIte Ts 项目 —— 并集成eslint 、prettier、stylelint、husky、lint-staged、pinia、axios、loding、动态路由…_彩色之外的博客-CSDN博客 实现功能&#xff1a; 取消重复请求&#xff1a;完全相同的接口在上一个pending状态时&#xff0c;自动取…

C语言库函数之 qsort 讲解、使用及模拟实现

引入 我们在学习排序的时候&#xff0c;第一个接触到的应该都是冒泡排序&#xff0c;我们先来复习一下冒泡排序的代码&#xff0c;来作为一个铺垫和引入。 代码如下&#xff1a; #include<stdio.h>void bubble_sort(int *arr, int sz) {int i 0;for (i 0; i < sz…

python爬虫实战(2)--爬取某博热搜数据

1. 准备工作 使用python语言可以快速实现&#xff0c;调用BeautifulSoup包里面的方法 安装BeautifulSoup pip install BeautifulSoup完成以后引入项目 2. 开发 定义url url https://s.微博.com/top/summary?caterealtimehot定义请求头&#xff0c;微博请求数据需要cookie…

推荐 4 个 yyds 的 GitHub 项目

本期推荐开源项目目录&#xff1a; 1. 开源的 Markdown 编辑器 2. MetaGPT 3. SuperAGI 4. 一个舒适的笔记平台 01 开源的 Markdown 编辑器 Cherry 是腾讯开源的 Markdown 编辑器&#xff0c;基于 Javascript具有轻量简洁、易于扩展等特点&#xff0c; 它可以运行在浏览器或服…