springcloud(java)+spring-data-mongo 集成 mongodb 副本集集群

本贴最后更新于 2265 天前,其中的信息可能已经时移世易

springcloud(java)+spring-data-mongo 集成 mongodb 副本集集群

环境

springcloud版本Dalston.SR1
springboot版本1.5.2.RELEASE
mongodb3.4.14 3台3分片
  1. 在 pomx.ml 引入 spring-boot-starter-data-mongodb
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

2.在 yml 里配置

spring: 
   data:
      mongodb:
        uri: mongodb://admin:123456@192.168.31.1:20000,192.168.31.2:20000,192.168.31.3:20000/testdb?authSource=admin&connect=replicaSet&readPreference=secondaryPreferred&safe=true&authMechanism=SCRAM-SHA-1&maxPoolSize=500&minPoolSize=10
  1. uri 参数说明
  • uri 格式

mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]

  • mongodb:// 这是固定的格式,必须要指定。
  • username:password@ 可选项,如果设置,在连接数据库服务器之后,驱动都会尝试登陆这个数据库
  • host1 必须的指定至少一个 host, host1 是这个 URI 唯一要填写的。它指定了要连接服务器的地址。如果要连接复制集,请指定多个主机地址。
  • portX 可选的指定端口,如果不填,默认为 27017
  • /database 如果指定 username:password@,连接并验证登陆指定数据库。若不指定,默认打开 test 数据库。
  • ?options 是连接选项。如果不使用/database,则前面需要加上/。所有连接选项都是键值对 name=value,键值对之间通过&或;(分号)隔开

标准的连接格式包含了多个选项(options),如下所示:

选项 描述
replicaSet=name 验证 replica set 的名称。 Impliesconnect=replicaSet.
safe=true|false true:在执行更新操作之后,驱动都会发送 getLastError 命令来确保更新成功。(还要参考 wtimeoutMS).false: 在每次更新之后,驱动不会发送 getLastError 来确保更新成功。
w=n 驱动添加 { w : n } 到 getLastError 命令. 应用于 safe=true。
wtimeoutMS=ms 驱动添加 { wtimeout : ms } 到 getlasterror 命令. 应用于 safe=true.
fsync=true|false true: 驱动添加 { fsync : true } 到 getlasterror 命令.应用于 safe=true
journal=true|false 如果设置为 true, 同步到 journal (在提交到数据库前写入到实体中). 应用于 safe=true
connectTimeoutMS=ms 可以打开连接的时间。
socketTimeoutMS=ms 发送和接受 sockets 的时间。

跟多参数参考:mongodb 官方文档

  1. 建立通用 model
    MongoOpenDoorLog.java
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.CompoundIndexes;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import java.io.Serializable;

@Document(collection="open_door_log")
@CompoundIndexes({
        @CompoundIndex(name = "id_1_user_id_1_open_time_1", def = "{'user_id': 1, 'open_time': 1}"),
        @CompoundIndex(name = "door_id_1_community_id_1_build_id_1_cell_id_1", def = "{'door_id': 1, 'community_id': 1,'build_id':1,'cell_id':1}")
})
public class MongoOpenDoorLog implements Serializable{
    @Id
    private String id;
    private String tid;
    @Field("log_time")
    private long logTime;
    @Field("door_id")
    private String doorId;
    @Field("user_id")
    private String userId;
    @Field("user_type")
    private String userType;
    @Field("device_type")
    private String deviceType;
    @Field("device_sys")
    private String deviceSys;
    @Field("open_time")
    private long openTime;
    @Field("lock_type")
    private int lockType;
    @Field("open_type")
    private int openType;
    @Field("open_info")
    private String openInfo;
    @Field("area_id")
    private String areaId;
    private String area;
    @Field("community_id")
    private String communityId;
    @Field("community_name")
    private String communityName;
    @Field("build_id")
    private String buildId;
    @Field("build_name")
    private String buildName;
    @Field("build_num")
    private String buildNum;
    @Field("cell_id")
    private String cellId;
    @Field("cell_name")
    private String cellName;
    @Field("cell_num")
    private String cellNum;
    private int feedback;
    @Field("feedback_time")
    private long feedbackTime;
    private String img;
    private boolean deletec;


    /**
     * 创建时间
     */
    @Field("create_time")
    private long createTime;
    /**
     * 更新时间
     */
    @Field("update_time")
    private long updateTime;

    public void setId(String id){
        this.id = id;
    }

    public String getId(){
        return this.id;
    }

    public void setLogTime(long logTime){
        this.logTime = logTime;
    }

    public long getLogTime(){
        return this.logTime;
    }

    public void setDoorId(String doorId){
        this.doorId = doorId;
    }

    public String getDoorId(){
        return this.doorId;
    }

    public void setDeviceType(String deviceType){
        this.deviceType = deviceType;
    }

    public String getDeviceType(){
        return this.deviceType;
    }

    public void setDeviceSys(String deviceSys){
        this.deviceSys = deviceSys;
    }

    public String getDeviceSys(){
        return this.deviceSys;
    }

    public void setOpenTime(long openTime){
        this.openTime = openTime;
    }

    public long getOpenTime(){
        return this.openTime;
    }

    public void setLockType(int lockType){
        this.lockType = lockType;
    }

    public int getLockType(){
        return this.lockType;
    }

    public void setOpenType(int openType){
        this.openType = openType;
    }

    public int getOpenType(){
        return this.openType;
    }

    public void setOpenInfo(String openInfo){
        this.openInfo = openInfo;
    }

    public String getOpenInfo(){
        return this.openInfo;
    }

    public void setAreaId(String areaId){
        this.areaId = areaId;
    }

    public String getAreaId(){
        return this.areaId;
    }

    public void setArea(String area){
        this.area = area;
    }

    public String getArea(){
        return this.area;
    }

    public void setCommunityId(String communityId){
        this.communityId = communityId;
    }

    public String getCommunityId(){
        return this.communityId;
    }

    public void setCommunityName(String communityName){
        this.communityName = communityName;
    }

    public String getCommunityName(){
        return this.communityName;
    }

    public void setBuildId(String buildId){
        this.buildId = buildId;
    }

    public String getBuildId(){
        return this.buildId;
    }

    public void setBuildName(String buildName){
        this.buildName = buildName;
    }

    public String getBuildName(){
        return this.buildName;
    }

    public void setBuildNum(String buildNum){
        this.buildNum = buildNum;
    }

    public String getBuildNum(){
        return this.buildNum;
    }

    public void setCellId(String cellId){
        this.cellId = cellId;
    }

    public String getCellId(){
        return this.cellId;
    }

    public void setCellName(String cellName){
        this.cellName = cellName;
    }

    public String getCellName(){
        return this.cellName;
    }

    public void setCellNum(String cellNum){
        this.cellNum = cellNum;
    }

    public String getCellNum(){
        return this.cellNum;
    }

    public void setFeedback(int feedback){
        this.feedback = feedback;
    }

    public int getFeedback(){
        return this.feedback;
    }

    public void setFeedbackTime(long feedbackTime){
        this.feedbackTime = feedbackTime;
    }

    public long getFeedbackTime(){
        return this.feedbackTime;
    }

    public void setImg(String img){
        this.img = img;
    }

    public String getImg(){
        return this.img;
    }

    public boolean isDeletec() {
        return deletec;
    }

    public void setDeletec(boolean deletec) {
        this.deletec = deletec;
    }

    public long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(long createTime) {
        this.createTime = createTime;
    }

    public long getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(long updateTime) {
        this.updateTime = updateTime;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserType() {
        return userType;
    }

    public void setUserType(String userType) {
        this.userType = userType;
    }

    public String getTid() {
        return tid;
    }

    public void setTid(String tid) {
        this.tid = tid;
    }
}

  1. 建立通用 dao
    MongoBaseDao.java
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.List;


public interface  MongoBaseDao<T> {
    //保存一个对象到mongodb
    public T save(T bean);

    // 根据id删除对象
    public void deleteById(T t);

    // 根据对象的属性删除
    public void deleteByCondition(T t);

    // 通过条件查询更新数据
    public void update(Query query, Update update);

    // 根据id进行更新
    public void updateById(String id, T t);

    // 通过条件查询实体(集合)
    public List<T> find(Query query);

    public List<T> findByCondition(T t);

    // 通过一定的条件查询一个实体
    public T findOne(Query query);

    // 通过ID获取记录
    public T get(String id);

    // 通过ID获取记录,并且指定了集合名(表的意思)
    public T get(String id, String collectionName);

    public MongoTemplate getMongoTemplate();
}

  1. 建立通用 daosuport
    MongoDaoSupport.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.List;

public abstract  class MongoDaoSupport<T extends Serializable> implements MongoBaseDao<T> {

    @Autowired
    @Qualifier("mongoTemplate")
    protected MongoTemplate mongoTemplate;
    //保存一个对象到mongodb
    public T save(T bean) {
        mongoTemplate.save(bean);
        return bean;
    }

    // 根据id删除对象
    public void deleteById(T t) {
        mongoTemplate.remove(t);
    }


    // 根据对象的属性删除
    public void deleteByCondition(T t) {
        Query query = buildBaseQuery(t);
        mongoTemplate.remove(query, getEntityClass());
    }

    // 通过条件查询更新数据
    public void update(Query query, Update update) {
        mongoTemplate.updateMulti(query, update, this.getEntityClass());
    }

    // 根据id进行更新
    public void updateById(String id, T t) {
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(id));
        Update update = buildBaseUpdate(t);
        update(query, update);
    }

    // 通过条件查询实体(集合)
    public List<T> find(Query query) {
        return mongoTemplate.find(query, this.getEntityClass());
    }

    public List<T> findByCondition(T t) {
        Query query = buildBaseQuery(t);
        return mongoTemplate.find(query, getEntityClass());
    }

    // 通过一定的条件查询一个实体
    public T findOne(Query query) {
        return mongoTemplate.findOne(query, this.getEntityClass());
    }


    // 通过ID获取记录
    public T get(String id) {
        return mongoTemplate.findById(id, this.getEntityClass());
    }

    // 通过ID获取记录,并且指定了集合名(表的意思)
    public T get(String id, String collectionName) {
        return mongoTemplate.findById(id, this.getEntityClass(), collectionName);
    }

    // 根据vo构建查询条件Query
    private Query buildBaseQuery(T t) {
        Query query = new Query();
        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    org.springframework.data.mongodb.core.mapping.Field queryField = field.getAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
                    if (queryField != null) {
                        query.addCriteria(Criteria.where(queryField.value()).is(value));
                    }else{
                        query.addCriteria(Criteria.where(field.getName()).is(value));
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return query;
    }

    private Update buildBaseUpdate(T t) {
        Update update = new Update();

        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    update.set(field.getName(), value);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return update;
    }

    // 获取需要操作的实体类class
    @SuppressWarnings("unchecked")
    protected Class<T> getEntityClass() {
        return ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }

    public MongoTemplate getMongoTemplate() {
        return mongoTemplate;
    }
}

8.建立 service 接口
MongoOpenDoorLogService.java


public interface MongoOpenDoorLogService extends MongoBaseDao<MongoOpenDoorLog> {

}

  1. 建立 service 实现类
    MongoOpenDoorLogServiceImpl.java

import org.springframework.stereotype.Service;

@Service("mongoOpenDoorLogServiceImpl")
public class MongoOpenDoorLogServiceImpl extends MongoDaoSupport<MongoOpenDoorLog> implements MongoOpenDoorLogService{

}
  1. 暴露接口
    RequestObject.java
import com.zzwtec.common.validation.constraint.ZNotBlank;
import com.zzwtec.common.validation.constraint.ZNotNullWithStr;

import javax.validation.Valid;

public class RequestObject<T> {

    @ZNotBlank
    private String requestId;
    @Valid @ZNotNullWithStr
    private T data;

    public RequestObject() {
        this.requestId = RequestIdContext.get();
    }

    public RequestObject(T data) {
        this();
        this.data = data;
    }


    public RequestObject(String requestId, T data) {
        this.requestId = requestId;
        this.data = data;
    }

    public String getRequestId() {
        return requestId;
    }

    public RequestObject<T> setRequestId(String requestId) {
        this.requestId = requestId;
        return this;
    }

    public T getData() {
        return data;
    }

    public RequestObject<T> setData(T data) {
        this.data = data;
        return this;
    }

    public static <T> RequestObject<T> build(T data) {
        return new RequestObject<T>(data);
    }

    public static <T> RequestObject<T> build(T data,String requestId) {
        return new RequestObject<T>(requestId,data);
    }
    public static <T> RequestObject<T> build(String requestId,T data) {
       return new RequestObject<T>(requestId,data);
    }

    @Override
    public String toString() {
        return "RequestObject [requestId=" + requestId + ", data=" + data + "]";
    }

}

ResultObject.java


public class ResultObject<T> {

    public static final String SUCCESS_CODE = "0";
    public static final String SUCCESS_MSG = "success";
    public static final String ERROR_CODE = "1";
    public static final String ERROR_MSG = "error";

    private String requestId;
    /**
     * 返回码
     */
    private String code;
    /**
     * 返回描述
     */
    private String msg;
    /**
     * 请求数据
     */
    private T data;

    public ResultObject() {
    }

    public ResultObject(String requestId) {
        this.requestId = requestId;
    }

    @SuppressWarnings("rawtypes")
    public ResultObject(RequestObject request) {
        this(request.getRequestId());
    }

    @SuppressWarnings("rawtypes")
    public ResultObject(RequestObject request, String code, String msg) {
        this(request);
        this.code = code;
        this.msg = msg;
    }

    @SuppressWarnings("rawtypes")
    public ResultObject(RequestObject request, String code, String msg, T data) {
        this(request, code, msg);
        this.data = data;
    }

    public String getRequestId() {
        return requestId;
    }

    public ResultObject<T> setRequestId(String requestId) {
        this.requestId = requestId;
        return this;
    }

    public String getCode() {
        return code;
    }

    public ResultObject<T> setCode(String code) {
        this.code = code;
        return this;
    }

    public String getMsg() {
        return msg;
    }

    public ResultObject<T> setMsg(String msg) {
        this.msg = msg;
        return this;
    }

    public T getData() {
        return data;
    }

    public ResultObject<T> setData(T data) {
        this.data = data;
        return this;
    }

    public boolean isSuccess() {
        return SUCCESS_CODE.equals(code);
    }

    @Override
    public String toString() {
        return "ResultObject [requestId=" + requestId + ", code=" + code + ", msg=" + msg + ", data=" + data + "]";
    }
}

OpenDoorLogService.java

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@FeignClient(name = "a", fallback = OpenDoorLogServiceFallback.class)
@RequestMapping("odls")
@Api(value="OpenDoorLogService",
		description="mongodb开门日志操作外部服务接口",
		consumes = "application/json",
		produces = "application/json",
		protocols = "http")
public interface OpenDoorLogService {

	/**
	 * 添加单条开门日志
	 * @param request
	 * @return
	 */
	@ApiOperation(value="add", notes="添加单条开门日志")
	@ApiImplicitParam(name = "request", required = true, dataType = "RequestObject<MongoOpenDoorLog>", paramType = "body")
	@RequestMapping(value = "add", method = RequestMethod.POST)
	public ResultObject<Void> saveOpenDoorLog(@GroupsValid({Add.class}) @RequestBody RequestObject<MongoOpenDoorLog> request);

}

OpenDoorLogServiceController.java


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class OpenDoorLogServiceController implements OpenDoorLogService {

	@Autowired
	private MongoOpenDoorLogService mongoOpenDoorLogService;

	@Override
	public ResultObject<Void> saveOpenDoorLog(@GroupsValid({Add.class}) @RequestBody RequestObject<MongoOpenDoorLog> request){
		MongoOpenDoorLog t = request.getData();
		mongoOpenDoorLogService.save(t);
		return ResultBuilder.success(request);
	}
}

  1. 其它辅助类
    GroupsValid.java
import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GroupsValid {

    Class<?>[] value() default {};
}

Add.java
public interface Add {
}
Delete.java
public interface Delete {

}
Query.java
public interface Query {

}
Update.java
public interface Update {
}
  • 集群
    29 引用 • 65 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 65 回帖 • 439 关注
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖 • 1 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖 • 1 关注
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    75 引用 • 1737 回帖 • 3 关注
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    149 引用 • 257 回帖
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    12 引用 • 54 回帖 • 164 关注
  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    6 引用 • 38 回帖
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    568 引用 • 3532 回帖 • 1 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • WebComponents

    Web Components 是 W3C 定义的标准,它给了前端开发者扩展浏览器标签的能力,可以方便地定制可复用组件,更好的进行模块化开发,解放了前端开发者的生产力。

    1 引用
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖 • 2 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 2 关注
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    70 引用 • 193 回帖 • 431 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 1 关注
  • JetBrains

    JetBrains 是一家捷克的软件开发公司,该公司位于捷克的布拉格,并在俄国的圣彼得堡及美国麻州波士顿都设有办公室,该公司最为人所熟知的产品是 Java 编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA

    18 引用 • 54 回帖 • 1 关注
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 1 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    22378 引用 • 89573 回帖
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 597 关注
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    186 引用 • 318 回帖 • 304 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 210 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    179 引用 • 407 回帖 • 488 关注
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 530 关注
  • ActiveMQ

    ActiveMQ 是 Apache 旗下的一款开源消息总线系统,它完整实现了 JMS 规范,是一个企业级的消息中间件。

    19 引用 • 13 回帖 • 673 关注