SpringBoot: Eureka入门

news2024/12/23 3:27:33

1. IP列表

公司发展到一定的规模之后,应用拆分是无可避免的。假设我们有2个服务(服务A、服务B),如果服务A要调用服务B,我们能怎么做呢?最简单的方法是让服务A配置服务B的所有节点的IP,在服务A内部做负载均衡调用服务B的不同节点。

这种方式有3个明显的问题

  1. 服务B的节点变更,需要将服务B的IP列表更新进每个服务A,可以是通过配置文件、配置中心,甚至是数据库
  2. 服务A需要对服务B的节点做健康检测,避免将调用无效的节点
  3. 服务A要实现服务B调用的负载均衡策略

2. 反向代理

熟悉反向代理的人发现,反向代理不正是解决这个问题的办法吗?如果在服务A和服务B之间添加一个nginx,网络拓扑看起来就是这样的

我们需要将服务B的节点配置为upstream,定义nginx的server,服务A通过nginx调用服务B,我们看看下面的核心配置

  1. weight指定每个服务器的权重,值越大调用的次数越多
  2. max_fails指定失败多少次后标记为不可用,fail_timeout指过多长时间后将失败节点再次加入到服务列表中
  3. backup作为备用节点,其他节点不可用的时候,back节点会接受负载均衡
  4. down标记节点下线
  5. ip_hash指定负载均衡策略,通一个IP调度到同一个IP,默认是round-robin
  6. proxy_next_stream指定哪些条件认为是请求失败的(max_fails统计的条件)
upstream service_b {  
    # 默认轮询策略round-robin,默认权重1
    server 192.168.100.11 weight=1;  
    server 192.168.100.12 weight=2 max_fails=10 fail_timeout=30s ;
    server 192.168.100.13 backup;                                   # 备用服务器,当其他服务器都不可用时才使用  
    server 192.168.100.14 down;                                     # 标记为不可用,不参与负载均衡  

    ip_hash;                                                       # 负载均衡策略,基于客户端IP选择,默认round-robin
}

server {  
    listen 80;  

    location / {
        proxy_pass http://service_b;  
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; 
    }  
}

通过这种方式基本解决1.1里提到的3个问题了,不过upstream的修改仍然是手动的,而且需要重启nginx。 nginx提供了一个模块ngx_http_dyups_module让我们可以通过HTTP调用动态的修改upstream,如果我们想要将service_b的节点改成下面2个节点,我们可以这么做:

curl 127.0.0.1:8000/upstream/service_b  -d 
"server 192.168.100.11:8080 max_fails=3 fail_timeout=5s weight=10;
 server 192.168.100.12:8080 max_fails=3 fail_timeout=5s weight=10;"

不过这个方案没有开始流行就已经没落了,最明显的问题是所有对服务B的调用都要经过中心节点(nginx),而且经过了一次转发,影响了调用性能。

3. 注册中心

大概在2010年开始国内的大中厂都开始走向服务化,但并没有一个成熟的中间件,dubbo、motan、hedwig都是这个时代产物。服务提供者在启动的时候会将自己注册到服务注册中心(zookeeper、consul等实现),服务消费者从注册中心拿到服务提供者的IP,在客户端做负载均衡,直接连接服务提供者的IP,相较于反向代理的方案好处是服务A和服务B是直接调用,避免了一次中间转发。

现在主流的注册中心实现有很多,这里我们选几个常见的对比一下

名称

CAP

语言

算法

数据结构

场景

存储

Zookeeper

CP

Java

Zab协议

树ZNode

服务发现、锁、选主、配置

文件

Eureka

AP

Java

Gossip

key-value

服务发现

内存

Nacos

CP + AP

Java

Raft

key-value

服务发现、锁、选主、配置

配置推送、流量管理(灰度发布)

MySQL

Consul

AP

Go

Raft

key-value

类似于Nacos

文件

  1. Zookeeper最早出现,常用于大数据系统的协调,比如Hadoop/Kafka等,生态成熟,但特性就比较老,出现在云原生之前,没有考虑云原生/微服务的支持
  2. Eureka专门为微服务设计,不支持一致性协议,适用gossip同步数据,选择AP,一致性较弱,功能单一,仅用于服务发现
  3. Nacos基于Raft算法,支持云原生,比如流量管理、灰度发布等功能

一般Java语言开发的新系统的注册中心是在Eureka和Nacos之间选择,Eureka天然和Spring Cloud集成,适用简单,当然功能也相当较弱。我们先来看看Eureka的使用。

4. Eureka入门

1. 创建应用

先创建Spring Boot应用,参见1. 手动创建应用,引入Spring Cloud的依赖管理

<project>
    ...
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>   <!-- 创建为SpringBoot应用 -->
        <version>3.2.7</version>
    </parent>
    ...
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-parent</artifactId>  <!-- 使用Spring Cloud依赖 -->
                <version>2023.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> <!-- 添加EurekaServer依赖 -->
        </dependency>

    </dependencies>
</project>
2. 启动类

添加启动类,除了正常的Spring Boot应用的注解,额外增加了@EnableEurekaServer注解

package org.keyniu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class StartEurekaServer {
    public static void main(String[] args) {
        SpringApplication.run(StartEurekaServer.class, args);
    }
}
3. 核心配置

创建配置文件 application.yml,包含的内容如下,我们我看看每个配置字段的含义

配置

含义

Eureka字段

spring.application.name

应用名,注册Eureka是的应用名

application.instance.app

eureka.instance.hostname

运行实例的主机名或IP,默认取当前机器的主机名 ;

为了方便识别一般会在/etc/hosts绑定IP和主机名,设置对应节点机器名 ;

Docker环境会选择prefer-ip-address=true,直接采用IP地址

application.instance.hostName

eureka.instance.lease-renewal-interval-in-seconds

客户端向EurekaServer续租的心跳,默认30s

application.instance.leaseInfo.renewalIntervalInSecs

eureka.instance.lease-expiration-duration-in-seconds

最大的心跳时间间隔,超过时间没心跳的客户端被认为宕机,默认90s

application.instance.leaseInfo.durationInSecs

eureka.server.eviction-interval-timer-in-ms

Eureka定时任务,清理lease-expiration-duration没心跳的节点,默认60s

eureka.client.register-with-eureka

是否向EurekaServer注册自己

eureka.client.fetch-registry

是否从EurekaServer获取注册表信息

eureka.client.registry-fetch-interval-seconds

从EurekaServer获取注册表信息的时间间隔

eureka.client.serviceUrl.defaultZone

客户端向这个地址注册和拉取注册信息,服务端节点用它来感知其他peer节点

eureka.server.wait-time-in-ms-when-sync-empty

长轮询的概念,同步数据时如果没有数据变更,请求会阻塞等待的时间

eureka.server.renewal-percent-threshold

心跳到底比例,如果少于少于这个比例,不会清理无心跳的节点,默认0.85

注意点:

  1. Eureka服务端,一般register-with-eureka、fetch-registry都设置为false,不注册自己也从eureka拉取注册信息,信息的同步通过内部的gossip协议进行
  2. Eureka服务端,通过eureka.server.serviceUrl的配置感知其他peer节点,运行期间新增/删除节点通过修改配置文件实现,或配置中心配置
  3. Client周期性(lease-renewal-interval-in-seconds)向服务端续租,如果超过最大时间(lease-expiration-duration-in-seconds)没收到续租请求,这个节点被认为不可用
  4. Server周期性(eviction-interval-timer-in-ms)检查服务器节点,清理不可用的节点
  5. Client向Server注册后,Server之间通过gossip同步,同步后每个Server节点存储自己的注册表,evict线程(eviction-interval-timer-in-ms)统计本地注册表,查看阈值(renewal-percent-threshold)看是否进入保护模式,如果不是保护模式,清理过去的节点,节点的leaseInfo中保存了每个节点最后一次renewal的时间
server:
  port: 8080
spring:
  application:
    name: keyniu-eureka-server
eureka:
  instance:
    hostname: localhost
    lease-renewal-interval-in-seconds: 30
    lease-expiration-duration-in-seconds: 90
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://127.0.0.1:${server.port}/eureka/
  server:
    wait-time-in-ms-when-sync-empty: 5
    enable-self-preservation: true
    eviction-interval-timer-in-ms: 10000
    renewal-percent-threshold: 0.85

5. REST API

默认Eureka的接口返回的XML,可以通过提交请求时指定Accept HTTP头设置响应内容的格式为JSON,这一点对所有接口有效,后续不再赘述

curl -s -H 'Accept: application/json' http://192.168.31.52:8080/eureka/apps/${app}/${instanceId}
1. 获取所有实例

通过curl http://192.168.31.52:8080/eureka/apps能查看所有可用的节点列表,包括所有的应用(application),应用下所有的节点(instance),节点的元数据(metadata)、租约(leaseInfo)等等

2. 指定app的实例

通过如下命令读取数据,这里的KEYNIU-EUREKA-SERVER是app,需要替换成对应的值。

curl http://192.168.31.52:8080/eureka/apps/${app}
curl http://192.168.31.52:8080/eureka/apps/KEYNIU-EUREKA-SERVER
3. 指定app/instanceId的实例

通过如下命令读取数据,其中KEYNIU-EUREKA-SERVER是app,Randy:keyniu-eureka-server:8080是instanceId

curl http://192.168.31.52:8080/eureka/apps/${app}/${instanceId}
curl http://192.168.31.52:8080/eureka/apps/KEYNIU-EUREKA-SERVER/Randy:keyniu-eureka-server:8080
4. 服务上下线

通过修改instance的status字段,我们能控制服务的上下线,比如将节点状态改为OUT_OF_SERVICE

curl -v -XPUT http://192.168.31.52:8080/eureka/apps/${app}/${instanceId}/status?value=OUT_OF_SERVICE
curl -v -XPUT http://192.168.31.52:8080/eureka/apps/KEYNIU-EUREKA-SERVER/Randy:keyniu-eureka-server:8080/status?value=OUT_OF_SERVICE

如果想让节点恢复为上线状态,通过如下命令修改

curl -v -XDELETE http://192.168.31.52:8080/eureka/apps/${app}/${instanceId}/status?value=UP
curl -v -XDELETE http://192.168.31.52:8080/eureka/apps/KEYNIU-EUREKA-SERVER/Randy:keyniu-eureka-server:8080/status?value=UP
5. 更新元数据

比如我们要在元数据里添加一个admin字段,值是zhangsan,我们可以这么做

curl -v -XPUT http://192.168.31.52:8080/eureka/apps/${app}/${instanceId}/metadata?${key}=${value}
curl -v -XPUT http://192.168.31.52:8080/eureka/apps/KEYNIU-EUREKA-SERVER/Randy:keyniu-eureka-server:8080/metadata?admin=zhangsan

6. 新增instance

通过POST请求,请求体可以是JSON,格式按我们读取到的实例格式,假设我们要新增一个节点: Randy1:keyniu-eureka-server:8080, 命令看起来是这样的

curl -v -H 'Content-Type: application/json' -XPOST http://192.168.31.52:8080/eureka/apps/KEYNIU-EUREKA-SERVER -d '{
  "instance": {
    "instanceId": "Randy1:keyniu-eureka-server:8080",
    "hostName": "192.168.31.53",
    "app": "KEYNIU-EUREKA-SERVER",
    "ipAddr": "192.168.31.53",
    "status": "UP",
    "overriddenStatus": "UNKNOWN",
    "port": {
      "$": 8080,
      "@enabled": "true"
    },
    "securePort": {
      "$": 443,
      "@enabled": "false"
    },
    "countryId": 1,
    "dataCenterInfo": {
      "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
      "name": "MyOwn"
    },
    "leaseInfo": {
      "renewalIntervalInSecs": 30,
      "durationInSecs": 90,
      "registrationTimestamp": 1719660811233,
      "lastRenewalTimestamp": 1719661187133,
      "evictionTimestamp": 0,
      "serviceUpTimestamp": 1719655154360
    },
    "metadata": {
      "admin": "zhangsan",
      "management.port": "8080",
      "group": "secondKill"
    },
    "homePageUrl": "http://192.168.31.53:8080/",
    "statusPageUrl": "http://192.168.31.53:8080/actuator/info",
    "healthCheckUrl": "http://192.168.31.53:8080/actuator/health",
    "vipAddress": "keyniu-eureka-server",
    "secureVipAddress": "keyniu-eureka-server",
    "isCoordinatingDiscoveryServer": "true",
    "lastUpdatedTimestamp": "1719660811233",
    "lastDirtyTimestamp": "1719655154170",
    "actionType": "ADDED"
  }
}'
7. 删除instance

通过指定app、instanceId删除对应实例

curl -XDELETE http://192.168.31.52:8080/eureka/${app}/${instanceId}
curl -XDELETE http://192.168.31.52:8080/eureka/KEYNIU-EUREKA-SERVER/Randy1:keyniu-eureka-server:8080
8. 发送心跳
curl -XPUT http://192.168.31.52:8080/eureka/apps/${app}/${instanceId}
curl -XPUT http://192.168.31.52:8080/eureka/apps/KEYNIU-EUREKA-SERVER/Randy1:keyniu-eureka-server:8080

6. 案例解析

现在我们反过来,从Eureka UI来看,显示的每个字段是从何而来,怎么配置

1. Eureka首页

下图是Eureka UI首页显示的内容,我们主要关系其中的6个显示字段,对应图上的数字,下面列表中是它的说明

  1. 环境,通过eureka.environment配置,默认test
  2. 数据中心,通过eureka.datacenter配置,默认MyOwn
  3. 是否删除过去租约,只要不处于自我保护模式,这个值就是true,不启动eureka.server.enable-self-preservation
  4. 进入自我保护模式的阈值,资料上计算公式:  客户端数量 * (60 / lease-renewal-interval-in-seconds ) * renewal-percent-threshold,实测下来有出入,待进一步研究
    1. 仅一个客户端,lease-renewal-interval-in-seconds = 30 , Renews threshod = 1,计算值= 1 * (60/30) * 0.85 = 1.7
    2. 仅一个客户端,lease-renewal-interval-in-seconds = 20 , Renews threshod = 3,计算值= 1 * (60/20) * 0.85 = 2.55
  5. EurekaServer的节点,取值是eureka.client.serviceUrl.defaultZone中配的机器
  6. 注册到EurekaServer的节点,这个值是我们配置的spring.application.name,Status里显示的是我们的nodeName,默认格式是: ${hostName}:${app.name}:${server.port}

2. 注册表信息

通过Eureka Server的REST接口,我们能读到注册表信息,下面这个连接能查看所有的APP信息,不过我们这里只有一个节点

http://127.0.0.1:8080/eureka/apps

下面是其中一个节点的内容,下面有序列表的数字对应图片里的数字

  1. dataCenterInfo表示数据中心,一般配置到Eureka Client,定义是个枚举DataCenterInfo.Name类,可选值有Netflix、Amazon、MyOwn,默认MyOwn
    1. 对应配置项eureka.instance.data-center-info
    2. eureka.datacenter也是数据中心的概念,一般配置到Eureka Server
    3. 暂时没看到这两个值如果不同的话有什么影响,但会让人觉得混乱,需要进一步研究
  2. metadata元数据信息,默认只有management.port,可以做自定义配置
    1. 对应配置项eureka.instance.metadata-map
    2. 基于元数据可以做服务分组,用MetadataAwarePredicate实现调用对应分组的节点
  3. instanceId实际是拼接值:  ${主机名}:${app.name}:${server.port}
  4. overriddenstatus用来覆盖默认状态,节点的状态默认通过心跳来维护,心跳正常状态为UP
  5. lastRenewalTimestamp表示最后一次收到心跳的时间,Eureka用这个时间来判断节点是否可用
  6. lastDirtyTimestamp表示节点信息最后一次更新的时间,用来Eureka Server节点之间的增量

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

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

相关文章

跟《经济学人》学英文:2024年6月22日这期 India’s electronics industry is surging

India’s electronics industry is surging Foreign and domestic firms are investing in local manufacturing surge:激增&#xff1b;急剧上升&#xff1b; 原文&#xff1a; To witness India’s growing role as a manufacturing hub, dodge Bangalore’s notorious t…

FreeBSD虚拟化解决之道:高效、安全、灵活的虚拟解决方案全览

FreeBSD下的虚拟化技术 虚拟化软件可让一台计算机同时运行多个操作系统。这种用于个人电脑的系统软件通常涉及一个运行虚拟化软件的宿主机&#xff08;host&#xff09;操作系统&#xff0c;并支持任何数量的客户机&#xff08;guest&#xff09;操作系统。 FreeBSD下的虚拟解…

惠海H6392 2.6v升5V 3.7V升9V 4.2V升12V 升压恒压芯片 小家电IC

惠海H6392升压恒压芯片是一款小家电、移动设备以及其他需要升压恒压电源的电子设备设计的DC-DC转换器。这款芯片以其独特的产品特性和广泛的应用场景&#xff0c;为电子产品设计者提供了高效、稳定的电源解决方案。 产品描述&#xff1a; H6392采用了简单的电流模式升压技术&a…

数据质量管理-时效性管理

前情提要 根据GB/T 36344-2018《信息技术 数据质量评价指标》的标准文档&#xff0c;当前数据质量评价指标框架中包含6评价指标&#xff0c;在实际的数据治理过程中&#xff0c;存在一个关联性指标。7个指标中存在4个定性指标&#xff0c;3个定量指标&#xff1b; 定性指标&am…

【漏洞复现】科立讯通信有限公司指挥调度管理平台uploadgps.php存在SQL注入

0x01 产品简介 科立讯通信指挥调度管理平台是一个专门针对通信行业的管理平台。该产品旨在提供高效的指挥调度和管理解决方案&#xff0c;以帮助通信运营商或相关机构实现更好的运营效率和服务质量。该平台提供强大的指挥调度功能&#xff0c;可以实时监控和管理通信网络设备、…

文件加密|电脑文件夹怎么设置密码?5个文件加密软件,新手必看!

电脑文件夹怎么设置密码&#xff1f;您是否希望更好地在电脑上保护您的个人或敏感文件&#xff1f;设置电脑文件夹密码是一种简单而有效的方式来确保你的隐私不被侵犯。通过使用文件加密软件&#xff0c;您可以轻松地为您的文件和文件夹设置密码保护。因此&#xff0c;本文将介…

4.x86游戏实战-人物状态标志位

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;3.x86游戏实战-寄存器 人物状态标志位&#xff1a; 什么叫人物状态标志位&…

【机器学习】大模型训练的深入探讨——Fine-tuning技术阐述与Dify平台介绍

目录 引言 Fine-tuning技术的原理阐 预训练模型 迁移学习 模型初始化 模型微调 超参数调整 任务设计 数学模型公式 Dify平台介绍 Dify部署 创建AI 接入大模型api 选择知识库 个人主页链接&#xff1a;东洛的克莱斯韦克-CSDN博客 引言 Fine-tuning技术允许用户根…

pytorch-01

加载mnist数据集 one-hot编码实现 import numpy as np import torch x_train np.load("../dataset/mnist/x_train.npy") # 从网站提前下载数据集&#xff0c;并解压缩 y_train_label np.load("../dataset/mnist/y_train_label.npy") x torch.tensor(y…

【单片机毕业设计11-基于stm32c8t6的智能水质检测】

【单片机毕业设计11-基于stm32c8t6的智能水质检测】 前言一、功能介绍二、硬件部分三、软件部分总结 前言 &#x1f525;这里是小殷学长&#xff0c;单片机毕业设计篇11基于stm32的智能水质检测系统 &#x1f9ff;创作不易&#xff0c;拒绝白嫖可私 一、功能介绍 -------------…

基于VMware的linux操作系统安装(附安装包)

目录 一、linux操作系统下载链接 二、开始导入镜像源 注&#xff1a;若是还没安装VMware请转到高效实现虚拟机&#xff08;VMware&#xff09;安装教程&#xff08;附安装包&#xff09;-CSDN博客 一、linux操作系统下载链接 1.官网链接下载 ubuntu&#xff1a;ubuntu官网…

连环计 | 第6集 | 百姓有倒悬之危,君臣有累卵之急 | 貂蝉 | 三国演义 | 逐鹿群雄

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;这篇博客分享的是《三国演义》文学剧本第Ⅰ部分《群雄逐鹿》的第6️⃣集《连环计》的经典语句和文学剧本全集台词 文章目录 1.经典语句2.文学剧本台词 …

【Spring Boot】Java 的数据库连接模板:JDBCTemplate

Java 的数据库连接模板&#xff1a;JDBCTemplate 1.JDBCTemplate 初识1.1 JDBC1.2 JDBCTemplate 2.JDBCTemplate 实现数据的增加、删除、修改和查询2.1 配置基础依赖2.2 新建实体类2.3 操作数据2.3.1 创建数据表2.3.2 添加数据2.3.3 查询数据2.3.4 查询所有记录2.3.5 修改数据2…

AXI接口简介

AXI接口&#xff0c;全称为Advanced eXtensible Interface&#xff0c;是ARM公司推出的一种高性能、低成本、可扩展的高速总线接口。AXI接口是ARM公司提出的AMBA&#xff08;Advanced Microcontroller Bus Architecture&#xff09;高级微控制器总线架构的一部分。2003年发布了…

List接口, ArrayList Vector LinkedList

Collection接口的子接口 子类Vector&#xff0c;ArrayList&#xff0c;LinkedList 1.元素的添加顺序和取出顺序一致&#xff0c;且可重复 2.每个元素都有其对应的顺序索引 方法 在index 1 的位置插入一个对象&#xff0c;list.add(1,list2)获取指定index位置的元素&#…

Lr、LrC软件下载安装 Adobe Lightroom专业摄影后期处理软件安装包分享

Adobe Lightroom它不仅为摄影师们提供了一个强大的照片管理平台&#xff0c;更以其出色的后期处理功能&#xff0c;成为了摄影爱好者们争相追捧的必备工具。 在这款软件中&#xff0c;摄影师们可以轻松地管理自己的照片库&#xff0c;无论是按拍摄日期、主题还是其他自定义标签…

LONGAGENT:优化大模型处理长文本

现有的大模型&#xff08;LLMs&#xff09;&#xff0c;尽管在语言理解和复杂推理任务上取得了显著进展&#xff0c;但在处理这些超长文本时却常常力不从心。它们在面对超过10万令牌的文本输入时&#xff0c;常常会出现性能严重下降的问题&#xff0c;这被称为“中间丢失”现象…

安全与加密常识(0)安全与加密概述

文章目录 一、信息安全的基本概念二、加密技术概述三、常见的安全协议和实践四、加密的挑战与应对 在数字时代&#xff0c;信息安全和加密已成为保护个人和企业数据不受侵犯的关键技术。本文将探讨信息安全的基础、加密的基本原理&#xff0c;以及实用的保护措施&#xff0c;以…

Installed Build Tools revision xxx is corrupted. Remove and install again 解决

1.在buildTools文件下找到对应的sdk版本&#xff0c;首先将版本对应目录下的d8.bat改名为dx.bat。 2.在lib文件下将d8.jar改名为dx.jar。 3.重新编译工程即可

响应式宠物商店网站pbootcms模板

模板介绍 这是一款源码下载响应式宠物商店网站pbootcms模板。该模板采用响应式自适应设计&#xff0c;非常适合宠物行业的任何服务项目或在线商店或宠物网站&#xff0c;下载即用&#xff0c;组织代码优秀。 模板截图 源码下载 响应式宠物商店网站pbootcms模板