首页 文章 精选 留言 我的

精选列表

搜索[快速],共10000篇文章
优秀的个人博客,低调大师

IoT+Tablestore快速构建智能售货机应用

一、 背景介绍 近年来,物联网(IoT)技术发生了巨大的变化。NB-IoT、LoRa等技术的产生解决了网络覆盖、设备功耗成本问题;尤其是最近5G技术的商用,意味着支持海量设备连接进行高质量数据通讯即将有坚实的基础设施支持。在过去的二十年,人的各种活动如社交、购物等产生了海量的数据。据分析,每个人身边至少有1000个左右的物体,如果将比人的数量多的多的各种物体、设备都接入网络,可以预见到,在不远的未来社会将会产生比现在多无数倍的各种数据,数据量将呈现爆炸式增长。那么问题来了,如何接入这些物体设备、如何将这些采集到的海量数据进行存储分析? 以零售行业为例,基于IoT技术的自助饮料机、福袋机、口红机、自助彩票机、自助售药机等遍地开花,广泛分布在商场、工厂、学校、写字楼、火车站、机场等区域。据最新的调查数据,日

优秀的个人博客,低调大师

借助URLOS快速安装jenkins-持续集成工具

环境需求 最低硬件配置:1核CPU,1G内存(1+1)提示:如果你的应用较多,而主机节点的硬件配置较低,建议在部署节点时开通虚拟虚拟内存; 生产环境建议使用2G或以上内存; 推荐安装系统:Ubuntu-16.04、Ubuntu-18.04、CentOS7.X、Debian9X的64位的纯净的操作系统; URLOS安装 curl -LO www.urlos.com/iu && sh iu Jenkins安装流程 1.在应用市场中搜索“jenkins”并安装,如下图: 2.填写服务名称、选择运行节点、服务端口、选择智能部署 然后点击“提交”按钮,等待部署完成; 3. jenkins初始化向导 访问http://IP:8080(其中的IP是你的服务器的IP) 这里需要填写初始密码,创建服务完成后,可在当前服务的文件管理中找到密码文件,路径为jenkins_home/secrets/initialAdminPassword 找到密码,将密码填入后继续步骤: 选择安装社区流行的插件还是自定义安装插件 我们选择自定义安装插件: 可以看到已经默认选择了一些插件,我们可以选择自己需要的来安装。 选择“install”进行下一步: 创建管理员账户: 登录后: jenkins的详细使用方法请查考官方文档: https://jenkins.io/zh/doc/

优秀的个人博客,低调大师

日志服务数据加工:快速开始(SLB日志加工实战)

背景 这里我们拿一个Logstore中的网关数据(阿里云SLB日志)举例,对其数据进行加工并分发到不同的Logstore中。 数据 源logstore(slb-log)的数据内容是一个阿里云SLB的网络日志,格式样例如下: __source__: log_service __tag__:__receive_time__: 1559799897 __topic__: body_bytes_sent: 740 client_ip: 1.2.3.4 host: m.abcd.com http_host: m.abcd.com http_referer: - http_x_forwarded_for: - http_x_real_ip: - read_request_time: 0 request_length: 0 r

优秀的个人博客,低调大师

docker-compose快速搭建 Prometheus+Grafana监控系统

一、说明Prometheus负责收集数据,Grafana负责展示数据。其中采用Prometheus 中的 Exporter含:1)Node Exporter,负责收集 host 硬件和操作系统数据。它将以容器方式运行在所有 host 上。2)cAdvisor,负责收集容器数据。它将以容器方式运行在所有 host 上。3)Alertmanager,负责告警。它将以容器方式运行在所有 host 上。完整Exporter列表请参考:https://prometheus.io/docs/instrumenting/exporters/ 二、安装docker,docker-compose2.1 安装docker先安装一个64位的Linux主机,其内核必须高于3.10,内存不低于1GB。在该主机上安装Docker。 # 安装依赖包 yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加Docker软件包源 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装Docker CE yum install docker-ce -y # 启动 systemctl start docker # 开机启动 systemctl enable docker # 查看Docker信息 docker info 2.2 安装docker-compose curl -L https://github.com/docker/compose/releases/download/1.23.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose 三、添加配置文件 mkdir -p /usr/local/src/config cd /usr/local/src/config 2.1 添加prometheus.yml配置文件,vim prometheus.yml # my global config global: scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. # scrape_timeout is set to the global default (10s). # Alertmanager configuration alerting: alertmanagers: - static_configs: - targets: ['192.168.159.129:9093'] # - alertmanager:9093 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. rule_files: - "node_down.yml" # - "first_rules.yml" # - "second_rules.yml" # A scrape configuration containing exactly one endpoint to scrape: # Here it's Prometheus itself. scrape_configs: # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config. - job_name: 'prometheus' static_configs: - targets: ['192.168.159.129:9090'] - job_name: 'cadvisor' static_configs: - targets: ['192.168.159.129:8080'] - job_name: 'node' scrape_interval: 8s static_configs: - targets: ['192.168.159.129:9100'] 2.2 添加邮件告警配置文件添加配置文件alertmanager.yml,配置收发邮件邮箱vim alertmanager.yml global: smtp_smarthost: 'smtp.163.com:25' #163服务器 smtp_from: 'tsiyuetian@163.com' #发邮件的邮箱 smtp_auth_username: 'tsiyuetian@163.com' #发邮件的邮箱用户名,也就是你的邮箱 smtp_auth_password: 'TPP***' #发邮件的邮箱密码 smtp_require_tls: false #不进行tls验证 route: group_by: ['alertname'] group_wait: 10s group_interval: 10s repeat_interval: 10m receiver: live-monitoring receivers: - name: 'live-monitoring' email_configs: - to: '1933306137@qq.com' #收邮件的邮箱 2.3 添加报警规则添加一个node_down.yml为 prometheus targets 监控vim node_down.yml groups: - name: node_down rules: - alert: InstanceDown expr: up == 0 for: 1m labels: user: test annotations: summary: "Instance {{ $labels.instance }} down" description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minutes." 四、编写docker-composevim docker-compose-monitor.yml version: '2' networks: monitor: driver: bridge services: prometheus: image: prom/prometheus container_name: prometheus hostname: prometheus restart: always volumes: - /usr/local/src/config/prometheus.yml:/etc/prometheus/prometheus.yml - /usr/local/src/config/node_down.yml:/etc/prometheus/node_down.yml ports: - "9090:9090" networks: - monitor alertmanager: image: prom/alertmanager container_name: alertmanager hostname: alertmanager restart: always volumes: - /usr/local/src/config/alertmanager.yml:/etc/alertmanager/alertmanager.yml ports: - "9093:9093" networks: - monitor grafana: image: grafana/grafana container_name: grafana hostname: grafana restart: always ports: - "3000:3000" networks: - monitor node-exporter: image: quay.io/prometheus/node-exporter container_name: node-exporter hostname: node-exporter restart: always ports: - "9100:9100" networks: - monitor cadvisor: image: google/cadvisor:latest container_name: cadvisor hostname: cadvisor restart: always volumes: - /:/rootfs:ro - /var/run:/var/run:rw - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro ports: - "8080:8080" networks: - monitor 五、启动docker-compose #启动容器: docker-compose -f /usr/local/src/config/docker-compose-monitor.yml up -d #删除容器: docker-compose -f /usr/local/src/config/docker-compose-monitor.yml down #重启容器: docker restart id 容器启动如下: prometheus targets界面如下: 备注:如果State为Down,应该是防火墙问题,参考下面防火墙配置。 prometheus graph界面如下: 备注:如果没有数据,同步下时间。 六、防火墙配置6.1 关闭selinux setenforce 0 vim /etc/sysconfig/selinux 6.2 配置iptables #删除自带防火墙 systemctl stop firewalld.service systemctl disable firewalld.service #安装iptables yum install -y iptables-services #配置 vim /etc/sysconfig/iptables *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [24:11326] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 9090 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 8080 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 3000 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 9093 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 9100 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT #启动 systemctl restart iptables.service systemctl enable iptables.service 七、配置Grafana7.1 添加Prometheus数据源 2 配置dashboards 说明:可以用自带模板,也可以去https://grafana.com/dashboards,下载对应的模板。 7.3 查看数据我从网页下载了docker相关的模板:Docker and system monitoring,893输入893,就会加载出下面的信息 导入后去首页查看数据 八、附录:单独命令启动各容器 #启动prometheus docker run -d -p 9090:9090 --name=prometheus \ -v /usr/local/src/config/prometheus.yml:/etc/prometheus/prometheus.yml \ -v /usr/local/src/config/node_down.yml:/etc/prometheus/node_down.yml \ prom/prometheus # 启动grafana docker run -d -p 3000:3000 --name=grafana grafana/grafana #启动alertmanager容器 docker run -d -p 9093:9093 -v /usr/local/src/config/config.yml:/etc/alertmanager/config.yml --name alertmanager prom/alertmanager #启动node exporter docker run -d \ -p 9100:9100 \ -v "/:/host:ro,rslave" \ --name=node_exporter \ quay.io/prometheus/node-exporter \ --path.rootfs /host #启动cadvisor docker run \ --volume=/:/rootfs:ro \ --volume=/var/run:/var/run:rw \ --volume=/sys:/sys:ro \ --volume=/var/lib/docker/:/var/lib/docker:ro \ --publish=8080:8080 \ --detach=true \ --name=cadvisor \ google/cadvisor:latest

优秀的个人博客,低调大师

Spring Boot 2.x基础教程:快速入门

简介 在您第1次接触和学习Spring框架的时候,是否因为其繁杂的配置而退却了?在你第n次使用Spring框架的时候,是否觉得一堆反复黏贴的配置有一些厌烦?那么您就不妨来试试使用Spring Boot来让你更易上手,更简单快捷地构建Spring应用! Spring Boot让我们的Spring应用变的更轻量化。我们不必像以前那样繁琐的构建项目、打包应用、部署到Tomcat等应用服务器中来运行我们的业务服务。通过Spring Boot实现的服务,只需要依靠一个Java类,把它打包成jar,并通过java -jar命令就可以运行起来。这一切相较于传统Spring应用来说,已经变得非常的轻便、简单。 总结一下Spring Boot的主要优点: 为所有Spring开发者更快的入门 开箱即用,提供各种默认配置来简化项目配置 内嵌式容器简化Web项目 没有

优秀的个人博客,低调大师

ALiyun LOG Go Consumer Library 快速入门及原理剖析

前言 Aliyun LOG Go Consumer Library是用go语言编写的协同消费库,主要处理多个消费者同时消费logstore时自动分配shard问题。 其中消费组会自动处理shard的负载均衡,消费者failover等逻辑,这部分说明会在本篇下面的篇幅中进行详细的介绍。用户只需专注在自己业务逻辑上,而无需关心shard分配、CheckPoint、Failover等事宜。 功能特点 Aliyun LOG Go Consumer Library是一个使用简单以及高度可配置的go类库,它有如下功能特点: 线程安全 - consumer 内所有的方法以及暴露的接口都是线程安全的。 异步拉取 - 调用consumer的拉取日志接口,会把当前拉取任务新开一个groutine中去执行,不会阻塞主groutine的执行。 自动重试 - 对程序运行当中出现的可重试的异常,consumer会自动重试,重试过程不会导致数据的重复消费。 优雅关闭 - 调用关闭程序接口,consumer会等待当前所有已开出的groutine任务结束后在结束主程序,保证下次开始不会重复消费数据。 本地调试 - 可通过配置支持将日志内容输出到本地或控制台,并支持轮转、日志数、轮转大小设置。 高性能 - 基于go语言的特性,go的goroutine在并发多任务处理能力上有着与生俱来的优势。所以consumer 对每一个获得的可消费分区都会开启一个单独的groutine去执行消费任务,相对比直接使用cpu线程处理,对系统性能消耗更小,效率更高。 使用简单 - 在整个使用过程中,不会产生数据丢失,以及重复,用户只需要配置简单配置文件,创建消费者实例,写处理日志的代码就可以,用户只需要把重心放到自己的消费逻辑上面即可,不需关心消费断点保存,以及错误重试等问题。 消费组原理剖析 为了带您更好的理解 go版本的consumer library,本篇讲介绍consumer library的实现原理,在了解原理之前,进行简单的术语简介。 在consumer library 中主要有4个概念,分别是consumer group,consumer,heartbeat和checkpoint,它们之间关系如下(图片出处:https://github.com/aliyun/loghub_consumer_library): ​ consumer group: ​ 是logstore的子资源,拥有相同consumer group 名字的消费者共同消费同一个logstore的所有数据,这些消费者之间不会重复消费数据,一个logstore下面可以最多创建5个consumer group,不可以重名,同一个logstore下面的consumer group之间消费数据不会互相影响。consumer group有两个很重要的属性: { "order":boolean, "timeout": integer } order属性:在单个shard中是否按顺序消费。true:表示在单个shard中按顺序消费。shard分裂后,先消费原shard数据,然后并列消费两个新shard的数据。false:表示不按顺序消费。 timeout属性:表示consumer group中消费者的超时时间,单位是秒,当一个消费者汇报心跳的时间间隔超过timeout,会被认为已经超时,服务端认为这个consumer此时已经下线了。 consumer: ​ 消费者,每个consumer上会被分配若干个shard,consumer的职责就是要消费这些shard上的数据,同一个consumer group中的consumer必须不重名。 heartbeat: ​ 消费者心跳,consumer需要定期向服务端汇报一个心跳包,用于表明自己还处于存活状态。 checkpoint: ​ 消费者定期将分配给自己的shard消费到的位置保存到服务端,这样当这个shard被分配给其它消费者时,从服务端可以获取shard的消费断点,接着从断点继续消费数据。 自动负载均衡与自动处理消费者failover 如上图,当前有两个消费者A和B ,分别持有1,2,3,4个分区进行消费,消费者A被取消或者因为其他原因停止消费,服务端会自动将A消费者持有的1,2分区分配给B消费者进行消费。如下图 那么服务端是如何实现自动负载均衡的呢,还回到当消费者A,B共同持有4个分区的情况下,此时,如果新的消费者C注册加入到消费组当中来,服务端消费组会将logstore下所有的可消费分区对所有的消费者进行平均分配,如下图,从而在服务端自动的去实现负载均衡,用户即可不用关心消费者之间的负载均衡关系。 Consumer Lib实现原理 1.当调用一个消费者实例的run方法启动的时候,会开启一个心跳线程去给服务端发送当前心跳信息,同时主线程继续执行,检查是否获得可消费分区。 2.Heartbeat心跳线程会汇报当前消费者持有的消费分区到服务端,服务端会分配给消费者可消费的分区ID。 3.主线程会遍历从心跳线程获得的所有可消费分区,同时会将持有的消费分区汇报给心跳线程,每一个可消费分区都会调用consume方法。 4.每一个消费分区中的consume方法(如上图)只负责更新任务状态,调用会立刻返回,具体的消费任务会开启单独的groutine去执行,每个分区都会维护自己的一个任务状态机,不同分区间任务状态不会相互影响。 5.每次消费任务完成,会调用flushCheck方法,检查当前时间相对上次更新检查点时间是否超过60秒,超过就刷新当前检查点到服务端。如果执行退出程序函数,会立即将当前消费者的检查点保存到服务端。 6.如果执行任务中发生可重试异常,会在任务groutine中重试该任务,并且会在每一次遍历中跳过当前正在执行的任务groutine,不会造成任务的重复开启。 使用步骤 1.源码下载 请先克隆代码到自己的GOPATH路径下(源码地址:aliyun-go-consumer-library),项目使用了vendor工具管理第三方依赖包,所以克隆下来项目以后无需安装任何第三方工具包。 2.配置LogHubConfig LogHubConfig是提供给用户的配置类,用于配置消费策略,您可以根据不同的需求设定不同的值,各参数含义如文章尾部图示 LogHubConfig详细配置 3.覆写消费逻辑 func process(shardId int, logGroupList *sls.LogGroupList) string { for _, logGroup := range logGroupList.LogGroups { err := client.PutLogs(option.Project, "copy-logstore", logGroup) if err != nil { fmt.Println(err) } } fmt.Println("shardId %v processing works sucess", shardId) return "" // 不需要重置检查点情况下,请返回空字符串,如需要重置检查点,请返回需要重置的检查点游标。 } 在实际消费当中,您只需要根据自己的需要重新覆写消费函数process 即可,上图只是一个简单的demo,将consumer获取到的日志进行了打印处理,注意,该函数参数和返回值不可改变,否则会导致消费失败。 4.创建消费者并开始消费 // option是LogHubConfig的实例 consumerWorker := consumerLibrary.InitConsumerWorker(option, process) // 调用Start方法开始消费 consumerWorker.Start() 调用InitConsumerWorkwer方法,将配置实例对象和消费函数传递到参数中生成消费者实例对象,调用Start方法进行消费。 5.关闭消费者 ch:=make(chan os.Signal) //将os信号值作为信道 signal.Notify(ch) consumerWorker.Start() if _, ok := <-ch; ok { // 当获取到os停止信号以后,例如ctrl+c触发的os信号,会调用消费者退出方法进行退出。 consumerWorker.StopAndWait() } 上图中的例子通过go的信道做了os信号的监听,当监听到用户触发了os退出信号以后,调用StopAndWait()方法进行退出,用户可以根据自己的需要设计自己的退出逻辑,只需要调用StopAndWait()即可。 Cosumer Lib使用进阶 1.消费失败处理办法 当您拉取到日志对日志进行消费处理,处理失败的时候,可以在process方法里面重新执行您的消费逻辑,不跳出process方法,当process方法不结束的情况下会一直处理当前未消费成功的数据,从而避免重复消费,以下为简单的样例: func process(shardId int, logGroupList *sls.LogGroupList) string { for { err := func(shardId int, logGroupList *sls.LogGroupList) error { // 在这个函数当中去写具体的消费逻辑,如果消费失败,返回自定义的error return nil }(shardId,logGroupList) if err != nil{ // 当捕获到消费失败的error以后只要重复继续执行消费逻辑即可,不要跳出process方法。 continue }else{ // 消费成功的话,跳出循环,process方法执行完毕,会继续拉取数据进行下次消费。 break } } return "" // 不需要重置检查点情况下,请返回空字符串,如需要重置检查点,请返回需要重置的检查点游标。 } 2.重置Checkpoint 在补数据或重复计算等场景中,需要将某个ConsumerGroup点位设置为某一个时间点,使当前消费组能够从新位置开始消费。可通过以下三种方式完成。 (a.) 删除消费组。 停止消费程序,并在控制台删除消费组。 修改代码,使用指定时间点消费,重新启动程序。 (b.) 通过SDK将当前消费组重置到某一个时间点。 停止消费程序。 使用SDK修改位点,重新启动消费程序。具体示例如下图所示: func updateCheckPoint(){ err := client.UpdateCheckpoint(project,logstore,"ConsumerGroupName","consumerName",1,"youCheckPoint",true) if err != nil{ fmt.Println(err) } } (c.) 在process消费函数中返回需要重置的检查点。 为了满足在用户可以在不停止程序的情况下重置检查点的需求,提供了在process消费函数中充值检查点的方法,只要将重置的检查点作为返回值返回就可以,如果不需要只需要返回空字符串即可。 下图的所示例的代码设置了一个触发时间,当程序时间等于触发时间的时候,会重置检查点为开始的游标,进行分区的重新消费。 func process(shardId int,logGroupList *sls.LogGroupList) string { futureTime := 18888888 // 例如设置一个未来的Unix时间戳 if time.Now().Unix() == futureTime { // 当程序运行的当时时间等于设置的未来时间,返回需要重置的检查点 cursur, err := client.GetCursor(project,logstore,shardId,"begin") if err != nil { fmt.Println(err) } // 返回当前分区开始游标,就会重置消费位置为开始位置,进行当前分区的从新消费。 return cursor // 需要注意,如果每次消费完都返回指定游标的话,则每一次都会重置下次消费位置为返回的游标。 } return "" } 下图为LogHubConfig详细配置参数。 参数 类型 能否为空 描述 Endpoint String 否 服务入口,关于如何确定project对应的服务入口可参考文章服务入口。 AccessKeyID String 否 账户的AK id。 AccessKeySecret String 否 账户的AK 密钥。 Project String 否 将要消费的项目名称。 Logstore String 否 将要消费的项目下的日志库的名字。 ConsumerGroupName String 否 消费组的名称。 ConsumerName String 否 消费者的名称。注意:同一个消费组下面的消费者名称不能重复,不同消费组之间互不影响。 CursorPosition String 否 开始消费的时间点,该参数只在第一次创建消费组的时候使用,当再次启动消费组进行消费的时候会从上次消费到的断点进行继续消费,该参数可选的参数有3个: “BEGIN_CURSOR”,"END_CURSOR","SPECIAL_TIMER_CURSOR",分别为开始位置,结束位置,和自定义起始位置。 HeartbeatIntervalInSecond Int 是 向服务端发送的心跳间隔,默认值为20秒,如果超过设定值的3倍没有向服务端汇报心跳,服务端就任务该分区已经掉线会给该分区重新分配消费者。建议使用默认值。 DataFetchInterval Int64 是 从服务端拉取日志时间间隔,默认值是2秒,不能把这个值设置小于1s。 MaxFetchLogGroupCount Int 是 从服务端一次拉取日志组数量,日志组可参考内容日志组,默认值是1000,其取值范围是1-1000。 CursorStartTime Int64 否 自定义日志消费时间点,只有当CursorPosition设置为"SPECIAL_TIMER_CURSOR"时,该参数才能使用,参数为unix时间戳。时间单位为秒。 InOrder Bool 否 是否按序消费,默认为false。 AllowLogLevel String 否 设置日志输出级别,默认值是Info,consumer中一共有4种日志输出级别,分别为debug,info,warn和error。 LogFileName String 否 日志文件输出路径,不设置的话默认输出到stdout。 IsJsonType Bool 否 是否格式化文件输出格式,默认为false。 LogMaxSize Int 否 单个日志存储数量,默认为10M。 LogMaxBackups Int 否 日志轮转数量,默认为10。 LogCompass Bool 否 是否使用gzip 压缩日志,默认为false。 应用实例 为了进一步减少学习成本,我们为您准备了 Aliyun LOG Go Consumer Library 应用实例。示例中包含了consumer 从创建到关闭的全部流程。

优秀的个人博客,低调大师

实战:基于Spring Boot快速开发RESTful风格API接口

写在前面的话 这篇文章计划是在过年期间完成的,示例代码都写好了,结果亲戚来我家做客,文章没来得及写。已经很久没有更新文章了,小伙伴们,有没有想我啊。言归正传,下面开始,今天的话题。 目标 写一套符合规范,并且具有RESTful风格的API接口。 假定 你已会使用Spring Boot 2.x。 你已会使用Gradle构建Spring Boot工程。 你已会基于Spring Boot编写API接口。 你已会使用接口调试工具。 如果你还不会使用Spring Boot写接口,建议先看一下这篇文章 : 用Spring Boot开发API接口 步骤 1、基于Gradle构建Spring Boot示例项目。 2、引入JavaLib。 3、编写接口代码。 4、测试接口。 引入JavaLib 测试版(SNAPSHOT),都会发布到 JitPack 上,所以,从这里拉取的,都会是最新的,但是需要配置仓库地址。 正式版(RELEASE),才会推送到 Maven中央。 UserModel 我们用UserModel来存放我们的数据,以便存取。我个人比较喜欢用bean的,如果你喜欢用Map,那也是可以的。不过需要注意的是,需要加@JsonInclude(JsonInclude.Include.NON_NULL) ,他的作用是,如果某个字段为空时,在返回的JSON中,则不显示,如果没有,将为 null。 完整代码如下: package com.fengwenyi.demojavalibresult.model; import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; /** * User Model * @author Wenyi Feng * @since 2019-02-05 */ @Data @Accessors(chain = true) @JsonInclude(JsonInclude.Include.NON_NULL) public class UserModel implements Serializable { private static final long serialVersionUID = -835481508750383832L; /** UID */ private String uid; /** Name */ private String name; /** Age */ private Integer age; } 编写接口返回码 这里我们使用 JavaLib 中result模块为我们提供的方法。只需要调用 BaseCodeMsg.app(Integer, String)即可。这里我们只写几个用作示例,完整代码如下: package com.fengwenyi.demojavalibresult.util; import com.fengwenyi.javalib.result.BaseCodeMsg; /** * 自定义返回码以及描述信息 * @author Wenyi Feng * @since 2019-02-05 */ public class CodeMsg { /* user error ------------------------------------------------------------------------------------------------------------*/ /** 用户不存在 */ public static final BaseCodeMsg ERROR_USER_NOT_EXIST = BaseCodeMsg.app(10001, "User Not Exist"); /** UID不能为空 */ public static final BaseCodeMsg ERROR_USER_UID_NOT_NULL = BaseCodeMsg.app(10002, "User UID Must Not null"); } BaseCodeMsg 我们看一下源码: package com.fengwenyi.javalib.result; /** * (基类)返回码及描述信息 * @author Wenyi Feng * @since 2019-01-22 */ public class BaseCodeMsg { /** 返回码 */ private Integer code; /** 返回码描述 */ private String msg; /** * 无参数构造方法 */ private BaseCodeMsg() {} /** * 构造方法 * @param code * @param msg */ private BaseCodeMsg(Integer code, String msg) { this.code = code; this.msg = msg; } public static BaseCodeMsg app(Integer code, String msg) { return new BaseCodeMsg(code, msg); } /** * 返回码填充 * @param args 填充内容 * @return CodeMsgEnum */ public BaseCodeMsg fillArgs(Object ... args) { this.msg = String.format(this.msg, args); return this; } /** * 获取返回码 * @return 返回码 */ public Integer getCode() { return code; } /** * 获取描述信息 * @return 描述信息 */ public String getMsg() { return msg; } /** 成功 */ public static final BaseCodeMsg SUCCESS = BaseCodeMsg.app(0, "Success"); /** 失败 */ public static final BaseCodeMsg ERROR_INIT = BaseCodeMsg.app(-1, "Error"); } 成功的标识是:当 code=0 时。 另外,我们还为你提供了预留字符串替换的方法。比如你想告诉用户某个字段不合法,那么你可以这样: 第一步:在CodeMsg中添加 public static final BaseCodeMsg ERROR_PARAM_ILLEGAL = BaseCodeMsg.app(20001, "Request Param Illegal : %s"); 第二步:返回 /** * 测试参数错误 * @return {@link Result} */ @GetMapping("/test-param-error") public Result testParamError() { return Result.error(CodeMsg.ERROR_PARAM_ILLEGAL.fillArgs("account")); } 测试结果: 编写接口代码 接下来,开始编写我们的接口代码。 首先指明,我们的接口接收和返回的文档格式。 consumes = MediaType.APPLICATION_JSON_UTF8_VALUE produces = MediaType.APPLICATION_JSON_UTF8_VALUE 再使用 JavaLib 中 Result。完整代码如下: package com.fengwenyi.demojavalibresult.controller; import com.fengwenyi.demojavalibresult.model.UserModel; import com.fengwenyi.demojavalibresult.util.CodeMsg; import com.fengwenyi.javalib.result.Result; import org.springframework.http.MediaType; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * User Controller : 用户操作 * @author Wenyi Feng * @since 2019-02-05 */ @RestController @RequestMapping(value = "/user", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public class UserController { /** 临时存放用户信息 */ private List<UserModel> userModelList = new ArrayList<>(); /** * 初始化用户 */ @PostConstruct public void init() { for (int i = 0; i < 10; i++) userModelList.add(new UserModel().setUid(UUID.randomUUID().toString()).setName("u" + i).setAge(10 + i)); } /** * 查询用户列表 * @return {@link Result} */ @GetMapping("/list") public Result list() { return Result.success(userModelList); } /** * 添加用户 * @param userModel 这里传JSON字符串 * @return {@link Result} */ @PostMapping("/add") public Result add(@RequestBody UserModel userModel) { if (userModel != null) { userModelList.add(userModel.setUid(UUID.randomUUID().toString())); return Result.success(); } return Result.error(); } /** * 根据UID获取用户 * @param uid UID * @return {@link Result} */ @GetMapping("/get/{uid}") public Result getByUid(@PathVariable("uid") String uid) { if (StringUtils.isEmpty(uid)) return Result.error(CodeMsg.ERROR_USER_UID_NOT_NULL); for (UserModel userModel : userModelList) if (userModel.getUid().equals(uid)) return Result.success(userModel); return Result.error(CodeMsg.ERROR_USER_NOT_EXIST); } } 测试 1、启动 2、list 访问:http://localhost:8080/user/list { "code": 0, "msg": "Success", "data": [ { "uid": "d8e2dfac-b6e8-46c7-9d43-5bb6bf99ce30", "name": "u0", "age": 10 }, { "uid": "87001637-9f21-4bc7-b589-bea1b2c795c4", "name": "u1", "age": 11 }, { "uid": "5e1398ca-8322-4a68-b0d2-1eb4c1cac9de", "name": "u2", "age": 12 }, { "uid": "e6ee5452-4148-4f6d-b820-9cc24e5c91b5", "name": "u3", "age": 13 }, { "uid": "3f428e26-57e1-4661-8275-ce3777b5da54", "name": "u4", "age": 14 }, { "uid": "b9d994b4-f090-40de-b0f3-e89c613061f2", "name": "u5", "age": 15 }, { "uid": "748d1349-5978-4746-b0c1-949eb5613a28", "name": "u6", "age": 16 }, { "uid": "abaadb7c-23fb-4297-a531-0c490927f6d5", "name": "u7", "age": 17 }, { "uid": "5e5917a1-8674-4367-94c6-6a3fd10a08d6", "name": "u8", "age": 18 }, { "uid": "03ed6a83-0cc0-4714-9d0d-f653ebb3a2eb", "name": "u9", "age": 19 } ] } 2、添加数据 看一下,数据是什么样子 与我们预想的结果一样。 获取数据 有数据样式: 无数据样式: 关于 冯文议。 2017年毕业于阿坝师范学院计算机应用专业。 现就职于深圳警圣技术股份有限公司,主要负责服务器接口开发工作。 技术方向:Java。 开源软件:JavaLib。 后记 到这里就结束了,如果在遇到什么问题,或者有不明白的地方,可以通过评论、留言或者私信等方式,告诉我。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册