
能力说明:
精通JVM运行机制,包括类生命、内存模型、垃圾回收及JVM常见参数;能够熟练使用Runnable接口创建线程和使用ExecutorService并发执行任务、识别潜在的死锁线程问题;能够使用Synchronized关键字和atomic包控制线程的执行顺序,使用并行Fork/Join框架;能过开发使用原始版本函数式接口的代码。
暂时未有相关云产品技术能力~
阿里云技能认证
详细说明我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
elasticsearch docker run -dit -v "$PWD/esdata":/usr/share/elasticsearch/data -p 9200:9200 -p 9300:9300 docker.io/elasticsearch:alpine docker run -dit -v "/root/esdocker/esdata":/usr/share/elasticsearch/data -p 9200:9200 -p 9300:9300 docker.io/elasticsearch:alpine logstash docker run -dit --name logstash -v /opt/access.log:/opt/access.log registry.cn-hangzhou.aliyuncs.com/yasaka/logstash zookeeper //docker run --restart always -d -p 2181:2181 zookeeper docker run -d --name zookeeper --publish 2181:2181 zookeeper:latest kafka docker run -d --name kafka --publish 9092:9092 --link zookeeper --env KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 --env KAFKA_ADVERTISED_HOST_NAME=192.168.33.130 --env KAFKA_ADVERTISED_PORT=9092 wurstmeister/kafka:latest
Docker 容器镜像删除 1.停止所有的container,这样才能够删除其中的images: docker stop $(docker ps -a -q) 如果想要删除所有container的话再加一个指令: docker rm $(docker ps -a -q) 2.查看当前有些什么images docker images 3.删除images,通过image的id来指定删除谁 docker rmi <image id> 想要删除untagged images,也就是那些id为<None>的image的话可以用 docker rmi 3}") 要删除全部image的话 docker rmi $(docker images -q)
ELK+logback+kafka+nginx 搭建分布式日志分析平台 ELK(Elasticsearch , Logstash, Kibana)是一套开源的日志收集、存储和分析软件组合。而且不只是java能用,其他的开发语言也可以使用,今天给大家带来的是elk+logback+kafka搭建分布式日志分析平台。本文主要讲解一下两种流程,全程linux环境(模拟现实环境,可用内存一定要大于2G,当然也可以使用windows),至于elk这些组件的原理,百度太多了,我就不重复了,重在整合。 1.我们是通过logback打印日志,然后将日志通过kafka消息队列发送到Logstash,经过处理以后存储到Elasticsearch中,然后通过Kibana图形化界面进行分析和处理。 2.我们使用Logstash读取日志文件,经过处理以后存储到Elasticsearch中,然后通过Kibana图形化界面进行分析和处理。例如我们读取nginx的日志文件,可以统计访问用户的ip地域,请求地址等等。 一、文章案例环境 1.centos 7.2(linux) 2.elasticsearch / logstash / kibana 6.3.2 下载地址 3.nginx 1.12.2 4.kafka 2.12 下载地址 5.logback/springboot 使用springboot2.0.4.RELEASE和默认的logback 6.zookeeper 3.4.12 下载地址 二、安装Elasticsearch 1.创建用户 如果你是root用户,要新建一个用户,elasticsearch不允许root用户登录,如果不是root登录请忽略这一步。 adduser elsearch su elsearch 2.下载安装elasticsearch,以下简称es wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.tar.gz tar -zxvf elasticsearch-6.3.2.tar.gz 3.修改配置 进入es的config目录,vi elasticsearch.yml,打开注释并修改 #如果配置集群的话,只要name一样就可以自动集群了,不需要单独配置 cluster.name: nelson network.host: 0.0.0.0 #4个0表示外网可以访问 http.port: 9200 #默认http端口 transport.tcp.port: 9300 #默认tcp端口 vi jvm.options 修改一下内存配置,我这里内存不是很多所以修改为450Mb,两者保持一致,如果你内存足够,这个可以忽略。 -Xms450M -Xmx450M 然后就可以启动es,执行 /bin/elasticsearch 启动的时候可能会有一些提示,比如修改一些配置等,复制提示然后百度就会找到解决方案。 4.测试 如果es启动成功,可以通过浏览器访问 ip:9200,下图表示安装成功,如果无法访问,检查es是否成功启动或者是否防火墙拦截 1.png 二、安装Nginx yum install -y nginx 然后 vi /etc/nginx/nginx.conf修改nginx的日志默认输出格式 log_format json '{"@timestamp":"$time_iso8601",' '"@version":"1",' '"client":"$remote_addr",' '"url":"$uri",' '"status":"$status",' '"domian":"$host",' '"host":"$server_addr",' '"size":"$body_bytes_sent",' '"responsetime":"$request_time",' '"referer":"$http_referer",' '"ua":"$http_user_agent"' '}'; access_log /opt/access.log json; 安装完成以后service nginx start启动nginx服务 打开浏览器访问 ip,nginx默认是80端口,如果可以访问表示成功安装 2.png 三、安装Logstash 1.下载安装 logstash wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.tar.gz tar -zxvf elasticsearch-6.3.2.tar.gz 2.修改内存 vi jvm.options,内存足够的话,跳过这一步 -Xms400M -Xmx400M 3.配置输入输出 在config目录下新建文件nginx.conf 1)input表示输入源,他这里有好多插件,支持很多数据源包括文件,http等,我们这里首先收集nginx的日志。file表示读取文件;codec表示读取的文件格式,因为我们前边配置了nginx的日志格式为json,所以这里是json;start_position表示从那一行读取,他会记录上一次读取到那个位置,所以就不用担心遗漏日志了。type相当于一个tag一样,可能这里有很多输入源,后面会根据这个type进行过滤。 2)filter表示处理输入数据,因为我们前边配置了nginx的日志里边记录了用户的ip,所以我们使用geoip组件,可以根据ip匹配位置信息,下面表示你将使用那些fields字段;source表示输入json的那个属性。 3)output表示输出到哪里,可以文件、redis等,这里我们保存到es里。利用elasticsearch插件,然后配置一下es的地址,索引我们是通过日期自动生成,表示每天创建一个索引 input { file { path => "/var/log/nginx/access.log" type => "nginx" codec => "json" start_position => "beginning" } } filter { geoip { fields => ["city_name", "country_name", "latitude", "longitude", "region_name","region_code"] source => "client" } } output { if [type] == "nginx" { elasticsearch { hosts => ["127.0.0.1:9200"] index => "nelson-nginx-%{+YYYY.MM.dd}" } stdout {} } } 4.启动 进入到logstash 目录执行以下命令,记得加-f ./bin/logstash -f ./config/nginx.conf 然后我们在浏览器访问nginx,输入ip就可以,这时候可以在控制台看到如下输出。 3.png 四、安装Kibana 1.下载解压 我买的服务器内存只有2G,所以我用的windows安装的Kibana wget https://artifacts.elastic.co/downloads/kibana/kibana-6.3.2-linux-x86_64.tar.gz tar -zxvf kibana-6.3.2-linux-x86_64.tar.gz windows下载地址,我这里用的是windowshttps://artifacts.elastic.co/downloads/kibana/kibana-6.3.2-windows-x86_64.zip 2.修改配置 进入到config下,修改kibana.yml文件,如果你的kibana和es在一台机器上请忽略这一步,如果不在一台机器上,放开注释修改地址,我这里是在windows上运行的。 elasticsearch.url: "http://localhost:9200" //我的配置 //elasticsearch.url: "http://47.98.109.5:9200" 3.启动 进入到kibana目录下 //linux ./bin/kibana //windows 双击运行bin目录下的kibana.bat文件 4.实战 打开浏览器访问ip:5601 第一次进来我们要创建index pattern,因为我们的日志是按照日期每天存储的,所以要将这些日志聚合到一起。按照下图进行设置,因为我已经有4天的日志了,所以过滤后有四条满足。 4.png 5.png 可以根据时间段过滤,查看数据的录入量,这也表示网站访问量。 6.png 当然也可以通过rest api查询数据,支持复杂查询。索引也可以使用通配符 7.png 5.统计用户区域分布 我们要创建一个统计,然后选择饼状图,下一步选择你要统计的index pattern,这个在上一步已经创建成功 8.png 9.png 添加子查询 10.png 最后我们来一个很复杂的统计,按照国家->城市->浏览器类型,但是用kibana是很简单的,而且速度超快。 11.png 到这里利用elk分析nginx的日志就算完成了,剩下的自己研究,基本类似,一些基本概念还是要自己去百度了。 接下来是通过logback+kafka保存程序日志。因为生产环境中,分布式系统,你的服务可能有N个,例如基于docker,我们不可能给每个docker容器里安装一个logstash,所以需要通过网络向logbash传输数据。这里是通过logback产生日志,然后通过kafka消息队列传输到logstash。 五、安装Zookeeper kafka 是需要zookeeper的,下面简称zk。 wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.12/zookeeper-3.4.12.tar.gz tar -zxvf zookeeper-3.4.12.tar.gz #复制配置 cp zoo_sample.cfg zoo.cfg 修改配置 vi zoo.cfg dataDir=/root/zk/data #改为你zk目录/data 然后进入zk目录启动,如果不保存说明就启动成功了。 ./bin/zkServer.sh start 六、安装Kafka 安装解压 wget http://mirror.bit.edu.cn/apache/kafka/2.0.0/kafka_2.12-2.0.0.tgz tar -zxvf kafka_2.12-2.0.0.tgz cd kafka_2.12-2.0.0 然后修改config目录吓得server.properties,如果你的zk和你的kafka不在一台机器的话,你要修改zk的地址。 还有一点要注意的是如果你使用阿里云这一类产品的时候一定要注意下面配置,特别坑: listeners=PLAINTEXT://172.31.167.25:9092 #阿里云内网地址 advertised.listeners=PLAINTEXT://47.104.255.217:9092 #阿里云外网地址 启动kafka server bin/kafka-server-start.sh config/server.properties 创建一个 名称为applog 的topic bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic applog 查看所有topic bin/kafka-topics.sh --list --zookeeper localhost:2181 消息的生产者,启动以后,在控制台输入信息,然后回车发送 bin/kafka-console-producer.sh --broker-list localhost:9092 --topic applog 消息的消费者,如果生产者那里给applog这个top输入信息发送,消费者这边就会在收到,然后在控制台打印出来。 bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic applog --from-beginning 接下来开发,我们只需要启动kafka server即可。上面这个消费者和生产者只是为了测试。 七、程序中使用logback 我们新建一个springboot项目,然后加入如下依赖。coding地址:点击访问 compile('org.springframework.boot:spring-boot-starter-webflux') compile('org.springframework.kafka:spring-kafka') compile group: 'net.logstash.logback', name: 'logstash-logback-encoder', version: '5.2' compile group: 'com.github.danielwegener', name: 'logback-kafka-appender', version: '0.1.0' 因为springboot自带logback,所以我们也不需要手动增加依赖。然后我们在resource目录下新建文件logback-spring.xml,这样话,springboot自动读取配置文件优先顺序比较高,具体文章可以去springboot文档去查看。 <?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <contextName>logback</contextName> <property name="log.path" value="logs/elk.log" /> <!--输出到控制台--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter>--> <encoder> <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!--输出到文件--> <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logback.%d{yyyy-MM-dd}.log</fileNamePattern> </rollingPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!--输出到kafka--> <appender name="KafkaAppender" class="com.github.danielwegener.logback.kafka.KafkaAppender"> <encoder class="com.github.danielwegener.logback.kafka.encoding.LayoutKafkaMessageEncoder"> <layout class="net.logstash.logback.layout.LogstashLayout" > <includeContext>false</includeContext> <includeCallerData>true</includeCallerData> <customFields>{"system":"test"}</customFields> <fieldNames class="net.logstash.logback.fieldnames.ShortenedFieldNames"/> </layout> <charset>UTF-8</charset> </encoder> <!--kafka topic 需要与配置文件里面的topic一致 否则kafka会沉默并鄙视你--> <topic>applog</topic> <keyingStrategy class="com.github.danielwegener.logback.kafka.keying.HostNameKeyingStrategy" /> <deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy" /> <producerConfig>bootstrap.servers=47.104.255.217:9092</producerConfig> </appender> <!--你可能还需要加点这个玩意儿--> <logger name="Application_ERROR"> <appender-ref ref="KafkaAppender"/> </logger> <root level="info"> <appender-ref ref="console" /> <appender-ref ref="file" /> <appender-ref ref="KafkaAppender" /> </root> </configuration> 然后我们新建controller import org.slf4j.Logger; import org.slf4j.LoggerFactory; // 注意导包,不要导错 @RestController public class IndexController { private Logger logger = LoggerFactory.getLogger(this.getClass()); @RequestMapping("/") public void index(){ logger.trace("日志输出 trace"); logger.debug("日志输出 debug"); logger.info("日志输出 info"); logger.warn("日志输出 warn"); logger.error("日志输出 error"); } } 这个时候我们需要,修改一下logback的配置文件了,要加入kafka的输入。修改logbstash的config目录吓得nginx.conf input { #nginx日志的输入 file { path => "/opt/access.log" type => "nginx" codec => "json" start_position => "beginning" } #kafka日志输入 kafka { topics => "applog" type => "kafka" bootstrap_servers => "47.104.255.217:9092" codec => "json" } } filter { if [type] == "nginx" { geoip { fields => ["city_name", "country_name", "latitude", "longitude", "region_name","region_code"] source => "client" } } } output { #都输出到es中,但是索引不一样 if [type] == "nginx" { elasticsearch { hosts => ["127.0.0.1:9200"] index => "nelson-nginx-%{+YYYY.MM.dd}" } stdout {} } if [type] == "kafka" { elasticsearch { hosts => ["127.0.0.1:9200"] index => "nelson-applogs-%{+YYYY.MM.dd}" } stdout {} } } 然后执行../bin/logstash -f ./nginx.conf,启动logstash,这时候我们的logstash就有两个输入源了。 访问项目中controller地址,看日志是否打印出来。 idea的控制台打印了日志 12.png 这是logstash打印出来的日志,如果这个出来基本可以说明成功了。 13.png 最后我们在kibana中通过rest请求es,这里表示查到数据。 14.png 要是想统计日志,可以参考上边kibanam那一块,类似。 到这里,本篇文章就结束了,elk+nginx 和 elk+logback+kafka都已经实现了,考虑篇幅,所以这里没有细讲这些概念。这些呢就自行百度吧,不重复造轮子了,只要能串通,剩下的用到啥看官方文档或者百度。 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。
sb2.0新版springcloud微服务实战:Eureka+Zuul+Feign/Ribbon+Hystrix Turbine+SpringConfig+sleuth+zipkin springboot 版本是 2.0.3.RELEASE ,springcloud 版本是 Finchley.RELEASE 本篇文章是springboot2.x升级后的升级springcloud专贴,因为之前版本更新已经好久了,好多人评论可不可以出个新版本,大家一定要注意,这是springboot2.x版本的,springboot1.x的请参考 点击查看文章,基本组件都不变就是升级jar包版本,主要就是hystrix-dashboard使用有点变化。 相信现在已经有很多小伙伴已经或者准备使用springcloud微服务了,接下来为大家搭建一个微服务框架,后期可以自己进行扩展。会提供一个小案例: 服务提供者和服务消费者 ,消费者会调用提供者的服务,新建的项目都是用springboot,附源码下载,推荐使用coding地址下载,因为可以切换分支,后期可以及时更新。 coding仓库地址(推荐下载): coding地址 远程配置仓库地址 远程配置仓库地址 如果有问题请在下边评论,或者200909980加群交流。或者关注文章结尾微信公众号,私信后台 Eureka/Consul/Zookeeper:服务发现 (根据情况选择一个,eureka已经宣布闭源) Hystrix:断路器 Zuul:智能路由 Ribbon/Feign:客户端负载均衡 (Feign用的更多) Turbine&hystrix-dashboard:集群监控 Springcloud-config:远程获取配置文件 接下来,我们开始搭建项目,首先我们到spring为我们提供的一个网站快速搭建springboot项目,点击访问,我这里用的是gradle,如果各位客官喜欢用maven,好吧你可以到http://mvnrepository.com/查看对应的依赖,点我访问。 微信截图_20180811140827.png 一、搭建consul服务 使用 eureka 作为服务发现 请参考点击查看使用eureka作为服务注册中心 springclound-consul作为服务发现的核心,第一个搭建,后面的服务都要注册到consul上,意思是告诉consul自己的服务地址是啥。当然还可以用zookeeper或者eureka。 1下载安装consul consul的下载地址consul下载地址,根据自己电脑系统下载对应的包。 我这里是windows64,网上说要配置环境变量,就可以直接使用,但是我这里不行。具体方法是,下载解压后是一个.exe可执行文件,然后配置当前路劲到path中,然后直接在cmd命令窗口中,输入consul agent -dev 就可以启动了,如果不行的话,就在cmd命令窗口进如到consul的下载目录,然后执行上面的命令。然后consul的默认端口是8500,直接在浏览器打开http://localhost:8500,就可以看到控制台了 2.png 二、搭建config-server服务sc-config-server springcloud-config-server是用来将远程git仓库的配置文件动态拉下来,这样配置文件就可以动态的维护了。当然也可以选择本地仓库。 新建一个springboot项目,修改maven私服地址,并加入一下依赖。 1.修改build.gradle文件 compile('org.springframework.cloud:spring-cloud-config-server') compile('org.springframework.cloud:spring-cloud-starter-consul-discovery') //连接config-server也需要用户名和密码 compile('org.springframework.boot:spring-boot-starter-security') compile('org.springframework.boot:spring-boot-starter-actuator') 2.修改application.yml文件 server: port: 8800 spring: security: basic: enabled: true user: name: root password: booszy application: name: sc-config-server cloud: config: server: git: uri: https://git.coding.net/yirenyishi/springcloud-config-profile searchPaths: '{application}' consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} ip-address: true 3.修改启动类 修改启动类,要加入这三个注解,因为要注册到consul上,所以需要@EnableDiscoveryClient这个注解 @EnableConfigServer @EnableDiscoveryClient @SpringBootApplication public class Sb2scConfigApplication { public static void main(String[] args) { SpringApplication.run(Sb2scConfigApplication.class, args); } } 然后运行启动springboot项目,等启动成功后访问consul的页面,会发现sc-config-server已经注册到上面了,如果启动报错,请检查错误信息。 3.png 三、搭建服务提供者服务sc-provider 编写一个服务提供者,为下边的消费者提供服务,用到了spring-webflux(spring新出的非阻塞式框架)不是springmvc,当然你们公司用什么你还是继续用什么。 注意 : 这里除了application.xml,还需要一个bootstrap.yml, 因为bootstrap.yml得加载顺序是在application.xml前边,服务注册和config配置必须放到bootstrap.yml。 修改build.gradle文件 compile('org.springframework.cloud:spring-cloud-starter-consul-discovery') compile('org.springframework.cloud:spring-cloud-starter-config') compile('org.springframework.boot:spring-boot-starter-webflux') compile('org.springframework.boot:spring-boot-starter-actuator') 2.编写配置文件bootstrap.yml ** 注意 : 这里除了application.xml,还需要一个bootstrap.yml* application.xml我是放到远程仓库地址的,大家可以直接到我的远程仓库,根据项目名(sc-provider-config)查询。配置文件的仓库地址:点击访问。 spring: application: name: sc-provider cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} ip-address: true config: discovery: enabled: true service-id: sc-config-server fail-fast: true username: root password: booszy profile: csdn 3.编写代码 编写主类 @EnableDiscoveryClient @SpringBootApplication public class Sb2scProviderApplication { public static void main(String[] args) { SpringApplication.run(Sb2scProviderApplication.class, args); } } 新建IndexController进行测试,这里只是为了测试,案例代码使用的是webflux,如果想使用springmvc,修改jar包依赖即可。 @RestController @RequestMapping("test") public class IndexController { //返回一个实体 @GetMapping("{msg}") public Mono<String> sayHelloWorld(@PathVariable("msg") String msg) { System.out.println("come on " + msg); return Mono.just("sc-provider receive : " +msg); } //返回一个列表 @GetMapping("list") public Flux<Integer> list() { List<Integer> list = new ArrayList<>(); list.add(8); list.add(22); list.add(75); list.add(93); Flux<Integer> userFlux = Flux.fromIterable(list); return userFlux; } } 运行springboot项目,去consul查看,有没有注册上。 4.png 我们的sc-provider已经注册到eureka上了,访问接口,成功。 6.png 四、搭建消费者服务sc-consumer 消费者要访问服务提供者的服务,这里用的是通过RestTemplate/feign请求resetful接口,使用ribbon做客户端负载均衡,hystrix做错误处理,feign和ribbon二选一,案例中ribbon和feign都有,也可以都用。 还是熟悉的配方,熟悉的味道,新建springboot项目,添加项目依赖。 1.修改build.gradle文件 compile('org.springframework.cloud:spring-cloud-starter-consul-discovery') compile('org.springframework.cloud:spring-cloud-starter-config') compile('org.springframework.boot:spring-boot-starter-webflux') compile('org.springframework.boot:spring-boot-starter-actuator') compile('org.springframework.cloud:spring-cloud-starter-openfeign') compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix') 2.修改bootstrap.yml文件 application.yml 在git仓库,请前往git仓库查看。 spring: application: name: sc-consumer cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} ip-address: true config: discovery: enabled: true service-id: sc-config-server fail-fast: true username: root password: booszy profile: csdn #新版配置,否则后面dashboard无法找到hystrix.stream management: endpoints: web: exposure: include: '*' 3.编写代码 启动类代码 @RibbonClient 指定服务使用的负载均衡类型,name不指定服务则为所有的服务打开负载均衡,也可以在用yml中进行配置。 @EnableHystrix 是支持hystrix打开断路器,在规定时间内失败参数超过一定参数,就会打开断路器,不会发起请求,而是直接进入到错误处理方法。 @EnableDiscoveryClient @EnableFeignClients @EnableCircuitBreaker @EnableHystrix @SpringBootApplication public class Sb2scConsumerApplication { // ribbon需要配置,负载均衡 @Autowired private RestTemplateBuilder builder; // ribbon需要配置,负载均衡 @Bean @LoadBalanced public RestTemplate restTemplate() { return builder.build(); } public static void main(String[] args) { SpringApplication.run(Sb2scConsumerApplication.class, args); } } 1.ribbon案例 ribbon不需要单独依赖,新建 RibbonController ribbon一个坑,不能接受List类型,要使用数组接收。 @HystrixCommand(fallbackMethod="fallbackMethod") 如果请求失败,会进入fallbackMethod这个方法,fallbackMethod这个方法要求参数和返回值与回调他的方法保持一致。 @RestController public class RibbonController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/ribbon/{wd}") @HystrixCommand(fallbackMethod="fallbackMethod") public Mono<String> sayHelloWorld(@PathVariable("wd") String parm) { String res = this.restTemplate.getForObject("http://sc-provider/test/" + parm, String.class); return Mono.just(res); } public Mono<String> fallbackMethod(@PathVariable("wd") String parm) { return Mono.just("fallback"); } 运行springboot项目,先看有没有注册到consul上。 5.png 注册成功后,访问接口,测试是否正确。 8.png ribbon使用就是这么简单,ribbon是springboot自带,所以不需要单独添加依赖。 2.feign案例 在实际开发中,feign使用的还是挺多的,feign底层还是使用了ribbon。废话不多说,直接上步骤,在服务消费者中使用feign访问服务提供者。 1配置文件 ribbon: ReadTimeout: 30000 ConnectTimeout: 15000 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 10000 feign的默认请求超时时间是1s,所以经常会出现超时的问题,这里我设置的是10s。ribbon的请求时间也要设置,因为feign用的是ribbon。这里贴的是application.yml文件中的一小段 2 编码 1、主类注解 @EnableFeignClients @EnableCircuitBreaker @EnableHystrix 这三个都要,hystrix主要作用是断路器,会进如fein的fallback中。 主类代码在上面已经贴出来了 2、编写feign接口,MFeignClient.class name是指要请求的服务名称。这里请求的是服务提供者 fallback 是指请求失败,进入断路器的类,和使用ribbon是一样的。 configuration 是feign的一些配置,例如编码器等。 @FeignClient(name = "sc-provider",fallback = MFeignClientFallback.class, configuration = MFeignConfig.class) public interface MFeignClient { // 这是被请求微服务的地址,也就是provider的地址 @GetMapping(value = "/test/{msg}") String sayHelloWorld(@PathVariable("msg") String msg); @GetMapping(value = "/test/list") List<Integer> list(); @GetMapping(value = "/test/list") Integer[] array(); } 3 MFeignConfig.class feign的配置 这里配置了feign的打印日志等级 @Configuration public class MFeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } 4 MFeignClientFallback.class ,断路器回调方法 断路器要实现上边定义的MFeignClient接口,请求失败,进入断路器时,会回调这里的方法。 @Component public class MFeignClientFallback implements MFeignClient{ @Override public String sayHelloWorld(String msg) { return "fallback"; } @Override public List<Integer> list() { return new ArrayList<>(); } @Override public Integer[] array() { return new Integer[0]; } } 5 在controller中使用feign @RestController public class FeignController { @Autowired private MFeignClient feignClient; @GetMapping("/feign/{wd}") public Mono<String> sayHelloWorld(@PathVariable("wd") String parm) { String result = feignClient.sayHelloWorld(parm); return Mono.just(result); } @GetMapping("/feign/list") public Flux<Integer> list() { List<Integer> list = feignClient.list(); Flux<Integer> userFlux = Flux.fromIterable(list); return userFlux; } @GetMapping("/feign/array") public Flux<Integer> array() { Integer[] arrays = feignClient.array(); Flux<Integer> userFlux = Flux.fromArray(arrays); return userFlux; } } 9.png 五、用zuul做路由转发和负载均衡 这些微服务都是隐藏在后端的,用户是看不到,或者不是直接接触,可以用nginx或者zuul进行路由转发和负载均衡,zuul负载均衡默认用的是ribbon。 1.修改build.gradle文件 compile('org.springframework.cloud:spring-cloud-starter-consul-discovery') compile('org.springframework.cloud:spring-cloud-starter-config') compile('org.springframework.cloud:spring-cloud-starter-netflix-zuul') compile('org.springframework.boot:spring-boot-starter-actuator') 2.修改bootstrap.yml 还是原来的配方,application.yml在git仓库 spring: application: name: sc-zuul cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} ip-address: true config: discovery: enabled: true service-id: sc-config-server fail-fast: true username: root password: booszy profile: csdn 3.启动类 @RefreshScope这个注解是当application.yml配置文件发生变化的时候,不需要手动的进行重启,调用localhost:8400/refresh,就会加载新的配置文件,当然正在访问的客户并不影响还是使用旧的配置文件,因为不是重启,后来的用户会使用新的配置文件。注意这块的刷新要用post请求。 @EnableDiscoveryClient @SpringBootApplication @EnableZuulProxy @RefreshScope public class Sb2scZuulApplication { public static void main(String[] args) { SpringApplication.run(Sb2scZuulApplication.class, args); } } 启动springboot项目,访问consul 6.png 这时候,我们就要通过zuul访问微服务了,而不是直接去访问微服务。 应该访问地址http://localhost:8400/sc-consumer/feign/list,这块你要换成你的zuul地址。 但是有些人就会说,这样以后用户请求会不会太长,比较反感,所以可以通过配置进行修改访问地址。 zuul: routes: springcloud-consumer-config: /consumer/** springcloud-provider-config: /provider/** 在application.yml中加入这样一段配置,其实就是nginx中的反向代理,使用一下简短的可以代理这个微服务。这个时候我们就可以这样去访问了http://localhost:8400/consumer/feign/list,是不是简短了很多 11.png 六、用hystrix-turbine-dashboard 做集群监控 项目在生产环境中,每个服务的访问量都不通,有些服务的访问量比较大,有时候有些服务挂了,不能继续服务,需要重启的时候,我们并不知道,所以这时候就需要使用hystrix-turbine-dashboard做一个监控,监控所有的微服务,可以看到这个接口实时访问量,和健康状况。 新建一个springboot项目,老套路,加入如下依赖 1 添加依赖 compile('org.springframework.cloud:spring-cloud-starter-consul-discovery') compile('org.springframework.boot:spring-boot-starter-actuator') compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix') compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard') compile('org.springframework.cloud:spring-cloud-starter-netflix-turbine') 如果启动的时候报错要排除一下eureka依赖 configurations { compile.exclude module: 'eureka-client' } 2 修改application.yml配置文件 注意:是application.yml,这里不需要bootstrap.yml server: port: 8900 turbine: aggregator: clusterConfig: default appConfig: sc-consumer clusterNameExpression: "'default'" spring: application: name: sc-dashboard cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} ip-address: true appConfig 后面是要检测的注册在consul上的服务名,必须要有 如果启动报错,找不到hystrix.stream,在你要监控的微服务加入如下配置。 management: endpoints: web: exposure: include: '*' 3 修改主类 @EnableTurbine ,@EnableHystrixDashboard 一个都不能少 @EnableDiscoveryClient @SpringBootApplication @EnableTurbine @EnableHystrixDashboard public class Sb2scDashboardApplication { public static void main(String[] args) { SpringApplication.run(Sb2scDashboardApplication.class, args); } } 4 访问测试 这块的端口是8900,访问地址http://localhost:8900/hystrix,看到的是下面的页面。 13.png 然后在那个网址的输入框里输网址http://localhost:8900/turbine.stream,点击monitor stream。刚打开的时候可能是空的,什么也没有,这并不表示你已经错了。这时候你访问消费者服务的接口,例如访问http://localhost:8400/consumer/feign/list,多访问几次,然后看控制台有没有出现一个监控面板,没有就等会刷新一次,如果一直不出现,应该是配置有问题。 12.png 七、使用sleuth+zipkin 实现链路追踪服务 在使用微服务的时候,我们发现,有时候排错不好排查,所以就给大家整个这个链路追踪,很方便知道是哪一个服务调用哪一个服务出现了问题。因为有些项目可能服务比较多。 1 添加依赖 新建一个springboot项目 虽然其他服务调用zipkin不是从eureka上动态过去服务地址,而是硬编码,但是这块还是考虑吧zipkin注册到eureka上。 compile('org.springframework.cloud:spring-cloud-starter-consul-discovery') compile group: 'io.zipkin.java', name: 'zipkin-server', version: '2.9.3' compile group: 'io.zipkin.java', name: 'zipkin-autoconfigure-ui', version: '2.9.3' compile('org.springframework.boot:spring-boot-starter-actuator') 如果提示log4j有冲突,要排除依赖 configurations { compile.exclude module: 'log4j' compile.exclude module: 'slf4j-log4j12' compile.exclude module: 'spring-boot-starter-logging' } 2 修改application配置文件 server: port: 9411 spring: application: name: sc-sc-zipkin profiles: active: csdn cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} ip-address: true management: metrics: web: server: auto-time-requests: false 3 主类注解添加 @EnableZipkinServer 主要是这个注解 启动服务后访问http://localhost:9411,就可以打开zipkin的控制台页面,这时候应该是什么都没有 @EnableDiscoveryClient @SpringBootApplication @EnableZipkinServer public class Sb2scZipkinApplication { public static void main(String[] args) { SpringApplication.run(Sb2scZipkinApplication.class, args); } } 4 其他服务中调用 这里我们在消费者服务和提供者服务里都加入如下依赖 compile('org.springframework.cloud:spring-cloud-starter-sleuth') compile('org.springframework.cloud:spring-cloud-starter-zipkin') 然后修改配置文件,bootstrap.yml、 这块zipkin的地址是硬编码的,目前还没发现怎么从服务注册中心eureka上动态获取,以后有解决方案,会更新帖子 sleuth这个是配置提取率,可以配置也可以不配置 spring: zipkin: base-url: http://localhost:9411 sleuth: sampler: percentage: 1.0 启动服务,然后访问消费者服务的接口,这时候访问zipkin的控制台http://localhost:9411 14.png 点击依赖分析,可以看到调用服务链,因为这块只涉及到两个服务,所以只有两个,在实际生产环境中,这块可能有很多,到时候看起来就特别直观了。 微信截图_20180811123550.png 微信截图_20180811123512.png 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。
android点击全屏预览照片第三方库使用-imgepreviewlibrary 移动端我们经常会遇到放大预览照片,如果是一张照片,那就全屏展示图片就好了,但是如果是一个列表,滑动查看,我们一般会借助viewpager进行实现,但是每次自己弄,感觉效率很低,今天给大家推荐一个第三方库,很轻松实现,扩展也还可以哦。 这是点击预览的效果图,下边是数字,也可以显示成点 微信截图_20180801172357.png 1.安装配置 先添加依赖 implementation 'com.ycjiang:imgepreviewlibrary:1.1.3' 我们需要自定义一个类ImageLoader ,进行图片加载,不限制框架,一般使用glide,当然你也可以使用其他的。 public class ImageLoader implements IZoomMediaLoader { RequestOptions options; { options = new RequestOptions() .centerCrop() .placeholder(R.drawable.ic_default_image) .error(R.drawable.ic_default_image) .priority(Priority.HIGH); } @Override public void displayImage(Fragment context, String path, final MySimpleTarget<Bitmap> simpleTarget) { Glide.with(context) .asBitmap() .load(path) .apply(options) .into(new SimpleTarget<Bitmap>() { @Override public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) { simpleTarget.onResourceReady(resource); } @Override public void onLoadStarted(Drawable placeholder) { super.onLoadStarted(placeholder); simpleTarget.onLoadStarted(); } @Override public void onLoadFailed(Drawable errorDrawable) { super.onLoadFailed(errorDrawable); simpleTarget.onLoadFailed(errorDrawable); } }); } @Override public void onStop(@NonNull Fragment context) { Glide.with(context).onStop(); } @Override public void clearMemory(@NonNull Context c) { Glide.get(c).clearMemory(); } } 在初始化的时候,初始化图片加载类。 ZoomMediaLoader.getInstance().init(new ImageLoader()); 2.项目使用 我们在点击的回调函数中,打开图片预览代码,根据自己的实际情况调整。 //组织数据 ArrayList<ThumbViewInfo> mThumbViewInfoList = new ArrayList<>(); // 这个最好定义成成员变量 ThumbViewInfo item; mThumbViewInfoList.clear(); for (int i = 0;i < resultList.size(); i++) { Rect bounds = new Rect(); //new ThumbViewInfo(图片地址); item=new ThumbViewInfo(resultList.get(i).getOriginUrl()); item.setBounds(bounds); mThumbViewInfoList.add(item); } //打开预览界面 GPreviewBuilder.from(Context context) //是否使用自定义预览界面,当然8.0之后因为配置问题,必须要使用 .to(ImageLookActivity.class) .setData(mThumbViewInfoList) .setCurrentIndex(position) .setSingleFling(true) .setType(GPreviewBuilder.IndicatorType.Number) // 小圆点 // .setType(GPreviewBuilder.IndicatorType.Dot) .start();//启动 3.自定义预览界面 自定义预览图片,可以扩展加一下自己的按钮功能等。 public class ImageLookActivity extends GPreviewActivity { /*** * 重写该方法 * 使用你的自定义布局 **/ @Override public int setContentLayout() { return R.layout.activity_image_look; } } 自定义预览的布局 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".activity.ImageLookActivity"> // 这是第三方库提供的,也就是默认的布局文件 <include layout="@layout/activity_image_preview_photo"/> </FrameLayout> 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。
springcloud 中使用 hystrix-dashboard 进行监控的时候启动报错"path":"/actuator/hystrix.stream","status":404,"error":"Not Found" 这是因为springboot2.x使用了endpoint 解决办法是在被监控的微服务的bootstrap.yml文件中加入如下配置 management: endpoints: web: exposure: include: ["hystrix-stream"] 这样就可以了,但是actuator/health就无法访问了,所以还可以选择全部放开。 management: endpoints: web: exposure: include: '*'
sb2.0新版springcloud微服务实战:Eureka+Zuul+Feign/Ribbon+Hystrix Turbine+SpringConfig+sleuth+zipkin springboot 版本是 2.0.3.RELEASE ,springcloud 版本是 Finchley.RELEASE 本篇文章是springboot2.x升级后的升级springcloud专贴,因为之前版本更新已经好久了,好多人评论可不可以出个新版本,大家一定要注意,这是springboot2.x版本的,springboot1.x的请参考 点击查看文章,基本组件都不变就是升级jar包版本,主要就是hystrix-dashboard使用有点变化。还有一点要注意的是sc默认使用的是eureka1.9.x版本,大家一定要主要,不要自己手动改为2.x版本,因为2.x版本还没有正式发布,而且停止开发了,官方还在积极的维护1.x版本(并不是网传的闭源)。 相信现在已经有很多小伙伴已经或者准备使用springcloud微服务了,接下来为大家搭建一个微服务框架,后期可以自己进行扩展。会提供一个小案例: 服务提供者和服务消费者 ,消费者会调用提供者的服务,新建的项目都是用springboot,附源码下载,推荐使用coding地址下载,因为可以切换分支,后期可以及时更新。 coding仓库地址(推荐下载): coding地址 远程配置仓库地址 远程配置仓库地址 如果有问题请在下边评论,或者200909980加群交流。或者关注文章结尾微信公众号,私信后台 Eureka/Consul/Zookeeper:服务发现 (根据情况选择一个,eureka已经宣布闭源) Hystrix:断路器 Zuul:智能路由 Ribbon/Feign:客户端负载均衡 (Feign用的更多) Turbine&hystrix-dashboard:集群监控 Springcloud-config:远程获取配置文件 接下来,我们开始搭建项目,首先我们到spring为我们提供的一个网站快速搭建springboot项目,点击访问,我这里用的是gradle,如果各位客官喜欢用maven,好吧你可以到http://mvnrepository.com/查看对应的依赖,点我访问。 1.png 一、搭建eureka-server服务sc-eureka-server 使用 spring-cloud-consul 作为服务发现 请参考 点击查看使用springcloud consul 作为服务发现 eureka-server作为服务发现的核心,第一个搭建,后面的服务都要注册到eureka-server上,意思是告诉eureka-server自己的服务地址是啥。当然还可以用zookeeper或者springconsul。 1.修改build.gradle文件 如果是maven项目请对应的修改pom.xml //加入阿里的私服仓库地址 maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } //加入依赖 compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-server') //加入security,是因为访问eureka-server需要用户名和密码访问,为了安全 compile('org.springframework.boot:spring-boot-starter-security') 还有几点需要修改的,大家对应图片看看,就是springboot打包的时候会提示找不到主类。 2.png 2.修改 application.yml,建议用yml。 server: port: 8761 eureka: datacenter: trmap environment: product server: # 关闭自我保护 enable-self-preservation: false # 清理服务器 eviction-interval-timer-in-ms: 5000 client: healthcheck: enabled: true service-url: defaultZone: http://root:booszy@localhost:8761/eureka/ register-with-eureka: false fetch-registry: false spring: security: basic: enabled: true user: name: root password: booszy 3.修改程序的主类,建议修改类名,要加如eureka的 @EnableEurekaServer 注解,然后运行main方法。 @EnableEurekaServer @SpringBootApplication public class Sb2scEurekaApplication { public static void main(String[] args) { SpringApplication.run(Sb2scEurekaApplication.class, args); } } 4.png http://localhost:8761/ 这个是eureka-server的页面地址,密码在yml配置文件中,到这里,说明eureka-server搭建好了,简单吧,这一步一定要成功,否则后面的就不能继续进行下去了,后边基本类似。 二、搭建config-server服务sc-config-server springcloud-config-server是用来将远程git仓库的配置文件动态拉下来,这样配置文件就可以动态的维护了。当然也可以选择本地仓库。 新建一个springboot项目,修改maven私服地址,并加入一下依赖。 1.修改build.gradle文件 compile('org.springframework.cloud:spring-cloud-config-server') compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') //连接config-server也需要用户名和密码 compile('org.springframework.boot:spring-boot-starter-security') compile('org.springframework.boot:spring-boot-starter-actuator') 2.修改application.yml文件 server: port: 8800 spring: security: basic: enabled: true user: name: root password: booszy application: name: sc-config-server cloud: config: server: git: uri: https://git.coding.net/yirenyishi/springcloud-config-profile searchPaths: '{application}' eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka/ instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} appname: sc-config-server 3.修改启动类 修改启动类,要加入这三个注解,因为要注册到eureka-server上,所以需要@EnableDiscoveryClient这个注解 @EnableConfigServer @EnableDiscoveryClient @SpringBootApplication public class Sb2scConfigApplication { public static void main(String[] args) { SpringApplication.run(Sb2scConfigApplication.class, args); } } 然后运行启动springboot项目,等启动成功后访问eureka的页面,会发现sc-config-server已经注册到上面了,如果启动报错,请检查错误信息。 3.png 三、搭建服务提供者服务sc-provider 编写一个服务提供者,为下边的消费者提供服务,用到了spring-webflux(spring新出的非阻塞式框架)不是springmvc,当然你们公司用什么你还是继续用什么。 注意 : 这里除了application.xml,还需要一个bootstrap.yml, 因为bootstrap.yml得加载顺序是在application.xml前边,服务注册和config配置必须放到bootstrap.yml。 修改build.gradle文件 compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') compile('org.springframework.cloud:spring-cloud-starter-config') compile('org.springframework.boot:spring-boot-starter-webflux') compile('org.springframework.boot:spring-boot-starter-actuator') 2.编写配置文件bootstrap.yml ** 注意 : 这里除了application.xml,还需要一个bootstrap.yml* application.xml我是放到远程仓库地址的,大家可以直接到我的远程仓库,根据项目名(sc-provider-config)查询。配置文件的仓库地址:点击访问。 eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka/ instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} appname: sc-provider spring: application: name: sc-provider cloud: config: discovery: enabled: true service-id: sc-config-server fail-fast: true username: root password: booszy profile: csdn 3.编写代码 编写主类 @EnableDiscoveryClient @SpringBootApplication public class Sb2scProviderApplication { public static void main(String[] args) { SpringApplication.run(Sb2scProviderApplication.class, args); } } 新建IndexController进行测试,这里只是为了测试,案例代码使用的是webflux,如果想使用springmvc,修改jar包依赖即可。 @RestController @RequestMapping("test") public class IndexController { //返回一个实体 @GetMapping("{msg}") public Mono<String> sayHelloWorld(@PathVariable("msg") String msg) { System.out.println("come on " + msg); return Mono.just("sc-provider receive : " +msg); } //返回一个列表 @GetMapping("list") public Flux<Integer> list() { List<Integer> list = new ArrayList<>(); list.add(8); list.add(22); list.add(75); list.add(93); Flux<Integer> userFlux = Flux.fromIterable(list); return userFlux; } } 运行springboot项目,去eureka-server查看,有没有注册上。 5.png 我们的sc-provider已经注册到eureka上了,访问接口,成功。 6.png 四、搭建消费者服务sc-consumer 消费者要访问服务提供者的服务,这里用的是通过RestTemplate/feign请求resetful接口,使用ribbon做客户端负载均衡,hystrix做错误处理,feign和ribbon二选一,案例中ribbon和feign都有,也可以都用。 还是熟悉的配方,熟悉的味道,新建springboot项目,添加项目依赖。 1.修改build.gradle文件 compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') compile('org.springframework.cloud:spring-cloud-starter-config') compile('org.springframework.boot:spring-boot-starter-webflux') compile('org.springframework.boot:spring-boot-starter-actuator') compile('org.springframework.cloud:spring-cloud-starter-openfeign') compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix') 2.修改bootstrap.yml文件 application.yml 在git仓库,请前往git仓库查看。 eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka/ instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} appname: sc-consumer spring: application: name: sc-consumer cloud: config: discovery: enabled: true service-id: sc-config-server fail-fast: true username: root password: booszy profile: csdn #新版配置,否则后面dashboard无法找到hystrix.stream management: endpoints: web: exposure: include: '*' 3.编写代码 启动类代码 @RibbonClient 指定服务使用的负载均衡类型,name不指定服务则为所有的服务打开负载均衡,也可以在用yml中进行配置。 @EnableHystrix 是支持hystrix打开断路器,在规定时间内失败参数超过一定参数,就会打开断路器,不会发起请求,而是直接进入到错误处理方法。 @EnableDiscoveryClient @EnableFeignClients @EnableCircuitBreaker @EnableHystrix @SpringBootApplication public class Sb2scConsumerApplication { // ribbon需要配置,负载均衡 @Autowired private RestTemplateBuilder builder; // ribbon需要配置,负载均衡 @Bean @LoadBalanced public RestTemplate restTemplate() { return builder.build(); } public static void main(String[] args) { SpringApplication.run(Sb2scConsumerApplication.class, args); } } 1.ribbon案例 ribbon不需要单独依赖,新建 RibbonController ribbon一个坑,不能接受List类型,要使用数组接收。 @HystrixCommand(fallbackMethod="fallbackMethod") 如果请求失败,会进入fallbackMethod这个方法,fallbackMethod这个方法要求参数和返回值与回调他的方法保持一致。 @RestController public class RibbonController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/ribbon/{wd}") @HystrixCommand(fallbackMethod="fallbackMethod") public Mono<String> sayHelloWorld(@PathVariable("wd") String parm) { String res = this.restTemplate.getForObject("http://sc-provider/test/" + parm, String.class); return Mono.just(res); } public Mono<String> fallbackMethod(@PathVariable("wd") String parm) { return Mono.just("fallback"); } 运行springboot项目,先看有没有注册到eureka-server上。 7.png 注册成功后,访问接口,测试是否正确。 8.png ribbon使用就是这么简单,ribbon是springboot自带,所以不需要单独添加依赖。 2.feign案例 在实际开发中,feign使用的还是挺多的,feign底层还是使用了ribbon。废话不多说,直接上步骤,在服务消费者中使用feign访问服务提供者。 1配置文件 ribbon: ReadTimeout: 30000 ConnectTimeout: 15000 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 10000 feign的默认请求超时时间是1s,所以经常会出现超时的问题,这里我设置的是10s,因为我的数据库服务器在美国,所以有时候请求会比较慢。ribbon的请求时间也要设置,因为feign用的是ribbon。这里贴的是application.yml文件中的一小段 2 编码 1、主类注解 @EnableFeignClients @EnableCircuitBreaker @EnableHystrix 这三个都要,hystrix主要作用是断路器,会进如fein的fallback中。 主类代码在上面已经贴出来了 2、编写feign接口,MFeignClient.class name是指要请求的服务名称。这里请求的是服务提供者 fallback 是指请求失败,进入断路器的类,和使用ribbon是一样的。 configuration 是feign的一些配置,例如编码器等。 @FeignClient(name = "sc-provider",fallback = MFeignClientFallback.class, configuration = MFeignConfig.class) public interface MFeignClient { // 这是被请求微服务的地址,也就是provider的地址 @GetMapping(value = "/test/{msg}") String sayHelloWorld(@PathVariable("msg") String msg); @GetMapping(value = "/test/list") List<Integer> list(); @GetMapping(value = "/test/list") Integer[] array(); } 3 MFeignConfig.class feign的配置 这里配置了feign的打印日志等级 @Configuration public class MFeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } 4 MFeignClientFallback.class ,断路器回调方法 断路器要实现上边定义的MFeignClient接口,请求失败,进入断路器时,会回调这里的方法。 @Component public class MFeignClientFallback implements MFeignClient{ @Override public String sayHelloWorld(String msg) { return "fallback"; } @Override public List<Integer> list() { return new ArrayList<>(); } @Override public Integer[] array() { return new Integer[0]; } } 5 在controller中使用feign @RestController public class FeignController { @Autowired private MFeignClient feignClient; @GetMapping("/feign/{wd}") public Mono<String> sayHelloWorld(@PathVariable("wd") String parm) { String result = feignClient.sayHelloWorld(parm); return Mono.just(result); } @GetMapping("/feign/list") public Flux<Integer> list() { List<Integer> list = feignClient.list(); Flux<Integer> userFlux = Flux.fromIterable(list); return userFlux; } @GetMapping("/feign/array") public Flux<Integer> array() { Integer[] arrays = feignClient.array(); Flux<Integer> userFlux = Flux.fromArray(arrays); return userFlux; } } 9.png 五、用zuul做路由转发和负载均衡 这些微服务都是隐藏在后端的,用户是看不到,或者不是直接接触,可以用nginx或者zuul进行路由转发和负载均衡,zuul负载均衡默认用的是ribbon。 1.修改build.gradle文件 compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') compile('org.springframework.cloud:spring-cloud-starter-config') compile('org.springframework.cloud:spring-cloud-starter-netflix-zuul') compile('org.springframework.boot:spring-boot-starter-actuator') 2.修改bootstrap.yml 还是原来的配方,application.yml在git仓库 eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka/ instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} appname: sc-zuul spring: application: name: sc-zuul cloud: config: discovery: enabled: true service-id: sc-config-server fail-fast: true username: root password: booszy profile: csdn 3.启动类 @RefreshScope这个注解是当application.yml配置文件发生变化的时候,不需要手动的进行重启,调用localhost:8400/refresh,就会加载新的配置文件,当然正在访问的客户并不影响还是使用旧的配置文件,因为不是重启,后来的用户会使用新的配置文件。注意这块的刷新要用post请求。 @EnableDiscoveryClient @SpringBootApplication @EnableZuulProxy @RefreshScope public class Sb2scZuulApplication { public static void main(String[] args) { SpringApplication.run(Sb2scZuulApplication.class, args); } } 启动springboot项目,访问eureka-server 10.png 这时候,我们就要通过zuul访问微服务了,而不是直接去访问微服务。 应该访问地址http://localhost:8400/sc-consumer/feign/list,这块你要换成你的zuul地址。 但是有些人就会说,这样以后用户请求会不会太长,比较反感,所以可以通过配置进行修改访问地址。 zuul: routes: springcloud-consumer-config: /consumer/** springcloud-provider-config: /provider/** 在application.yml中加入这样一段配置,其实就是nginx中的反向代理,使用一下简短的可以代理这个微服务。这个时候我们就可以这样去访问了http://localhost:8400/consumer/feign/list,是不是简短了很多 11.png 六、用hystrix-turbine-dashboard 做集群监控 项目在生产环境中,每个服务的访问量都不通,有些服务的访问量比较大,有时候有些服务挂了,不能继续服务,需要重启的时候,我们并不知道,所以这时候就需要使用hystrix-turbine-dashboard做一个监控,监控所有的微服务,可以看到这个接口实时访问量,和健康状况。 新建一个springboot项目,老套路,加入如下依赖 1 添加依赖 compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') compile('org.springframework.boot:spring-boot-starter-actuator') compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix') compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard') compile('org.springframework.cloud:spring-cloud-starter-netflix-turbine') 2 修改application.yml配置文件 注意:是application.yml,这里不需要bootstrap.yml server: port: 8900 eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka/ instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} appname: sc-dashboard turbine: aggregator: clusterConfig: default appConfig: sc-consumer clusterNameExpression: "'default'" spring: application: name: sc-dashboard #management: # endpoints: # web: # exposure: # include: '*' appConfig 后面是要检测的注册在eureka上的服务名,必须要有 3 修改主类 @EnableTurbine ,@EnableHystrixDashboard 一个都不能少 @EnableDiscoveryClient @SpringBootApplication @EnableTurbine @EnableHystrixDashboard public class Sb2scDashboardApplication { public static void main(String[] args) { SpringApplication.run(Sb2scDashboardApplication.class, args); } } 4 访问测试 这块的端口是8900,访问地址http://localhost:8900/hystrix,看到的是下面的页面。 13.png 然后在那个网址的输入框里输网址http://localhost:8900/turbine.stream,点击monitor stream。刚打开的时候可能是空的,什么也没有,这并不表示你已经错了。这时候你访问消费者服务的接口,例如访问http://localhost:8400/consumer/feign/list,多访问几次,然后看控制台有没有出现一个监控面板,没有就等会刷新一次,如果一直不出现,应该是配置有问题。 12.png 七、使用sleuth+zipkin 实现链路追踪服务 在使用微服务的时候,我们发现,有时候排错不好排查,所以就给大家整个这个链路追踪,很方便知道是哪一个服务调用哪一个服务出现了问题。因为有些项目可能服务比较多。 1 添加依赖 新建一个springboot项目 虽然其他服务调用zipkin不是从eureka上动态过去服务地址,而是硬编码,但是这块还是考虑吧zipkin注册到eureka上。 compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') compile group: 'io.zipkin.java', name: 'zipkin-server', version: '2.9.3' compile group: 'io.zipkin.java', name: 'zipkin-autoconfigure-ui', version: '2.9.3' compile('org.springframework.boot:spring-boot-starter-actuator') 如果提示log4j有冲突,要排除依赖 configurations { compile.exclude module: 'log4j' compile.exclude module: 'slf4j-log4j12' compile.exclude module: 'spring-boot-starter-logging' } 2 修改application配置文件 server: port: 9411 spring: application: name: sc-sc-zipkin profiles: active: csdn eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka/ instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} appname: sc-zipkin management: metrics: web: server: auto-time-requests: false 3 主类注解添加 @EnableZipkinServer 主要是这个注解 启动服务后访问http://localhost:9411,就可以打开zipkin的控制台页面,这时候应该是什么都没有 @EnableDiscoveryClient @SpringBootApplication @EnableZipkinServer public class Sb2scZipkinApplication { public static void main(String[] args) { SpringApplication.run(Sb2scZipkinApplication.class, args); } } 4 其他服务中调用 这里我们在消费者服务和提供者服务里都加入如下依赖 .... compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-sleuth', version: '1.3.1.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-sleuth-zipkin', version: '1.3.1.RELEASE' ... 然后修改配置文件,bootstrap.yml、 这块zipkin的地址是硬编码的,目前还没发现怎么从服务注册中心eureka上动态获取,以后有解决方案,会更新帖子 sleuth这个是配置提取率,可以配置也可以不配置 spring: zipkin: base-url: http://localhost:9411 sleuth: sampler: percentage: 1.0 启动服务,然后访问消费者服务的接口,这时候访问zipkin的控制台http://localhost:9411 14.png 点击依赖分析,可以看到调用服务链,因为这块只涉及到两个服务,所以只有两个,在实际生产环境中,这块可能有很多,到时候看起来就特别直观了。 15.png 16.png 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。
android library打包发布到jitpack.io远程maven仓库中详细教程 本文介绍,如果将自己写的开源代码上传到github.com,然后打包传到jitpack.iomaven仓库,这样别人就可以通过gradle或者maven依赖使用你的开源代码了。 implementation 'com.github.yirenyishi:common-utils:v1.0' 这是我的jar包依赖,大家可以试试哦 一、准备环境 1.新建项目 在androidstudio中新建一个工程项目,建完之后,新建一个module,然后选择library,我们将开源代码放到这个library中。库项目建好后,目录结构如下图所示: 1.png 2.编写或者复制代码 将你要开源的代码复制到库项目中,然后用工程项目依赖这个库,测试通过后,进行下一步。如图所示: 2.png 3.打包环境准备 jitpack官方文档,同志们一定要记得看官方文档,因为软件版本的不断迭代更新,可能某一天这篇帖子的内容就跟不上时代了。我这里使用的gradle版本是4.6,所以要在build中增加如下代码。 3.png 在工程项目的根目录下build.gradle中加入如下依赖,版本号请看官方文档使用最新版。 classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 4.png 然后在你的库项目build.gradle文件中增加如下代码 apply plugin: 'com.github.dcendents.android-maven' group='com.github.yirenyishi' // group='com.github.你的github用户名' 5.png 二、github发布版本 1.新建项目 在github新建一个仓库,注意项目配置,最好是输入项目名称,然后其他的默认,如图所示: https://github.com/yirenyishi/common-utils 这是我项目的地址 6.png 然后将我们的项目上传到github上。 7.png 2.发布release版本 请看上图,我们在code页面。点击release,我们发布一个新的release,其实发布release就是相当于打包的意思。 8.png 三、发布到jitpack.io上 1.发布版本 github release发布成功后,我们打开网址 https://jitpack.io/ ,登陆,使用github授权登陆成功后。 我们选择我们刚才新建的项目,然后点击lookup,就会把你的版本号全部列出来,这时候一定要看打包日志,确认是否打包成功,如果打包失败,我们会看到报错原因。 9.png 2.如何使用 我们点击get it 就可以看到如何使用了,首先添加maven 仓库地址,然后加入如下依赖。 // 格式 com.github.github 用户名: github 仓库名称 : 版本号 implementation 'com.github.yirenyishi:common-utils:v1.0' 三、报错异常处理 1.Configuration on demand is not supported by the current version of the Android Gradle plugin since you are using Gradle version 4.6 or above。 这是因为as的gradle插件不支持你的gradle进行自动配置导致的,按图去掉图中勾选的,就不回报错了。 10.png 2.Gradle wrapper not found. Please add. Using default gradle to build. 这种就是因为你在给github上传的时候没有上传gradle的相关文件。一下图中所示的文件都要上传。 112.png 上传后重新打包发布,如果还提示这个错误,请检查你的gradle版本号。请检查下图中的两个版本号是否一致。如果不一致,删除这个目录。然后进入到项目根目录下,在cmd中执行如下命令 gradle wrapper --gradle-version 4.6,版本号,请使用你自己的版本号,执行命令成功后会重新生成 gradle-wrapper.jar 这个文件,然后上传重新打包发布。 13.png 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。
android8.0采坑 Only fullscreen opaque activities can request orientation 也就是说只有全屏不透明的activity才可以设置方向,既然知道问题所在就好办了。 找到你设置透明的Activity,然后在他的theme中加入如下配置。 <item name="android:windowIsTranslucent">false</item> <item name="android:windowDisablePreview">true</item> 但是我的项目使用的是第三方的库com.ycjiang:imgepreviewlibrary:1.1.3 之前使用点击图片预览大图imgepreviewlibrary都是一次性成功的,但是今天在新的产品上使用,却一直报错,无语。没办法只能自定义activity了。 1.我们自定义一个activity, ImageLookActivity public class ImageLookActivity extends GPreviewActivity { /*** * 重写该方法 * 使用你的自定义布局 **/ @Override public int setContentLayout() { return R.layout.activity_image_look; } } <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".activity.ImageLookActivity"> <!--这个布局是jar包中的--> <include layout="@layout/activity_image_preview_photo"/> </FrameLayout> GPreviewBuilder.from(ModelDetailActivity.this) //这里使用我们自定义的activity .to(ImageLookActivity.class) .setData(mThumbViewInfoList) .setCurrentIndex(position) .setSingleFling(true) .setType(GPreviewBuilder.IndicatorType.Number) // .setType(GPreviewBuilder.IndicatorType.Dot) .start();//启动 点击预览的效果图 微信截图_20180801172357.png 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。
更换安装源 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo 或者 curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo CentOS No package nginx available. yum install epel-release 403权限问题解决 user改为你的用户名,我是root登录,所以就是root 微信截图_20180715210025.png
Android 7.0 上安装apk android.os.FileUriExposedException问题 如果你的系统版本是 8.0+,那你需要多加一个权限,否则无法跳转到安装页 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> 如果安装报错,可能是临时文件访问路径没有配置,或者百度上找到的安装代码是旧版本的,7.0以后不在支持,文章最下面有适配的代码。 android.os.FileUriExposedException: file:///storage/emulated/0/trgis/1511427343635.apk exposed beyond app through Intent.getData() 今天做自动更新的时候,自己下载好的apk安装包调用系统的安装服务就报错,很是郁闷,因为之前的代码是好着的,后来查了下资料,原来是Android N 7.0版本之后不支持之前的写法了,好了直接上解决方案。 1.在AndroidManifest.xml application标签中添加如下代码 <provider android:name="android.support.v4.content.FileProvider" android:authorities="你的包名.fileProvider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> 注意 authorities:你app的包名.fileProvider grantUriPermissions:必须是true,表示授予 URI 临时访问权限 exported:必须是false resource:中的@xml/file_paths是我们接下来要添加的文件 2.在res/xml下新建file_paths.xml <?xml version="1.0" encoding="utf-8"?> <resources> <paths> <external-path name="files_root" path="" /> </paths> </resources> 注意 path:需要临时授权访问的路径(.代表所有路径) name:就是你给这个访问路径起个名字 3.适配AndroidN 以前我们直接 Uri.fromFile(apkFile)构建出一个Uri,现在我们使用 FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile); BuildConfig.APPLICATION_ID直接是应用的包名 Intent intent = new Intent(Intent.ACTION_VIEW); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { /* Android N 写法*/ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".fileProvider", new File("apk地址")); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); } else { /* Android N之前的老版本写法*/ intent.setDataAndType(Uri.fromFile(new File("apk地址")), "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } startActivity(intent); 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 这里写图片描述
1、docker ps //查看redis镜像的imgid 2、docker exec -i -t 镜像id或者镜像名称 /bin/bash // 进入容器 3、进入redis-cli目录 执行命令 dbsize && flushall 然后 exit 4、指定key值缓存清理: 1)登录至指定端口的redis服务器 redis-cli -h 127.0.0.1 -p 6379 其中,127.0.0.1可以写成服务器的IP地址,6379为端口号。 2)查看所有key值 keys * 3)删除指定索引的值 del key //例如 del
Android全能开源项目xUtils3开发教程、简单封装 一、简介 xUtils是一个比较全能的开源项目了, 包含了orm, http(s), image, view注解, 但依然很轻量级(246K), 并且特性强大, 方便扩展。这是xUtils3 的 github地址https://github.com/wyouflf/xUtils3 xUtils3 的一些特性 xUtils支持超大文件(超过2G)上传,更全面的http请求协议支持(11种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响; xUtils3变化较多所以建立了新的项目不在旧版(github.com/wyouflf/xUtils)上继续维护, 相对于旧版本: (1)HTTP实现替换HttpClient为UrlConnection, 自动解析回调泛型, 更安全的断点续传策略; (2)支持标准的Cookie策略, 区分domain, path; (3)事件注解去除不常用的功能, 提高性能; (4)数据库api简化提高性能, 达到和greenDao一致的性能; (5)图片绑定支持gif(受系统兼容性影响, 部分gif文件只能静态显示), webp; 支持圆角, 圆形, 方形等裁剪, 支持自动旋转。 二、环境搭建 在build.gradle中加入如下依赖,编写文章时,最新版本为3.5.0,可以到github或者maven仓库查询最新版本。 //gradle4.4之前 compile 'org.xutils:xutils:3.5.0' //gradle4.4之后 implementation 'org.xutils:xutils:3.5.0' 需要的权限 <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 配置 public class TRApplicaction extends Application { @Override public void onCreate() { super.onCreate(); x.Ext.init(this); x.Ext.setDebug(BuildConfig.DEBUG); // 是否输出debug日志, 开启debug会影响性能. } } 在AndroidManifest文件中注册TRApplicaction <application android:name=".TRApplicaction" .../> </application> 三、注解模块的使用 xUtils3自动注入注解 @ViewInject 真的是很好用,这样就不用一个个findById去注入组件了。 Activity中注解的使用 @ContentView(R.layout.activity_main) public class MainActivity extends AppCompatActivity { cc(R.id.viewpager) ViewPager viewPager; //自动注入,不需要findById了 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); //使用@ContentView注解就不需要了 x.view().inject(MainActivity.this); //这里尽量吧类名加上 ... } } Fragment中注解的使用 @ContentView(R.layout.fragment_home) public class HomeFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return x.view().inject(this, inflater, container); } @Override public void onViewCreated(View v, @Nullable Bundle savedInstanceState) { super.onViewCreated(v, savedInstanceState); } } 绑定事件 /** * 1. 方法必须私有限定, * 2. 方法参数形式必须和type对应的Listener接口一致. * 3. 注解参数value支持数组: value={id1, id2, id3} * 4. 其它参数说明见{@link org.xutils.event.annotation.Event}类的说明. **/ @Event({R.id.new_model_btn,...}) private void onClick(View v){ .... } 四、网络请求 由于Android6.0版本之后将HttpClient替换为UrlConnection,所以修改老项目的时候一定要注意。 这里我们简单封装一下, 请求参数通过map传过来,然后通过回调返回请求结果。 /** * @author nelson */ public class c { private static final String BASE_URL = "http://10.168.11.11/"; public static void get(String url, Map<String, Object> parms, final GetDataCallback callback) { RequestParams params = new RequestParams(GetDataTask.BASE_URL + url); if(parms!=null){ for (String key : parms.keySet()) { params.addParameter(key, parms.get(key)); } } x.http().get(params, new Callback.CommonCallback<String>() { @Override public void onSuccess(String result) { callback.success(result); } @Override public void onError(Throwable ex, boolean isOnCallback) { callback.failed(); } @Override public void onCancelled(CancelledException cex) {} @Override public void onFinished() {} }); } public static void post(String url, Map<String, Object> parms, final GetDataCallback callback) { RequestParams params = new RequestParams(GetDataTask.BASE_URL + url); if(parms!=null){ for (String key : parms.keySet()) { params.addParameter(key, parms.get(key)); } } x.http().post(params, new Callback.CommonCallback<String>() { @Override public void onSuccess(String result) { if(callback!=null){ callback.success(result); } } @Override public void onError(Throwable ex, boolean isOnCallback) { if(callback!=null){ callback.failed(); } } @Override public void onCancelled(CancelledException cex) {} @Override public void onFinished() {} }); } /**上传文件*/ public static void uplodFile(List<String> path, Map<String, Object> map, final GetDataCallback callback) { RequestParams params = new RequestParams(GetDataTask.BASE_URL+"upload"); params.setMultipart(true); for (String key : map.keySet()) { params.addBodyParameter(key, map.get(key).toString()); } for (int i = 0; i < path.size(); i++) { params.addBodyParameter("uploadfile" + i, new File(path.get(i))); } x.http().post(params, new Callback.CommonCallback<String>() { @Override public void onSuccess(String result) { callback.success(result); } @Override public void onError(Throwable ex, boolean isOnCallback) { callback.failed(); } @Override public void onCancelled(CancelledException cex) {} @Override public void onFinished() {} }); } /**回调接口*/ public interface GetDataCallback { void success(String result); void failed(String... args); } } 发起网络请求 Map<String,Object> map = new HashMap<>(); map.put("pageNumber",page); map.put("typeid",typeid); //如果请求不需要参数,传null // GetDataTask.post("app/types", null, new GetDataTask.GetDataCallback(){} GetDataTask.post("app/types", map, new GetDataTask.GetDataCallback() { @Override public void success(String response) { Gson gson = new Gson(); //后台返回来的json格式,其他格式自己处理 Result result = gson.fromJson(response, Result.class); } @Override public void failed(String... args) { } }); 五、绑定图片 //通过ImageOptions.Builder().set方法设置图片的属性 ImageOptions imageOptions= new ImageOptions.Builder().setFadeIn(true).build(); //淡入效果 //ImageOptions.Builder()的一些其他属性: .setCircular(true) //设置图片显示为圆形 .setSquare(true) //设置图片显示为正方形 .setCrop(true).setSize(200,200) //设置大小 .setAnimation(animation) //设置动画 .setFailureDrawable(Drawable failureDrawable) //设置加载失败的动画 .setFailureDrawableId(int failureDrawable) //以资源id设置加载失败的动画 .setLoadingDrawable(Drawable loadingDrawable) //设置加载中的动画 .setLoadingDrawableId(int loadingDrawable) //以资源id设置加载中的动画 .setIgnoreGif(false) //忽略Gif图片 .setParamsBuilder(ParamsBuilder paramsBuilder) //在网络请求中添加一些参数 .setRaduis(int raduis) //设置拐角弧度 .setUseMemCache(true) //设置使用MemCache,默认true x.image().bind(imageView, url, imageOptions); // assets file x.image().bind(imageView, "assets://test.gif", imageOptions); // local file x.image().bind(imageView, new File("/sdcard/test.gif").toURI().toString(), imageOptions); x.image().bind(imageView, "/sdcard/test.gif", imageOptions); x.image().bind(imageView, "file:///sdcard/test.gif", imageOptions); x.image().bind(imageView, "file:/sdcard/test.gif", imageOptions); x.image().bind(imageView, url, imageOptions, new Callback.CommonCallback<Drawable>() { @Override public void onSuccess(Drawable result) { } @Override public void onError(Throwable ex, boolean isOnCallback) { } @Override public void onCancelled(CancelledException cex) { } @Override public void onFinished() { } x.image().loadDrawable(url, imageOptions, new Callback.CommonCallback<Drawable>() {...}); // 用来获取缓存文件 x.image().loadFile(url, imageOptions, new Callback.CommonCallback<File>() {...}); 六、异步执行任务 x.task().run(new Runnable() { @Override public void run() { //异步任务 } }); x.task().post(new Runnable() { @Override public void run() { //同步代码 } }); 七、ORM 数据库操作 Application中进行初始化配置DaoConfig DbManager.DaoConfig daoConfig = new DbManager.DaoConfig() //设置数据库名,默认xutils.db .setDbName("myapp.db") //设置数据库路径,默认存储在app的私有目录 .setDbDir(new File("/mnt/sdcard/")) //设置数据库的版本号 .setDbVersion(2) //设置数据库打开的监听 .setDbOpenListener(new DbManager.DbOpenListener() { @Override public void onDbOpened(DbManager db) { //开启数据库支持多线程操作,提升性能,对写入加速提升巨大 db.getDatabase().enableWriteAheadLogging(); } }) //设置数据库更新的监听 .setDbUpgradeListener(new DbManager.DbUpgradeListener() { @Override public void onUpgrade(DbManager db, int oldVersion, int newVersion) { } }) //设置表创建的监听 .setTableCreateListener(new DbManager.TableCreateListener() { @Override public void onTableCreated(DbManager db, TableEntity<?> table){ Log.i("JAVA", "onTableCreated:" + table.getName()); } }); 操作数据库 // User类在最底下 DbManager db = x.getDb(daoConfig); db.dropDb(); // 删除数据库 db.dropTable(User.class); // 删除表 db.save(new User("nelson")); //新增数据 db.delete(User.class); //mtb_user表中数据将被全部删除 //条件删除: WhereBuilder b = WhereBuilder.b(); b.and("id",">",2); //构造修改的条件 b.and("id","<",4); db.delete(User.class, b); 修改数据 //第一种写法: ChildInfo first = db.findFirst(ChildInfo.class); first.setcName("zhansan2"); db.update(first,"c_name"); //c_name:表中的字段名 //第二种写法: WhereBuilder b = WhereBuilder.b(); b.and("id","=",first.getId()); //构造修改的条件 KeyValue name = new KeyValue("c_name","zhansan3"); db.update(ChildInfo.class,b,name); //第三种写法: first.setcName("zhansan4"); db.saveOrUpdate(first); 条件查询 Parent test = db.selector(Parent.class).where("id", "in", new int[]{1, 3, 6}).findFirst(); long count = db.selector(Parent.class).where("name", "LIKE", "w%").and("age", ">", 32).count(); List<Parent> testList = db.selector(Parent.class).where("id", "between", new String[]{"1", "5"}).findAll(); @Table(name = "mtb_user",onCreated = "") public class User { /** * name = "id":数据库表中的一个字段 * isId = true:是否是主键 * autoGen = true:是否自动增长 * property = "NOT NULL":添加约束 */ @Column(name = "id",isId = true,autoGen = true,property = "NOT NULL") private int id; @Column(name = "username") private String username; ...... } 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 这里写图片描述
BP算法是一种有监督式的学习算法,其主要思想是:输入学习样本,使用反向传播算法对网络的权值和偏差进行反复的调整训练,使输出的向量与期望向量尽可能地接近,当网络输出层的误差平方和小于指定的误差时训练完成,保存网络的权值和偏差。具体步骤如下: (1)初始化,随机给定各连接权[w],[v]及阀值θi,rt。 (2)由给定的输入输出模式对计算隐层、输出层各单元输出 bj=f(■wijai-θj) ct=f(■vjtbj-rt) 式中:bj为隐层第j个神经元实际输出;ct为输出层第t个神经元的实际输出;wij为输入层至隐层的连接权;vjt为隐层至输出层的连接权。 dtk=(ytk-ct)ct(1-ct) ejk=[■dtvjt] bj(1-bj) (3)选取下一个输入模式对返回第2步反复训练直到网络设输出误差达到要求结束训练。 传统的BP算法,实质上是把一组样本输入/输出问题转化为一个非线性优化问题,并通过负梯度下降算法,利用迭代运算求解权值问题的一种学习方法,但其收敛速度慢且容易陷入局部极小,为此提出了一种新的算法,即高斯消元法。 折叠改进的BP网络算法 2.1 改进算法概述 此前有人提出:任意选定一组自由权,通过对传递函数建立线性方程组,解得待求权。本文在此基础上将给定的目标输出直接作为线性方程等式代数和来建立线性方程组,不再通过对传递函数求逆来计算神经元的净输出,简化了运算步骤。没有采用误差反馈原理,因此用此法训练出来的神经网络结果与传统算法是等效的。其基本思想是:由所给的输入、输出模式对通过作用于神经网络来建立线性方程组,运用高斯消元法解线性方程组来求得未知权值,而未采用传统BP网络的非线性函数误差反馈寻优的思想。 2.2 改进算法的具体步骤 对给定的样本模式对,随机选定一组自由权,作为输出层和隐含层之间固定权值,通过传递函数计算隐层的实际输出,再将输出层与隐层间的权值作为待求量,直接将目标输出作为等式的右边建立方程组来求解。 现定义如下符号(见图1):x (p)输入层的输入矢量;y (p)输入层输入为x (p)时输出层的实际输出矢量;t (p)目标输出矢量;n,m,r分别为输入层、隐层和输出层神经元个数;W为隐层与输入层间的权矩阵;V为输出层与隐层间的权矩阵。具体步骤如下: (1)随机给定隐层和输入层间神经元的初始权值wij。 (2)由给定的样本输入xi(p)计算出隐层的实际输出aj(p)。为方便起见将图1网络中的阀值写入连接权中去,令:隐层阀值θj=wnj,x(n)=-1,则: aj(p)=f(■wijxi(p)) (j=1,2…m-1)。 (3)计算输出层与隐层间的权值vjr。以输出层的第r个神经元为对象,由给定的输出目标值tr(p)作为等式的多项式值建立方程,用线性方程组表示为: a0(1)v1r+a1(1)v2r+…+am(1)vmr=tr(1)a0(2)v1r+a1(2)v2r+…+am(2)vmr=tr(2) ……a0(p)v1r+a1(p)v2r+…+am(p)vmr=tr(p) 简写为: Av=T 为了使该方程组有唯一解,方程矩阵A为非奇异矩阵,其秩等于其增广矩阵的秩,即:r(A)=r(A┊B),且方程的个数等于未知数的个数,故取m=p,此时方程组的唯一解为: Vr=v0r,v2r,…vmr (4)重复第三步就可以求出输出层m个神经元的权值,以求的输出层的权矩阵加上随机固定的隐层与输入层的权值就等于神经网络最后训练的权矩阵。 折叠计算机运算实例 现以神经网络最简单的XOR问题用VC编程运算进行比较(取神经网络结构为2-4-1型),传统算法和改进BP算法的误差(取动量因子α=0.001 5,步长η=1.653) BP神经网络模型拓扑结构包括: 输入层(Input),这一区域相当于外界的刺激,是刺激的来源并且将刺激传递给神经元。 隐藏层( Hide layer),这一区域表示神经元相互之间传递刺激,相当于人脑里面。 输出层(Output layer),这一区域表示神经元经过多层次相互传递后,对外界的反应。 这里写图片描述 BP反馈机制 简单的描述就是,输入层将刺激传递给隐藏层,隐藏层通过神经元之间,联系的权重和激活函数,将刺激传到输出层,输出层整理隐藏层处理后的刺激,产生最终结果。 若有正确的结果,那么将正确的结果和产生的结果进行比较,得到误差,再逆推对神经网中的链接权重进行反馈修正,从而来完成学习的过程。 这就是BP (Back Propagation)神经网的反馈机制,也是名字的来源,即运用向后反馈的学习机制,来修正神经网中的权重,最终达到输出正确结果的目的。 双向信号传播 BP算法由数据流的前向(正向)传播和误差信号的反向传播两个过程构成。 –正向传播时,传播方向为输入层-隐层-输出层,每层神经元的状态只影响下一层神经元。 –若在输出层得不到期望的输出,则转向误差信号的反向传播流程。 这两个过程的交替进行 –在权向量空间,执行误差函数梯度下降策略,动态迭代搜索一组权向量。 –使网络误差函数达到最小值,从而完成信息提取,和记忆过程。 正向传播 设BP神经网络的输入层有n个节点,隐层有q个节点,输出层有m个节点,输入层与隐层之间有权值为vki,隐层与输出层之间的权值为wjk,三层神经网络的拓扑结构,如下图所示。 这里写图片描述 隐层传递函数为ƒ1(•),输出层的传递函数为ƒ2(•),则隐层节点的输出为(将阈值写入求和项中,k=1,2,…q) 这里写图片描述 •输出层节点的输出为(j=l,2,…,m): 这里写图片描述 •至此BP网络完成n维空间向量对m维空间的近似映射 反向传播 反向传播,目的是传递误差信号 –所以要进行定义误差函数、输出层权值变化、以及隐层权值变化等操作。 –以上作用可分别以数据公式表达出来: 这里写图片描述 –式中x1,x2,…,xq为输入信号,wj1,wj2,…,wji,…,wjn为神经元k之权值,uk为线性组合结果,θk为阈值,f (•)为激活函数,yk为神经元k的输出。 这里写图片描述 –若把输入的维数增加一维,则可把阈值θk包括进去。 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
Git使用教程 idea、webstorm、phpstorm、androidstudio中git使用教程 首先你的安装git,windows版点击下载,linux用apt或者yum可以直接安装。 案例我们会在coding上新建一个测试项目,使用git方式。 一、新建项目 新建项目后记得复制git仓库的地址。 这里写图片描述 这里写图片描述 二、上传项目到git仓库 在你的idea里新建git仓库,这是新建本地仓库,等会会同步到线上git仓库 这里写图片描述 新建后如果代码不是文件名不是绿色的表示没有加入到git索引中 这里写图片描述 将需要上传的文件按照下图方式add 这里写图片描述 添加后,相应的文件名会变成绿色 这里写图片描述 然后commit项目,提交项目,这里是先提交带本地仓库,然后push到线上仓库。注意这里和svn不一样。 这里一定要选择commit and push .(先提交带本地仓库,然后push到线上仓库) 这里写图片描述 如果第一次提交项目的话,这里是没有远程地址。点击蓝色的字,定义远程地址。如果之前提交过的话,这里是有分支信息的。 这里写图片描述 输入刚才coding仓库中复制的地址,然后应用 这里写图片描述 然后我们就可以看到远程的分支信息了吗,默认是master分支,如果你想提交到其他的分支上,点击master修改分支。 这里写图片描述 三、从git仓库检出项目 选择checkout 这里写图片描述 输入远程仓库地址,然后clone即可 这里写图片描述 四、更新项目、解决冲突 按照图片中的步骤,顺序不能乱,先stash,然后pull,最后unstash 这里写图片描述 stash会让你输入标记名称,unstash的时候选择想要的标记 这里写图片描述 如果代码有冲突,我们需要编辑冲突,我们一般选择merge,就是合并的意思,当然你也可以不合并直接使用线上的或者暂存里的代码。 这里写图片描述 合并代码的时候,图片中描述了,三屏分别对应的是什么。 这里写图片描述 五、切换git分支 右下角有个当前分支名称,点击会谈出一分支信息。如果你想切换分支,选中分支,点击右键checkout即可。 这里写图片描述 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
如果你的网站想接入微信支付,那么你的有个公众号(微信公众平台),然后开通支付功能,在微信商户平台操作。仔细看哦,这是两个平台,商家平台有详细的接入流程,这里只介绍程序方面。 1、准备 准备商家帐户 下载证书,重置密钥(密钥重置后请妥善保管) 内网穿透软件(微信支付成功后会有回调) 2、代码 加入依赖 微信支付比较麻烦,所以我们采用第三方封装的jar包 compile group: 'com.github.binarywang', name: 'weixin-java-pay', version: '3.0.0' 新建 pay.properties #wxpay WX.APPID=wxdd088f0c1d70cbf9 WX.MCHID=你的商户id WX.MCHKEY=你的密钥 WX.SUBAPPID= WX.SUBMCHID= WX.KEYPATH= #下面这个是微信回调地址,要保证外网可以访问,内网测试就用内网穿透软件 WX.NOTIFYURL=http://tdcloud.trmap.cn/reward/getOrderNotifyResult WX.TRADETYPE=NATIVE 配置类 WxPayConfig 将配置文件里的值读取出来,微信支付需要的相关配置 @Configuration @PropertySource(value="classpath:pay.properties", ignoreResourceNotFound=true) public class WxPayConfig { /** * http请求连接超时时间 */ private int httpConnectionTimeout = 5000; /** * http请求数据读取等待时间 */ private int httpTimeout = 10000; private String appId; private String subAppId; private String mchId; private String mchKey; private String subMchId; private String notifyUrl; private String tradeType; private String signType; private SSLContext sslContext; private String keyPath; private boolean useSandboxEnv = false; private String httpProxyHost; private Integer httpProxyPort; private String httpProxyUsername; private String httpProxyPassword; public String getKeyPath() { return keyPath; } /** * 设置证书 * * @param keyPath apiclient_cert.p12的文件的绝对路径 */ public void setKeyPath(String keyPath) { this.keyPath = keyPath; } /** * 商户号 */ public String getMchId() { return this.mchId; } public void setMchId(String mchId) { this.mchId = mchId; } /** * 商户密钥 */ public String getMchKey() { return this.mchKey; } public void setMchKey(String mchKey) { this.mchKey = mchKey; } /** * 公众号appid */ public String getAppId() { return this.appId; } public void setAppId(String appId) { this.appId = appId; } /** * 服务商模式下的子商户公众账号ID */ public String getSubAppId() { return this.subAppId; } public void setSubAppId(String subAppId) { this.subAppId = subAppId; } /** * 服务商模式下的子商户号 */ public String getSubMchId() { return this.subMchId; } public void setSubMchId(String subMchId) { this.subMchId = subMchId; } /** * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。 */ public String getNotifyUrl() { return this.notifyUrl; } public void setNotifyUrl(String notifyUrl) { this.notifyUrl = notifyUrl; } /** * 交易类型 * <pre> * JSAPI--公众号支付 * NATIVE--原生扫码支付 * APP--app支付 * </pre> */ public String getTradeType() { return this.tradeType; } public void setTradeType(String tradeType) { this.tradeType = tradeType; } /** * 签名方式 * 有两种HMAC_SHA256 和MD5 * @see com.github.binarywang.wxpay.constant.WxPayConstants.SignType */ public String getSignType() { return this.signType; } public void setSignType(String signType) { this.signType = signType; } public SSLContext getSslContext() { return this.sslContext; } public void setSslContext(SSLContext sslContext) { this.sslContext = sslContext; } /** * 微信支付是否使用仿真测试环境 * 默认不使用 */ public boolean useSandbox() { return this.useSandboxEnv; } /** * 设置是否使用沙箱仿真测试环境 */ public void setUseSandboxEnv(boolean useSandboxEnv) { this.useSandboxEnv = useSandboxEnv; } public SSLContext initSSLContext() throws WxPayException { if (StringUtils.isBlank(this.getMchId())) { throw new WxPayException("请确保商户号mchId已设置"); } if (StringUtils.isBlank(this.getKeyPath())) { throw new WxPayException("请确保证书文件地址keyPath已配置"); } InputStream inputStream; final String prefix = "classpath:"; String fileHasProblemMsg = "证书文件【" + this.getKeyPath() + "】有问题,请核实!"; String fileNotFoundMsg = "证书文件【" + this.getKeyPath() + "】不存在,请核实!"; if (this.getKeyPath().startsWith(prefix)) { String path = StringUtils.removeFirst(this.getKeyPath(), prefix); if (!path.startsWith("/")) { path = "/" + path; } inputStream = WxPayConfig.class.getResourceAsStream(path); if (inputStream == null) { throw new WxPayException(fileNotFoundMsg); } } else { try { File file = new File(this.getKeyPath()); if (!file.exists()) { throw new WxPayException(fileNotFoundMsg); } inputStream = new FileInputStream(file); } catch (IOException e) { throw new WxPayException(fileHasProblemMsg, e); } } try { KeyStore keystore = KeyStore.getInstance("PKCS12"); char[] partnerId2charArray = this.getMchId().toCharArray(); keystore.load(inputStream, partnerId2charArray); this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); return this.sslContext; } catch (Exception e) { throw new WxPayException(fileHasProblemMsg, e); } finally { IOUtils.closeQuietly(inputStream); } } /** * http请求连接超时时间 */ public int getHttpConnectionTimeout() { return this.httpConnectionTimeout; } public void setHttpConnectionTimeout(int httpConnectionTimeout) { this.httpConnectionTimeout = httpConnectionTimeout; } /** * http请求数据读取等待时间 */ public int getHttpTimeout() { return this.httpTimeout; } public void setHttpTimeout(int httpTimeout) { this.httpTimeout = httpTimeout; } public String getHttpProxyHost() { return httpProxyHost; } public void setHttpProxyHost(String httpProxyHost) { this.httpProxyHost = httpProxyHost; } public Integer getHttpProxyPort() { return httpProxyPort; } public void setHttpProxyPort(Integer httpProxyPort) { this.httpProxyPort = httpProxyPort; } public String getHttpProxyUsername() { return httpProxyUsername; } public void setHttpProxyUsername(String httpProxyUsername) { this.httpProxyUsername = httpProxyUsername; } public String getHttpProxyPassword() { return httpProxyPassword; } public void setHttpProxyPassword(String httpProxyPassword) { this.httpProxyPassword = httpProxyPassword; } } 发起支付,获取生成二维码的地址 微信支付的单位是分,例如你支付金额是9.9元(保留两位小数),那你微信支付的时候支付金额是9.9元*100 = 990分,最后支付金额是整数。 封装请求参数,请求微信接口后会返回生成二维码的地址 @Autowired private WxPayConfig payConfig; @Autowired private WxPayService wxService; //上边依赖第三方库提供的 @RequestMapping("wxpay") private ResponseEntity<Result> rewardWxpay(@RequestParam Double money, HttpServletRequest request,WxPayUnifiedOrderRequest wxreq){ try { @RequestMapping("wxpay") private ResponseEntity<Result> rewardWxpay(String userid,@RequestParam String produceid,@RequestParam Double money,String remark, HttpServletRequest request,WxPayUnifiedOrderRequest wxreq){ try { // TODO 生成你的订单信息,然后返回订单id,这里代码忽略,根据自己的业务调整 String ip = req.getHeader("x-forwarded-for"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = req.getHeader("Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = req.getHeader("WL-Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = req.getRemoteAddr(); } if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { // 多次反向代理后会有多个ip值,第一个ip才是真实ip if( ip.indexOf(",")!=-1 ){ ip = ip.split(",")[0]; } } request.setBody("实景在线打赏-"); // 支付标题 request.setOutTradeNo(你自己的订单编号); // 金额一定要以分为单位,所以最后支付金额为整数 String string = String.valueOf((reward.getMoney()*100)); request.setTotalFee(Integer.parseInt(string.substring(0,string.indexOf(".")))); request.setAppid(payConfig.getAppId()); request.setMchId(payConfig.getMchId()); request.setNotifyUrl(payConfig.getNotifyUrl()); request.setSpbillCreateIp(ip); request.setTradeType(payConfig.getTradeType()); //this.wxService.unifiedOrder(request) 发起请求,获取地址,然后根据地址生成二维码 return SUCCESS(this.wxService.unifiedOrder(request);); } catch (WxPayException e) { e.printStackTrace(); } return ERROR("发起支付失败"); } } catch (WxPayException e) { e.printStackTrace(); } return ERROR("发起支付失败"); } 生成二维码 根据上一步请求返回的地址生成二维码,生成的二维码是base64格式的字节码,前台用img标签直接显示即可,这时候用户就可以扫描生成的二维码进行支付了 /** * <pre> * 扫码支付模式二生成二维码的方法 * 对应链接格式:weixin://wxpay/bizpayurl?sr=XXXXX。请商户调用第三方库将code_url生成二维码图片。 * 该模式链接较短,生成的二维码打印到结账小票上的识别率较高。 * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5 * </pre> * * @param codeUrl * 微信返回的交易会话的二维码链接 * @param logoFile * 商户logo图片的文件对象,可以为空 * @param sideLength * 要生成的二维码的边长,如果为空,则取默认值400 * @return 生成的二维码的字节数组 */ @PostMapping(value = "/createScanPayQrcode") public ResponseEntity<Result> createQrcode(String codeUrl,File logoFile, Integer sideLength) { byte[] by = this.wxService.createScanPayQrcodeMode2(codeUrl, logoFile, sideLength); return SUCCESS(by); } 回调接口 用户扫描支付后,微信会异步通知,请求地址为配置文件中的接口地址,所以要保证公网可以访问。微信共会请求8次回调接口,如果处理成功后,将不在请求回调接口 /** * 读取支付结果通知 * * @param xmlData * @throws WxPayException */ @RequestMapping(value = "/getOrderNotifyResult", method = RequestMethod.POST) public String getOrderNotifyResult(@RequestBody String xmlData,HttpServletRequest request, HttpServletResponse response) throws WxErrorException, WxPayException { WxPayOrderNotifyResult orderNotifyResult = this.wxService.parseOrderNotifyResult(xmlData); String noticeStr = null; try { BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); // 支付成功,商户处理后同步返回给微信参数 if (!orderNotifyResult.getResultCode().equals("SUCCESS")) { // 支付失败, 记录流水失败 System.out.println("===============支付失败=============="); noticeStr = setXML("FAIL", "支付失败"); outputStream.write(noticeStr.getBytes()); outputStream.flush(); outputStream.close(); } else { // TODO 处理你的业务,修改订单状态等 // 通知微信已经收到消息,不要再给我发消息了,否则微信会8连击调用本接口 noticeStr = setXML("SUCCESS", "支付成功"); outputStream.write(noticeStr.getBytes()); outputStream.flush(); outputStream.close(); } } catch (Exception e) { e.printStackTrace(); } return noticeStr; } public String setXML(String return_code, String return_msg) { return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>"; } 到这里,网站接入微信扫码支付,代码部分就全部完成了,最后效果图请看下方图片。 这里写图片描述 这里写图片描述 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
手动下载genymotion ova镜像文件,genymotion下载失败、慢解决方案 genymotion 模拟器基本是开发人员必备的软件了,但是有时候下载镜像文件的时候容易报错,要不就是很慢,但是迅雷有p2p加速功能,所以可以使用迅雷加快下载速度 这里写图片描述 1.安装包下载 https://pan.baidu.com/s/1q6g7YtLYgARQOSIATsyLww 最好是下载genymotion和viturlbox版本对应的包,写文章时,最新版本是2.12 genymotion-2.12.0-vbox.exe。共享文件里的ova文件,可以双击直接导入,导入的时候注意重置网络。 2.获取下载地址 进去登录账户,选择要下载的镜像文件,等到进入下载界面的时候,取消下载就可以。然后进入目录C:\Users\Administrator\AppData\Local\Genymobile,用记事本等打开日志文件genymotion.log,如下图所以,会看到相对应版本的下载地址,复制地址,使用迅雷进行下载。 这里写图片描述 这里写图片描述 我这网速不是很好,但是迅雷下载还是很快。 这里写图片描述 3.部署 将下载的ova文件,复制到C:\Users\Administrator\AppData\Local\Genymobile\Genymotion\ova目录下,然后在genymotion中重新添加,选择相应版本。然后会发现他不下载,直接进入到部署步骤。部署完成后,就可以启动了哦。 这里写图片描述 这里写图片描述 这里写图片描述 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
现在前后分离已经是很常见的一种开发方式了,所以难免会遇到跨域问题,之前用的比较多的是jsonp(本人表示没用过),之前我遇到这种问题一般都是用nginx做反向代理实现跨域请求。 不过springmvc4.2版本增加了对cors的支持,所以解决办法就更简单了,后端一个全局配置轻松解决跨域问题,比之前的都简单轻松。 cors协议不懂的可以百度哦,这里就不废话了。 由于现在大部分项目都是基于springboot做的,目前微服务的开发模式也很火,所以这块就用springboot做案例,用xml配置方式的自己看着改。 1、 全局配置 @Configuration public class WebAppConfigurer extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") //.allowedOrigins("http://192.168.89.89") //rest集中请求方式 .allowedMethods("GET", "POST","DELETE") .allowCredentials(false).maxAge(3600); } } 2、单个接口配置 @CrossOrigin(origins = "*", maxAge = 3600) //* 可以改成ip地址 @PostMapping("save") public ResponseEntity<Result> addNote(@RequestParam String noteName){ 3、 微服务相关帖子 微服务架构搭建:Consul+sleuth+zipkin+Feign/Ribbon+SpringConfig+Zuul+Hystrix Dash-Board-Turbine 码农笔录二维码
好郁闷,这句sql看着很正常,可是他就是报错,百度查资料基本都是修mysql的配置文件 SELECT item_id from tb_order_item as aa,tb_order as bb WHERE aa.order_id = bb.order_id GROUP BY aa.item_id ORDER BY bb.create_time desc 解决方法 SELECT item_id from tb_order_item as aa,tb_order as bb WHERE aa.order_id = bb.order_id GROUP BY aa.item_id ORDER BY MAX(bb.create_time) desc 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
简化 @RestController @RequestMapping ,自定义spring注解 @RestController @RequestMapping(value = "/mark") public class MarkController 这是我们经常看到的两个注解,每次都要写两行,为了提高速度,所以要封装一下这俩个注解。不知道为什么spring为什么不封装一个,比如他官方提供的PostMapping等,以下是注解的代码。 /**@Title: TRestController.java * @Description: 简化 @RestController @RequestMapping * @author nelson * @date 2018年4月14日 下午1:50:33 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @RestController @RequestMapping public @interface TRestController { /** * Alias for {@link RequestMapping#name}. */ @AliasFor(annotation = RequestMapping.class) String name() default ""; /** * Alias for {@link RequestMapping#value}. */ @AliasFor(annotation = RequestMapping.class) String[] value() default {}; /** * Alias for {@link RequestMapping#path}. */ @AliasFor(annotation = RequestMapping.class) String[] path() default {}; } 封装后就这样使用即可,是不是省了一行代码 @TRestController("forum") public class ForumController @Target(ElementType.TYPE) 指定只能在类上使用 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
alpine docker exec: "/bin/bash": stat /bin/bash: no such file or directory 解决方案 docker exec -it 029e6df30836 /bin/bash exec: "/bin/bash": stat /bin/bash: no such file or directory 我们一般可能会在容器启动后进入容器,常用的是docker attach 镜像id,但是启动镜像的时候如果没有带 参数 -it的话,attach进去后可能是日志界面,并不能执行命令。所以我们会用docker exec -it 镜像id /bin/bash/ 平常的容器一般都可以执行/bin/bash,很是alpine没有,改成 docker exec -it 镜像id sh 就好了。
mysql官网下载最新版升级版本多版本安装教程## 有好多人经常问我有没有mysql的安装包,很是无语,因为问的很多 ,所以今天就写一个教程。 1、下载安装包### 点击打开下载地址:mysql官网下载地址,打开之后按照图片箭头步骤提示,打开找到相应位置。 这里写图片描述 在图片所在位置继续往下拉,就会看到下载地址,然后点击下图中圈中的那个图片,就会跳转到安装包下载地址。 这里写图片描述 然后下载图片中选择的安装包,download 这里写图片描述 这里就不要登录了,直接下载 这里写图片描述 2、安装mysql### 如果之前安装过mysql,请跳过这一步直接进入第三步。 双击安装包,同意安装协议 这里写图片描述 选择server,然后一直下一步,如果遇到提示需要安装依赖库就都安装一下。 这里写图片描述 这里写图片描述 一般端口都是默认3306,但是如果3306端口被占,可以修改为其他的,下一步输入root的密码。 这里写图片描述 到这里就算安装完成了 3、更新安装mysql### 更新安装mysql是指你之前已经装过mysql,现在要更新到新版本,如果之前没有装过mysql,请跳转到第二步。 双击下载的安装包,会打开安装提示,然后点击next,选择你要升级的版本,如下图所示,可以选择很多版本,但是都是些小版本。假如说5.5版本要升级到5.7是不行的,但是你要继续更新,到最后会让你安装5.7版本的,但是又不能跳过,所以这块也是一直下一步。 这里写图片描述 选择好版本点击next,然后会下载安装包,然后跳转到安装页面,选择exe那个,一直往下走。 这里写图片描述 这里要输入就版本的密码,然后check,成功后继续next,然后Exeute一直默认往下走 这里写图片描述 到这里重点就来了,如果你只是想更新,已经达到你的木的,你就可以关闭安装程序了。如果你想卸载就版本,就remove;图片中我就是安装了两个版本,如果想保留旧版本,并且安装新的版本,就点击add。 这里写图片描述 选择*64版本的,然后用箭头转移到右边,然后一直下一步,就会安装新的版本。 这里有个注意的是,如果要安装两个版本,相当于是装了两个mysql,互不干扰,所以后边会让你修改端口号,输入root的密码等。 这里写图片描述 这里写图片描述 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
tengine2.2.0编译安装、开机启动、反向代理配置及健康检查 tengine是由淘宝发起的一个基于nginx的开源项目,nginx的吞吐量比较高、快速、稳定,而且反向代理和负载均衡使用nginx,也是最常见的。本文介绍在Linux(centos)下如何编译安装,并设置nginx开机自启动及配置反向代理和配置健康检查。官网地址 1、下载tengine 下载 下边是使用linux直接下载,或者用windows下载然后上传到linux上,但是这样比较麻烦。 wget http://tengine.taobao.org/download/tengine-2.2.0.tar.gz 解压 tar -zxvf tengine-2.2.0.tar.gz 2、编译安装 安装编译依赖 yum -y install gcc gcc-c++ autoconf automake yum -y install openssl-devel pcre-devel zlib-devel 编译tengine 进入tengine解压目录,必须进入解压以后的目录,负责后边无法继续下去 cd tengine-2.2.0 检查编译环境,注意:--prefix 后面跟着的是你的nginx安装的路径 ./configure --prefix=/home/msoft/tengine/ \ --with-http_gzip_static_module \ --with-http_realip_module \ --with-http_stub_status_module \ --with-http_concat_module \ --with-pcre 如果这一步没有提示缺少依赖的话,说明检查通过,如果缺少依赖,请看上边安装依赖库。接下来就是编译了。 make && make install 到这里就已经算是编译安装好了,如果改天不想用了,可以直接删除掉这个安装目录。然后测试安装结果,执行启动命令之后,nginx的默认端口是80,windows可以直接访问linux的ip,linux 下使用curl localhost,或者wget localhost //进入你的安装目录 cd /home/msoft/tengine/sbin //执行nginx的启动文件 ./nginx 3、加入系统服务,设置开机启动 创建启动文件 点击nginx启动文件下载启动文件,可能会因为windows的编码问题导致linux无法识别,所以用windows下载,然后用记事本之类的软件打开。然后Linux进入cd /etc/init.d/,用vi编辑器创建文件vi nginx。吧记事本里的直接全部复制到vi编辑器里。 注意:这块一定要记得修改你的安装路径,和配置路径 //这是你电脑nginx的启动文件的地址, nginx="/home/msoft/tengine/sbin/nginx" //nginx的配置文件地址 NGINX_CONF_FILE="/home/msoft/tengine/conf/nginx.conf" 然后保存文件,这时候会发现nginx这文件颜色不是绿色的,表示没有执行权限。 配置开机启动 赋予执行权限 chmod 755 nginx 将该文件加到系统服务 chkconfig --add nginx 设置开机启动 chkconfig nginx on 查看是否添加成功 chkconfig --list nginx 启动,停止,重新装载 service nginx start|stop|reload 4、配置反向代理 进入你的nginx的你装目录 cd /home/msoft/tengine/conf/ vi nginx.conf 配置反向代理,目录结构看下图,可以配置多个,案例中只配置了一个。 upstream myback { server localhost:8080; server 192.168.1.110:8080; } location / { proxy_pass http://myback ; proxy_set_header X-Real-IP $remote_addr; client_max_body_size 100m; } 这里写图片描述 注意:这块直接配置ip:端口,也可以转发到具体某个模块上,图中/yasaka,那个就是配置的某个模块。到时候在浏览器直接输入以下地址即可被代理到具体的后端服务器上。 http://yasaka.iask.in/ http://yasaka.iask.in/yasaka 5、配置健康检查 tengine自带健康检查,加入如下配置,配置结构看图片 这是案例地址http://yasaka.iask.in/status location /status { check_status; } 然后在反向代理中加入,那些后端服务器需要进行健康检查。 check interval=3000 rise=2 fall=5 timeout=1000 type=http; check_http_send "HEAD / HTTP/1.0\r\n\r\n"; check_http_expect_alive http_2xx http_3xx; 这里写图片描述 注意:这里记得换行,否则会解析错误 这里写图片描述 这里写图片描述 6、总结 本文介绍了tengine(nginx)编译安装,加入开机启动,配置反向代理和健康检查一整套。具体的细节或者哪一个模块想深入,请查阅官方文档,我就不在这里重复的造轮子了。 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
springcloud微服务实战:Eureka+Zuul+Feign/Ribbon+Hystrix Turbine+SpringConfig+sleuth+zipkin 注意:这篇文章是基于springboot1.x老版本springcloud,springboot2.x的springcloud(Finchley之后)新版本文章,请移步升级版专贴 相信现在已经有很多小伙伴已经或者准备使用springcloud微服务了,接下来为大家搭建一个微服务框架,后期可以自己进行扩展。会提供一个小案例: 服务提供者和服务消费者 ,消费者会调用提供者的服务,新建的项目都是用springboot,附源码下载,推荐使用coding地址下载,因为可以切换分支,后期可以及时更新。 coding仓库地址(推荐下载): coding地址 csdn下载地址: csdn下载地址 远程配置仓库地址 远程配置仓库地址 如果有问题请在下边评论,或者200909980加群交流。 Eureka/Consul:服务发现 (根据情况选择一个) Hystrix:断路器 Zuul:智能路由 Ribbon/Feign:客户端负载均衡 (Feign用的更多) Turbine:集群监控 Springcloud-config:远程获取配置文件 这里写图片描述 接下来,我们开始搭建项目,首先我们到spring为我们提供的一个网站快速搭建springboot项目,点击访问,我这里用的是gradle,如果各位客官喜欢用maven,好吧你可以到http://mvnrepository.com/查看对应的依赖,点我访问。 一、搭建eureka-server服务springcloud-eureka-server 使用 spring-cloud-consul 作为服务发现 请参考 点击查看使用springcloud consul 作为服务发现 eureka-server作为服务发现的核心,第一个搭建,后面的服务都要注册到eureka-server上,意思是告诉eureka-server自己的服务地址是啥。当然还可以用zookeeper或者springconsul。 这里写图片描述 1.修改build.gradle文件 如果是maven项目请对应的修改pom.xml //加入阿里的私服仓库地址 maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } //加入依赖 compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka-server', version: '1.3.4.RELEASE' //加入security,是因为访问eureka-server需要用户名和密码访问,为了安全 compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '1.5.6.RELEASE' 还有几点需要修改的,大家对应图片看看,就是springboot打包的时候会提示找不到主累。 这里写图片描述 2.修改 application.yml,建议用yml。 server: port: 8761 eureka: datacenter: trmap environment: product client: healthcheck: enabled: true service-url: defaultZone: http://root:booszy@localhost:8761/eureka register-with-eureka: false #关闭自己作为客户端注册 fetch-registry: false security: basic: enabled: true user: name: root #用户名和密码,等会访问的时候,会要求你登录,服务注册的时候也需要带着用户名和密码 password: booszy 3.修改程序的主类,建议修改类名,要加如eureka的 @EnableEurekaServer 注解,然后运行main方法。 @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } 这里写图片描述 http://localhost:8761/ 这个是eureka-server的页面地址,到这里,说明eureka-server搭建好了,简单吧,这一步一定要成功,否则后面的就不能继续进行下去了,后边基本类似。 二、搭建config-server服务springcloud-config-server springcloud-config-server是用来将远程git仓库的配置文件动态拉下来,这样配置文件就可以动态的维护了。当然也可以选择本地仓库。 新建一个springboot项目,修改maven私服地址,并加入一下依赖。 1.修改build.gradle文件 compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka', version: '1.3.4.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-config-server', version: '1.3.2.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' //连接config-server也需要用户名和密码 compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '1.5.6.RELEASE' 2.修改application.yml文件 server: port: 8500 eureka: client: service-url: #注册服务到eureka上,记住这里要加上eureka-server的用户名和密码 defaultZone: http://root:booszy@localhost:8761/eureka instance: prefer-ip-address: true #可能比较长,复制的时候请写在一行 instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} appname: springcloud-config-server spring: application: name: springcloud-config-server cloud: config: server: git: #这是其他项目配置文件的git仓库地址 uri: https://git.coding.net/yirenyishi/springcloud-config-profile searchPaths: '{application}' security: basic: enabled: true user: #这是config-server的用户名和密码 name: root password: booszy 3.修改启动类 修改启动类,要加入这三个注解,因为要注册到eureka-server上,所以需要@EnableEurekaClient这个注解 @SpringBootApplication @EnableConfigServer @EnableEurekaClient public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } 然后运行启动springboot项目,等启动成功后访问eureka的页面,会发现springcloud-config-server已经注册到上面了,如果启动报错,请检查错误信息。 这里写图片描述 三、搭建服务提供者服务springcloud-provider-config 编写一个服务提供者,提供两个接口,即获取单个用户的信息和获取一个用户列表。用到了spring-data-jpa 和 spring-webmvc ,当然你们公司用什么你还是继续用什么。 注意 : 这里除了application.xml,还需要一个bootstrap.yml, 因为bootstrap.yml得加载顺序是在application.xml前边 修改build.gradle文件 compile('org.springframework.boot:spring-boot-starter-data-jpa') compile('org.springframework.boot:spring-boot-starter-web') compile("com.alibaba:druid-spring-boot-starter:1.1.2") compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka', version: '1.3.4.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '1.3.2.RELEASE' compile group: 'org.springframework.session', name: 'spring-session-data-redis', version: '1.3.1.RELEASE' runtime('mysql:mysql-connector-java') 2.编写配置文件bootstrap.yml ** 注意 : 这里除了application.xml,还需要一个bootstrap.yml* application.xml我是放到远程仓库地址的,大家可以直接到我的远程仓库,根据项目名(springcloud-provider-config)查询。配置文件的仓库地址:点击访问。 spring: application: name: springcloud-provider-config cloud: config: #config-server的配置,不需要硬编码config-server的地址,使用service-id去eureka-server获取cong-server的地址 discovery: enabled: true service-id: springcloud-config-server fail-fast: true username: root password: booszy profile: dev eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} appname: springcloud-provider-config 3.编写代码 编写主类 @SpringBootApplication @EnableEurekaClient public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } } 新建UserController, 考虑篇幅 UserService 和 UserRepository就不贴代码了,想看的可以下载我的代码。 @RequestMapping("user") @RestController public class UserController { @Autowired private UserService userService; /** * @param id * @return */ @GetMapping("{id}") public User getuser(@PathVariable String id) { User user = null; try { System.out.println(id); user = userService.find(id); } catch (Exception e) { e.printStackTrace(); } return user; } /** * @return */ @GetMapping("list") public List<User> users() { try { List<User> user = userService.findAll(); if (user != null && user.size() != 0) { return user; } return null; } catch (Exception e) { e.printStackTrace(); } return null; } } 运行springboot项目,去eureka-server查看,有没有注册上。 这里写图片描述 我们的springcloud-provider-config已经注册到eureka上了,访问接口,成功。 这里写图片描述 四、搭建消费者服务springcloud-consumer-ribbon-config-swagger 消费者要访问服务提供者的服务,这里用的是通过RestTemplate请求resetful接口,使用ribbon做客户端负载均衡,hystrix做错误处理,swagger生成接口文档。文章结尾处会更新feign的案例,feign和ribbon二选一,也可以都用。 还是熟悉的配方,熟悉的味道,新建springboot项目,添加项目依赖。 1.修改build.gradle文件 compile('org.springframework.boot:spring-boot-starter-web') compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka', version: '1.3.4.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '1.3.2.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix', version: '1.3.4.RELEASE' compile( "io.springfox:springfox-swagger2:2.7.0", "io.springfox:springfox-swagger-ui:2.7.0" ) 2.修改bootstrap.yml文件 application.yml 在git仓库,请前往git仓库查看。 spring: application: name: springcloud-consumer-config cloud: config: discovery: enabled: true service-id: springcloud-config-server fail-fast: true username: root password: booszy profile: dev eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} appname: springcloud-consumer-config 3.编写代码 启动类代码 @RibbonClient 指定服务使用的负载均衡类型,name不指定服务则为所有的服务打开负载均衡,也可以在用yml中进行配置。 @EnableHystrix 是支持hystrix打开断路器,在规定时间内失败参数超过一定参数,就会打开断路器,不会发起请求,而是直接进入到错误处理方法。 @SpringBootApplication @EnableEurekaClient @RibbonClient(name = "springcloud-provider-config", configuration = RibbonConfiguration.class) @ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExtendRibbon.class)}) @EnableHystrix public class ConsumerApplication { @Autowired private RestTemplateBuilder builder; @Bean @LoadBalanced public RestTemplate restTemplate() { return builder.build(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } 新建UserController ribbon一个坑,不能接受List类型,要使用数组接收。 @Api xxx 是swagger的注解 @HystrixCommand(fallbackMethod="userFallbackMethod") 如果请求失败,会进入userFallbackMethod这个方法,userFallbackMethod这个方法要求参数和返回值与回调他的方法保持一致。 ribbon这个方法就是通过service-id获取获取服务实际的地址,这样服务的地址就不用硬编码了。 @Api("springcloud consumer user 控制器") @RequestMapping("user") @RestController public class UserController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; /** * @param id * @return */ @ApiOperation(value = "根据用户id查询用户信息", httpMethod = "GET", produces = "application/json") @ApiResponse(code = 200, message = "success", response = User.class) @GetMapping("{id}") @HystrixCommand(fallbackMethod="userFallbackMethod") public User getUser(@ApiParam(name = "id", required = true, value = "用户Id") @PathVariable String id) { return this.restTemplate.getForObject("http://springcloud-provider-config/user/" + id, User.class); } public User userFallbackMethod(String id){ return null; } /** * 这块ribbon不支持复杂数据类型list,所以要用数组接受,然后转list * @return */ @GetMapping("list") @HystrixCommand(fallbackMethod = "userList") public List<User> users(HttpServletRequest request) { try { User[] forObject = this.restTemplate.getForObject("http://springcloud-provider-config/user/list", User[].class); List<User> users = Arrays.asList(forObject); return users == null ? new ArrayList<User>() : users; } catch (Exception e) { e.printStackTrace(); } return null; } public List<User> userList(HttpServletRequest request) { return null; } /** * 通过服务id获取服务的地址 * @return */ @GetMapping("ribbon") public String ribbon(){ ServiceInstance serviceInstance = loadBalancerClient.choose("springcloud-provider-config"); return serviceInstance.getUri().toString(); } } 运行springboot项目,先看有没有注册到eureka-server上。 这里写图片描述 注册成功后,访问接口,测试是否正确。 这里写图片描述 测试swagger-ui,访问localhost:8200/swagger-ui.html 这里写图片描述 到这里消费者服务就算是完成了,后边大家自己进行扩展。 五、用zuul做路由转发和负载均衡 这些微服务都是隐藏在后端的,用户是看不到,或者不是直接接触,可以用nginx或者zuul进行路由转发和负载均衡,zuul负载均衡默认用的是ribbon。 1.修改build.gradle文件 compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka', version: '1.3.4.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-zuul', version: '1.3.4.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '1.3.2.RELEASE' 2.修改bootstrap.yml 还是原来的配方,application.yml在git仓库 spring: application: name: springcloud-zuul cloud: config: discovery: enabled: true service-id: springcloud-config-server fail-fast: true username: root password: booszy profile: dev eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} appname: springcloud-zuul 3.启动类 @RefreshScope这个注解是当application.yml配置文件发生变化的时候,不需要手动的进行重启,调用localhost:8400/refresh,就会加载新的配置文件,当然正在访问的客户并不影响还是使用旧的配置文件,因为不是重启,后来的用户会使用新的配置文件。注意这块的刷新要用post请求。 @SpringBootApplication @EnableEurekaClient @EnableZuulProxy @RefreshScope public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } 启动springboot项目,访问eureka-server 这里写图片描述 这时候,我们就要通过zuul访问微服务了,而不是直接去访问微服务。 应该访问地址http://192.168.89.89:8400/springcloud-consumer-config/user/list,这块你要换成你的zuul地址。 但是有些人就会说,这样以后用户请求会不会太长,比较反感,所以可以通过配置进行修改访问地址。 zuul: routes: springcloud-consumer-config: /consumer/** springcloud-provider-config: /provider/** 在application.yml中加入这样一段配置,其实就是nginx中的反向代理,使用一下简短的可以代理这个微服务。这个时候我们就可以这样去访问了http://192.168.89.89:8400/consumer/user/list,是不是简短了很多 这里写图片描述 六、用hystrix-turbine-dashboard 做集群监控 项目在生产环境中,每个服务的访问量都不通,有些服务的访问量比较大,有时候有些服务挂了,不能继续服务,需要重启的时候,我们并不知道,所以这时候就需要使用hystrix-turbine-dashboard做一个监控,监控所有的微服务,可以看到这个接口实时访问量,和健康状况。 新建一个springboot项目,老套路,加入如下依赖 1 添加依赖 compile('org.springframework.boot:spring-boot-starter-web') compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka', version: '1.3.4.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-netflix-turbine', version: '1.3.4.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix', version: '1.3.4.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix-dashboard', version: '1.3.4.RELEASE' 2 修改application.yml配置文件 server: port: 8900 eureka: client: healthcheck: enabled: true service-url: defaultZone: http://root:booszy@localhost:8761/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} appname: springcloud-turbine-dashboard turbine: aggregator: clusterConfig: default appConfig: springcloud-consumer-config,springcloud-provider-config clusterNameExpression: "'default'" spring: application: name: springcloud-turbine-dashboard appConfig 后面是要检测的注册在eureka上的服务名,必须要有 3 修改主类 @EnableTurbine ,@EnableHystrixDashboard 一个都不能少 @SpringBootApplication @EnableTurbine @EnableEurekaClient @EnableHystrixDashboard public class TurbineDashboardApplication { public static void main(String[] args) { SpringApplication.run(TurbineDashboardApplication.class, args); } } 4 访问测试 这块的端口是8900,访问地址http://localhost:8900/hystrix.stream,看到的是下面的页面。 访问hystrix-dashboard页面 然后在那个网址的输入框里输网址http://localhost:8900/turbine.stream,点击monitor stream。刚打开的时候可能是空的,什么也没有,这并不表示你已经错了。这时候你访问消费者服务的接口,例如访问http://localhost:8200/user/list,多访问几次,然后看控制台有没有出现一个监控面板,没有就等会刷新一次,如果一直不出现,应该是配置有问题。 这里写图片描述 后边更新会追加到后边,后边随时更新 2018-01-24 更新:补充使用feign 七、使用feign 在实际开发中,feign使用的还是挺多的,feign底层还是使用了ribbon。废话不多说,直接上步骤,在服务消费者中使用feign访问服务提供者。新建一个springboot项目,或者复制一个。 1加入依赖 swagger不使用的话,可以删掉。 compile('org.springframework.boot:spring-boot-starter-web') compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka', version: '1.3.4.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '1.3.2.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-feign', version: '1.3.4.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix', version: '1.3.4.RELEASE' compile( "io.springfox:springfox-swagger2:2.7.0", "io.springfox:springfox-swagger-ui:2.7.0" ) 2修改配置文件 这里修改的是bootstrap.yml,这里吧application.yml的配置也贴出来,这个是放在远程仓库的,通过config动态拉取下来。 spring: application: name: springcloud-consumer-feign cloud: config: discovery: enabled: true service-id: springcloud-config-server fail-fast: true username: root password: booszy profile: csdn eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} appname: springcloud-consumer-feign application.yml server: port: 8201 logging: level: com.yasaka.stock.feign.MFeignClient: debug feign: hystrix: enabled: true ribbon: ReadTimeout: 30000 ConnectTimeout: 15000 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 10000 feign的默认请求超时时间是1s,所以经常会出现超时的问题,这里我设置的是10s,因为我的数据库服务器在美国,所以有时候请求会比较慢。ribbon的请求时间也要设置,因为feign用的是ribbon。 3 编码 1、主类注解 @EnableFeignClients @EnableCircuitBreaker @EnableHystrix 这三个都要,hystrix主要作用是断路器,会进如fein的fallback中。 @SpringBootApplication @EnableEurekaClient @EnableFeignClients @EnableCircuitBreaker @EnableHystrix public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } 2、编写feign接口,MFeignClient.class name是指要请求的服务名称。这里请求的是服务提供者 fallback 是指请求失败,进入断路器的类,和使用ribbon是一样的。 configuration 是feign的一些配置,例如编码器等。 @FeignClient(name = "springcloud-provider-config",fallback = HystrixFeignFallback.class, configuration = MFeignConfig.class) public interface MFeignClient { //这里是使用feign请求的地址 @RequestMapping(method = RequestMethod.GET, value = "/user/{id}") User getUser(@PathVariable("id") String id); //新版本支持使用GetMapping,之前的老版本可能不支持 @GetMapping(value = "/user/list") List<User> getUsers(); } 3 MFeignConfig.class feign的配置 这里配置了feign的打印日志等级 @Configuration public class MFeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } 4 HystrixFeignFallback.class ,断路器回调方法 断路器要实现上边定义的MFeignClient接口,请求失败,进入断路器时,会回调这里的方法。 @Component public class HystrixFeignFallback implements MFeignClient { @Override public User getUser(String id) { User user = new User(); user.setId("233"); return user; } @Override public List<User> getUsers() { return new ArrayList<User>(); } } 5 在controller中使用feign @Api("springcloud consumer user 控制器") @RequestMapping("user") @RestController public class UserController { @Autowired private MFeignClient feignClient; /** * @param id * @return */ @ApiOperation(value = "根据用户id查询用户信息", httpMethod = "GET", produces = "application/json") @ApiResponse(code = 200, message = "success", response = User.class) @GetMapping("{id}") public User getUser(@ApiParam(name = "id", required = true, value = "用户Id") @PathVariable String id) { User user = feignClient.getUser(id); return user; } @GetMapping("list") public List<User> users(HttpServletRequest request) { try { List<User> users = feignClient.getUsers(); return users == null ? new ArrayList<User>() : users; } catch (Exception e) { e.printStackTrace(); } return null; } } 20180125更新: 补充使用sleuth+zipkin 实现链路追踪服务 八、使用sleuth+zipkin 实现链路追踪服务 在使用微服务的时候,我们发现,有时候排错不好排查,所以就给大家整个这个链路追踪,很方便知道是哪一个服务调用哪一个服务出现了问题。因为有些项目可能服务比较多。 1 添加依赖 新建一个springboot项目 虽然其他服务调用zipkin不是从eureka上动态过去服务地址,而是硬编码,但是这块还是考虑吧zipkin注册到eureka上。 compile group: 'io.zipkin.java', name: 'zipkin-server', version: '2.4.4' compile group: 'io.zipkin.java', name: 'zipkin-autoconfigure-ui', version: '2.4.4' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka', version: '1.3.4.RELEASE' 2 修改application配置文件 server: port: 9411 eureka: client: service-url: defaultZone: http://root:booszy@localhost:8761/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} appname: springcloud-zipkin-server spring: application: name: springcloud-zipkin-server 3 主类注解添加 @EnableZipkinServer 主要是这个注解 启动服务后访问http://localhost:9411,就可以打开zipkin的控制台页面,这时候应该是什么都没有 @SpringBootApplication @EnableZipkinServer @EnableEurekaClient public class ZipkinServerApplication { public static void main(String[] args) { SpringApplication.run(ZipkinServerApplication.class, args); } } 4 其他服务中调用 这里我们在消费者服务和提供者服务里都加入如下依赖 .... compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-sleuth', version: '1.3.1.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-sleuth-zipkin', version: '1.3.1.RELEASE' ... 然后修改配置文件,bootstrap.yml、 这块zipkin的地址是硬编码的,目前还没发现怎么从服务注册中心eureka上动态获取,以后有解决方案,会更新帖子 sleuth这个是配置提取率,可以配置也可以不配置 spring: zipkin: base-url: http://localhost:9411 sleuth: sampler: percentage: 1.0 启动服务,然后访问消费者服务的接口,这时候访问zipkin的控制台http://localhost:9411 查看服务列表 点击依赖分析,可以看到调用服务链,因为这块只涉及到两个服务,所以只有两个,在实际生产环境中,这块可能有很多,到时候看起来就特别直观了。 这里写图片描述 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
springcloud微服务架构搭建:Consul+sleuth+zipkin+Feign/Ribbon+SpringConfig+Zuul+Hystrix Dash-Board-Turbine 相信现在已经有很多小伙伴已经或者准备使用springcloud微服务了,接下来为大家搭建一个微服务框架,后期可以自己进行扩展。会提供一个小案例: 服务提供者和服务消费者 ,消费者会调用提供者的服务,新建的项目都是用springboot,附源码下载,推荐使用coding地址下载,因为可以切换分支,后期可以及时更新。 coding仓库地址(推荐下载),请选择consul分支哦,默认的分支使用的是eureka: coding地址 csdn下载地址: csdn下载地址 远程配置仓库地址 远程配置仓库地址 如果有问题请在下边评论,或者200909980加群交流。 Consul/Eureka:服务发现 (根据情况选择一个) Hystrix:断路器 Zuul:智能路由 Ribbon/Feign:客户端负载均衡 (Feign用的更多) Turbine:集群监控 Springcloud-config:远程获取配置文件 这里写图片描述 接下来,我们开始搭建项目,首先我们到spring为我们提供的一个网站快速搭建springboot项目,点击访问,我这里用的是gradle,如果各位客官喜欢用maven,好吧你可以到http://mvnrepository.com/查看对应的依赖,点我访问。重点内容 一、搭建spring-consul服务 使用 eureka 作为服务发现 请参考点击查看使用eureka作为服务注册中心 spring-consul作为服务发现的核心,第一个搭建,后面的服务都要注册到consul上,意思是告诉spring-consul自己的服务地址是啥。当然还可以用zookeeper或者eureka。 1下载安装consul consul的下载地址consul下载地址,根据自己电脑系统下载对应的包。 我这里是windows64,网上说要配置环境变量,就可以直接使用,但是我这里不行。具体方法是,下载解压后是一个.exe可执行文件,然后配置当前路劲到path中,然后直接在cmd命令窗口中,输入consul agent -dev 就可以启动了,如果不行的话,就在cmd命令窗口进如到consul的下载目录,然后执行上面的命令。然后consul的默认端口是8500,直接在浏览器打开http://localhost:8500,就可以看到控制台了 consul控制台 到这里,说明consul搭建好了,简单吧,这一步一定要成功,否则后面的就不能继续进行下去了。 二、搭建config-server服务springcloud-config-server springcloud-config-server是用来将远程git仓库的配置文件动态拉下来,这样配置文件就可以动态的维护了。当然也可以选择本地仓库。远程配置仓库地址 远程配置仓库地址 新建一个springboot项目,修改maven私服地址,并加入一下依赖。 1.修改build.gradle文件 compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '1.3.1.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-config-server', version: '1.3.2.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '1.5.6.RELEASE' 2.修改application.yml文件 server: port: 8800 spring: application: name: springcloud-config-server cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} config: server: git: uri: https://git.coding.net/yirenyishi/springcloud-config-profile searchPaths: '{application}' security: basic: enabled: true user: name: root password: booszy 3.修改启动类 修改启动类,要加入这三个注解,因为要注册到consul上,所以需要@EnableDiscoveryClient这个注解 @SpringBootApplication @EnableConfigServer @EnableDiscoveryClient public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } 然后运行启动springboot项目,等启动成功后访问consul的页面,会发现springcloud-config-server已经注册到上面了,如果启动报错,请检查错误信息。 这里写图片描述 三、搭建服务提供者服务springcloud-provider-config 编写一个服务提供者,提供两个接口,即获取单个用户的信息和获取一个用户列表。用到了spring-data-jpa 和 spring-webmvc ,当然你们公司用什么你还是继续用什么。 注意 : 这里除了application.xml,还需要一个bootstrap.yml, 因为bootstrap.yml得加载顺序是在application.xml前边 修改build.gradle文件 compile('org.springframework.boot:spring-boot-starter-data-jpa') compile('org.springframework.boot:spring-boot-starter-web') compile("com.alibaba:druid-spring-boot-starter:1.1.2") compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '1.3.1.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '1.3.2.RELEASE' compile group: 'com.alibaba', name: 'fastjson', version: '1.2.38' runtime('mysql:mysql-connector-java') 2.编写配置文件bootstrap.yml ** 注意 : 这里除了application.xml,还需要一个bootstrap.yml* application.xml我是放到远程仓库地址的,大家可以直接到我的远程仓库,根据项目名(springcloud-provider-config)查询。配置文件的仓库地址:点击访问。 spring: application: name: springcloud-provider-config cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} service-name: springcloud-provider-config config: discovery: enabled: true service-id: springcloud-config-server fail-fast: true username: root password: booszy profile: csdn 3.编写代码 编写主类 @SpringBootApplication @EnableDiscoveryClient public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } } 新建UserController, 考虑篇幅 UserService 和 UserRepository就不贴代码了,想看的可以下载我的代码。 @RequestMapping("user") @RestController public class UserController { @Autowired private UserService userService; /** * @param id * @return */ @GetMapping("{id}") public User getuser(@PathVariable String id) { User user = null; try { System.out.println(id); user = userService.find(id); } catch (Exception e) { e.printStackTrace(); } return user; } /** * @return */ @GetMapping("list") public List<User> users() { try { List<User> user = userService.findAll(); if (user != null && user.size() != 0) { return user; } return null; } catch (Exception e) { e.printStackTrace(); } return null; } } 运行springboot项目,去consul查看,有没有注册上。 这里写图片描述 看到我们的springcloud-provider-config已经注册到consul上了,访问接口,成功。 这里写图片描述 四、搭建消费者服务springcloud-consumer-ribbon-config-swagger 消费者要访问服务提供者的服务,这里用的是通过RestTemplate请求resetful接口,使用ribbon做客户端负载均衡,hystrix做错误处理,swagger生成接口文档。文章结尾处会更新feign的案例,feign和ribbon二选一,也可以都用。 还是熟悉的配方,熟悉的味道,新建springboot项目,添加项目依赖。 1.修改build.gradle文件 compile('org.springframework.boot:spring-boot-starter-web') compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '1.3.1.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '1.3.2.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix', version: '1.3.4.RELEASE' compile( "io.springfox:springfox-swagger2:2.7.0", "io.springfox:springfox-swagger-ui:2.7.0" ) 2.修改bootstrap.yml文件 application.yml 在git仓库,请前往git仓库查看。 spring: application: name: springcloud-consumer-config cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} config: discovery: enabled: true service-id: springcloud-config-server fail-fast: true username: root password: booszy profile: csdn 3.编写代码 启动类代码 @RibbonClient 指定服务使用的负载均衡类型,name不指定服务则为所有的服务打开负载均衡,也可以在用yml中进行配置。 @EnableHystrix 是支持hystrix打开断路器,在规定时间内失败参数超过一定参数,就会打开断路器,不会发起请求,而是直接进入到错误处理方法。 @SpringBootApplication @EnableDiscoveryClient @RibbonClient(name = "springcloud-provider-config", configuration = RibbonConfiguration.class) @ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExtendRibbon.class)}) @EnableHystrix public class ConsumerApplication { @Autowired private RestTemplateBuilder builder; @Bean @LoadBalanced public RestTemplate restTemplate() { return builder.build(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } 新建UserController ribbon一个坑,不能接受List类型,要使用数组接收。 @Api xxx 是swagger的注解 @HystrixCommand(fallbackMethod="userFallbackMethod") 如果请求失败,会进入userFallbackMethod这个方法,userFallbackMethod这个方法要求参数和返回值与回调他的方法保持一致。 ribbon这个方法就是通过service-id获取获取服务实际的地址,这样服务的地址就不用硬编码了。 @Api("springcloud consumer user 控制器") @RequestMapping("user") @RestController public class UserController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; /** * @param id * @return */ @ApiOperation(value = "根据用户id查询用户信息", httpMethod = "GET", produces = "application/json") @ApiResponse(code = 200, message = "success", response = User.class) @GetMapping("{id}") @HystrixCommand(fallbackMethod="userFallbackMethod") public User getUser(@ApiParam(name = "id", required = true, value = "用户Id") @PathVariable String id) { return this.restTemplate.getForObject("http://springcloud-provider-config/user/" + id, User.class); } public User userFallbackMethod(String id){ return null; } /** * 这块ribbon不支持复杂数据类型list,所以要用数组接受,然后转list * @return */ @GetMapping("list") @HystrixCommand(fallbackMethod = "userList") public List<User> users(HttpServletRequest request) { try { User[] forObject = this.restTemplate.getForObject("http://springcloud-provider-config/user/list", User[].class); List<User> users = Arrays.asList(forObject); return users == null ? new ArrayList<User>() : users; } catch (Exception e) { e.printStackTrace(); } return null; } public List<User> userList(HttpServletRequest request) { return null; } /** * 通过服务id获取服务的地址 * @return */ @GetMapping("ribbon") public String ribbon(){ ServiceInstance serviceInstance = loadBalancerClient.choose("springcloud-provider-config"); return serviceInstance.getUri().toString(); } } 运行springboot项目,先看有没有注册到consul上。 这里写图片描述 注册成功后,访问接口,测试是否正确。 这里写图片描述 测试swagger-ui,访问localhost:8200/swagger-ui.html 这里写图片描述 到这里消费者服务就算是完成了,后边大家自己进行扩展。 五、用zuul做路由转发和负载均衡 这些微服务都是隐藏在后端的,用户是看不到,或者不是直接接触,可以用nginx或者zuul进行路由转发和负载均衡,zuul负载均衡默认用的是ribbon。 1.修改build.gradle文件 compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '1.3.1.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-zuul', version: '1.3.4.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '1.3.2.RELEASE' 2.修改bootstrap.yml 还是原来的配方,application.yml在git仓库 spring: application: name: springcloud-zuul cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} service-name: springcloud-zuul config: discovery: enabled: true service-id: springcloud-config-server fail-fast: true username: root password: booszy profile: csdn 3.启动类 @RefreshScope这个注解是当application.yml配置文件发生变化的时候,不需要手动的进行重启,调用localhost:8400/refresh,就会加载新的配置文件,当然正在访问的客户并不影响还是使用旧的配置文件,因为不是重启,后来的用户会使用新的配置文件。注意这块的刷新要用post请求。 @SpringBootApplication @EnableZuulProxy @EnableDiscoveryClient @RefreshScope public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } 启动springboot项目,访问consul ui 这里写图片描述 这时候,我们就要通过zuul访问微服务了,而不是直接去访问微服务。 应该访问地址http://192.168.89.89:8400/springcloud-consumer-config/user/list,这块你要换成你的zuul地址。 但是有些人就会说,这样以后用户请求会不会太长,比较反感,所以可以通过配置进行修改访问地址。 zuul: routes: springcloud-consumer-config: /consumer/** springcloud-provider-config: /provider/** 在application.yml中加入这样一段配置,其实就是nginx中的反向代理,使用一下简短的可以代理这个微服务。这个时候我们就可以这样去访问了http://192.168.89.89:8400/consumer/user/list,是不是简短了很多 这里写图片描述 六、用hystrix-turbine-dashboard 做集群监控 项目在生产环境中,每个服务的访问量都不通,有些服务的访问量比较大,有时候有些服务挂了,不能继续服务,需要重启的时候,我们并不知道,所以这时候就需要使用hystrix-turbine-dashboard做一个监控,监控所有的微服务,可以看到这个接口实时访问量,和健康状况。 新建一个springboot项目,老套路,加入如下依赖 1 添加依赖 compile('org.springframework.boot:spring-boot-starter-web') compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '1.3.1.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-netflix-turbine', version: '1.3.4.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix', version: '1.3.4.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix-dashboard', version: '1.3.4.RELEASE' 2 修改application.yml配置文件 server: port: 8900 turbine: aggregator: clusterConfig: default appConfig: springcloud-consumer-config,springcloud-provider-config clusterNameExpression: new String('default') # clusterNameExpression: "'default'" spring: application: name: springcloud-turbine-dashboard cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} service-name: springcloud-turbine-dashboard appConfig 后面是要检测的注册在consul上的服务名,必须要有 3 修改主类 @EnableTurbine ,@EnableHystrixDashboard 一个都不能少 @SpringBootApplication @EnableTurbine @EnableHystrix @EnableDiscoveryClient @EnableHystrixDashboard public class TurbineDashboardApplication { public static void main(String[] args) { SpringApplication.run(TurbineDashboardApplication.class, args); } } 4 访问测试 这块的端口是8900,访问地址http://localhost:8900/hystrix.stream,看到的是下面的页面。 访问hystrix-dashboard页面 然后在那个网址的输入框里输网址http://localhost:8900/turbine.stream,点击monitor stream。刚打开的时候可能是空的,什么也没有,这并不表示你已经错了。这时候你访问消费者服务的接口,例如访问http://localhost:8200/user/list,多访问几次,然后看控制台有没有出现一个监控面板,没有就等会刷新一次,如果一直不出现,应该是配置有问题。 这里写图片描述 后边更新会追加到后边,后边随时更新 2018-01-25 更新:补充使用feign 七、使用feign 在实际开发中,feign使用的还是挺多的,feign底层还是使用了ribbon。废话不多说,直接上步骤,在服务消费者中使用feign访问服务提供者。新建一个springboot项目,或者复制一个。 1加入依赖 swagger不使用的话,可以删掉。 compile('org.springframework.boot:spring-boot-starter-web') compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '1.3.1.RELEASE' compile group: 'org.springframework.boot', name: 'spring-boot-actuator', version: '1.5.6.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '1.3.2.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-feign', version: '1.3.4.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix', version: '1.3.4.RELEASE' compile( "io.springfox:springfox-swagger2:2.7.0", "io.springfox:springfox-swagger-ui:2.7.0" ) 2修改配置文件 这里修改的是bootstrap.yml,这里吧application.yml的配置也贴出来,这个是放在远程仓库的,通过config动态拉取下来。 spring: application: name: springcloud-consumer-feign cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} service-name: springcloud-consumer-feign config: discovery: enabled: true service-id: springcloud-config-server fail-fast: true username: root password: booszy profile: csdn application.yml server: port: 8201 logging: level: com.yasaka.stock.feign.MFeignClient: debug feign: hystrix: enabled: true ribbon: ReadTimeout: 30000 ConnectTimeout: 15000 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 10000 feign的默认请求超时时间是1s,所以经常会出现超时的问题,这里我设置的是10s,因为我的数据库服务器在美国,所以有时候请求会比较慢。ribbon的请求时间也要设置,因为feign用的是ribbon。 3 编码 1、主类注解 @EnableFeignClients @EnableCircuitBreaker @EnableHystrix 这三个都要,hystrix主要作用是断路器,会进如fein的fallback中。 @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableCircuitBreaker @EnableHystrix public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } 2、编写feign接口,MFeignClient.class name是指要请求的服务名称。这里请求的是服务提供者 fallback 是指请求失败,进入断路器的类,和使用ribbon是一样的。 configuration 是feign的一些配置,例如编码器等。 @FeignClient(name = "springcloud-provider-config",fallback = HystrixFeignFallback.class, configuration = MFeignConfig.class) public interface MFeignClient { //这里是使用feign请求的地址 @RequestMapping(method = RequestMethod.GET, value = "/user/{id}") User getUser(@PathVariable("id") String id); //新版本支持使用GetMapping,之前的老版本可能不支持 @GetMapping(value = "/user/list") List<User> getUsers(); } 3 MFeignConfig.class feign的配置 这里配置了feign的打印日志等级 @Configuration public class MFeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } 4 HystrixFeignFallback.class ,断路器回调方法 断路器要实现上边定义的MFeignClient接口,请求失败,进入断路器时,会回调这里的方法。 @Component public class HystrixFeignFallback implements MFeignClient { @Override public User getUser(String id) { User user = new User(); user.setId("233"); return user; } @Override public List<User> getUsers() { return new ArrayList<User>(); } } 5 在controller中使用feign @Api("springcloud consumer user 控制器") @RequestMapping("user") @RestController public class UserController { @Autowired private MFeignClient feignClient; /** * @param id * @return */ @ApiOperation(value = "根据用户id查询用户信息", httpMethod = "GET", produces = "application/json") @ApiResponse(code = 200, message = "success", response = User.class) @GetMapping("{id}") public User getUser(@ApiParam(name = "id", required = true, value = "用户Id") @PathVariable String id) { User user = feignClient.getUser(id); return user; } @GetMapping("list") public List<User> users(HttpServletRequest request) { try { List<User> users = feignClient.getUsers(); return users == null ? new ArrayList<User>() : users; } catch (Exception e) { e.printStackTrace(); } return null; } } 20180125更新: 补充使用sleuth+zipkin 实现链路追踪服务 八、使用sleuth+zipkin 实现链路追踪服务 在使用微服务的时候,我们发现,有时候排错不好排查,所以就给大家整个这个链路追踪,很方便知道是哪一个服务调用哪一个服务出现了问题。因为有些项目可能服务比较多。 1 添加依赖 新建一个springboot项目 虽然其他服务调用zipkin不是从consul上动态过去服务地址,而是硬编码,但是这块还是考虑吧zipkin注册到consul上。 compile group: 'io.zipkin.java', name: 'zipkin-server', version: '2.4.4' compile group: 'io.zipkin.java', name: 'zipkin-autoconfigure-ui', version: '2.4.4' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '1.3.1.RELEASE' 2 修改application配置文件 server: port: 9411 spring: application: name: springcloud-zipkin-server cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} service-name: springcloud-zipkin-server 3 主类注解添加 @EnableZipkinServer 主要是这个注解 启动服务后访问http://localhost:9411,就可以打开zipkin的控制台页面,这时候应该是什么都没有 @SpringBootApplication @EnableZipkinServer @EnableDiscoveryClient public class ZipkinServerApplication { public static void main(String[] args) { SpringApplication.run(ZipkinServerApplication.class, args); } } 4 其他服务中调用 这里我们在消费者服务和提供者服务里都加入如下依赖 .... compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-sleuth', version: '1.3.1.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-sleuth-zipkin', version: '1.3.1.RELEASE' ... 然后修改配置文件,bootstrap.yml、 这块zipkin的地址是硬编码的,目前还没发现怎么从服务注册中心consul上动态获取,以后有解决方案,会更新帖子 sleuth这个是配置提取率,可以配置也可以不配置 spring: zipkin: base-url: http://localhost:9411 sleuth: sampler: percentage: 1.0 启动服务,然后访问消费者服务的接口,这时候访问zipkin的控制台http://localhost:9411 查看服务列表 点击依赖分析,可以看到调用服务链,因为这块只涉及到两个服务,所以只有两个,在实际生产环境中,这块可能有很多,到时候看起来就特别直观了。 这里写图片描述 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
网站调用支付宝进行支付-Java后台调用支付宝支付 最近公司一个产品用到了打赏支付,其中一个是支付宝,记录一下java后台调用支付宝的过程付代码。本文讲的是使用沙箱环境(支付宝提供的一种开发专用模式,不要实际支付,可以走通整个流程)。 一、准备工作 1.首先要到 蚂蚁金服开发者中心 注册商家账户,并认证。 2.下载java版的sdk和demo sdk&demo下载地址 3.将sdk加入到项目中,在项目根路径下新建libs文件夹,将jar包复制进去,我这里使用的是gradle,如果各位使用的是maven,请自行修改。 compile fileTree(dir:'libs',include:['*.jar']) compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0' //谷歌json相关的包,可以用其他的 4.公钥、私钥的生成。生成公钥、私钥官方教程 ,按照官网教程操作,很简单的,这里就不上图片了。生成完之后将你的公钥要上传上去,具体位置看图片。 这里写图片描述 二、开发接口 1、因为开发环境是使用沙箱环境,上线后会使用真实环境,所以支付宝的一些参数我们放到配置文件里 pay-dev.properties appid和支付宝公钥上面图片中有,直接在网页上复制就好。 注意: 支付宝回调接口一定要是公网可以访问到的,我这里用了花生壳内网穿透,你们可以自行解决,只要能实现公网访问即可 ALIPAY.PRIVATEKEY=你生成的私钥 ALIPAY.PUBLICKEY=支付宝的公钥 ALIPAY.APPID=2016080300156724 #沙箱环境下的appid ALIPAY.SERVER=https://openapi.alipaydev.com/gateway.do ALIPAY.NOTIFY_URL=http://sanqi.iask.in:31017/alipay/notify #公网可以访问的地址 ALIPAY.RETURNA_URL=http://sanqi.iask.in:31017/alipay/return ALIPAY.SIGN=RSA2 ALIPAY.LOG=C:\\alipaylog\\ 2、加载配置文件,我的项目是基于springboot的,如果是基于xml配置的,请自行在xml中配置。 /** * @ClassName: PropertiesConfig * @Description: 读取配置文件 * @author nelson * @date 2018年3月21日 下午5:35:21 * */ @Configuration public class PropertiesConfig { @Bean public PropertySourcesPlaceholderConfigurer createPropertySourcesPlaceholderConfigurer() { ClassPathResource resource = new ClassPathResource("pay-dev.properties"); PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer(); propertyPlaceholderConfigurer.setLocation(resource); return propertyPlaceholderConfigurer; } } 3、开始编写写接口 这里支付宝要用的一些参数,我是通过@Value自动注入进来的,官方给的demo是,定义个AlipayConfig类,然后全部定义成静态变量,根据个人喜好问题选择,官方的demo中有,可以直接复制,然后修改为你自己的参数即可。 代码中适当的写了一些参数,但是并非全部参数,官网参数列表及解释。 /** * @Title: AlipayController.java * @Package cn.trmap.tdcloud.pay * @Description: 支付宝后台接口 * @author nelson * @date 2018年3月21日 下午5:31:03 */ @RequestMapping("alipay") @RestController public class AlipayController { @Value("${ALIPAY.APPID}") private String app_id; @Value("${ALIPAY.PRIVATEKEY}") private String merchant_private_key; @Value("${ALIPAY.PUBLICKEY}") private String alipay_public_key; @Value("${ALIPAY.NOTIFY_URL}") private String notify_url; @Value("${ALIPAY.RETURNA_URL}") private String return_url; @Value("${ALIPAY.SIGN}") private String sign_type = "RSA2"; private String charset = "utf-8"; @Value("${ALIPAY.SERVER}") private String gatewayUrL; @GetMapping("pay") private String alipayPay() throws AlipayApiException { //这个应该是从前端端传过来的,这里为了测试就从后台写死了 AlipayVo vo = new AlipayVo(); vo.setOut_trade_no(UUID.randomUUID().toString().replace("-", "")); vo.setTotal_amount("0.01"); vo.setSubject("nelson-test-title"); vo.setProduct_code("FAST_INSTANT_TRADE_PAY"); //这个是固定的 String json = new Gson().toJson(vo); System.out.println(json); AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrL, app_id, merchant_private_key, "json",charset,alipay_public_key,sign_type); // 设置请求参数 AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(return_url); alipayRequest.setNotifyUrl(notify_url); alipayRequest.setBizContent(json); String result = alipayClient.pageExecute(alipayRequest).getBody(); System.out.println(result); return result; //这里生成一个表单,会自动提交 } /** * @Title: AlipayVo.java * @Package cn.trmap.tdcloud.pay.vo * @Description: 支付请求参数 * @author nelson * @date 2018年3月23日 上午9:00:02 */ public class AlipayVo implements Serializable{ private static final long serialVersionUID = 1L; /** * 订单名称 */ private String subject; /** * 商户网站唯一订单号 */ private String out_trade_no; /** * 该笔订单允许的最晚付款时间 */ private String timeout_express; /** * 付款金额 */ private String total_amount; /** * 销售产品码,与支付宝签约的产品码名称 */ private String product_code; ****此处省去get和set方法 } 4、支付宝异步通知回调接口 这个接口的请求方式只能是post,如果用get会拿不到数据,不支持。 /** * @Title: alipayNotify * @Description: 支付宝回调接口 * @author nelson * @param request * @param out_trade_no 商户订单号 * @param trade_no 支付宝交易凭证号 * @param trade_status 交易状态 * @throws AlipayApiException * @return String * @throws */ @PostMapping("notify") private String alipayNotify(HttpServletRequest request, String out_trade_no,String trade_no,String trade_status) throws AlipayApiException { Map<String, String> map = new HashMap<String, String>(); Map<String, String[]> requestParams = request.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = iter.next(); String[] values = requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; System.out.println(valueStr); } map.put(name, valueStr); } boolean signVerified = false; try { signVerified = AlipaySignature.rsaCheckV1(map,alipay_public_key,charset,sign_type); } catch (AlipayApiException e) { e.printStackTrace(); return ("fail");// 验签发生异常,则直接返回失败 } if (signVerified) { //处理你的业务逻辑,更细订单状态等 return ("success"); } else { System.out.println("验证失败,不去更新状态"); return ("fail"); } } 5、支付宝回调接口 return url /** * @Title: alipayReturn * @Description: 支付宝回调接口 * @author nelson * @param request * @param out_trade_no 商户订单号 * @param trade_no 支付宝交易凭证号 * @param trade_status 交易状态 * @throws AlipayApiException * @return String * @throws */ @GetMapping("return") private String alipayReturn(Map<String, String> params, HttpServletRequest request, String out_trade_no,String trade_no,String total_amount) throws AlipayApiException { Map<String, String> map = new HashMap<String, String>(); Map<String, String[]> requestParams = request.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = iter.next(); String[] values = requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; System.out.println(valueStr); } map.put(name, valueStr); } boolean signVerified = false; try { signVerified = AlipaySignature.rsaCheckV1(map, alipay_public_key, charset, sign_type); } catch (AlipayApiException e) { e.printStackTrace(); return ("fail");// 验签发生异常,则直接返回失败 } if (signVerified) { return ("success"); } else { System.out.println("验证失败,不去更新状态"); return ("fail"); } } 三、测试接口 启动项目,访问接口地址 项目地址/alipay/pay 我的是 http://sanqi.iask.in:31017/alipay/pay 如果后台没有报错的话,他会自动重定向到,支付宝的付款页面,如下图所示。这时候我们下载安装沙箱版的app,然后使用官方提供的账户扫描然后直接付款,付款成功后会回调后面那两个接口,在通知的那个接口里处理你的业务逻辑。 查看沙箱app的登录帐户名和密码 这里写图片描述 四、关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
vue2(webpack)调用amap高德地图及其UI组件和标记物# 今天一个app项目中要使用vue2加入高德地图,本来以为有第三方的插件,结果没找到适合自己用的,因为地图这块,一般都是自定义程度比较高的。注:这块针对的是app版开发,更多更详细请阅读官方api 1、申请key### 点击下边的网址,创建应用,然后申请key,后期要用到,如果已经有key,忽略这一步 http://lbs.amap.com/dev/key/app 2、修改配置### 首先要在build/webpack.base.conf.js 加入如下配置,负责vue中使用import会报错。 如果如下导入还AMap报错,请使用window.AMap。因为后边要使用高德的infowindow,最简单的不好扩展,而且没有点击事件,所以用用到了高德的ui。位置见下图 externals: { 'AMap': 'AMap', 'AMapUI': 'AMapUI' } 这里写图片描述 3、调用高德地图### 首先在index.html中加入如下引用 <!--引入高德地图JSAPI --> <script src="//webapi.amap.com/maps?v=1.3&key=您申请的key值"></script> <!--引入UI组件库(1.0版本) --> <script src="//webapi.amap.com/ui/1.0/main.js"></script> 新建一个map.vue组件 在script中引入如下组件,如果第二步不配置,这里会报错 import AMap from 'AMap' import AMapUI from 'AMapUI' 注意:如果前边都操作了,import AMap还报错undefined的话,有个终极大招,那就是直接加入如下注释,并且把 import AMap 删除掉,意思是告诉eslint忽略这块的检查,因为在index.html引入是全局引入,所以可以直接使用。 这里写图片描述 template中加入map的div,注意如果map不显示,没报错的时候,请检查div的宽高。 <div id="map-container"> </div> 然后再mounted中初始化地图,这时候地图就会显示出来了,但是没有任何组件,注意vue2废弃ready,请使用mounted。代码位置不懂的看图片 这里写图片描述 <!--创建地图 --> let mapObj = new AMap.Map('map-container', { center: [117.000923, 36.675807], zoom: 6 }) 两个组件:放大缩小按钮和图层的切换 mapObj.plugin(['AMap.ToolBar', 'AMap.MapType'], function () { mapObj.addControl(new AMap.ToolBar()) mapObj.addControl(new AMap.MapType({showTraffic: false, showRoad: false})) }) 使用高德定位 mapObj.plugin(['AMap.Geolocation'], function () { let geolocation = new AMap.Geolocation({ enableHighAccuracy: true, // 是否使用高精度定位,默认:true timeout: 10000, // 超过10秒后停止定位,默认:无穷大 maximumAge: 0, // 定位结果缓存0毫秒,默认:0 convert: true, // 自动偏移坐标,偏移后的坐标为高德坐标,默认:true showButton: true, // 显示定位按钮,默认:true buttonPosition: 'LB', // 定位按钮停靠位置,默认:'LB',左下角 buttonOffset: new AMap.Pixel(10, 20), // 定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20) showMarker: true, // 定位成功后在定位到的位置显示点标记,默认:true showCircle: true, // 定位成功后用圆圈表示定位精度范围,默认:true panToLocation: true, // 定位成功后将定位到的位置作为地图中心点,默认:true zoomToAccuracy: true // 定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false }) mapObj.addControl(geolocation) geolocation.getCurrentPosition() AMap.event.addListener(geolocation, 'complete', (result) => { mapObj.setCenter(result.position) }) // 返回定位信息 AMap.event.addListener(geolocation, 'error', (result) => { console.log(result) }) // 返回定位出错信息 }) 这里写图片描述 4、地图添加标记物和信息框 marker和infowindow### 因为我从后台取到的点是gps定位的点,因为坐标系不同,所以要将gps的点转成高德坐标系的点;点击marker显示infowindow,并给infowindow添加点击事件 // 后台取到的点,新建LngLat对象 let lngLat = new AMap.LngLat(lanlet[0].trim(), lanlet[1].trim()) // 转高德坐标系 AMap.convertFrom(lngLat, 'gps', function (info, result) { lngLat = result.locations[0]// 转换后的坐标位置 var marker = new AMap.Marker({ position: lngLat, map: mapObj }) AMap.event.addListener(marker, 'click', (e) => { AMapUI.loadUI(['overlay/SimpleInfoWindow'], function (SimpleInfoWindow) { var infoWindow = new SimpleInfoWindow({ infoTitle: '<strong>这是标题</strong>', infoBody: '<p>这是信息</p>', offset: new AMap.Pixel(0, -20), autoMove: true }) infoWindow.open(mapObj, e.target.getPosition()) // 最坑的就是这一步了,他的infowindow没有点击事件,所以infoWindow.get$Container()会返回这个infowindow(jquery)对象 let infoEle = infoWindow.get$Container() //给infowindow添加点击事件,并在回调函数中处理业务或者跳转等 infoEle.on('click', (e) => { router.push({name: 'proinfo', params: pro}) }) }) }) }) 这里写图片描述 5、最后本人的效果图 这个title是准备做浮动布局,做返回按钮和标题栏的,还没写css,最后一张是我项目中最终的效果 这里写图片描述 微信图片_20180411103117.png 关注 如果有问题,请在下方评论,或者加群讨论 128806068 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码
java根据ip地址获取城市地域信息 这里提供两个公开的接口,一个是阿里的,一个是新浪的http://ip.taobao.com/service/getIpInfo.php?ip=123.139.94.139 阿里返回json http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=218.192.3.42 新浪返回json 接下来上代码,我这里用的是springboot自带的RestTemplate,各位如果没用到可以用HttpURLConnection。案例是在拦截器里获取ip,并查询地址。如果内网测试的话,获取到的是内网ip,通过内网穿透出去访问,可以获取你的公网出口ip,或者吧ip直接写死。 @SpringBootApplication public class LgmallRestApplication { @Autowired private RestTemplateBuilder builder; @Bean public RestTemplate restTemplate() { return builder.build(); } public static void main(String[] args) { SpringApplication.run(LgmallRestApplication.class, args); } } /** * @Author: nelson * @Description: 商品浏览记录 * @Date: created in 2018/03/31/16:49 */ public class BrowseItemInterceptor implements HandlerInterceptor { @Autowired private RestTemplate restTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String ip = request.getHeader("x-forwarded-for"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { // 多次反向代理后会有多个ip值,第一个ip才是真实ip if( ip.indexOf(",")!=-1 ){ ip = ip.split(",")[0]; } } //新浪查询失败查询阿里 String sina = restTemplate.getForObject("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip={ip}", String.class,ip); SinaIpVo sinaIpVo = new Gson().fromJson(sina, SinaIpVo.class); if(sinaIpVo.getRet()!=-1){ System.out.println(sinaIpVo.getProvince()); System.out.println(sinaIpVo.getCity()); }else{ String object = restTemplate.getForObject("http://ip.taobao.com/service/getIpInfo.php?ip={ip}", String.class,ip); IpVo ipVo = new Gson().fromJson(object, IpVo.class); // XX表示内网 if(ipVo.getCode()==0 && !ipVo.getAddress().getRegion().equals("XX")){ System.out.println(ipVo.getAddress().getRegion()); System.out.println(ipVo.getAddress().getCity()); } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } } 阿里返回结果封装的vo,省去get、set方法,需要其他的属性根据返回json自己扩展。 /** * @Author: nelson * @Description: get city by ip * @Date: created in 2018/03/31/17:40 */ public class IpVo implements Serializable{ private Integer code; private Address address; public class Address implements Serializable{ private String ip; private String region; private String city; } } 新浪返回结果封装的vo,省去get、set方法,需要其他的属性根据返回json自己扩展。 /** * @Author: nelson * @Description: get city by ip * @Date: created in 2018/03/31/17:40 */ public class SinaIpVo implements Serializable{ private Integer ret; private String province; private String city; } 关注 如果有问题,请在下方评论,或者加群讨论 200909980 关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。 码农笔录二维码