百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

微服务架构下的服务关联图(微服务架构关键特征)

toyiye 2024-07-03 02:07 12 浏览 0 评论

在微服务架构下,服务之间的关系是非常复杂的,是一个典型的有向有环图,在一个中等规模的项目中,一般会有100多个服务,而大型项目中,则会有数百个服务。

假设我们有如下6个服务:

每个服务都指定了自己依赖的服务:

AaaSvc:

BbbSvc:

CccSvc:

DddSvc:

EeeSvc:

FffSvc:

我们如何把如上6个服务中跟服务AaaSvc相关的服务可视化呢?如下图所示:

要完成这样的服务关联图,需要如下几个步骤:

1、遍历指定项目下的所有服务,构造两个map,serviceToServiceMap 和 reverseServiceToServiceMap,存储所有服务的直接依赖和反向直接依赖。

 public static void runService(String projectId,
 Map<String, Set<String>> serviceToServiceMap,
 Map<String, Set<String>> reverseServiceToServiceMap){
 if(! (serviceToServiceMap instanceof ConcurrentHashMap) ){
 throw new RuntimeException("参数serviceToServiceMap必须是ConcurrentHashMap的实例");
 }
 if(! (reverseServiceToServiceMap instanceof ConcurrentHashMap) ){
 throw new RuntimeException("参数reverseServiceToServiceMap必须是ConcurrentHashMap的实例");
 }
 MetaServiceRepository metaServiceRepository = SpringContextUtils.getBean("metaServiceRepository");
 List<MetaService> services = metaServiceRepository.findByProjectId(projectId);
 services.parallelStream().filter(item->!item.getName().contains("Deprecate")).forEach(item->{
 List<DependencyService> dependencyServices = item.constructDependencyServices();
 String key = item.getName()+"("+item.getDescription()+")";
 if(dependencyServices != null){
 dependencyServices.parallelStream().filter(dep->!dep.getName().contains("Deprecate")).forEach(dependencyService->{
 String value = dependencyService.getName()+"("+dependencyService.getDescription()+")";
 serviceToServiceMap.putIfAbsent(key, Collections.newSetFromMap(new ConcurrentHashMap<>()));
 serviceToServiceMap.get(key).add(value);
 reverseServiceToServiceMap.putIfAbsent(value, Collections.newSetFromMap(new ConcurrentHashMap<>()));
 reverseServiceToServiceMap.get(value).add(key);
 });
 }
 });
 }

2、以服务AaaSvc为入口,利用直接依赖和反向直接依赖,构造服务依赖图和反向服务依赖图。

String name = metaService.getName()+"("+metaService.getDescription()+")";
Set<String> set = serviceToServiceMap.get(name);
ServiceDependencyGraph serviceDependencyGraph = new ServiceDependencyGraph(new HashMap<>(), name, set, serviceToServiceMap);
set = reverseServiceToServiceMap.get(name);
ServiceDependencyGraph reverseServiceDependencyGraph = new ServiceDependencyGraph(new HashMap<>(), name, set, reverseServiceToServiceMap);
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * 服务依赖图
 * @date 2017-09-2
 * @author 杨尚川
 */
public class ServiceDependencyGraph {
 private String name;
 private List<ServiceDependencyGraph> children = new ArrayList<>();
 public ServiceDependencyGraph(Map<String, AtomicInteger> stopServiceNames, String name, Set<String> set, Map<String, Set<String>> serviceToServiceMap){
 this.name = name;
 if(CollectionUtils.isNotEmpty(set)) {
 for (String item : set) {
 String key = name+"_"+item;
 stopServiceNames.putIfAbsent(key, new AtomicInteger());
 stopServiceNames.get(key).incrementAndGet();
 if(stopServiceNames.get(key).get()<10) {
 Set<String> sub = serviceToServiceMap.get(item);
 ServiceDependencyGraph serviceDependencyGraph = new ServiceDependencyGraph(stopServiceNames, item, sub, serviceToServiceMap);
 children.add(serviceDependencyGraph);
 }
 }
 }
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public List<ServiceDependencyGraph> getChildren() {
 return children;
 }
}

3、利用服务依赖图和反向服务依赖图,构造带有点和边描述信息的JSON结构:

List<Map<String, String>> nodes = new ArrayList<>();
addNode(serviceDependencyGraph, nodes);
addNode(reverseServiceDependencyGraph, nodes);
List<Map<String, Object>> edges = new ArrayList<>();
addEdge(reverseServiceToServiceMap, serviceDependencyGraph, edges, false);
addEdge(reverseServiceToServiceMap, reverseServiceDependencyGraph, edges, true);
Map<String, Object> graph = new HashMap<>();
graph.put("edges", edges);
graph.put("nodes", nodes);
private void addNode(String node, List<Map<String, String>> nodes){
 for(Map<String, String> item : nodes){
 if(node.equals(item.get("id"))){
 return;
 }
 }
 Map<String, String> nodeMap = new HashMap<>();
 nodeMap.put("id", node);
 nodeMap.put("name", node);
 nodes.add(nodeMap);
}
private void addNode(ServiceDependencyGraph serviceDependencyGraph, List<Map<String, String>> nodes){
 if(serviceDependencyGraph == null){
 return;
 }
 String node = serviceDependencyGraph.getName();
 addNode(node, nodes);
 if(serviceDependencyGraph.getChildren() != null){
 serviceDependencyGraph.getChildren().forEach(item->addNode(item, nodes));
 }
}
private void addEdge(Map<String, Set<String>> reverseServiceToServiceMap, ServiceDependencyGraph serviceDependencyGraph, List<Map<String, Object>> edges, boolean reverse){
 if(serviceDependencyGraph == null){
 return;
 }
 String source = serviceDependencyGraph.getName();
 serviceDependencyGraph.getChildren().forEach(target -> {
 boolean duplicate = false;
 Map<String, Object> map = new HashMap<>();
 if(reverse){
 String id = target.getName()+"-->"+source;
 for(Map<String, Object> item : edges){
 if(id.equals(item.get("id"))){
 duplicate = true;
 }
 }
 map.put("id", id);
 map.put("target", source);
 map.put("source", target.getName());
 map.put("directed", true);
 map.put("source_score", reverseServiceToServiceMap.get(target.getName()) == null ? 0 : reverseServiceToServiceMap.get(target.getName()).size());
 map.put("target_score", reverseServiceToServiceMap.get(source) == null ? 0 : reverseServiceToServiceMap.get(source).size());
 }else {
 String id = source+"-->"+target.getName();
 for(Map<String, Object> item : edges){
 if(id.equals(item.get("id"))){
 duplicate = true;
 }
 }
 map.put("id", id);
 map.put("source", source);
 map.put("target", target.getName());
 map.put("directed", true);
 map.put("source_score", reverseServiceToServiceMap.get(source) == null ? 0 : reverseServiceToServiceMap.get(source).size());
 map.put("target_score", reverseServiceToServiceMap.get(target.getName()) == null ? 0 : reverseServiceToServiceMap.get(target.getName()).size());
 }
 if(!duplicate) {
 edges.add(map);
 }
 addEdge(reverseServiceToServiceMap, target, edges, reverse);
 });
}

生成的JSON结构如下所示:

{

"nodes":[

{

"globalWeight":4,

"name":"AaaSvc(服务Aaa)"

},

{

"globalWeight":4,

"name":"CccSvc(服务Ccc)"

},

{

"globalWeight":5,

"name":"DddSvc(服务Ddd)"

},

{

"globalWeight":4,

"name":"EeeSvc(服务Eee)"

},

{

"globalWeight":4,

"name":"FffSvc(服务Fff)"

},

{

"globalWeight":3,

"name":"BbbSvc(服务Bbb)"

}

],

"edges":[

{

"distance":8,

"source":0,

"target":1

},

{

"distance":8,

"source":1,

"target":0

},

{

"distance":9,

"source":0,

"target":2

},

{

"distance":9,

"source":2,

"target":3

},

{

"distance":9,

"source":3,

"target":2

},

{

"distance":9,

"source":2,

"target":4

},

{

"distance":8,

"source":4,

"target":3

},

{

"distance":8,

"source":3,

"target":4

},

{

"distance":9,

"source":4,

"target":2

},

{

"distance":7,

"source":0,

"target":5

},

{

"distance":7,

"source":5,

"target":1

},

{

"distance":7,

"source":1,

"target":5

}

]

}

4、使用d3-force对如上的JSON进行展示:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="utf-8">
 <title>测试项目_testDemo -- 服务Aaa_AaaSvc -- 服务关联图</title>
 <style>
 .node {
 stroke: #fff;
 stroke-width: 1.5px;
 }
 .node-active{
 stroke: #555;
 stroke-width: 1.5px;
 }
 .link {
 stroke: #555;
 stroke-opacity: .3;
 }
 .link-active {
 stroke-opacity: 1;
 stroke-width: 1.5px;
 }
 .overlay {
 fill: none;
 pointer-events: all;
 }
 #map{
 height:100%;
 }
 #ex1Slider .slider-selection {
 background: #BABABA;
 }
 #ex2Slider .slider-selection {
 background: #BABABA;
 }
 #ex3Slider .slider-selection {
 background: #BABABA;
 }
 #ex4Slider .slider-selection {
 background: #BABABA;
 }
 #ex5Slider .slider-selection {
 background: #BABABA;
 }
 </style>
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/10.2.0/css/bootstrap-slider.min.css"/>
 <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
 <script src="https://code.jquery.com/jquery-2.2.4.min.js" charset="utf-8"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/10.2.0/bootstrap-slider.min.js" charset="utf-8"></script>
 <script type="text/javascript">
 var gravity = 0.03;
 var linkDistanceMultiFactor = 10;
 var linkDistance = 100;
 var circleRadiusMultiFactor = 2;
 var circleRadius = 4;
 var arrowhead = 10;
 var graph;
 function load(graph){
 console.log("loading ...... ");
 console.log("gravity: "+gravity);
 console.log("linkDistanceMultiFactor: "+linkDistanceMultiFactor);
 console.log("linkDistance: "+linkDistance);
 console.log("circleRadiusMultiFactor: "+circleRadiusMultiFactor);
 console.log("circleRadius: "+circleRadius);
 var margin = {top: -5, right: -5, bottom: -5, left: -5};
 var width = $(window).width() - margin.left - margin.right,
 height = $(window).height() - margin.top - margin.bottom;
 var color = d3.scale.category10();
 var force = d3.layout.force()
 .charge(-200)
 .linkDistance(function(d) {return (d.source.weight+d.target.weight)*linkDistanceMultiFactor+linkDistance;})
 .size([width + margin.left + margin.right, height + margin.top + margin.bottom])
 .charge([-500])
 .theta(0.1)
 .gravity(gravity);
 var zoom = d3.behavior.zoom()
 .scaleExtent([0.3, 10])
 .on("zoom", zoomed);
 var drag = d3.behavior.drag()
 .origin(function(d) { return d; })
 .on("dragstart", dragstarted)
 .on("drag", dragged)
 .on("dragend", dragended);
 var svg = d3.select("#map").append("svg")
 .attr("width", width + margin.left + margin.right)
 .attr("height", height + margin.top + margin.bottom)
 .append("g")
 .attr("transform", "translate(" + margin.left + "," + margin.right + ")")
 .call(zoom);
 var rect = svg.append("rect")
 .attr("width", width)
 .attr("height", height)
 .style("fill", "none")
 .style("pointer-events", "all");
 var container = svg.append("g");
 force
 .nodes(graph.nodes)
 .links(graph.edges)
 .start();
 var link = container.append("g")
 .attr("class", "links")
 .selectAll(".link")
 .data(force.links())
 .enter()
 .append("line")
 .attr("class", "link")
 .attr('marker-end', function(d,i) { return d.rpm === 0 ? '' : 'url(#arrowhead)'})
 .style("stroke-width", function(d) { return Math.sqrt(d.value); });
 var node = container.append("g")
 .attr("class", "nodes")
 .selectAll(".node")
 .data(force.nodes())
 .enter().append("g")
 .attr("class", "node")
 .attr("cx", function(d) { return d.x; })
 .attr("cy", function(d) { return d.y; })
 .call(drag);
 var nodeLabel = container.selectAll(".nodelabel")
 .data(force.nodes())
 .enter()
 .append("text")
 .style("pointer-events", "none")
 .attr({"x":function(d){return d.x;},
 "y":function(d){return d.y;},
 "class":"nodelabel",
 "stroke":"#666"})
 .text(function(d){return d.name;});
 var linkPath = container.selectAll(".linkpath")
 .data(force.links())
 .enter()
 .append('path')
 .attr({'d': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y},
 'class':'linkpath',
 'fill-opacity':0,
 'stroke-opacity':0,
 'fill':'blue',
 'stroke':'red',
 'id':function(d,i) {return 'linkpath'+i}})
 .style("pointer-events", "none");
 var linkLabel = container.selectAll(".linklabel")
 .data(force.links())
 .enter()
 .append('text')
 .style("pointer-events", "none")
 .attr({'class':'linklabel',
 'id':function(d,i){return 'linklabel'+i},
 'dx':90,
 'dy':-5,
 'font-size':12,
 'fill':'#666'});
 linkLabel.append('textPath')
 .attr('xlink:href',function(d,i) {return '#linkpath'+i})
 .style("pointer-events", "none")
 .text(function(d,i){ return d.rpm > 0 ? d.rpm + ' req/min' : ""; });
 container.append('defs').append('marker')
 .attr({'id':'arrowhead',
 'viewBox':'-0 -5 10 10',
 'refX':25,
 'refY':0,
 'orient':'auto',
 'markerWidth':arrowhead,
 'markerHeight':arrowhead,
 'xoverflow':'visible'})
 .append('svg:path')
 .attr('d', 'M 0,-5 L 10 ,0 L 0,5')
 .attr('fill', '#000')
 .attr('stroke','#000');
 node.append("circle")
 .attr("r", function(d) { return d.weight*circleRadiusMultiFactor + circleRadius; })
 .style("fill", function(d,i) { return "#e7ba52"; });
 force.on("tick", function() {
 link.attr("x1", function(d) { return d.source.x; })
 .attr("y1", function(d) { return d.source.y; })
 .attr("x2", function(d) { return d.target.x; })
 .attr("y2", function(d) { return d.target.y; });
 node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
 nodeLabel.attr("x", function(d) { return d.x; })
 .attr("y", function(d) { return d.y; });
 linkPath.attr('d', function(d) {
 var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y;
 return path;
 });
 linkLabel.attr('transform',function(d){
 if (d.target.x < d.source.x) {
 bbox = this.getBBox();
 rx = bbox.x+bbox.width/2;
 ry = bbox.y+bbox.height/2;
 return 'rotate(180 ' + rx + ' ' + ry + ')';
 }
 else {
 return 'rotate(0)';
 }
 });
 });
 var linkedByIndex = {};
 force.links().forEach(function(d) {
 linkedByIndex[d.source.index + "," + d.target.index] = 1;
 });
 function isConnected(a, b) {
 return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index];
 }
 node.on("mouseover", function(d) {
 node.classed("node-active", function(o) {
 thisOpacity = isConnected(d, o) ? true : false;
 this.setAttribute('fill-opacity', thisOpacity);
 return thisOpacity;
 });
 link.classed("link-active", function(o) {
 return o.source === d || o.target === d ? true : false;
 });
 d3.select(this).classed("node-active", true);
 d3.select(this).select("circle").transition()
 .duration(750)
 .attr("r", function(d) { return (d.weight*circleRadiusMultiFactor + circleRadius)*1.5; });
 })
 .on("mouseout", function(d){
 node.classed("node-active", false);
 link.classed("link-active", false);
 d3.select(this).select("circle").transition()
 .duration(750)
 .attr("r", function(d) { return d.weight*circleRadiusMultiFactor + circleRadius; });
 });
 function dottype(d) {
 d.x = +d.x;
 d.y = +d.y;
 return d;
 }
 function zoomed() {
 container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
 }
 function dragstarted(d) {
 d3.event.sourceEvent.stopPropagation();
 d3.select(this).classed("dragging", true);
 force.start();
 }
 function dragged(d) {
 d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
 }
 function dragended(d) {
 d3.select(this).classed("dragging", false);
 }
 }
 $(function() {
 $('#ex1').slider({
 tooltip: 'hide'
 }).on('slideStop', function (e) {
 //获取新值
 gravity = e.value/100;
 load(graph);
 });
 $('#ex2').slider({
 tooltip: 'hide'
 }).on('slideStop', function (e) {
 //获取新值
 linkDistance = e.value;
 load(graph);
 });
 $('#ex3').slider({
 tooltip: 'hide'
 }).on('slideStop', function (e) {
 //获取新值
 linkDistanceMultiFactor = e.value;
 load(graph);
 });
 $('#ex4').slider({
 tooltip: 'hide'
 }).on('slideStop', function (e) {
 //获取新值
 circleRadius = e.value;
 load(graph);
 });
 $('#ex5').slider({
 tooltip: 'hide'
 }).on('slideStop', function (e) {
 //获取新值
 circleRadiusMultiFactor = e.value;
 load(graph);
 });
 $('#ex6').slider({
 tooltip: 'hide'
 }).on('slideStop', function (e) {
 //获取新值
 arrowhead = e.value;
 load(graph);
 });
 graph = JSON.parse('{"nodes":[{"globalWeight":4,"name":"AaaSvc(服务Aaa)"},{"globalWeight":4,"name":"CccSvc(服务Ccc)"},{"globalWeight":5,"name":"DddSvc(服务Ddd)"},{"globalWeight":4,"name":"EeeSvc(服务Eee)"},{"globalWeight":4,"name":"FffSvc(服务Fff)"},{"globalWeight":3,"name":"BbbSvc(服务Bbb)"}],"edges":[{"distance":8,"source":0,"target":1},{"distance":8,"source":1,"target":0},{"distance":9,"source":0,"target":2},{"distance":9,"source":2,"target":3},{"distance":9,"source":3,"target":2},{"distance":9,"source":2,"target":4},{"distance":8,"source":4,"target":3},{"distance":8,"source":3,"target":4},{"distance":9,"source":4,"target":2},{"distance":7,"source":0,"target":5},{"distance":7,"source":5,"target":1},{"distance":7,"source":1,"target":5}]}');
 load(graph);
 });
 </script>
</head>
<body>
<h3>测试项目_testDemo -- 服务Aaa_AaaSvc -- 服务关联图</h3>
<table>
 <tr>
 <td width="50"></td>
 <td>
 <span class="item">
 重力调节
 <input id="ex1" type="text" data-slider-id="ex1Slider" class="span2" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="0.03"/>
 </span>
 </td>
 <td width="50"></td>
 <td>
 <span class="item">
 最短边长
 <input id="ex2" type="text" data-slider-id="ex2Slider" class="span2" data-slider-min="0" data-slider-max="250" data-slider-step="1" data-slider-value="100"/>
 </span>
 </td>
 <td width="50"></td>
 <td>
 <span class="item">
 边长放大倍数
 <input id="ex3" type="text" data-slider-id="ex3Slider" class="span2" data-slider-min="0" data-slider-max="50" data-slider-step="1" data-slider-value="10"/>
 </span>
 </td>
 <td width="50"></td>
 <td>
 <span class="item">
 最短半径
 <input id="ex4" type="text" data-slider-id="ex4Slider" class="span2" data-slider-min="0" data-slider-max="10" data-slider-step="1" data-slider-value="4"/>
 </span>
 </td>
 <td width="50"></td>
 <td>
 <span class="item">
 半径放大倍数
 <input id="ex5" type="text" data-slider-id="ex5Slider" class="span2" data-slider-min="0" data-slider-max="5" data-slider-step="1" data-slider-value="2"/>
 </span>
 </td>
 <td width="50"></td>
 <td>
 <span class="item">
 箭头大小
 <input id="ex6" type="text" data-slider-id="ex5Slider" class="span2" data-slider-min="10" data-slider-max="30" data-slider-step="1" data-slider-value="10"/>
 </span>
 </td>
 </tr>
</table>
<div id="map"></div>
</body>
</html>

最终运行的效果如下:

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码