《深入理解Java虚拟机》前期准备

为什么要学习 JVM

对于 Java 开发来说 JVM 是一个坎,面试大厂必问内容,是一块硬骨头,同时也是 Java 开发的必备技能。

学习 JVM 的目的:

  • 了解 JVM运行原理,不做”CRUD Boy”
  • 个人技能提升( 呸❗️说实话❗️ 为了装B😏)
  • 面试不怂

工欲善其事必先利其器

  • IDEA
  • Xmind ZEN
  • Google

纸上得来终觉浅,绝知此事要躬行

代码还是要敲的,记录自己的成长历程,你会感谢当时那么努力的自己。

艿艿 在星球里面说要形成自己的“点 -> 线 -> 面”,这其实说的就是知识体系,而思维导图就是生成知识体系的神兵利器。

巨人的肩膀

在线 Java 编译网站

最后在推荐几个在线 Java 编译网站,不想用重重的 IDE 的时候可以试试看😉


欢迎大家关注 : LF工作室

简书 : https://www.jianshu.com/u/e61935d18b09

掘金 : https://juejin.im/user/59239002570c350069c5f0bb

微信公众号 :

头条号 :

Travis CI持续集成GitHub个人博客

什么是Travis CI

Travis CI 是目前新兴的开源持续集成构建项目,它与jenkins,GO的很明显的特别在于采用yaml格式,同时他是在在线的服务,
不像jenkins需要你本地打架服务器,简洁清新独树一帜。目前大多数的github项目都已经移入到Travis CI的构建队列中,
据说Travis CI每天运行超过4000次完整构建。对于做开源项目或者github的使用者,快将你的项目加入Travis CI构建队列吧!

目标

使用Hexo搭建托管在Github上的个人博客,每次推送新博客到Github,Travis CI 自动构建并推送到博客项目的master分支上.
由于GitPages服务规定网页文件必须在master分支上,所以博客源码内容在项目的hexo-source分支.


步骤

1.TravisCI创建账户

最好使用Github账户直接登录,登录后界面如下,勾选个人博客项目即可.

2.生成并配置Access Token

在GitHub生成Travis CI 的token





生成之后一定要保存好,因为只会出现一次,丢失了就只能再重新生成了。

之后将生成的token配置到Travis CI中

3.创建.travis.yml配置文件

在项目的hexo-source分支中,项目的根目录下创建.travis.yml配置文件 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
language: node_js
node_js: 6

# S: Build Lifecycle
install:
- npm install

#before_script:
# - npm install -g gulp

script:
- hexo g

after_script:
- cd ./public
- git init
- git config user.name "lujiahao0708"
- git config user.email "lujiahao0708@gmail.com"
- git add .
- git commit -m "Update docs"
- git push --force --quiet "https://${GH_TOKEN}@${GH_REF}" master:master

# E: Build LifeCycle
branches:
only:
- hexo-source
env:
global:
- GH_REF: github.com/lujiahao0708/lujiahao0708.github.io.git

替换git config信息为你自己的,GH_REF的值更改为你的仓库地址.

4.发布新博客

将博客内容推送到hexo-source分支上,就会触发Travis CI 的自动构建.

Q&A

1.Travis CI编译错误

我参照的教程中.travis.yml配置文件的node_js版本使用`stable`,但是会出现错误.
解决方案 : 
    使用低版本的NodeJS版本
    https://segmentfault.com/q/1010000011317783

2.自定义域名无法跳转

CNAME文件直接放到了工程的根目录下,将无法打包进去
解决方案 : 
    将CNAME文件放到source目录下

参考教程

https://blog.csdn.net/woblog/article/details/51319364
https://www.jianshu.com/p/5691815b81b6


欢迎大家关注😁

Simpleblog博客系统(一)---DaoCloud持续集成

DaoCloud持续集成

DaoCloud提供了非常棒的CI系统,配合github完美持续集成应用部署.

操作步骤

  • 创建项目
    01创建项目
  • 02创建项目
    02创建项目
  • 03初始界面
    03初始界面
  • 04流程
    04流程
  • 05安全构建
    05安全构建
  • 06构建准备
    06构建准备
  • 安全构建
    安全构建
  • 08手动触发
    08手动触发
  • 09master分支触发
    09master分支触发
  • 10查看日志
    10查看日志
  • 11执行成功
    11执行成功
  • 12应用-创建应用
    12应用-创建应用
  • 13部署最新版本
    13部署最新版本
  • 14基本信息
    14基本信息
  • 15应用设置
    15应用设置
  • 16启动应用
    16启动应用

欢迎大家关注😁

日志规范(一)

日志规范(一)

记录在首约的工作中遇到的日志需要规范的地方,感谢鑫哥教我这么多规范.

1. 全局查找标识

完整的日志是可以通过日志看到整个代码的流程.这就需要一个全局查找的标识,当我们根据这个标识搜索日志时,可以将代码执行的流程梳理出来,由此可以快速定位出问题,及时修复bug.

eg : 最常见的就是订单号之类

1
LOGGER.info("根据订单号查询优惠券id orderNo:{} response:{}", orderNo, response);

2. Http请求

  • 调用Http请求时将调用的url和参数输出出来,这样可以避免由于接口或参数配置错误导致无法联调成功.
  • 同时接口的返回值也要打输出出来,这样可以避免接口返回值格式错误导致的异常.
  • 调用接口的响应时间也要输出,调用接口的响应时间大大影响了自身接口的性能,因此输出接口响应时间是非常必要的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public void cancelOrderFee(Order order) {
long startTime = System.currentTimeMillis();
String orderNo = order.getOrderNo();
try {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("customerId", order.getBookingUserId());
paramMap.put("phone", order.getBookingUserPhone());
paramMap.put("orderNo", orderNo);
LOGGER.info("退还指定司机费 orderNo:{} url:{} param:{}", orderNo, REFUND_CANCELORDER_FEE_URL, paramMap);
String response = HttpUtil.getIntance().post(REFUND_CANCELORDER_FEE_URL, paramMap);
LOGGER.info("退还指定司机费 orderNo:{} 接口响应:{}", orderNo, response);
if (StringUtils.isNotBlank(response)) {
ResponseResult responseResult = JsonUtils.fromJson(response, ResponseResult.class);
if (ResponseResult.SUCCESS_CODE == responseResult.getCode()) {
int data = (int) responseResult.getData();
if (0 == data) {
LOGGER.info("退还指定司机费[成功] orderNo:{}", orderNo);
} else {
LOGGER.error("退还指定司机费[失败] orderNo:{} 失败原因:{}", orderNo, responseResult.getMsg());
}
} else {
LOGGER.error("退还指定司机费[失败] orderNo:{} 接口调用失败 失败原因:{}", orderNo, responseResult.getMsg());
}
} else {
LOGGER.error("退还指定司机费[失败] orderNo:{} 接口返回值错误 response:{}", orderNo, response);
}
} catch (Exception e) {
LOGGER.error("退还指定司机费[异常] orderNo:{} 异常信息:", orderNo, e);
} finally {
LOGGER.info("退还指定司机费 orderNo:{} 接口耗时:{}ms", orderNo, System.currentTimeMillis() - startTime);
}
}

3. 查询结果数组或集合输出个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<OrderTasks> unArrivedOrders = orderTaskService.getUnArrivedOrders();
if (unArrivedOrders != null && unArrivedOrders.size() > 0) {
LOGGER.info("定时任务-订单自动取消逻辑 listSize:" + unArrivedOrders.size() + " threadId:" + threadId);
for (OrderTasks orderTasks : unArrivedOrders) {
try {
CarFactOrderMongo order = carFactOrderSlaveMapper.selectVOByOrderNo(orderTasks.getOrderNo());
if (order != null && StringUtils.isNotBlank(order.getRiderPhone()) && orderTaskService.validCityAndServiceType(order)) {
LOGGER.info("定时任务-订单自动取消逻辑-将要执行取消 orderNo:" + order.getOrderNo() + " threadId:" + threadId);
orderTaskService.cancelOrder(order, orderTasks.getNotArraiveCancelPeriod());
} else {
LOGGER.info("定时任务-订单自动取消逻辑-条件不满足 orderNo:" + order.getOrderNo());
}
} catch (Exception e) {
LOGGER.error("定时任务-订单自动取消逻辑-异常 orderNo:" + orderTasks.getOrderNo() + " threadId:" + threadId, e);
}
}
}

4. 定时任务需要打印线程Id

1
2
long threadId = Thread.currentThread().getId();
LOGGER.info("定时任务-订单自动取消逻辑-job start threadId:{}", threadId);

emall商城-面向接口编程实践

1. 面向接口编程

在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,
各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关
系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都
是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

上述内容来自百度百科(说实话我没看懂).

2. 需求

emall商城中有用户登录后需要将用户的token信息在服务端保存一份,实现这个功能有两个思路:

  • Redis
  • Guava的LoadingCache

此处就需要将存储token的这一逻辑抽象成接口,两种方式分别实现此接口,从而达到业务逻辑与底层实现分离.

3. 实现

3.1 抽象ILocalCache接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 本地缓存
* @author lujiahao
* @version 1.0
* @date 2017-10-20 17:24
*/
public interface ILocalCache<T> {
/**
* 设置缓存
* @param key
* @param value
* @return
*/
boolean setCache(String key, T value);

/**
* 删除缓存
* @param key
* @return
*/
boolean cleanCache(String key);

/**
* 获取缓存
* @param key
* @return
*/
Object getCache(String key);
}

3.2 Redis实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* Redis实现缓存
* @author lujiahao
* @version 1.0
* @date 2017-10-20 17:33
*/
public class RedisCacheImpl<T> implements ILocalCache<T> {
public static final Logger LOGGER = LoggerFactory.getLogger(RedisCacheImpl.class);

@Autowired
private JedisClientDao jedisClientDao;

@Override
public boolean setCache(String key, T value) {
// 把用户信息写入redis
jedisClientDao.set(Const.CACHE_TOKEN + ":" + key, JsonUtils.objectToJson(value));
// 设置session过期时间
jedisClientDao.expire(Const.CACHE_TOKEN + ":" + key, Const.CACHE_TOKEN_EXPIRE);
return true;
}

@Override
public boolean cleanCache(String key) {
try {
// 根据token从redis中查询用户信息
String json = jedisClientDao.get(Const.CACHE_TOKEN + ":" + key);
if (StringUtils.isNoneBlank(json)) {
// 更新过期时间--清除key
jedisClientDao.expire(Const.CACHE_TOKEN + ":" + key, 0);
}
return true;
} catch (Exception e) {
return false;
}
}

@Override
public Object getCache(String key) {
try {
// 根据token从redis中查询用户信息
String json = jedisClientDao.get(Const.CACHE_TOKEN + ":" + key);
if (StringUtils.isBlank(json)) {
return ServerResponse.build(400, "此Session已经过期,请重新登录");
}
// 更新过期时间
jedisClientDao.expire(Const.CACHE_TOKEN + ":" + key, Const.CACHE_TOKEN_EXPIRE);
// 返回用户信息
EmallUser emallUser = JsonUtils.jsonToPojo(json, EmallUser.class);
return ServerResponse.success(emallUser);
} catch (Exception e) {
return ServerResponse.error("无法获取用户信息");
}
}
}

3.3 Guava的LoadingCache实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* Guava实现缓存
* @author lujiahao
* @version 1.0
* @date 2017-10-20 17:33
*/
public class GuavaCacheImpl<T> implements ILocalCache<T> {
public static final Logger LOGGER = LoggerFactory.getLogger(GuavaCacheImpl.class);

// LRU算法
private static LoadingCache<String, Object> localCache = CacheBuilder.newBuilder().initialCapacity(1000)
.maximumSize(10000).expireAfterAccess(12, TimeUnit.HOURS)
.build(new CacheLoader<String, Object>() {
// 默认的数据加载实现,当调用get取值是,如果没有key,就执行这个方法
@Override
public Object load(String s) throws Exception {
return null;
}
});

@Override
public boolean setCache(String key, T value) {
try {
localCache.put(key, value);
} catch (Exception e) {
return false;
}
return true;
}

@Override
public boolean cleanCache(String key) {
try {
localCache.invalidate(key);
} catch (Exception e) {
return false;
}
return true;
}

@Override
public Object getCache(String key) {
try {
return localCache.get(key);
} catch (Exception e) {
LOGGER.error("========== localCache get error ==========", e);
}
return null;
}
}

3.4 applicationContext.xml中配置

1
2
3
<!--根据具体实现采用缓存实现方式-->
<!--<bean class="com.lujiahao.sso.utils.cache.RedisCacheImpl"/>-->
<bean class="com.lujiahao.sso.utils.cache.GuavaCacheImpl"/>

3.5 代码中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Service
public class IUserServiceImpl implements IUserService {
private static final Logger LOGGER = LoggerFactory.getLogger(IUserServiceImpl.class);

@Autowired
private EmallUserMapper emallUserMapper;

@Autowired
private ILocalCache iLocalCache;

@Override
public ServerResponse userLogin(String username, String password) {
try {
String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
EmallUser emallUser = emallUserMapper.userLogin(username, md5Password);
if (emallUser == null) {
// 不能返回没有此用户名 没用用户名也返回这个信息是因为防止猜测用户名
return ServerResponse.error("用户名或密码错误");
}
// 保存用户信息前先把密码清除,为了安全起见
emallUser.setPassword(StringUtils.EMPTY);
String token = UUID.randomUUID().toString();
// 这里采用接口编程的方式,到底用redis还是用guava
boolean isSaveSuccess = iLocalCache.setCache(token, emallUser);
if (isSaveSuccess) {
return ServerResponse.success(token);
} else {
LOGGER.info("========== 用户信息保存缓存失败 ==========");
return ServerResponse.error("登录失败,请重试!");
}
} catch (Exception e) {
ExceptionUtil.getStackTrace(e);
return ServerResponse.error("服务器异常");
}
}
}

在代码中使用的时候,将接口ILocalCache注入,在applicationContex.xml中根据具体要求配置不同的
bean,由此就可以实现将缓存业务与缓存实现解耦.

4. 总结

我理解的面向接口编程就是将业务中的需求抽取出公共的几种方式或步骤,底层由不同的类来实现这个接口,
由此达到解耦的目的.个人理解,欢迎大家拍砖^_^.

emall商城-SSO单点登录

1. 前期准备

  1. 准备一台Redis服务器
  2. 添加host127.0.0.1 sso.emall.com
  3. 搭建emall-sso工程并整合响应的框架

2. 实现原理

单点登录的场景随处可见,此功能极大的简化了用户在网站间的重复登录,使得用户体验更加良好.本教程单点登录的实现原理:用户根据用户名和密码登录,成功后服务器返回token信息,并将token信息写入Redis和Cookie中,用户再次登录时,首先判断Cookie中是否有token信息,如果有则根据token去后台换取用户信息;否则提示用户重新登录.

3. 实现步骤

3.1 登录接口

/**
 * 用户登录
 */
@Override
public CommonResult userLogin(UserDTO userDTO, HttpServletRequest request, HttpServletResponse response) {
    String username = userDTO.getUsername();
    String password = userDTO.getPassword();

    TbUserExample example = new TbUserExample();
    TbUserExample.Criteria criteria = example.createCriteria();
    criteria.andUsernameEqualTo(username);
    List<TbUser> list = tbUserMapper.selectByExample(example);
    // 如果没有此用户名  没用用户名也返回这个信息是因为防止猜测用户名
    if (list == null || list.size() == 0) {
        return CommonResult.build(400, "用户名或密码错误");
    }
    TbUser tbUser = list.get(0);
    // 对比密码
    if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(tbUser.getPassword())) {
        return CommonResult.build(400, "用户名或密码错误");
    }
    // 生成token
    String token = UUID.randomUUID().toString();
    // 保存用户信息前先把密码清除,为了安全起见
    tbUser.setPassword(null);

    // 用户信息存入Redis
    saveUserInfoToRedis(tbUser, token);

    // 添加写cookie的逻辑  cookie有效期是关闭浏览器失效
    CookieUtils.setCookie(request, response, COOKIE_TOKEN, token);
    return CommonResult.ok(token);
}

3.2 根据token查询用户信息

/**
 * 根据token查询用户信息
 */
@Override
public CommonResult getUserByToken(String token) {
    // 根据token从redis中查询用户信息
    String json = jedisClientDao.get(REDIS_USER_SESSION_KEY + ":" + token);
    if (StringUtils.isBlank(json)) {
        return CommonResult.build(400, "此Session已经过期,请重新登录");
    }
    // 更新过期时间
    jedisClientDao.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);
    // 返回用户信息
    return CommonResult.ok(JsonUtils.jsonToPojo(json, TbUser.class));
}

4. 跨域

4.1 JSONP

JSONP的实现与 ajax 没有任何关系,JSONP是通过script的src实现的,最终都是向服务器发送请求数据然后回调,而且方便起见,jquery把 JSONP 封装在了 $.ajax 方法中,调用方式与 ajax 调用方式略有区别。JSONP本质是:动态创建script标签,然后通过他的src属性发送跨域请求.

/**
 * 通过token查询用户信息
 *
 * @param token
 * @return
 */
@RequestMapping(value = "/token/{token}")
@ResponseBody
public Object getUserByToken(@PathVariable String token, String callback) {
    CommonResult result = null;
    try {
        result = userService.getUserByToken(token);
    } catch (Exception e) {
        e.printStackTrace();
        result = CommonResult.build(500, ExceptionUtil.getStackTrace(e));
    }
    // 判断是否为jsonp调用
    if (StringUtils.isBlank(callback)) {
        // 不是jsonp调用
        return result;
    } else {
        MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
        mappingJacksonValue.setJsonpFunction(callback);
        return mappingJacksonValue;
    }
}

4.2 CORS

此种方式后端实现有两种方式:
    让所有的controller类继承自定义的BaseController类,改类中将对返回的头部做些特殊处理;
    通过filter实现所有的请求封装跨域.

4.2.1 继承BaseController

public abstract class BaseController {
      /**
     * description:send the ajax response back to the client side
     * @param responseObj
     * @param response
     */
    protected void writeAjaxJSONResponse(Object responseObj, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1
        response.setHeader("Pragma", "no-cache"); // HTTP 1.0
        /**
         * for ajax-cross-domain request TODO get the ip address from
         * configration(ajax-cross-domain.properties)
         */
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setDateHeader("Expires", 0); // Proxies.
        PrintWriter writer = getWriter(response);
        writeAjaxJSONResponse(responseObj, writer);
    }

      /**
     *
     * @param response
     * @return
     */
    protected PrintWriter getWriter(HttpServletResponse response) {
        if(null == response){
            return null;
        }
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
        } catch (IOException e) {
            logger.error("unknow exception", e);
        }
        return writer;
    }

    /**
     * description:send the ajax response back to the client side.
     *
     * @param responseObj
     * @param writer
     * @param writer
     */
    protected void writeAjaxJSONResponse(Object responseObj, PrintWriter writer) {
        if (writer == null || responseObj == null) {
            return;
        }
        try {         
            writer.write(JSON.toJSONString(responseObj,SerializerFeature.DisableCircularReferenceDetect));
        } finally {
            writer.flush();
            writer.close();
        }
    }
}

4.2.2 Filter实现

/**
 * @author lujiahao
 * @version 1.0
 * @date 2017-10-15 22:20
 */
public class HeadersCORSFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setHeader("Access-Control-Allow-Origin","*");
        filterChain.doFilter(servletRequest, response);
    }

    @Override
    public void destroy() {

    }
}

web.xml中配置:

<!-- Ajax Access-Control-Allow-Origin 跨域 拦截器解决方案 -->
<filter>
    <filter-name>ACAOFilter</filter-name>
    <filter-class>com.lujiahao.sso.filter.HeadersCORSFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ACAOFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

5. 代码详见emall-sso

6. 实现效果

7. 说明

emall商城系列是整合[淘淘商城]和[慕课网Java从零到企业级电商项目实战]的系列,这两部教程来自互联网.

MySQL笔记-SQL语句分类

SQL语句可以分为以下四类 : 数据操作语言(DML) , 数据定义语言(DDL) , 数据控制语言(DCL) , 事务控制语言(TCL).

数据定义语言(DDL : Data Definition Language)

用于定义SQL模式,基本表,视图和索引的创建和撤消操作.

主要包含CREATE , ALTER , DROP , TRUNCATE , COMMENT , REPLACE(RENAME)等语句,一般不需要commit等事务操作.

数据操作语言(DML : Data Manipulation Language)

由数据库管理系统(DBMS) 提供,用于让用户或程序员使用,实现对数据库中数据的操作.

主要包含SELECT , INSERT , UPDATE , DELETE , MERGE , CALL , EXPLAIN PLAN , LOCK TABLE等语句.

数据控制语言(DCL:Data Control Language)

授权用户或用户组操作和访问数据的权限.

主要包含GRANT , REVOKE等语句.

事务控制语言(TCL:Transaction Control Language)

用于数据库的事务管理,确保被DML语句影响的表的所有行及时得以更新.

主要包含SAVEPOINT , SET TRANSACTION , BEGIN TRANSACTION , COMMIT , ROLLBACK等语句。

非官方分法

##数据查询语言(DQL : Data Queries Language)
用以从表中获得数据.

主要包含SELECT , WHERE , GROUP BY , HAVINGORDER BY.

Docker教程(八)---构建Kafka镜像

Kafka

该容器的前提依赖是使用我的JDK的镜像的依赖,后面我会把镜像同步到线上.

构建镜像

下载好Dockerfile文件执行

docker build -t="lujiahao/kafkatest" .

启动容器

docker run --name test_kafka -it -p 2181:2181 -p 9092:9092 lujiahao/kafkatest /bin/bash

额外配置

容器中使用kafka还需要将容器的内部IP配置到config/server.properties

docker inspcet test_kafka查看容器的内部IP

然后更改config目录中的server.properties中的三个配置

#容器的ip地址
host.name=172.17.0.1
#容器的ip地址
listeners=PLAINTEXT://172.17.0.1:9092
#宿主机的IP地址,用于外部Producer和Consumer使用(公司服务器你懂得)
advertised.listeners=PLAINTEXT://xx.xx.xx.xx:9092

启动Kafka

./kafkastart.sh

错误总结

1.无法分配内存

Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000c0000000, 1073741824, 0) failed;
error='Cannot allocate memory' (errno=12)

解决方案

vi zookeeper-server-start.sh 
    将 export KAFKA_HEAP_OPTS="-Xmx512M -Xms512M"
    改成 export KAFKA_HEAP_OPTS="-Xmx512M -Xms128M"
vi kafka-server-start.sh 
    将 export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
    改成 export KAFKA_HEAP_OPTS="-Xmx512M -Xms128M"
把这两个值改成相同的就行了

参考资料

2.无法发送消息

Error when sending message to topic TESTTOPIC with key: null, value: 9 bytes with error: 
Failed to update metadata after 60000 ms. (org.apache.kafka.clients.producer.internals.ErrorLoggingCallback)

解决方案

在 server.properties 中添加 advertised.listeners=PLAINTEXT://xx.xx.xx.xx:9092 ,对应的IP为宿主机的IP

参考资料

Kafka的基本操作

  1. 创建topic

    kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
  2. 查看topic

    kafka-topics.sh --list --zookeeper localhost:2181
  3. 生产者

    kafka-console-producer.sh --broker-list localhost:9092 --topic test
  4. 消费者

    kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning

Docker教程(七)---构建Tomcat镜像

这次镜像构建是基于上篇的JDK镜像构建的.

Dockerfile

FROM lujiahao/jdk1.7
MAINTAINER lujiahao
ADD apache-tomcat-7.0.75.tar.gz /usr/local/
RUN mv /usr/local/apache-tomcat-7.0.75 /usr/local/tomcat7
WORKDIR /usr/local/tomcat7/bin
EXPOSE 8080

构建命令

docker build -t="lujiahao/tomcat7" .

启动容器测试

docker run --name tomcat7 -p 8080:8080 -d lujiahao/tomcat7 ./catalina.sh run

注意:在运行装有tomcat的容器的时候使用catalina.sh run (调试模式,在前台运行)来启动,如果使用startup.sh 的话会在后台运行,容器会认为进程down掉,容器也会自动停止。

然后在浏览器中查看能否访问tomcat主页

Docker教程(六)---构建JDK镜像

1.创建文件夹jdk

因为会把当前构建目录中的内容添加到镜像里面  所以要单独创建一个文件夹

2.编写Dockerfile文件

FROM centos
MAINTAINER lujiahao
ADD jdk-7u80-linux-x64.tar.gz /usr/local
RUN mv /usr/local/jdk1.7.0_80 /usr/local/jdk1.7
ENV JAVA_HOME /usr/local/jdk1.7
ENV JRE_HOME /usr/local/jdk1.7/jre
ENV CLASSPATH .:$JAVA_HOME/jre/lib/rt.jar  :$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $JAVA_HOME/bin:$PATH

3.构建镜像

docker build -t="lujiahao/jdk1.7" .

4.构建完成之后启动一个临时性容器来测试

docker run --rm -it lujiahao/jdk1.7 /bin/bash

在运行java和javac看看,java -version看版本

相关资源

可以到 DockerfileCollection 中寻找对应的 Dockerfile 文件