手写CDN基本原理

news2024/11/14 21:50:14
本文已收录于专栏
《中间件合集》

目录

  • 追本溯源
  • 概念说明
  • 需求分析
  • 核心功能
  • 代码实现
    • A用户模块
    • 本地DNS模块
    • CDN服务模块
    • 缓存服务模块
    • 原服务端
  • 总结提升

追本溯源

  CDN的发展源于对互联网内容传输速度和用户体验的需求。在互联网发展初期,内容的传输主要依赖于源服务器,但随着互联网用户规模的快速增长,单一服务器的容量和带宽很难满足全球用户的需求。为了解决这一问题,CDN技术应运而生。

概念说明

  CDN(Content Delivery Network)是一种通过在全球各地建立分布式节点服务器,将内容缓存到离用户最近的节点服务器上,以提供快速、高效的内容传输和分发服务的技术。CDN通过将内容存储在离用户最近的节点服务器上,减少了用户请求的网络延迟和带宽消耗,提高了内容的传输速度和用户体验。简单来说就是将内容缓存在终端用户附近

需求分析

  当我们去请求一个图片或者视频的时候,有时候会出现访问失败的问题,导致想要的资源获取不到。这时候就可以用到CDN了。对于资源的服务器来说我们可以把用户访问的域名注册到CDN中,这时候当我们在去访问资源的时候CDN会从我们距离我们最近访问最快的节点给我们返回资源,如果缓存服务器中没有我们想要的资源,那么缓存服务器会去请求原服务器,请求到资源之后保存到缓存服务器中,然后返回给用户,当用户第二次请求的时候就会从缓存服务器中直接返回了。
在这里插入图片描述

核心功能

  1. 「内容加速 」:CDN通过将内容缓存在全球各地的节点服务器上,使用户可以从离自己最近的节点服务器获取内容,减少了网络延迟,提高了内容传输速度。
  2. 「 负载均衡 」:CDN通过在全球各地建立分布式节点服务器,可以将用户请求分散到不同的节点服务器上,实现负载均衡,提高了服务器的处理能力和可靠性。
  3. 「 高可用性 」:多个应用程序可以共享同一份配置信息,避免了配置信息的重复存储和管理。通过配置的分组和命名空间,可以实现不同应用程序之间的配置隔离和共享。

代码实现

A用户模块

请求想要的资源并集成了本地的DNS服务

import com.example.localdns.Controller.LocalDNSController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @BelongsProject: UserA
 * @BelongsPackage: com.example.usera
 * @Author: Wuzilong
 * @Description: A用户请求资源
 * @CreateTime: 2023-06-29 19:48
 * @Version: 1.0
 */
@RestController
@RequestMapping("/userA")
public class UserAController {


    @Autowired
    private Environment environment;

    @Autowired
    private LocalDNSController localDNS;

    @GetMapping(value = {"/getResource"})
    public void getResource(String key) {
        String property = environment.getProperty("server.name");
        String ipPath = localDNS.getIpAddress(property);
        String url = "http://" + ipPath + "/server/getResourceInfo?resourceName="+key;
        RestTemplate restTemplate = new RestTemplateBuilder().build();
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        if (forEntity.getStatusCode() == HttpStatus.OK) {
            System.out.println("成功获取资源!资源为" + forEntity.getBody());
        }
    }

配置文件中需要配置连接nacos服务的地址以及本服务的信息

server:
  port: 8001
  name: www.wzl.com
  cdn: localhost:8005

在pom文件中引入我们自己封装的本地DNS服务

        <dependency>
            <groupId>com.example</groupId>
            <artifactId>LocalDNS</artifactId>
            <version>1.3-20230706.142442-1</version>
        </dependency>

本地DNS模块

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * @BelongsProject: LocalDNS
 * @BelongsPackage: com.example.localdns.Controller
 * @Author: Wuzilong
 * @Description: 描述什么人干什么事儿
 * @CreateTime: 2023-06-30 10:49
 * @Version: 1.0
 */
@RestController
@RequestMapping("/localDNS")
public class LocalDNSController {


        @Value("${server.cdn}")
        private String cdnServer;

        private Map<String, String> localDNS = new HashMap<>(){{
            put("www.wzl.com","localhost:9099");
        }};

        /**
        * @Author:Wuzilong
        * @Description: 将服务添加到CDN中,将域名进行加工起到域名加速的作用。并更新本地dns
        * @CreateTime: 2023/6/30 20:34
        * @param:
        * @return:
        **/
        @GetMapping("/addCDNServer")
        public void addCDNServer(String accelerateName) {
           String url="http://"+cdnServer+"/cdnServer/addDomainName?domainName="+accelerateName;
            RestTemplate restTemplate=new RestTemplateBuilder().build();
            ResponseEntity<Map<String,String>> forEntity = restTemplate.exchange(url, HttpMethod.GET, null,new ParameterizedTypeReference<Map<String, String>>() {});
            if (forEntity.getStatusCode() == HttpStatus.OK) {
                Map<String, String> body = forEntity.getBody();
                localDNS.putAll(body);
                localDNS.put(accelerateName, String.valueOf(body.keySet()).replaceAll("\\[|\\]", ""));
                System.out.println("已经将加速后的域名添加到本地DNS"+body);
            }
        }


        /**
        * @Author:Wuzilong
        * @Description: 获取域名的ip地址,通过cdn加速的域名获取之后还是一个域名那么需要再次解析域名直到得到ip地址为止
        * @CreateTime: 2023/7/1 10:27
        * @param:  域名
        * @return:  ip地址
        **/
        public String getIpAddress(String domainName){
            Boolean isRun=true;
            String ipAddress = localDNS.get(domainName);
            if (!ipAddress.contains(":")){

                ipAddress=this.getIpAddress(ipAddress);
                isRun=false;
            }
            if (isRun){
                String url="http://"+ipAddress+"/cdnServer/getNearbyIpAddress";
                RestTemplate restTemplate=new RestTemplateBuilder().build();
                ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
                if (forEntity.getStatusCode() == HttpStatus.OK) {
                    System.out.println("获取到了距离最近的ip地址,为:"+forEntity.getBody());
                    ipAddress=forEntity.getBody();

                }
            }
            return ipAddress;
        }
    }

CDN服务模块

  CDN服务端主要的服务:注册要加速度的服务和计算当前请求的ip地址距离哪个缓存服务器最近响应最快


import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.model.CityResponse;
import com.maxmind.geoip2.record.Location;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @BelongsProject: CDNServer
 * @BelongsPackage: com.example.cdnserver.Controller
 * @Author: Wuzilong
 * @Description: 描述什么人干什么事儿
 * @CreateTime: 2023-06-30 20:56
 * @Version: 1.0
 */
@RestController
@RequestMapping("/cdnServer")
public class CdnServerController {

    @Value("${server.port}")
    private String serverPort;

    private List<String> cityIPs=new ArrayList<>(){{
        //需要改成各个地方服务器的ip地址也就是给用户提供的缓存服务器的ip地址
        add("0.0.0.0");
        add("1.1.1.1");
        add("2.2.2.2");
        add("3.3.3.3");
    }};


    // 地球半径(单位:千米)
    private static final double EARTH_RADIUS = 6371;
    /**
    * @Author:Wuzilong
    * @Description: 返回通过cdn加工之后的域名和cdn 调度中心的ip地址
    * @CreateTime: 2023/7/1 9:52
    * @param:  需简要加速的域名
    * @return:  返回通过cdn加工之后的域名和cdn 调度中心的ip地址
    **/
    @GetMapping("/addDomainName")
    public Map<String,String> addDomainName(String domainName) throws UnknownHostException {
        System.out.println("请求加速的域名为"+domainName);
        Map<String,String> CDNInfo=new HashMap<>();
        domainName=domainName+".cdn";
        CDNInfo.put(domainName, InetAddress.getLocalHost().getHostAddress()+":"+serverPort);
        System.out.println("加速后的域名为"+domainName);
        return CDNInfo;
    }


    @GetMapping("/getNearbyIpAddress")
    public String getNearbyIpAddress() throws IOException, GeoIp2Exception {
        // 创建一个指向GeoIP2数据库文件的File对象
        File database = new File("C:\\Users\\Administrator\\Desktop\\city\\GeoLite2-City_20230704\\GeoLite2-City.mmdb");//解析ip地址的文件路径

        // 使用数据库文件创建一个DatabaseReader对象,用于查询城市的地理位置信息
        DatabaseReader reader = new DatabaseReader.Builder(database).build();
        //获取请求方的ip地址,需要调整成动态获取的
        String remoteAddr="111.111.111.111";

        // 查询单独的IP地址的地理位置信息
        InetAddress ip = InetAddress.getByName(remoteAddr);
        CityResponse response = reader.city(ip);
        // 获取城市的经纬度坐标
        Location location1 = response.getLocation();
        double lat1 = Math.toRadians(location1.getLatitude());
        double lon1 = Math.toRadians(location1.getLongitude());
        String nearestIp="";
        double minDistance =Double.MAX_VALUE;
        // 遍历各个城市的IP地址,计算距离并更新最小距离和对应的城市
        for (String cityIP : cityIPs) {
            // 初始化最小距离和对应的城市
            InetAddress cityIpAddress = InetAddress.getByName(cityIP);
            CityResponse cityResponse = reader.city(cityIpAddress);
            Location location2 = cityResponse.getLocation();
            double lat2 = Math.toRadians(location2.getLatitude());
            double lon2 = Math.toRadians(location2.getLongitude());

            double dlon = lon2 - lon1;
            double dlat = lat2 - lat1;

            double a = Math.sin(dlat / 2) * Math.sin(dlat / 2) +
                    Math.cos(lat1) * Math.cos(lat2) *
                            Math.sin(dlon / 2) * Math.sin(dlon / 2);
            double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

            double distance = EARTH_RADIUS * c;

            System.out.println("和"+cityResponse.getCity().getName()+"的距离是:" + cityIP+",距离是:"+distance);

            if (distance < minDistance) {
                minDistance = distance;
                nearestIp = cityIP;
            }

        }
        System.out.println("距离最短的城市对应的IP是:" + nearestIp);
        return nearestIp;
    }
}

缓存服务模块

  CDN是给用户提供访问响应最快距离最近的服务器,如果缓存服务器中没有用户想要的资源需要向原服务器继续请求,然后保存到缓存服务器中一份,方便下次访问相同的资源能够直接返回用户想要的资源,提高用户的体验。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * @BelongsProject: CDNLoadBalanceSlave1
 * @BelongsPackage: com.example.cdnloadbalanceslave1.Controller
 * @Author: Wuzilong
 * @Description: 描述什么人干什么事儿
 * @CreateTime: 2023-06-30 10:04
 * @Version: 1.0
 */
@RestController
@RequestMapping("/server")
public class LoadBalanceSlaveController  {
    @Autowired
    private Environment environment;
    
    //用来存储想访问的资源的-缓存版本
    private Map<String,String> resourceInfo=new HashMap<>();

    @GetMapping(value = {"/getResourceInfo"})
    public String getResourceInfo(String resourceName){
        String value = resourceInfo.get(resourceName);
        if (!StringUtils.isEmpty(value)){
            return resourceInfo.get(resourceName);
        }else{
            String originalService = environment.getProperty("server.url");
            String url="http://"+originalService+"server/getResourceInfo?resourceName="+resourceName;
            RestTemplate restTemplate = new RestTemplateBuilder().build();
            ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
            if (forEntity.getStatusCode() == HttpStatus.OK) {
                System.out.println("从原服务器获取资源成功,原服务器的ip是:" + originalService);
                resourceInfo.put(resourceName, forEntity.getBody());
                return forEntity.getBody();
            }
        }
        return "非常抱歉,没有获取到您想要的资源!";
    }
}

原服务端

  用来存储用户想要的数据,用户直接向原服务器进行添加资源。确保原服务器中一定有用户想要的资源。

import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @BelongsProject: CDNLoadBalance
 * @BelongsPackage: com.example.cdnloadbalance.Controller
 * @Author: Wuzilong
 * @Description: 原服务器
 * @CreateTime: 2023-06-29 21:11
 * @Version: 1.0
 */
@RestController
@RequestMapping("/server")
public class OriginalServerController {

    //缓存进来的资源
    private Map<String, String> resourceData = new HashMap<>(){{
        put("wzl","武梓龙");
        put("hhh","哈哈哈");
    }};


    /**
    * @Author:Wuzilong
    * @Description: 从缓存中没有获取到数据,直接请求原服务器的数据
    * @CreateTime: 2023/7/5 19:23
    * @param:
    * @return:
    **/
    @GetMapping(value = {"/getResourceInfo"})
    public String getResourceInfo(String resourceName){
        String value = resourceData.get(resourceName);
        if (StringUtils.isEmpty(value)){
            return null;
        }
        return value;
    }


    /**
    * @Author:Wuzilong
    * @Description: 新添加进来的信息
    * @CreateTime: 2023/7/5 19:33
    * @param:  新资源的键值对信息
    * @return: 资源添加成功提示语
    **/
    @PostMapping(value = {"/setResourceInfo"})
    public String setResourceInfo(@RequestBody Map<String, String> resourceInfo){
        resourceData.putAll(resourceInfo);
        return "新资源添加成功!";
    }

}

总结提升

  CDN是一种通过在全球建立分布式节点服务器,将内容缓存到离用户最近的节点上,提供快速、高效的内容传输和分发服务的技术。CDN通过加速内容传输、负载均衡、提高可用性、增强安全性等功能,提供了更好的用户体验和网站性能。随着互联网的发展,CDN将继续发挥重要作用,为用户提供更快速、可靠的内容访问服务。


在这里插入图片描述

🎯 此文章对你有用的话记得留言+点赞+收藏哦🎯

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

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

相关文章

Unity/Shader 零碎知识点

坐标系 Unity使用的是左手坐标系&#xff1b;观察空间&#xff0c;通俗来讲就是以摄像机为原点的坐标系&#xff0c;摄像机的前向是z轴的负方向&#xff0c;与模型和世界空间中的定义相反&#xff0c;z轴的坐标减少意味着场景深度的增加 点积 abba|a||b|cos<a,b> 结果为常…

联想ThinkPad如何用U盘重装系统?联想ThinkPad用U盘重装系统教程

联想ThinkPad如何用U盘重装系统&#xff1f;当联想ThinkPad出现系统问题或需要更新操作系统时&#xff0c;使用U盘进行重装是一种快速而可靠的解决方案&#xff0c;使用U盘重装系统不仅简单易行&#xff0c;还能有效避免出现磁盘损坏或网络不稳定所带来的问题&#xff0c;那具体…

零零信安-DD数据泄露报警日报【第203期】

2023.07.10共发现匿名网络资讯信息83,544条&#xff1b;最近7天同比增长-22.2%&#xff1b;最近30天共发现匿名网络资讯信息2,578,034条。 北京零零信安科技有限公司成立于2020年&#xff0c;是国内首家专注于外部攻击面管理&#xff08;EASM&#xff09;的网络安全公司。基于…

DragGAN 离线版V1.3 解压就能用-DragGAN Windows GUI

一、DragGAN是什么 DragGAN是由Max Planck研究所开发的一种新的人工智能工具&#xff0c;它允许用户通过几个点击和拖动来真实地修改照片。DragGAN模型本质上是给各种GAN开发的一种交互式图像操作方法&#xff0c;实现了通过鼠标拉伸图像即可自动生成新图像的神奇功能。用户先…

Shader Graph22-卡通风格(上)

一、打开Unreal&#xff0c;新建Material命名为DemoToonPaint。 二、0.5、0.5、0.5为模拟的光线方向&#xff0c;VertexNormalWS为法线的世界坐标&#xff0c;Dot点积之后得到指定光线方向分布&#xff0c;Multiply(,0.5)和Add(,0.5)是去掉负值。Texture我们需要用photoshop制作…

flutter开发实战-长链接WebSocket使用stomp协议stomp_dart_client

flutter开发实战-长链接WebSocket使用stomp协议stomp_dart_client 在app中经常会使用长连接进行消息通信&#xff0c;这里记录一下基于websocket使用stomp协议的使用。 一、stomp&#xff1a;流文本定向消息协议 1.1 stomp介绍 stomp&#xff0c;Streaming Text Orientate…

chatGLM2中的Multi Query Attention

目录 原理简介 代码实现和耗时比较 总结分析 近期一直在玩大模型&#xff0c;对中文支持比较好的就是清华的chatGLM&#xff0c;目前chatGLM由v1升级到了chatGLM2。在gihub上介绍信息如下&#xff1a; 试用了一下&#xff0c;效果和速度确实有所提升。 这个得益于chatGLM2应…

pnpm + workspace + changesets

pnpm workspace changesets 构建你的 monorepo 工程 什么是monorepo&#xff1f; 什么是 monorepo&#xff1f;以及和 multirepo 的区别是什么? 关于这些问题&#xff0c;在之前的一篇**介绍 lerna** 的文章中已经详细介绍过&#xff0c;感兴趣的同学可以再回顾下。 简而…

nacos学习积累

官方文档&#xff1a;https://nacos.io/zh-cn/docs/quick-start.html 1 注册中心简介 注册中心对比和选型&#xff1a;Zookeeper、Eureka、Nacos、Consul和ETCD 如果消费者直接连接的提供者。这样做的问题是&#xff0c;若提供者出现宕机&#xff0c;或消费者存在高并发情况&…

SSRF漏洞

前言 作者简介&#xff1a;不知名白帽&#xff0c;网络安全学习者。 博客主页&#xff1a;不知名白帽的博客_CSDN博客-网络安全,CTF,内网渗透领域博主 网络安全交流社区&#xff1a;https://bbs.csdn.net/forums/angluoanquan 目录 SSRF漏洞原理 产生CSRF的函数 SSRF中常见手…

MySQL原理探索——27 主库出问题了,从库怎么办

在前面的第24、25和26篇文章中&#xff0c;介绍了 MySQL 主备复制的基础结构&#xff0c;但这些都是一主一备的结构。 大多数的互联网应用场景都是读多写少&#xff0c;因此你负责的业务&#xff0c;在发展过程中很可能先会遇到读性能的问题。而在数据库层解决读性能问题&#…

Atlassian Bamboo Enterprise Crack

Atlassian Bamboo Enterprise Crack Bamboo Server是专业组织如持续集成、安装和运输的选择。 从代码到安装的连续运输。 在一个工作流中集中发布自动生成、测试和发布。 构建&#xff1a;专注于编码&#xff0c;依靠Bamboo作为自己的CI&#xff0c;构建一个主机!创建多阶段构建…

3.8.cuda运行时API-使用cuda核函数加速yolov5后处理

目录 前言1. Yolov5后处理2. 后处理案例2.1 cpu_decode2.2 gpu_decode 总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习精简…

【*2200线段树Pushup】CF1567 E

Problem - E - Codeforces 题意&#xff1a; 思路&#xff1a; 维护这些信息即可 Code&#xff1a; #include <bits/stdc.h>#define int long longusing namespace std;const int mxn2e510; const int mxe2e510; const int mod1e97; const int Inf1e18;struct info{in…

【C语言】gcc编译时报错 fatal error: stdio.h: 没有那个文件或目录

零、问题 在Ubuntu20.04.6中使用GCC编译一个HelloWorld代码时遇到如下问题&#xff1a; 首先确认了&#xff0c;自己单词没有拼写错。 然后再检查GCC的版本&#xff0c;确实没问题&#xff1a; 我用的是Ubuntu20.04.6的版本。 壹、解决 没有标准的头文件需要安装build-es…

和鲸社区数据分析每周挑战【第九十七期:技术博客文本分析】

和鲸社区数据分析每周挑战【第九十七期&#xff1a;技术博客文本分析】 文章目录 和鲸社区数据分析每周挑战【第九十七期&#xff1a;技术博客文本分析】一、背景描述二、数据说明三、问题描述四、数据导入五、数据探索性分析六、对文章标题进行文本分类预测1、数据预处理2、逻…

C++万字自学笔记

[TOC] 一、 C基础 C的IDE有CLion、Visual Studio、DEV C、eclipse等等&#xff0c;这里使用CLion进行学习。 0. C初识 0.1 第一个C程序 编写一个C程序总共分为4个步骤 创建项目创建文件编写代码运行程序 #include <iostream>int main() {using namespace std;cout…

新手如何快速安装电脑监控软件?

越来越多的管理者选择使用电脑监控软件&#xff0c;许多新手不知道具体怎样安装&#xff0c;本期将为大家介绍下具体的安装流程。 电脑监控软件购买之后&#xff0c;会提供网址和账号密码&#xff0c;登录后需要先添加员工信息&#xff0c;有三种方法&#xff1a; &#xff0…

Android性能优化(bin启动优化)

我们平时会在android里面写个bin程序来干点活&#xff0c;但是有时候我们会发现很奇怪的现象&#xff0c;我明明很早就启动这个bin了&#xff0c;但是过了很久bin程序的main函数才被调用~。这个是为啥呢&#xff1f;主要有2个原因&#xff1a; 一.bin程序依赖的so库太多&#…

steam搬砖项目,csgo游戏搬砖熟练操作后,可以月入过万~

科思创业汇 大家好&#xff0c;这里是科思创业汇&#xff0c;一个轻资产创业孵化平台。赚钱的方式有很多种&#xff0c;我希望在科思创业汇能够给你带来最快乐的那一种&#xff01; 网上创业创造了一批赚钱的人&#xff0c;年收入从几十万到几百万不等&#xff0c;营业额从几…