微服务系列四:热更新措施与配置共享

目录

前言

一、基于Nacos的管理中心整体方案

二、配置共享动态维护

2.1 分析哪些配置可拆,需要动态提供哪些参数

2.2 在nacos 分别创建共享配置

创建jdbc相关配置文件

 创建日志相关配置文件

创建接口文档配置文件

2.3 拉取本地合并配置文件

2.3.1 拉取出现的先后顺序问题——SpringCloud 和 SpringBoot 读取顺序问题

2.3.2 解决措施——添加引导配置文件,放在Springcloud初始化时读取

.2.3.3 引入依赖

2.3.4 新增bootstrap.yaml文件

2.3.5 重启测试购物车功能

2.3.6 同理修改其他微服务的配置文件

三、配置热更新(不停机更新)

3.1 前提条件

3.2 业务实践——热更新购物车容量

3.3 测试热更新效果

四、网关动态路由监听

监听Nacos配置变更

第一步:注入Nacos依赖

第二步:配置bootstrap.yaml文件

第三步:修改application.yaml文件,删除路由

第四步:编写动态路由加载器框架

第五步:更新路由方法拆解

【问题】如何获取RouteDefinition对象?如何更好的转换RouteDefinition对象?

第六步:动态路由加载器完整实现

第七步:在Nacos配置初始化的JSON格式路由

第八步:无需重启,测试网关生效

五、Nacos配置相关知识追问巩固


实验环境说明

本文有部分地方需要实验进行。首先对于看过黑马微服务的同学应该会比较熟悉。如果没有你也可以参考我实验的环境搭个简单的测试环境,用于实验。实验的目的也是为了更好的理解业务、理解知识点。

本地环境部分:

服务名端口号备注
nginx18080 / 18081前端运行环境
hm-gateway8080后端项目网关模块
item-service8081后端商品服务模块
cart-service8082 后端购物车服务模块
item-service28083后端商品服务模块
item-service38084后端商品服务模块
user-service8085后端用户管理模块
trade-service8086后端交易服务模块
pay-service8087后端支付服务模块

远程服务器环境部分:

服务名端口号备注
nacos8848注册中心及配置中心,非容器镜像
nginx18080/18081线上前端运行环境,容器镜像
mysql3306线上数据库,容器镜像
docker-hm8080线上后端运行环境,容器镜像

注意事项:

  • 远程环境中非容器镜像指nacos是后单独配置的容器,而mysql、nginx、docker-hm是使用compose统一部署的。
  • 在本节实验中,线上环境的nginx和docker-hm我们不会使用到,而是使用本地的nginx和后端项目
  • 请你确保在配置线上环境时,mysql必须先比nacos启动。如果nacos先启动将无法连接数据库。此时你需要停止nacos容器,先启动mysql。


前言

这是微服务相关技术学习记录的第四篇文章啦!到此为止,微服务开发相关的技术其实已经分享了七七八八了。本篇则是着重介绍微服务配置文件管理相关、配置更新相关的内容。

回顾先前我们学习的微服务开发技术,我们已经解决了以下几个问题:

  • 实现微服务远程调用 (OpenFeign)

  • 实现微服务注册、发现  (nacos)

  • 微服务请求路由、负载均衡  (nacos)

  • 微服务登录用户信息传递  (网关 + 拦截器)

还有哪些问题需要解决呢?

试想一下,假如网关路由发生了更新,你是打算重新启动网关模块更新配置么?那么停机更新会导致服务不可用怎么办?

再来,目前每个微服务都需要编写配置文件,但是实际上里面很多内容是一样的。如果对这一部分公共内容进行修改,是不是又得大规模停机更新微服务呢?

这就概括出了目前微服务配置方面存在的弊端和我们本篇改进的方向:

  • 如何简化重复配置,降低维护成本?
  • 如何实现项目配置的不停机维护?
  • 如何实现网关路由的动态维护?

一、基于Nacos的管理中心整体方案

上述问题我们不难想到,那就将所有配置文件统一管理起来,成立一个管理中心即可。确实,这个管理中心还可以由我们的Nacos一并担任!

整体方案图示

微服务共享的配置可以统一交给Nacos保存和管理,在Nacos控制台修改配置后,Nacos会将配置变更推送给相关的微服务,并且无需重启即可生效,实现配置热更新。

网关的路由同样是配置,因此同样可以基于这个功能实现动态路由功能,无需重启网关即可修改路由配置。

二、配置共享动态维护

我们可以把微服务共享的配置抽取到Nacos中统一管理,这样就不需要每个微服务都重复配置了。

主要步骤如下:

  • 在Nacos中添加共享配置

  • 微服务拉取配置

配置共享最大的好处就是将重复配置封装成一份交Nacos统一管理。其他微服务只需要提供一些配置参数就行了,极大的简化了开发。接下来我们以cart-service为例,实现配置文件拆分。

2.1 分析哪些配置可拆,需要动态提供哪些参数

2.2 在nacos 分别创建共享配置

在 配置管理->配置列表 中点击 新建一个配置:


创建jdbc相关配置文件

spring:
  datasource:
    url: jdbc:mysql://${hm.db.host:192.168.186.140}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: ${hm.db.un:root}
    password: ${hm.db.pw:123}
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto
  • 数据库ip:通过${hm.db.host:192.168.150.101}配置了默认值为192.168.150.101,同时允许通过${hm.db.host}来覆盖默认值

  • 数据库端口:通过${hm.db.port:3306}配置了默认值为3306,同时允许通过${hm.db.port}来覆盖默认值

  • 数据库database:可以通过${hm.db.database}来设定,无默认值


 创建日志相关配置文件

logging:
  level:
    com.hmall: debug
  pattern:
    dateformat: HH:mm:ss:SSS
  file:
    path: "logs/${spring.application.name}"

创建接口文档配置文件

knife4j:
  enable: true
  openapi:
    title: ${hm.swagger.title:黑马商城接口文档}
    description: ${hm.swagger.description:黑马商城接口文档}
    email: ${hm.swagger.email:weizhicong@stu.gpnu.cn}
    concat: ${hm.swagger.concat:weizhicong}
    url: https://www.weizhicong.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - ${hm.swagger.package}
  • title:接口文档标题,我们用了${hm.swagger.title}来代替,将来可以有用户手动指定

  • email:联系人邮箱,我们用了${hm.swagger.email:weizhicong@stu.gpnu.cn},默认值是weizhicong@stu.gpnu.cn,同时允许用户利用${hm.swagger.email}来覆盖。

2.3 拉取本地合并配置文件

现在线上的配置文件创建好了,我们需要想办法让微服务项目接下来拉取共享配置。将拉取到的共享配置与本地的application.yaml配置合并,完成项目上下文的初始化。但是这其中存在一些拉取的问题,让我们逐步分析。

2.3.1 拉取出现的先后顺序问题——SpringCloud 和 SpringBoot 读取顺序问题

读取Nacos配置是发生在SpringCloud上下文初始对象(ApplicationContext)时发生的,而Nacos地址配置在application.yaml却是SpringBoot启动时才发生的。也就是说在读取Nacos配置时,根本不知道去哪里读取Nacos地址信息。从而造成无法加载Nacos配置文件的问题。

2.3.2 解决措施——添加引导配置文件,放在Springcloud初始化时读取

.2.3.3 引入依赖

在cart-service模块引入依赖:

  <!--nacos配置管理-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
  <!--读取bootstrap文件-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
  </dependency>

2.3.4 新增bootstrap.yaml文件

在cart-service中的resources目录新建一个bootstrap.yaml文件:

spring:
  application:
    name: cart-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.186.140 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-jdbc.yaml # 共享mybatis配置
          - dataId: shared-log.yaml # 共享日志配置
          - dataId: shared-swagger.yaml # 共享日志配置

2.3.5 重启测试购物车功能

2.3.6 同理修改其他微服务的配置文件

user-service成功!

trade-service成功!

item-service成功!

pay-service成功!

联调测试成功!


三、配置热更新(不停机更新)

配置热更新 :当我们修改配置文件中的参数属性时,无需重启服务即可生效。

3.1 前提条件

3.2 业务实践——热更新购物车容量

购物车业务,购物车数量有一个上限,默认是10,对应代码如下:

第一步:创建容器配置类,并添加@ConfigurationProperties注解指定服务配置

第二步:注入使用修改业务代码

第三步:增加Nacos配置

文件名称由三部分组成:[服务名]-[spring.active.profile].[后缀名]

  • 服务名:我们是购物车服务,所以是cart-service

  • spring.active.profile:就是spring boot中的spring.active.profile,可以省略,则所有profile共享该配置

  • 后缀名:例如yaml

3.3 测试热更新效果

四、网关动态路由监听

目前,我们的网关路由是写死在配置文件的。一旦路由有所变动,必须重启网关才能生效。对此我们希望网关路由也能和上一小节的配置热更新一样,动态维护,无需停机即可更新。

但是,网关的路由配置全部是在项目启动时由org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator在项目启动的时候加载,并且一经加载就会缓存到内存中的路由表内(一个Map),不会改变。也不会监听路由变更,

所以,我们无法利用上节课学习的配置热更新来实现路由更新。

因此,我们必须监听Nacos的配置变更,然后手动把最新的路由更新到路由表中。这里有两个难点:

  • 如何监听Nacos配置变更?

  • 如何把路由信息更新到路由表?

监听Nacos配置变更

官方文档:Nacos 监听配置 Java SDKicon-default.png?t=O83Ahttps://nacos.io/zh-cn/docs/sdk.html

第一步:注入Nacos依赖

第二步:配置bootstrap.yaml文件

spring:
  application:
    name: hm-gateway # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.186.140:8848 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-log.yaml # 共享日志配置

第三步:修改application.yaml文件,删除路由

server:
  port: 8080 # 网关端口 前端请求统一处理

hm:
  jwt:
    location: classpath:hmall.jks
    alias: hmall
    password: hmall123
    tokenTTL: 30m
  auth:
    excludePaths:
      - /search/**
      - /users/login
      - /items/**
      - /hi

第四步:编写动态路由加载器框架


@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    // 用来获取ConfigServer中的配置信息,与nacos建立连接
    private final NacosConfigManager nacosConfigManager;

    private final String dataId = "gateway-routers.json";

    private final String group = "DEFAULT_GROUP";

    @PostConstruct // 初始化方法,这个Bean一初始化就会执行
    public void initRouteConfigListener() throws NacosException {

        //1. 项目启动,先拉取一次配置,并且添加监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 3000, new Listener() {
                    @Override
                    // 线程池
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override

                    public void receiveConfigInfo(String s) {
                        //TODO 2. 监听器,当配置发生变化时,需要更新路由表

                    }
                });

        // 3.第一次读取到配置,也需要更新到路由表
        updateConfigInfo(configInfo);
    }

    public void updateConfigInfo(String configInfo) {
        // TODO 更新路由表
    }
}

第五步:更新路由方法拆解

更新路由要用到org.springframework.cloud.gateway.route.RouteDefinitionWriter这个接口:

【问题】如何获取RouteDefinition对象?如何更好的转换RouteDefinition对象?

我们知道通过前面编写的这个方法可以获取到字符串格式的配置文件,保存在configInfo。它就和我们的配置文件一模一样,只不过是字符串表示。

所以第一个问题:如何获取RouteDefinition对象?我们需要解析字符串形式的yaml文件,将它转成java对象。

但是啊,将yaml文件转成Java的对象比较困难,那么什么格式转java对象比较方便呢?——JSON格式。

所以第二个问题:如何更好的转换RouteDefinition对象?我们将路由等信息存储成JSON格式而不是yaml格式,这样可以更好地转换成Java对象

第六步:动态路由加载器完整实现

package com.hmall.gateway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.hmall.common.utils.CollUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    private final RouteDefinitionWriter writer;
    // 用来获取ConfigServer中的配置信息,与nacos建立连接
    private final NacosConfigManager nacosConfigManager;

    // 配置文件id 和 分组
    private final String dataId = "gateway-routers.json";
    private final String group = "DEFAULT_GROUP";

    // 保存更新过的路由id
    private final Set<String> routeIds = new HashSet<>();

    
    @PostConstruct // 初始化方法,这个Bean一初始化就会执行
    public void initRouteConfigListener() throws NacosException {

        //1. 项目启动,先拉取一次配置,并且添加监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 3000, new Listener() {
                    @Override
                    // 线程池
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override

                    public void receiveConfigInfo(String configInfo) {
                        //2. 监听器,当配置发生变化时,需要更新路由表
                        updateConfigInfo(configInfo);
                    }
                });

        // 3.第一次读取到配置,也需要更新到路由表
        updateConfigInfo(configInfo);
    }

    /**
     * configInfo 是nacos中配置的json字符串
     * @param configInfo
     */
    public void updateConfigInfo(String configInfo) {
        // 更新路由表
        log.debug("监听到路由配置变更,{}", configInfo);
        // 1.反序列化
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        // 2.更新前先清空旧路由
        // 2.1.清除旧路由
        for (String routeId : routeIds) {
            writer.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();
        // 2.2.判断是否有新的路由要更新
        if (CollUtils.isEmpty(routeDefinitions)) {
            // 无新路由配置,直接结束
            return;
        }
        // 3.更新路由
        routeDefinitions.forEach(routeDefinition -> {
            // 3.1.更新路由
            writer.save(Mono.just(routeDefinition)).subscribe();
            // 3.2.记录路由id,方便将来删除
            routeIds.add(routeDefinition.getId());
        });
    }
}

第七步:在Nacos配置初始化的JSON格式路由

当前还没配置Nacos时,此时的网关没有任何路由,可以测试一下:

第八步:无需重启,测试网关生效

五、Nacos配置相关知识追问巩固

1. 如何简化重复配置,降低维护成本?谈谈具体实施步骤

2. 如何解决读取远程Nacos配置时无法从Springboot中获取到Nacos地址的配置信息?

3. 如何实现配置热更新?谈谈具体的实施步骤

4. 如何实现动态路由热更新?谈谈具体的实施步骤

5. 请你谈谈 @PostConstruct 注解的作用?

6. 请你谈谈监听Nacos配置中如何获取Nacos的ConfigServer中的配置信息?

7. 谈谈更新路由监听器方法是怎么实现的?你是否赞同采用先删除后更新的策略,为什么?

8. Nacos提供了RouteDefinitionWriter接口用于实现添加路由,路由对象是RouteDefinition类型的。请问你在路由监听中如何获取该对象?如何更好、更快的获取该对象?

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

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

相关文章

003-Kotlin界面开发之声明式编程范式

概念本源 在界面程序开发中&#xff0c;有两个非常典型的编程范式&#xff1a;命令式编程和声明式编程。命令式编程是指通过编写一系列命令来描述程序的运行逻辑&#xff0c;而声明式编程则是通过编写一系列声明来描述程序的状态。在命令式编程中&#xff0c;程序员需要关心程…

Python作业记录

复制过来的代码的换行有问题&#xff0c;但是也不是什么大问题。 后续我会进行补充和修改。 请将如下英文短句根据单词切分成列表&#xff1a; The continent of Antarctica is rising. It is due to a geological phenomenon called post-glacial uplift 并在切分好的列表…

pdmaner连接sqlexpress

别以为sqlserver默认的端口总是1433 案例 有台sqlserver2008 express服务器&#xff0c;刚安装&#xff0c;支持混合模式登录&#xff0c;其它什么配置也没改。 先看用ADO连接 这说明&#xff1a; 案例中sqlserver端口不是1433 &#xff01;&#xff01;&#xff01;ADO连接…

轻型民用无人驾驶航空器安全操控------理论考试多旋翼部分笔记

官网&#xff1a;民用无人驾驶航空器综合管理平台 (caac.gov.cn) 说明&#xff1a;一是法规部分&#xff1b;二是多旋翼部分 本笔记全部来源于轻型民用无人驾驶航空器安全操控视频讲解平台 目录 官网&#xff1a;民用无人驾驶航空器综合管理平台 (caac.gov.cn) 一、轻型民用无人…

二叉树相关习题

题目&#xff1a;100. 相同的树 - 力扣&#xff08;LeetCode&#xff09; 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; …

阅读笔记记录

论文作者将对话建模成一个seq2seq的映射问题&#xff0c;该seq2seq框架以对话历史数据&#xff08;通过belief tracker建模&#xff09;和数据库查询结果&#xff08;通过Database Operator得到结果&#xff09;作为支撑。 Abstract 教会机器完成与人自然交流的任务是充满挑战…

测试分层:减少对全链路回归依赖的探索!

引言&#xff1a;测试分层与全链路回归的挑战 在软件开发和测试过程中&#xff0c;全链路回归测试往往是一个复杂且耗费资源的环节&#xff0c;尤其在系统庞大且模块众多的场景下&#xff0c;全链路测试的集成难度显著提高。而“测试分层”作为一种结构化的测试方法&#xff0…

融合虚拟化与容器技术,打造灵活又安全的AI算力服务

随着人工智能技术的不断进步&#xff0c;AI企业在迅速推进大模型业务时&#xff0c;往往会倾向于采用容器化的轻量部署方案。相较于传统的虚拟机部署&#xff0c;容器化在快速部署、资源利用、环境一致性和自动化编排等方面具备显著优势。 然而&#xff0c;容器技术所固有的隔…

协程3 --- golang的协程调度

文章目录 单进程时代多进程/线程时代协程时代内核级线程模型&#xff08;1&#xff1a;1&#xff09;用户级线程模型&#xff08;N&#xff1a;1&#xff09;两级线程模型CMP&#xff08;M&#xff1a;N&#xff09;GM模型 GMP模型 单进程时代 描述&#xff1a;每一个程序就是一…

微服务透传日志traceId

问题 在微服务架构中&#xff0c;一次业务执行完可能需要跨多个服务&#xff0c;这个时候&#xff0c;我们想看到业务完整的日志信息&#xff0c;就要从各个服务中获取&#xff0c;即便是使用了ELK把日志收集到一起&#xff0c;但如果不做处理&#xff0c;也是无法完整把一次业…

【原创】java+ssm+mysql收纳培训网系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

apache poi 实现下拉框联动校验

apache poi 提供了 DataValidation​ 接口 让我们可以轻松实现 Excel 下拉框数据局校验。但是下拉框联动校验是无法直接通过 DataValidation ​实现&#xff0c;所以我们可以通过其他方式间接实现。 ‍ 步骤如下&#xff1a; 创建一个隐藏 sheet private static void create…

Linux权限概念 | 权限修改

文章目录 1.Linux的权限概念2.Linux权限管理3.文件访问权限的相关设置方法 1.Linux的权限概念 Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;和普通用户。对应root用户而言&#xff1a;可以在Linux系统下做任何事情&#xff0c;不受限制。而普通用户&am…

题目练习之二叉树那些事儿(续集)

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ 这一篇博客我们继…

删除MacOS下PowerPoint烦人的加载项

起因 最近要写论文&#xff0c;需要插入很多公式&#xff0c;利用自带的吧&#xff0c;太过繁琐&#xff0c;每次插入都需要点击插入-公式-符号&#xff0c;然后头脑发热想用下本科写论文时用过的MathType&#xff0c;结果这货现在要收费了&#xff0c;新版本只能适用30天&…

清华双臂机器人扩散大模型RDT:先预训练后微调,支持语言、图像、动作多种输入(1B参数)

前言 通过上文介绍的GR2&#xff0c;我们看到了视频生成模型在机器人训练中的应用 无独有偶&#xff0c;和GR2差不多一个时期出来的清华RDT&#xff0c;其模型架构便基于视频生成架构DiT改造而成(当然&#xff0c;该清华团队其实也在DiT之前推出了U-ViT&#xff0c;具体下文会…

Linux下GCC编译器的安装

Linux下GCC编译器的安装 以下所有的版本都可以在https://gcc.gnu.org/pub/gcc/infrastructure/这里找最新的 通过apt-get方式下载的Qt5.9的gcc编译器版本只是4.8.3&#xff0c;无法打开一些Qt5的库头文件&#xff0c;所以准备在Llinux下再安装一个gcc5.3.0。 查看gcc版本 ubu…

qt相关知识

lineEdit中的一些知识 首先我要设置lineEdit中的文本怎么操作 ui->lineEdit->setText(); 如何给窗口设置名字 this->setWindowTitle("计算器"); 如何给按钮设置我们的图片 QIcon ic("图片地址")&#xff1b; ui->button->setIcon(ic…

使用官网tar包制作OpenSSL及OpenSSH rpm包进行升级安装(OpenSSH_9.9p1, without OpenSSL未解决)

一、制作openssl-1.1.1w.rpm包 1、安装基础依赖包和rpmbuild及其依赖包 yum install curl which make gcc perl perl-WWW-Curl rpm-build rpm-build rpmdevtools tree -y yum install gcc-c glibc glibc-devel openssl openssl-devel \pcre-devel zlib zlib-devel perl…