0%

封面

前言

最近线上遇到一个问题:在消费kafka消息的时候如果长时间(大概半天到一天的时间)队列里没有消息就可能再也消费不了。针对这个问题我们反复调试多次。线下模拟,调整代码,但貌似还是没有找到原因。但是只要重启消费进程就又可以继续消费。

解决方案

由于线上业务非常依赖kafka的消费,但一时半会也没有找到原因,所以最后只能想一个临时的替换方案:

基于重启就可以消费这个特点,我们在每次消费的时候都记下当前的时间点,当这个时间点在十分钟之内都没有更新我们就认为当前队列中没有消息了,就需要重启下消费进程。

既然是需要重启,由于目前还没有上分布式调度中心所以需要crontab来配合调度:每隔一分钟会调用一个shell脚本,该脚本会判断当前进程是否存在,如果存在则什么都不作,不存在则启动消费进程。

Read more »

pexels-photo-306198.jpeg

前言

在一个高并发系统中对流量的把控是非常重要的,当巨大的流量直接请求到我们的服务器上没多久就可能造成接口不可用,不处理的话甚至会造成整个应用不可用。

比如最近就有个这样的需求,我作为客户端要向kafka生产数据,而kafka的消费者则再源源不断的消费数据,并将消费的数据全部请求到web服务器,虽说做了负载(有4台web服务器)但业务数据的量也是巨大的,每秒钟可能有上万条数据产生。如果生产者直接生产数据的话极有可能把web服务器拖垮。

对此就必须要做限流处理,每秒钟生产一定限额的数据到kafka,这样就能极大程度的保证web的正常运转。

其实不管处理何种场景,本质都是降低流量保证应用的高可用。

常见算法

对于限流常见有两种算法:

  • 漏桶算法
  • 令牌桶算法

漏桶算法比较简单,就是将流量放入桶中,漏桶同时也按照一定的速率流出,如果流量过快的话就会溢出(漏桶并不会提高流出速率)。溢出的流量则直接丢弃。

如下图所示:

漏桶算法,来自网络.png

Read more »

pexels-photo-9046.jpg

前言

之前看过SSM(十四) 基于annotation的http防重插件的朋友应该记得我后文说过之后要用SpringBoot来进行重构。

这次采用自定义的starter的方式来进行重构。

关于starter(起步依赖)其实在第一次使用SpringBoot的时候就已经用到了,比如其中的:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

我们只需要引入这一个依赖SpringBoot就会把相关的依赖都加入进来,自己也不需要再去担心各个版本之间的兼容问题(具体使用哪个版本由使用的spring-boot-starter-parent版本决定),这些SpringBoot都已经帮我们做好了。

01.jpg


Read more »

pexels-photo-516961.jpeg

前言

上一篇简单入门了SpringBoot+SpringCloud 构建微服务。但只能算是一个demo级别的应用。
这次会按照实际生产要求来搭建这套服务。

Swagger应用

上次提到我们调用自己的http接口的时候采用的是PostMan来模拟请求,这个在平时调试时自然没有什么问题,但当我们需要和前端联调开发的时候效率就比较低了。

通常来说现在前后端分离的项目一般都是后端接口先行。

后端大大们先把接口定义好(入参和出参),前端大大们来确定是否满足要求,可以了之后后端才开始着手写实现,这样整体效率要高上许多。

但也会带来一个问题:在接口定义阶段频繁变更接口定义而没有一个文档或类似的东西来记录,那么双方的沟通加上前端的调试都是比较困难的。

基于这个需求网上有各种解决方案,比如阿里的rap就是一个不错的例子。

但是springCould为我们在提供了一种在开发springCloud项目下更方便的工具swagger

实际效果如下:

01.png

Read more »

00.jpeg

前言

随着互联网的兴起,现在三高(高可用、高性能、高并发)项目是越来越流行。

本次来谈谈高并发。首先假设一个业务场景:数据库中有一条数据,需要获取到当前的值,在当前值的基础上+10,然后再更新回去。
如果此时有两个线程同时并发处理,第一个线程拿到数据是10,+10=20更新回去。第二个线程原本是要在第一个线程的基础上再+20=40,结果由于并发访问取到更新前的数据为10,+20=30

这就是典型的存在中间状态,导致数据不正确。来看以下的例子:

并发所带来的问题

和上文提到的类似,这里有一张price表,表结构如下:

1
2
3
4
5
6
7
CREATE TABLE `price` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`total` decimal(12,2) DEFAULT '0.00' COMMENT '总值',
`front` decimal(12,2) DEFAULT '0.00' COMMENT '消费前',
`end` decimal(12,2) DEFAULT '0.00' COMMENT '消费后',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1268 DEFAULT CHARSET=utf8
Read more »

前言

sb1.jpg

有看过我之前的SSM系列的朋友应该有一点印象是非常深刻的。

那就是需要配置的配置文件非常多,什么Springmybatisredismq之类的配置文件非常多,并且还存在各种版本,甚至有些版本还互不兼容。其中有很多可能就是刚开始整合的时候需要配置,之后压根就不会再动了。

鉴于此,Spring又推出了又一神器SpringBoot.

它可以让我们更加快速的开发Spring应用,甚至做到了开箱即用。
由于在实际开发中我们使用SpringBoot+SpringCloud进行了一段时间的持续交付,并在生产环境得到了验证,其中也有不少踩坑的地方,借此机会和大家分享交流一下。

本篇我们首先会用利用SpringBoot构建出一个简单的REST API.
接着会创建另一个SpringBoot项目,基于SpringCloud部署,并在两个应用之间进行调用。

使用SpringBoot构建REST API

我们可以使用Spring官方提供的初始化工具帮我们生成一个基础项目:http://start.spring.io/,如下图所示:
sb2.jpg

填入相应信息即可。由于只是要实现REST API所以这里只需要引用web依赖即可。

Read more »

防重插件封面.jpg

前言

针对于我们现在常用的RESTful API通常我们需要对请求进行唯一标识,也就是每次都要带上一个请求号,如reqNO

对于入库这种操作数据库的请求我们一般要保证他的唯一性,一个请求号通常只能用一次,所以需要我们对这种请求加上校验机制。

该需求的实现思路是通过自定义annotation,只给需要进行校验的接口加上注解。然后通过切面使用了注解的接口将每次请求号存进Redis,每次都进行判断是否存在这个请求号即可。

来看下加上本次插件的实际效果:
重复请求号01.jpg
重复请求号02.jpg
重复请求号03.jpg

Read more »

dubbo暴露为http服务.jpg

前言

通常来说一个dubbo服务都是对内给内部调用的,但也有可能一个服务就是需要提供给外部使用,并且还不能有使用语言的局限性。

比较标准的做法是对外的服务我们统一提供一个openAPI,这样的调用方需要按照标准提供相应的appID以及密钥来进行验签才能使用。这样固然是比较规范和安全,但复杂度也不亚于开发一个单独的系统了。

这里所讲到的没有那么复杂,就只是把一个不需要各种权限检验的dubbo服务对外提供为HTTP服务。

调用示例:
dubbo-http封面.jpg

Read more »

https.jpg

在如今的HTTPS大当其道的情况下自己的博客要是还没有用上。作为互联网的螺丝钉(码农)岂不是很没面子。

使用CLOUDFLARE

这里使用CLOUDFLARE来提供HTTPS服务。

  • 在其官网进行注册,按照提示添加好自己的域名即可。
  • 之后需要在自己域名的提供商处修改DNS服务器,我是在万网购买的修改后如下图:
    1.jpg
    其中的DNS服务器地址CLOUDFLARE是提供的。
    修改完成之后通常需要等待一段时间才能生效。
  • 接着在CLOUDFLARE配置DNS解析:
    DNS解析.jpg
    点击CLOUDFLARE顶部的DNS进行如我上图中的配置,和之前的配置没有什么区别。

等待一段时间之后发现使用HTTP,HTTPS都能访问,但是最好还是能在访问HTTP的时候能强制跳转到HTTPS.

  • CLOUDFLARE菜单栏点击page-rules之后新建一个page rule
    强制https.jpg
    这样整个网站的请求都会强制到请求到HTTPS.
Read more »

dubbo-filter.jpg

前言

在之前dubbo分布式框架中讲到了如何利用dubbo来搭建一个微服务项目。其中还有一些值得优化提高开发效率的地方,比如日志:

当我们一个项目拆分为N多个微服务之后,当其中一个调用另一个服务出现了问题,首先第一步自然是查看日志。

出现问题的有很多情况,如提供方自身代码的问题,调用方的姿势不对等。

自身的问题这个管不了,但是我们可以对每一个入参、返回都加上日志,这样首先就可以判断调用方是否姿势不对了。

为了规范日志已经后续的可扩展,我们可以单独提供一个插件给每个项目使用即可。

效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
2017-04-25 15:15:38,968 DEBUG [com.alibaba.dubbo.remoting.transport.DecodeHandler] -  [DUBBO] Decode decodeable message com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation, dubbo version: 2.5.3, current host: 127.0.0.1
2017-04-25 15:15:39,484 DEBUG [com.crossoverJie.dubbo.filter.DubboTraceFilter] - dubbo请求数据:{"args":[1],"interfaceName":"com.crossoverJie.api.UserInfoApi","methodName":"getUserInfo"}
2017-04-25 15:15:39,484 INFO [com.crossoverJie.api.impl.UserInfoApiImpl] - 用户查询Id=1
2017-04-25 15:15:39,505 DEBUG [org.mybatis.spring.SqlSessionUtils] - Creating a new SqlSession
2017-04-25 15:15:39,525 DEBUG [org.mybatis.spring.SqlSessionUtils] - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6f56b29] was not registered for synchronization because synchronization is not active
2017-04-25 15:15:39,549 DEBUG [org.mybatis.spring.transaction.SpringManagedTransaction] - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@778b3121] will not be managed by Spring
2017-04-25 15:15:39,555 DEBUG [com.crossoverJie.api.dubbo.dao.T_userDao.selectByPrimaryKey] - ==> Preparing: select id, username, password,roleId from t_user where id = ?
2017-04-25 15:15:39,591 DEBUG [com.crossoverJie.api.dubbo.dao.T_userDao.selectByPrimaryKey] - ==> Parameters: 1(Integer)
2017-04-25 15:15:39,616 DEBUG [com.crossoverJie.api.dubbo.dao.T_userDao.selectByPrimaryKey] - <== Total: 1
2017-04-25 15:15:39,616 DEBUG [com.alibaba.druid.pool.PreparedStatementPool] - {conn-10003, pstmt-20000} enter cache
2017-04-25 15:15:39,617 DEBUG [org.mybatis.spring.SqlSessionUtils] - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6f56b29]
2017-04-25 15:15:45,473 INFO [com.crossoverJie.dubbo.filter.DubboTraceFilter] - dubbo执行成功
2017-04-25 15:15:45,476 DEBUG [com.crossoverJie.dubbo.filter.DubboTraceFilter] - dubbo返回数据{"args":[{"id":1,"password":"123456","roleId":1,"userName":"crossoverJie"}],"interfaceName":"com.crossoverJie.api.UserInfoApi","methodName":"getUserInfo"}
Read more »