HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

uniapp中使用全景图片查看器,查看全景VR图片

uniapp

全景图片查看器,有了Three.js的支持,已经是很简单的东西,网络上相关的插件有很多,这里推荐几个:Panolens.jsPano.jsphoto-sphere-viewer.js,这些插件都是基于Three.js,我这次是用的是photo-sphere-viewer.js。
但是使用这些插件,都是H5端的,uniapp中不能直接用。但uniapp有个组件web-view,可以引入内部或外部的HTML,文档传送:web-view。那这样问题思路就很清晰了,写一个HTML,使用查看器插件,再在uniapp项目中使用web-view组件,将本地的HTML导入。。。很简单吧

而且,查看器的使用也是巨简单,


引入两个js文件,


写个放插件的div,js中new一个插件对象,放上全景图片路径,完事儿~~其他具体的参数请去熟读并背诵文档

接下来就是在uniapp项目中引入,阅读web-view文档,我们能看到:

说明HTML文件不是随便放的,有特定的文件路径

uni页面使用web-view:


这里弄好了,简单的demo就完成了,就可以去调试运行了

经测试,我的demo在H5端、模拟器、真机都可以运行使用,小程序端还有点兼容问题,
运行效果:由于查看器是H5的插件,所以demo效果在H5端调试效果最好,图片清晰度、运行速度以及查看器自带的一些功能都是H5端效果最好

总结就是:::①请熟读并背诵文档,②动手实操

继续阅读 »

全景图片查看器,有了Three.js的支持,已经是很简单的东西,网络上相关的插件有很多,这里推荐几个:Panolens.jsPano.jsphoto-sphere-viewer.js,这些插件都是基于Three.js,我这次是用的是photo-sphere-viewer.js。
但是使用这些插件,都是H5端的,uniapp中不能直接用。但uniapp有个组件web-view,可以引入内部或外部的HTML,文档传送:web-view。那这样问题思路就很清晰了,写一个HTML,使用查看器插件,再在uniapp项目中使用web-view组件,将本地的HTML导入。。。很简单吧

而且,查看器的使用也是巨简单,


引入两个js文件,


写个放插件的div,js中new一个插件对象,放上全景图片路径,完事儿~~其他具体的参数请去熟读并背诵文档

接下来就是在uniapp项目中引入,阅读web-view文档,我们能看到:

说明HTML文件不是随便放的,有特定的文件路径

uni页面使用web-view:


这里弄好了,简单的demo就完成了,就可以去调试运行了

经测试,我的demo在H5端、模拟器、真机都可以运行使用,小程序端还有点兼容问题,
运行效果:由于查看器是H5的插件,所以demo效果在H5端调试效果最好,图片清晰度、运行速度以及查看器自带的一些功能都是H5端效果最好

总结就是:::①请熟读并背诵文档,②动手实操

收起阅读 »

聊天(IM)界面发消息滚动到底部

今天有小伙伴问我,制作(IM)聊天时,发送的聊天消息不能自动滚动,通过一些了算法,随后发现有一些问题,不是很好,问我有没有相关的好的方法。嗯嗯嗯,当然,我这确实有一个比较好的方法。具体如下
原理 利用 scroll-view的scroll-into-view属性
效果图 见附件
具体代码如下

<template>  
    <view class='test'>  
        <scroll-view :scroll-into-view="viewIndex" scroll-view scroll-with-animation='true' :style="{'box-sizing':'border-box', 'padding':'20rpx','height':'200rpx'}"  
         scroll-y="true" class="scroll-Y">  
            <!-- 具体聊天内容 -->  
            <view v-for="(c,i) in chatList" :key="i">  
                {{c}}  
            </view>  

            <!-- 可滚动到底部的view标签 -->  
            <view :id="'im_'+chatList.length" class="bottom"></view>  
        </scroll-view>  
        <view>  
            <button @click="add">添加一行内容</button>  
        </view>  
    </view>  
</template>  
<!--  页面方法 -->  
<script>  
    export default {  
        data() {  
            return {  
                viewIndex: '',  
                chatList: ['你好', '很好'], // 模拟消息记录 为演示滚动效果,此处只有简单的消息信息  
            }  
        },  
        methods: {  
            add() {  
                //模拟发消息添加一条记录  
                this.chatList[this.chatList.length] = "你添加了一行内容" + this.chatList.length;  

                this.viewIndex = "";  
                //设置viewIndex值,使聊天滚动到底部  
                this.$nextTick(() => {  
                    this.viewIndex = "im_" + this.chatList.length;  

                })  
            }  
        },  
    }  
</script>  
<style scoped>  
    .bottom {  
        width: 100vw;  
        height: 20rpx;  
    }  
</style>  
继续阅读 »

今天有小伙伴问我,制作(IM)聊天时,发送的聊天消息不能自动滚动,通过一些了算法,随后发现有一些问题,不是很好,问我有没有相关的好的方法。嗯嗯嗯,当然,我这确实有一个比较好的方法。具体如下
原理 利用 scroll-view的scroll-into-view属性
效果图 见附件
具体代码如下

<template>  
    <view class='test'>  
        <scroll-view :scroll-into-view="viewIndex" scroll-view scroll-with-animation='true' :style="{'box-sizing':'border-box', 'padding':'20rpx','height':'200rpx'}"  
         scroll-y="true" class="scroll-Y">  
            <!-- 具体聊天内容 -->  
            <view v-for="(c,i) in chatList" :key="i">  
                {{c}}  
            </view>  

            <!-- 可滚动到底部的view标签 -->  
            <view :id="'im_'+chatList.length" class="bottom"></view>  
        </scroll-view>  
        <view>  
            <button @click="add">添加一行内容</button>  
        </view>  
    </view>  
</template>  
<!--  页面方法 -->  
<script>  
    export default {  
        data() {  
            return {  
                viewIndex: '',  
                chatList: ['你好', '很好'], // 模拟消息记录 为演示滚动效果,此处只有简单的消息信息  
            }  
        },  
        methods: {  
            add() {  
                //模拟发消息添加一条记录  
                this.chatList[this.chatList.length] = "你添加了一行内容" + this.chatList.length;  

                this.viewIndex = "";  
                //设置viewIndex值,使聊天滚动到底部  
                this.$nextTick(() => {  
                    this.viewIndex = "im_" + this.chatList.length;  

                })  
            }  
        },  
    }  
</script>  
<style scoped>  
    .bottom {  
        width: 100vw;  
        height: 20rpx;  
    }  
</style>  
收起阅读 »

uniapp使用mockjs模拟数据,支持H5、微信小程序、APP

1.安装node服务,我采用的是koa2脚手架:
npm install -g koa-generator (全局安装一次就好)

2.新建项目(cd到某个盘符或者桌面创建)
koa2 -e 项目名称

3.npm install

4.npm install koa2-cors(配置允许本地跨域)

5.npm install mockjs --sava(安装mockjs)

6.在app.js文件里引入如下
const cors = require('koa2-cors')
app.use(cors()) //注意书写位置(模板写哪里就写哪里)

7、npm run dev (koa2端口号默认是3000)请用Network地址

8、记得在nodeServer执行npm install

继续阅读 »

1.安装node服务,我采用的是koa2脚手架:
npm install -g koa-generator (全局安装一次就好)

2.新建项目(cd到某个盘符或者桌面创建)
koa2 -e 项目名称

3.npm install

4.npm install koa2-cors(配置允许本地跨域)

5.npm install mockjs --sava(安装mockjs)

6.在app.js文件里引入如下
const cors = require('koa2-cors')
app.use(cors()) //注意书写位置(模板写哪里就写哪里)

7、npm run dev (koa2端口号默认是3000)请用Network地址

8、记得在nodeServer执行npm install

收起阅读 »

pda的广播扫码uni-app

一直困惑在uni-app中实现pda的广播扫码功能,今天终于找到了一个解决方案。
方案就是做一个全局监听,只引入一次,来防止很多页面都引入了扫码的组件,造成多次扫描,数据出现多次
知识点:全局监听官网的全局监听地址
uni.$emit:触发全局的自定事件。附加参数都会传给监听器回调。
uni.$on:监听全局的自定义事件。事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数
uni.$off:移除全局自定义事件监听器。

一定要记得移除全局事件,不燃就会出现多次扫描

一:在你的pda中要是设置广播输出:模式是键盘方式输出
方法:设置---扫描---Default:把里面的键盘方式输出取消掉,就会看到下面的广播动作广播数据标签(这两个很重要,在后面会用到)
上图:

二:在你的uni-app项目里 新建一个公共组件 就叫 scan-code 吧,放到公共组件components中,目录:components/scan-code/scan-code.vue
这里就用到了广播动作广播数据标签
filter.addAction("android.intent.ACTION_DECODE_DATA")
intent.getStringExtra("barcode_string")
代码里的filter.addAction里换成你的广播动作,intent.getStringExtra(里换成你的广播标签
代码如下:

<template>  
    <view class="content"></view>  
</template>  

<script>  
var main,receiver,filter;    
var _codeQueryTag = false;    
export default {  
    data() {  
        return {  
            scanCode: ''  
        }  
    },  
    created: function (option) {  
        this.initScan()  
        this.startScan();    
    },    
    onHide:function(){    
        this.stopScan();    
    },  
    destroyed:function(){    
        /*页面退出时一定要卸载监听,否则下次进来时会重复,造成扫一次出2个以上的结果*/    
        this.stopScan();    
    },    
    methods: {  
        initScan() {  
            let _this = this;  
            main = plus.android.runtimeMainActivity();//获取activity  
            var IntentFilter = plus.android.importClass('android.content.IntentFilter');   
            filter = new IntentFilter();    
            filter.addAction("android.intent.ACTION_DECODE_DATA"); // 换你的广播动作  
            receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver',{  
            onReceive : function(context, intent) {  
                plus.android.importClass(intent);     
                let code = intent.getStringExtra("barcode_string");// 换你的广播标签  
                _this.queryCode(code);    
            }});    
        },    
        startScan(){    
            main.registerReceiver(receiver,filter);  
        },    
        stopScan(){  
            main.unregisterReceiver(receiver);    
        },    
        queryCode: function(code){  
            //防重复  
            if(_codeQueryTag)return false;    
            _codeQueryTag = true;    
            setTimeout(function(){    
                _codeQueryTag = false;    
            },150);  
            var id = code  
            console.log('id:', id)  
            uni.$emit('scancodedate',{code:id})  
        }  
    }  
}  
</script>  

<style>  
    page {  
        background-color: #efeff4;  
    }  
    .content {  
        text-align: center;  
    }  
</style>

三:页面引用,只引用一次就好,我是index.vue 引用的,我的app的其他功能都是在这个页面链接走的
直接上代码:
切记:首页往各个子页面跳转的时候 移除监听事件uni.$off('scancodedate'),要不首页的监听会一直存在,就会出现首页这个扫码
uni.navigateTo({ 之前、之前、之前 uni.$off('scancodedate')

<template>  
    <view>  
        <view>你的页面内容</view>  
        <scan-code></scan-code>  
    </view>  
</template>  

<script>  
import scanCode from "@/components/scan-code/scan-code.vue";  
export default {  
    components: { scanCode },  
    data() {  
        return {}  
    }  
    onShow: function() {  
        var _this = this  
        uni.$off('scancodedate') // 每次进来先 移除全局自定义事件监听器  
        uni.$on('scancodedate',function(data){  
            console.log('你想要的code:', data.code)  
        })  
    }  
} 

其他的页面引用方法:不需要再次引入scanCode
因为其他的页面都是从首页跳转过来的,所以其他的页面需要
onUnload() {
// 移除监听事件
uni.$off('scancodedate')
}

代码如下:

onLoad() {  
   var _this = this  
   uni.$on('scancodedate',function(data){  
        // _this 这里面的方法用这个 _this.code(data.code)  
    console.log('你想要的code:', data.code)  
   })  
},  
onUnload() {  
   // 移除监听事件      
   uni.$off('scancodedate')  
}  

如果这个页面还有详细页面需要跳转,切记 uni.navigateTo({ 之前、之前、之前 uni.$off('scancodedate')

到此所有的代码就完事了。

重点就是全局监听,真机测试的时候在控制台可以打印出你的code和routes,看下是否每次的扫描只是执行的本页面(路由)下的程序。

继续阅读 »

一直困惑在uni-app中实现pda的广播扫码功能,今天终于找到了一个解决方案。
方案就是做一个全局监听,只引入一次,来防止很多页面都引入了扫码的组件,造成多次扫描,数据出现多次
知识点:全局监听官网的全局监听地址
uni.$emit:触发全局的自定事件。附加参数都会传给监听器回调。
uni.$on:监听全局的自定义事件。事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数
uni.$off:移除全局自定义事件监听器。

一定要记得移除全局事件,不燃就会出现多次扫描

一:在你的pda中要是设置广播输出:模式是键盘方式输出
方法:设置---扫描---Default:把里面的键盘方式输出取消掉,就会看到下面的广播动作广播数据标签(这两个很重要,在后面会用到)
上图:

二:在你的uni-app项目里 新建一个公共组件 就叫 scan-code 吧,放到公共组件components中,目录:components/scan-code/scan-code.vue
这里就用到了广播动作广播数据标签
filter.addAction("android.intent.ACTION_DECODE_DATA")
intent.getStringExtra("barcode_string")
代码里的filter.addAction里换成你的广播动作,intent.getStringExtra(里换成你的广播标签
代码如下:

<template>  
    <view class="content"></view>  
</template>  

<script>  
var main,receiver,filter;    
var _codeQueryTag = false;    
export default {  
    data() {  
        return {  
            scanCode: ''  
        }  
    },  
    created: function (option) {  
        this.initScan()  
        this.startScan();    
    },    
    onHide:function(){    
        this.stopScan();    
    },  
    destroyed:function(){    
        /*页面退出时一定要卸载监听,否则下次进来时会重复,造成扫一次出2个以上的结果*/    
        this.stopScan();    
    },    
    methods: {  
        initScan() {  
            let _this = this;  
            main = plus.android.runtimeMainActivity();//获取activity  
            var IntentFilter = plus.android.importClass('android.content.IntentFilter');   
            filter = new IntentFilter();    
            filter.addAction("android.intent.ACTION_DECODE_DATA"); // 换你的广播动作  
            receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver',{  
            onReceive : function(context, intent) {  
                plus.android.importClass(intent);     
                let code = intent.getStringExtra("barcode_string");// 换你的广播标签  
                _this.queryCode(code);    
            }});    
        },    
        startScan(){    
            main.registerReceiver(receiver,filter);  
        },    
        stopScan(){  
            main.unregisterReceiver(receiver);    
        },    
        queryCode: function(code){  
            //防重复  
            if(_codeQueryTag)return false;    
            _codeQueryTag = true;    
            setTimeout(function(){    
                _codeQueryTag = false;    
            },150);  
            var id = code  
            console.log('id:', id)  
            uni.$emit('scancodedate',{code:id})  
        }  
    }  
}  
</script>  

<style>  
    page {  
        background-color: #efeff4;  
    }  
    .content {  
        text-align: center;  
    }  
</style>

三:页面引用,只引用一次就好,我是index.vue 引用的,我的app的其他功能都是在这个页面链接走的
直接上代码:
切记:首页往各个子页面跳转的时候 移除监听事件uni.$off('scancodedate'),要不首页的监听会一直存在,就会出现首页这个扫码
uni.navigateTo({ 之前、之前、之前 uni.$off('scancodedate')

<template>  
    <view>  
        <view>你的页面内容</view>  
        <scan-code></scan-code>  
    </view>  
</template>  

<script>  
import scanCode from "@/components/scan-code/scan-code.vue";  
export default {  
    components: { scanCode },  
    data() {  
        return {}  
    }  
    onShow: function() {  
        var _this = this  
        uni.$off('scancodedate') // 每次进来先 移除全局自定义事件监听器  
        uni.$on('scancodedate',function(data){  
            console.log('你想要的code:', data.code)  
        })  
    }  
} 

其他的页面引用方法:不需要再次引入scanCode
因为其他的页面都是从首页跳转过来的,所以其他的页面需要
onUnload() {
// 移除监听事件
uni.$off('scancodedate')
}

代码如下:

onLoad() {  
   var _this = this  
   uni.$on('scancodedate',function(data){  
        // _this 这里面的方法用这个 _this.code(data.code)  
    console.log('你想要的code:', data.code)  
   })  
},  
onUnload() {  
   // 移除监听事件      
   uni.$off('scancodedate')  
}  

如果这个页面还有详细页面需要跳转,切记 uni.navigateTo({ 之前、之前、之前 uni.$off('scancodedate')

到此所有的代码就完事了。

重点就是全局监听,真机测试的时候在控制台可以打印出你的code和routes,看下是否每次的扫描只是执行的本页面(路由)下的程序。

收起阅读 »

专业团队开发uniapp、uniapp定制开发项目

移动APP uniapp

uniapp定制开发项目 uniapp项目开发 uniapp项目制定
要想做一个APP。首先肯定是要先了解清楚整体项目的流程,从南到北从西到东必须要走顺走通了,如果不通顺的情况下,无法确认APP的功能价格以及报价;
1.需求明确:需求明确后可以知道项目整体的大概周期时间,以及所需费用多少;

  1. UI设计图确认:UI设计图等于是一个原型图,原型图定的是颜色布局以及基本功能都有哪些;
  2. 前端切图代码编写;
  3. 后台搭建,填充项目内容以及项目内容的基本修改;
  4. 内部测试,修改完善细节;
  5. 测试完毕后,填充内容,安排时间绑定一级域名进行上线;
  6. 158-3211-5099(vx)
继续阅读 »

uniapp定制开发项目 uniapp项目开发 uniapp项目制定
要想做一个APP。首先肯定是要先了解清楚整体项目的流程,从南到北从西到东必须要走顺走通了,如果不通顺的情况下,无法确认APP的功能价格以及报价;
1.需求明确:需求明确后可以知道项目整体的大概周期时间,以及所需费用多少;

  1. UI设计图确认:UI设计图等于是一个原型图,原型图定的是颜色布局以及基本功能都有哪些;
  2. 前端切图代码编写;
  3. 后台搭建,填充项目内容以及项目内容的基本修改;
  4. 内部测试,修改完善细节;
  5. 测试完毕后,填充内容,安排时间绑定一级域名进行上线;
  6. 158-3211-5099(vx)
收起阅读 »

【伤心】为什么非要强制开发者使用 uni-AD 呢?没有选择权吗?

广告联盟

之前的版本没有问题,升级到 4 月底的版本后,打包使用自己的广告系统会直接在页面上提示广告配置错误。

为什么非要强制使用 uni-AD 呢? 不能给开发者选择的权利吗?

之前的版本没有问题,升级到 4 月底的版本后,打包使用自己的广告系统会直接在页面上提示广告配置错误。

为什么非要强制使用 uni-AD 呢? 不能给开发者选择的权利吗?

关于小程序项目导入Hbuilder X之后被识别为Web项目的问题

微信小程序

已经创建好的小程序项目导入Hbuilder X之后可能会出现小程序项目被识别成Web项目的问题。我推测,有可能是使用了NPM包之后引起的。经过测试,证实了推测。知道了原因,解决方法也很简单,只要将项目更目录下的package.json和package-lock.json重命名,然后右键点击项目文件夹,重新识别项目即可。之后再把重命名的文件名改回来就OK了!

继续阅读 »

已经创建好的小程序项目导入Hbuilder X之后可能会出现小程序项目被识别成Web项目的问题。我推测,有可能是使用了NPM包之后引起的。经过测试,证实了推测。知道了原因,解决方法也很简单,只要将项目更目录下的package.json和package-lock.json重命名,然后右键点击项目文件夹,重新识别项目即可。之后再把重命名的文件名改回来就OK了!

收起阅读 »

招 UNI-APP开发!在深圳科技园

招聘

目前我们大前端团队的职位要求是:

有uni app 开发打包生成H5和APP,并且项目上线的经验;

薪资看能力,你来报!

麻烦符合条件的发简历到邮箱987654114@qq.com; 最好提供作品APP的下载地址,我这和同事评估后会做进一步约面笔试和沟通。

公司福利:

(1)保险保障:入职即购买五险一金(社保一档),每月还有绩效奖金;

(2)带薪假期:法定节假日(休),工作满一年6天年假(休),传统节日福利礼品(休 拿);

(3)周边设施:公司就在高新园B口旁,周围各种精品小吃,日常快餐,豪华大餐,应有尽有,品类繁多。繁华商场,高端科技园区,休闲舒心;

(4)团队建设:每天公司餐补和下班路费补贴,每周一次公司休闲的下午茶,每月一次公司爱的聚餐,每年一次公司豪华旅游和年底Patty年会,还有不限次团建,胡吃海喝,等你加入;

(5)工作满一年,年底13薪,过年回家动车、机票报销,优秀员工还有更多福利

(6)泡面、茶叶、咖啡无限供应

(7)技术书籍、教程培训全额报销,每年开发者大会、培训活动报销

继续阅读 »

目前我们大前端团队的职位要求是:

有uni app 开发打包生成H5和APP,并且项目上线的经验;

薪资看能力,你来报!

麻烦符合条件的发简历到邮箱987654114@qq.com; 最好提供作品APP的下载地址,我这和同事评估后会做进一步约面笔试和沟通。

公司福利:

(1)保险保障:入职即购买五险一金(社保一档),每月还有绩效奖金;

(2)带薪假期:法定节假日(休),工作满一年6天年假(休),传统节日福利礼品(休 拿);

(3)周边设施:公司就在高新园B口旁,周围各种精品小吃,日常快餐,豪华大餐,应有尽有,品类繁多。繁华商场,高端科技园区,休闲舒心;

(4)团队建设:每天公司餐补和下班路费补贴,每周一次公司休闲的下午茶,每月一次公司爱的聚餐,每年一次公司豪华旅游和年底Patty年会,还有不限次团建,胡吃海喝,等你加入;

(5)工作满一年,年底13薪,过年回家动车、机票报销,优秀员工还有更多福利

(6)泡面、茶叶、咖啡无限供应

(7)技术书籍、教程培训全额报销,每年开发者大会、培训活动报销

收起阅读 »

iOS平台 配置通用链接(Universal Links)

1.配置指定文件:创建一个内容为json格式的文件这个文件名必须为apple-app-site-association,切记没有后缀名,文件内容大概是这样子:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "9JA89QQLNQ.com.apple.wwdc",
"paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]
},
]
}
}

说明:

appID:组成方式是 teamId.yourapp’s bundle identifier。如上面的 9JA89QQLNQ就是teamId。登陆开发者中心,在Account -> Membership里面可以找到Team ID。

paths:设定你的app支持的路径列表,只有这些指定的路径的链接,才能被app所处理。星号的写法代表了可识 别域名下所有链接。

上传指定文件:上传该文件到你的域名所对应的根目录或者.well-known目录下,这是为了苹果能获取到你上传的文件。上传完后,自己先访问一下,看看是否能够获取到,当你在浏览器中输入这个文件链接后,应该是直接下载apple-app-site-association文件。

查看全部 https://www.cnblogs.com/guoshaobin/p/11164000.html

继续阅读 »

1.配置指定文件:创建一个内容为json格式的文件这个文件名必须为apple-app-site-association,切记没有后缀名,文件内容大概是这样子:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "9JA89QQLNQ.com.apple.wwdc",
"paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]
},
]
}
}

说明:

appID:组成方式是 teamId.yourapp’s bundle identifier。如上面的 9JA89QQLNQ就是teamId。登陆开发者中心,在Account -> Membership里面可以找到Team ID。

paths:设定你的app支持的路径列表,只有这些指定的路径的链接,才能被app所处理。星号的写法代表了可识 别域名下所有链接。

上传指定文件:上传该文件到你的域名所对应的根目录或者.well-known目录下,这是为了苹果能获取到你上传的文件。上传完后,自己先访问一下,看看是否能够获取到,当你在浏览器中输入这个文件链接后,应该是直接下载apple-app-site-association文件。

查看全部 https://www.cnblogs.com/guoshaobin/p/11164000.html

收起阅读 »

基于uniapp开发的小说阅读器

uniapp 开发的H5端 APP端小说阅读器

缘起

学习uniapp有一年多,前期做过一个同城项目,后面工作忙,没有继续深入学习。2020年由于疫情的缘故,在家搁置时间过长,期间父亲大人迷上了小说,我就去网上找各种破解版小说APP给他。刚开始还好,看着看着软件强制更新了,就又给他找其他的破解APP,看一段时间又更新了。。。周而复始,后来就想为什么不自己弄一个呢,于是就有了今天这个项目。

主要功能

我主张一切从简,小说阅读器的主要功能:能看能搜。无论是三岁小孩,还是八十老妪,打开就能懂如何使用。

界面设计

业余前端,不追求页面的极致,能看就行。

  1. 主页

  1. 分类页

  1. 书籍详情页

  1. 书籍阅读页

  1. 书架页

  1. 我的页

我的页没有实质性功能,没有登录操作。

制作期间去uniapp的插件市场观摩了其他大佬做的小说阅读器,本来是不想重复造轮子的,使用过程中发现了一些BUG,我无法解决,后来不得不自己造轮子了。

程序开发

鉴于自己都是前端小白,努力做到页面结构划分清晰,让人看着不乱,能自己扩展。所以组件我选用了官方组件,css纯手写。
页面内部js也有注释说明,能详尽详。

结语

经过半个月的努力,一套属于我自己的小说阅读器诞生了,uniapp可以打包成H5和APP。我自己买了个服务器,部署了H5端,运行了一段时间,自我感觉还好。1M的带宽,后台还要抓小说,正式站点就不放出来了。怕崩。

疑问:小说从哪里来?

百度 免费小说 会出来一堆网站,自己选一个反应快的界面不乱的广告少的网站进行抓取。
怎么抓取?

  1. 打包成APP的,可以参考uniapp插件里头的仔仔小说阅读器,直接前端抓取。
  2. 打包成H5形式的,有跨域问题,不能在前端抓取,需要有自己的后台进行抓。如果你会JAVA就用Java抓,会python就用python抓。 抓取技术Java我用的jsoup,会jquery就能分析出来界面结构。

采集之后的小说可以保存到自己的数据库中,这样页面基本是秒加载的。当然你可以不保存到库,页面打开的时候后台动态抓取,动态返回文章内容章节内容等。这样是做的一点好处就是:后台不用跑自动任务去更新小说章节;缺点:就是有点慢。目前我采用的是动态采集,孰优孰劣,自己取舍。

源码地址

开源中国: https://gitee.com/aper/iread/

继续阅读 »

uniapp 开发的H5端 APP端小说阅读器

缘起

学习uniapp有一年多,前期做过一个同城项目,后面工作忙,没有继续深入学习。2020年由于疫情的缘故,在家搁置时间过长,期间父亲大人迷上了小说,我就去网上找各种破解版小说APP给他。刚开始还好,看着看着软件强制更新了,就又给他找其他的破解APP,看一段时间又更新了。。。周而复始,后来就想为什么不自己弄一个呢,于是就有了今天这个项目。

主要功能

我主张一切从简,小说阅读器的主要功能:能看能搜。无论是三岁小孩,还是八十老妪,打开就能懂如何使用。

界面设计

业余前端,不追求页面的极致,能看就行。

  1. 主页

  1. 分类页

  1. 书籍详情页

  1. 书籍阅读页

  1. 书架页

  1. 我的页

我的页没有实质性功能,没有登录操作。

制作期间去uniapp的插件市场观摩了其他大佬做的小说阅读器,本来是不想重复造轮子的,使用过程中发现了一些BUG,我无法解决,后来不得不自己造轮子了。

程序开发

鉴于自己都是前端小白,努力做到页面结构划分清晰,让人看着不乱,能自己扩展。所以组件我选用了官方组件,css纯手写。
页面内部js也有注释说明,能详尽详。

结语

经过半个月的努力,一套属于我自己的小说阅读器诞生了,uniapp可以打包成H5和APP。我自己买了个服务器,部署了H5端,运行了一段时间,自我感觉还好。1M的带宽,后台还要抓小说,正式站点就不放出来了。怕崩。

疑问:小说从哪里来?

百度 免费小说 会出来一堆网站,自己选一个反应快的界面不乱的广告少的网站进行抓取。
怎么抓取?

  1. 打包成APP的,可以参考uniapp插件里头的仔仔小说阅读器,直接前端抓取。
  2. 打包成H5形式的,有跨域问题,不能在前端抓取,需要有自己的后台进行抓。如果你会JAVA就用Java抓,会python就用python抓。 抓取技术Java我用的jsoup,会jquery就能分析出来界面结构。

采集之后的小说可以保存到自己的数据库中,这样页面基本是秒加载的。当然你可以不保存到库,页面打开的时候后台动态抓取,动态返回文章内容章节内容等。这样是做的一点好处就是:后台不用跑自动任务去更新小说章节;缺点:就是有点慢。目前我采用的是动态采集,孰优孰劣,自己取舍。

源码地址

开源中国: https://gitee.com/aper/iread/

收起阅读 »

Uniapp使用GoEasy实现websocket实时通讯

即时通信 消息提醒 消息推送 WEBSOCKET

Uniapp作为近来最火的移动端开发技术,一套代码,可以打包成Android/iOS app和各种平台的小程序,可谓是没有最方便只有更方便。

GoEasy上架DCloud Uniapp插件市场已经有一段时间了,收到很多Uniapp开发人员的赞扬和好评,小编在此隔着屏幕向大家鞠躬,小编和GoEasy团队会继续努力,持续为Uniapp开发者提供最简单且优雅的API,稳定高速可靠的即时通讯服务。

这段时间,也收到了很多朋友的一些问题?比如:

1、GoEasy和Uniapp websocket API有什么区别和优势?

Uniapp官方的websocket API主要是用来与您的websocket服务通讯,所以使用Uniapp websocket的前提是,首先要搭建好您自己的websocket服务,然后与之通讯。这是一个纯技术的API,在建立网络连接后,还有很多的工作需要自己来完成,比如:

需要自己实现心跳机制,来维护网络连接,来判断客户端的网络连接状态;

需要自己实现断网自动重连;

需要自己维护消息列表,确保遇到断网重连后,消息能够补发;

需要自己维护一个客户端列表;

...

等等很多细致而繁杂的工作,比如websocket的安全机制和性能优化;

除此之外服务端也有很多工作需要自己完成,有兴趣自己搭建websocket的话,可以参考这篇技术分享《搭建websocket消息推送服务,必须要考虑的几个问题

而GoEasy是一个成熟稳定的websocket企业级PAAS服务平台,开发人员不需要考虑websocket服务端的搭建,只需要几行代码,就可以轻松实现客户端与客户端之间,服务器与客户端之间的的websocket通信,不需要考虑性能,安全,高可用集群的问题。只需要全力专注于开发自己的业务功能就好了。

GoEasy已经内置websocket中必备的心跳,断网重连,消息补发,历史消息和客户端上下线提醒等特性,开发人员也不需要自己搭建websocket服务处理集群高可用,安全和性能问题。GoEasy已经稳定运行了5年,支持千万级并发,成功支撑过很多知名企业的重要活动,安全性和可靠性都是久经考验。

2、GoEasy在Uniapp开发中主要用在哪些场景呢?

首先从技术上说,Uniapp支持的所有技术android, iOS和小程序,GoEasy都是完美支持的。

从应用场景上来说,所有需要websocket通信的场景,GoEasy都可以完美支持:

聊天,IM,直播弹幕,用户上下线提醒, 在线用户列表

扫码点菜,扫码登录, 扫码支付, 扫码签到, 扫码打印

事件提醒,工单,订单实时提醒

在线拍卖, 在线点餐,在线选座 实时数据展示,实时监控大屏, 金融实时行情显示,设备监控系统

实时位置跟踪,外卖实时跟踪,物流实时跟踪

远程画板,远程医疗,远程在线授课

...

3、GoEasy的文档为什么这么简单?简单到我都不知道如何使用

简单还不好吗?GoEasy从研发的第一天,就把追求API的极简作为我们的工作重点。严格控制接口的数量,就是是为了降低开发人员的学习成本,其实就是为了让您爽啊!但这并不影响GoEasy完美支持所有的websocket即时通讯需求。

今天小编就手把手的教您用GoEasy在Uniapp下,最短的时间实现一个的web即时通讯Demo。

image

本demo已经通过小程序, iOS和Android的真机测试,完整源代码已经上传gitee,下载后,只需要将代码里的appkey换成自己的common key,就可以体验了。下载地址:

uniapp websocket体验demo:https://ext.dcloud.net.cn/plugin?id=1334

1、获取appkey

GoEasy官网(http://www.goeasy.io/)上注册账号,创建一个应用,拿到您的appkey

image

GoEasy提供了两种类型的appkey:

Common key: 即可以接收消息,也可以发送消息,与Subscriber Key最大的区别就是有写权限,可以发消息。适用于有消息发送需求的客户端和服务端开发。

Subscriber key: 只能接收消息,不可以发送消息,与Common Key最大的区别就是没有写权限,只能收消息。可以用于一些没有发送需求的客户端。

2、获取GoEasy SDK

两个方式:

DCloud Uniapp的插件市场下载:https://ext.dcloud.net.cn/plugin?id=1334

也可以直接在goeasy官网下载:https://www.goeasy.io/cn/doc/client/get-goeasy-js.html

3、初始化GoEasy对象

在main.js中将goeasy初始化为Uniapp的全局对象,方便所有页面都能方便的调用,同时也避免多个页面反复new GoEasy对象。

根据您在GoEasy后台创建应用时选择的区域,来传入不同的Host,如果您创建goeasy应用时,选择了杭州,那么host:"hangzhou.goeasy.io"。选择了新加坡,host:"singapore.goeasy.io"。

如果您的大部分用户都是在国内,创建应用时,记得选择杭州,以便获得更快的通讯速度。

// 在main.js中将goeasy初始化为全局对象,所有页面都能方便的调用,也避免多个页面反复new GoEasy对象  

Vue.prototype.$goEasy = new GoEasy({  

host: "hangzhou.goeasy.io",  

appkey: "my_appkey", //替换为您的应用appkey  

    onConnected: function() {  

      console.log('连接成功!')  

    },  

    onDisconnected: function() {  

      console.log('连接断开!')  

    },  

    onConnectFailed: function(error) {  

      console.log('连接失败或错误!')  

    }  

});

4、uniapp端接收消息

this.$goEasy.subscribe({  

    channel: "my_channel", //替换为您自己的channel  

    onMessage: function (message) {  

        alert("Channel:" + message.channel + " content:" + message.content);  

    }  

});  

很多朋友会问channel从哪里来,如何创建,应该传入什么呢?

根据您的业务需求来设定,channel可以为任意字符串,除了不能包含空格,和不建议使用中文外,没有任何限制,只需要和消息的发送端保持一致,就可以收到消息。channel可以是您直播间的uuid,也可以是一个用户的唯一表示符,可以任意定义,channel不需要创建,可以随用随弃。

5、uniapp端发送消息

发送时,需要注意channel一定要和subscriber的channel完全一致,否则无法收到。

this.$goEasy.publish({  

    channel: "my_channel", //替换为您自己的channel  

    message: "Hello, GoEasy!" //替换为您想要发送的消息内容  

});  

本代码源码下载:

uniapp websocket体验demo:https://ext.dcloud.net.cn/plugin?id=1334

小程序特别强调:

若您需要打包为微信小程序,需要在微信公众号平台配置socket合法域名,否则无法使用。 具体步骤:

访问https://mp.weixin.qq.com,进入微信公众平台|小程序 -> 设置 -> 开发设置 -> 服务器域名

socket合法域名-> 添加 goeasy的地址:wx-hangzhou.goeasy.io(记得wx-开头)

若您创建GoEasy应用时选择了新加坡区域则添加地址:wx-singapore.goeasy.io

答疑时间:

1、我的服务器端可以发送消息吗?都支持些哪些语言?

当然可以,任何语言都可以通过调用goeasy的Rest API发送消息,同时为了大家方便,GoEasy的官方文档里,也准备了Java, C#,NodeJS,PHP,Ruby和Python等常见语言调用REST API的代码,这里获取更多详情:https://www.goeasy.io/cn/doc/server/publish.html

2、GoEasy可以发送图片,语音和视频吗?

当然可以,您可以通过推送文件路径的方式来实现文件的发送。

按照行业惯例,不论MSN,微信,QQ对于图片和视频,通常的做法都是,只推送文件路径,而不会推送文件本身。你如果有注意的话,当您接受图片和视频的时候,收到消息后,等一会儿才能看,就是因为发送的时候,只发送了路径。

GoEasy系列教程:

继续阅读 »

Uniapp作为近来最火的移动端开发技术,一套代码,可以打包成Android/iOS app和各种平台的小程序,可谓是没有最方便只有更方便。

GoEasy上架DCloud Uniapp插件市场已经有一段时间了,收到很多Uniapp开发人员的赞扬和好评,小编在此隔着屏幕向大家鞠躬,小编和GoEasy团队会继续努力,持续为Uniapp开发者提供最简单且优雅的API,稳定高速可靠的即时通讯服务。

这段时间,也收到了很多朋友的一些问题?比如:

1、GoEasy和Uniapp websocket API有什么区别和优势?

Uniapp官方的websocket API主要是用来与您的websocket服务通讯,所以使用Uniapp websocket的前提是,首先要搭建好您自己的websocket服务,然后与之通讯。这是一个纯技术的API,在建立网络连接后,还有很多的工作需要自己来完成,比如:

需要自己实现心跳机制,来维护网络连接,来判断客户端的网络连接状态;

需要自己实现断网自动重连;

需要自己维护消息列表,确保遇到断网重连后,消息能够补发;

需要自己维护一个客户端列表;

...

等等很多细致而繁杂的工作,比如websocket的安全机制和性能优化;

除此之外服务端也有很多工作需要自己完成,有兴趣自己搭建websocket的话,可以参考这篇技术分享《搭建websocket消息推送服务,必须要考虑的几个问题

而GoEasy是一个成熟稳定的websocket企业级PAAS服务平台,开发人员不需要考虑websocket服务端的搭建,只需要几行代码,就可以轻松实现客户端与客户端之间,服务器与客户端之间的的websocket通信,不需要考虑性能,安全,高可用集群的问题。只需要全力专注于开发自己的业务功能就好了。

GoEasy已经内置websocket中必备的心跳,断网重连,消息补发,历史消息和客户端上下线提醒等特性,开发人员也不需要自己搭建websocket服务处理集群高可用,安全和性能问题。GoEasy已经稳定运行了5年,支持千万级并发,成功支撑过很多知名企业的重要活动,安全性和可靠性都是久经考验。

2、GoEasy在Uniapp开发中主要用在哪些场景呢?

首先从技术上说,Uniapp支持的所有技术android, iOS和小程序,GoEasy都是完美支持的。

从应用场景上来说,所有需要websocket通信的场景,GoEasy都可以完美支持:

聊天,IM,直播弹幕,用户上下线提醒, 在线用户列表

扫码点菜,扫码登录, 扫码支付, 扫码签到, 扫码打印

事件提醒,工单,订单实时提醒

在线拍卖, 在线点餐,在线选座 实时数据展示,实时监控大屏, 金融实时行情显示,设备监控系统

实时位置跟踪,外卖实时跟踪,物流实时跟踪

远程画板,远程医疗,远程在线授课

...

3、GoEasy的文档为什么这么简单?简单到我都不知道如何使用

简单还不好吗?GoEasy从研发的第一天,就把追求API的极简作为我们的工作重点。严格控制接口的数量,就是是为了降低开发人员的学习成本,其实就是为了让您爽啊!但这并不影响GoEasy完美支持所有的websocket即时通讯需求。

今天小编就手把手的教您用GoEasy在Uniapp下,最短的时间实现一个的web即时通讯Demo。

image

本demo已经通过小程序, iOS和Android的真机测试,完整源代码已经上传gitee,下载后,只需要将代码里的appkey换成自己的common key,就可以体验了。下载地址:

uniapp websocket体验demo:https://ext.dcloud.net.cn/plugin?id=1334

1、获取appkey

GoEasy官网(http://www.goeasy.io/)上注册账号,创建一个应用,拿到您的appkey

image

GoEasy提供了两种类型的appkey:

Common key: 即可以接收消息,也可以发送消息,与Subscriber Key最大的区别就是有写权限,可以发消息。适用于有消息发送需求的客户端和服务端开发。

Subscriber key: 只能接收消息,不可以发送消息,与Common Key最大的区别就是没有写权限,只能收消息。可以用于一些没有发送需求的客户端。

2、获取GoEasy SDK

两个方式:

DCloud Uniapp的插件市场下载:https://ext.dcloud.net.cn/plugin?id=1334

也可以直接在goeasy官网下载:https://www.goeasy.io/cn/doc/client/get-goeasy-js.html

3、初始化GoEasy对象

在main.js中将goeasy初始化为Uniapp的全局对象,方便所有页面都能方便的调用,同时也避免多个页面反复new GoEasy对象。

根据您在GoEasy后台创建应用时选择的区域,来传入不同的Host,如果您创建goeasy应用时,选择了杭州,那么host:"hangzhou.goeasy.io"。选择了新加坡,host:"singapore.goeasy.io"。

如果您的大部分用户都是在国内,创建应用时,记得选择杭州,以便获得更快的通讯速度。

// 在main.js中将goeasy初始化为全局对象,所有页面都能方便的调用,也避免多个页面反复new GoEasy对象  

Vue.prototype.$goEasy = new GoEasy({  

host: "hangzhou.goeasy.io",  

appkey: "my_appkey", //替换为您的应用appkey  

    onConnected: function() {  

      console.log('连接成功!')  

    },  

    onDisconnected: function() {  

      console.log('连接断开!')  

    },  

    onConnectFailed: function(error) {  

      console.log('连接失败或错误!')  

    }  

});

4、uniapp端接收消息

this.$goEasy.subscribe({  

    channel: "my_channel", //替换为您自己的channel  

    onMessage: function (message) {  

        alert("Channel:" + message.channel + " content:" + message.content);  

    }  

});  

很多朋友会问channel从哪里来,如何创建,应该传入什么呢?

根据您的业务需求来设定,channel可以为任意字符串,除了不能包含空格,和不建议使用中文外,没有任何限制,只需要和消息的发送端保持一致,就可以收到消息。channel可以是您直播间的uuid,也可以是一个用户的唯一表示符,可以任意定义,channel不需要创建,可以随用随弃。

5、uniapp端发送消息

发送时,需要注意channel一定要和subscriber的channel完全一致,否则无法收到。

this.$goEasy.publish({  

    channel: "my_channel", //替换为您自己的channel  

    message: "Hello, GoEasy!" //替换为您想要发送的消息内容  

});  

本代码源码下载:

uniapp websocket体验demo:https://ext.dcloud.net.cn/plugin?id=1334

小程序特别强调:

若您需要打包为微信小程序,需要在微信公众号平台配置socket合法域名,否则无法使用。 具体步骤:

访问https://mp.weixin.qq.com,进入微信公众平台|小程序 -> 设置 -> 开发设置 -> 服务器域名

socket合法域名-> 添加 goeasy的地址:wx-hangzhou.goeasy.io(记得wx-开头)

若您创建GoEasy应用时选择了新加坡区域则添加地址:wx-singapore.goeasy.io

答疑时间:

1、我的服务器端可以发送消息吗?都支持些哪些语言?

当然可以,任何语言都可以通过调用goeasy的Rest API发送消息,同时为了大家方便,GoEasy的官方文档里,也准备了Java, C#,NodeJS,PHP,Ruby和Python等常见语言调用REST API的代码,这里获取更多详情:https://www.goeasy.io/cn/doc/server/publish.html

2、GoEasy可以发送图片,语音和视频吗?

当然可以,您可以通过推送文件路径的方式来实现文件的发送。

按照行业惯例,不论MSN,微信,QQ对于图片和视频,通常的做法都是,只推送文件路径,而不会推送文件本身。你如果有注意的话,当您接受图片和视频的时候,收到消息后,等一会儿才能看,就是因为发送的时候,只发送了路径。

GoEasy系列教程:

收起阅读 »

搭建websocket消息推送服务,必须要考虑的几个问题

消息提醒 消息推送 即时通信 WEBSOCKET

近年,不论是正在快速增长的直播,远程教育以及IM聊天场景,还是在常规企业级系统中用到的系统提醒,对websocket的需求越来越大,对websocket的要求也越来越高。从早期对websocket的应用仅限于少部分功能和IM等特殊场景,逐步发展为追求支持高并发,百万、千万级每秒通讯的高可用websocket服务。

面对各种新场景对websocket功能和性能越来越高的需求,不同的团队有不同的选择,有的直接使用由专业团队开发的成熟稳定的第三方websocket服务,有些则选择自建websocket服务。

作为一个具有多年websocket开发经验的老程序猿,经历了GoEasy企业级websocket服务从无到有,从小到大的过程,此文是根据过去几年在GoEasy开发过程中踩过的坑,以及为众多开发团队提供websocket服务、与众多开发者交流中的总结的一些经验和体会。

这次主要从搭建websocket服务的基本功能和特性方面做一些分享,下次有机会再从构建一个高可用websocket时要面对的高并发,海量消息,集群容灾,横向扩展,以及自动化运维等方面进更多的分享。

以下几点是个人认为在构建websocket服务时必须要考虑的一些技术特性以及能显著提高用户体验的功能,供各位同学参考:

1.建立心跳机制
心跳机制几乎是所有网络编程的第一步,经常容易被新手忽略。因为在websocket长连接中,客户端和服务端并不会一直通信,如果双方长期没有沟通则都不清楚彼此当前状态,所以需要发送一段很小的报文告诉对方“我还活着”。另外还有两个目的:
服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线;
客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。

2.建立具有良好兼容性的客户端SDK
虽说现在主流浏览器都支持websocket,但在编码中还是会遇到浏览器兼容性问题,而且通过websocket通信的客户端早已不仅限于各种web浏览器,还包括越来越多的APP,小程序。因此就要求构建的websocket服务必须能够很友好的支持各种客户端。最好的方式就是构建一个能够兼容所有主流浏览器、小程序和APP,以及uni-app、vue、react-native等目前常见的各种前端框架的客户端SDK,这样不论公司的各个项目使用什么样的前端技术,都能够快速的集成websocket服务。

3.断网自动重连和消息补发机制
移动互联网时代,终端用户所处的网络环境多样且复杂,如用户进出电梯,出入地下室或地铁等网络不稳定的场所,或其他原因导致的网络不稳定都是很常见的场景。因此,一个可靠的websocket服务必须具备完善的断网自动重连机制。确保断网后,网络一旦恢复,能第一时间自动重新建立长连接,并且能够立即补发在网络不稳定期间发送的消息。

4.离线消息
基础的Websocket通讯从技术上来说,消息送达的前提条件就是建立起一个长连接,没有建立网络连接就来讨论通讯那是耍流氓。但是从使用者的角度上来说,随手关闭浏览器,或者将小程序、APP进程直接杀掉而导致网络连接断开的情况是随时都在发生的。然后我们下意识的期待,就是我下次打开浏览器访问网页,或者打开APP时,能够收到用户离开系统期间的所有信息。从技术上这是一个跟websocket没有多大关系的需求,但实际上却是websocket服务不可或缺的基本特性,也是一个能够极大提升用户体验的功能。

5.上下线提醒,客户端在线列表
掌握当前系统有哪些用户在线,捕捉用户上下线事件,是搭建一个企业级websocket服务,必不可少的特性,尤其是开发IM产品。

6.支持历史消息查询
websocket服务,某种意义也是属于一个消息系统,对于历史消息的查询需求,是无法绕开的话题。比如IM系统中常见的历史消息,因此在websocket服务内部实现一个高速,可靠的消息队列机制来支持websocket服务实现历史消息的查询就是一个必须的工作。

7.消息的压缩机制
不论是为了保证消息通讯的速度和实时性,还是为了节约流量和带宽费用,或者是出于提高网卡的使用效率和增加系统的吞吐量,在通讯过程中对消息进行必要的压缩都是必不可少的。

除了需要考虑以上七点以外,笔者认为,还有几个问题也是很值得初学者积极关注的:

1.缓存和持久化
选择合适的消息缓存机制,是企业级websocket服务保证性能必须要考虑的问题。

2.异步调用
要支持大量消息通讯的高性能系统,必然推荐异步调用。若设计为同步调用,调用方就需要一直等待被调用方完成。如果一层一层的同步调用下去,所有的调用方需要相同的等待时间,调用方的资源会被大量的浪费。更糟糕的是一旦被调用方出问题,其他调用就会出现多米诺骨牌效应跟着出问题,导致故障蔓延。收到请求立即返回结果,然后再异步执行,不仅可以增加系统的吞吐量,最大的好处是让服务之间的解耦更为彻底。

3.独立于业务和标准化
尽管在一个web项目中可以同时存在常规http服务和websocket服务,尤其对性能要求不高的单应用web系统,这种方式更简单,更便于维护。但对于性能和可用性高的企业级系统或者互联网平台,更好的方式,是将websocket服务作为一个单独的微服务来进行设计,避免和常规的http服务抢占资源,导致系统性能不可控,同时也更便于横向扩展。

一个设计良好的企业级websocket服务应该是一个独立于业务系统、标准化的单独存在的技术性微服务,能够作为公司基础架构的一部分为公司的所有项目提供通讯服务。

4.幂等性和重复消息的过滤
所谓幂等性,就是一次和多次请求一个接口都应该具有同样的后果。为什么需要?对每个接口的调用都会有三种可能的结果:成功,失败和超时。对最后一种的原因很多可能是网络丢包,可能请求没有到达,也有可能返回没有收到。于是在对接口的调用时往往都会有重试机制,但重试机制很容易导致消息的重复发送,从用户层面这往往是不可接受的,因此在接口的设计时,我们就需要考虑接口的幂等性,确保同一条消息发送一次和十次都不回导致消息的重复到达。

5.支持QoS 服务质量分级
其实对于上一点消息重复的问题,行业已经有了解决方案和标准规范,对于消息到达率和重复,常用的手段就是通过消息确认的方式来确保消息到达,要求越高,意味着确认机制越复杂,成本越高。为了在成本和到达率之间有很好的平衡,通常对消息系统的服务质量(QoS)分为以下三个级别 :

  • QoS 0(At most once):“最多发一次”,意味着发送就可以了,不需要确认机制,发送了即可,适用于要求不高的场景,可以接受一定的不到达率,成本最低。

  • QoS 1(At least once):“至少发一次”,意味着发送方必须明确收到接收方的确认信号,否则就会反复发,每条消息至少需要两次通信来确认到达,可以接受一些消息被重发,但成本不高 。

  • QoS 2(Exactly once):“确保只发一次”,意味着每条消息只能到达一次,且不允许重复到达,为了达到这个目标就需要双方至少通讯三次,成本最高。

一个完善的websocket服务面对不同的应用场景,应该能够支持选择不同等级的QoS,在成本和服务质量之间取得平衡。

最后
虽然websocket已经广泛的应用于各种系统和平台,但如果要搭建一个满足企业级或者大型互联网平台的可靠、安全稳定的websocket服务,对于没有经验的同学,在具体的技术实践过程依然是有不少的坑要踩。

对websocket服务有较高要求,选择成熟可靠的第三方websocket服务其实也是一个成本更低和高效的选择。GoEasy作为国内领先的第三方websocket消息平台,已经稳定运行了5年时间,支持千万级消息并发,除了兼容所有常见的浏览器以外,同时也兼容uni-app,各种小程序,以及vue、react-native等常见的前端框架。

希望本文能为初次搭建websocket服务的同学在思路上有所帮助和参考,也欢迎各位前辈多多批评指正,同时也希望未来有机会就更多的技术与大家进行交流。

GoEasy官网:https://www.goeasy.io/

GoEasy系列教程:

继续阅读 »

近年,不论是正在快速增长的直播,远程教育以及IM聊天场景,还是在常规企业级系统中用到的系统提醒,对websocket的需求越来越大,对websocket的要求也越来越高。从早期对websocket的应用仅限于少部分功能和IM等特殊场景,逐步发展为追求支持高并发,百万、千万级每秒通讯的高可用websocket服务。

面对各种新场景对websocket功能和性能越来越高的需求,不同的团队有不同的选择,有的直接使用由专业团队开发的成熟稳定的第三方websocket服务,有些则选择自建websocket服务。

作为一个具有多年websocket开发经验的老程序猿,经历了GoEasy企业级websocket服务从无到有,从小到大的过程,此文是根据过去几年在GoEasy开发过程中踩过的坑,以及为众多开发团队提供websocket服务、与众多开发者交流中的总结的一些经验和体会。

这次主要从搭建websocket服务的基本功能和特性方面做一些分享,下次有机会再从构建一个高可用websocket时要面对的高并发,海量消息,集群容灾,横向扩展,以及自动化运维等方面进更多的分享。

以下几点是个人认为在构建websocket服务时必须要考虑的一些技术特性以及能显著提高用户体验的功能,供各位同学参考:

1.建立心跳机制
心跳机制几乎是所有网络编程的第一步,经常容易被新手忽略。因为在websocket长连接中,客户端和服务端并不会一直通信,如果双方长期没有沟通则都不清楚彼此当前状态,所以需要发送一段很小的报文告诉对方“我还活着”。另外还有两个目的:
服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线;
客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。

2.建立具有良好兼容性的客户端SDK
虽说现在主流浏览器都支持websocket,但在编码中还是会遇到浏览器兼容性问题,而且通过websocket通信的客户端早已不仅限于各种web浏览器,还包括越来越多的APP,小程序。因此就要求构建的websocket服务必须能够很友好的支持各种客户端。最好的方式就是构建一个能够兼容所有主流浏览器、小程序和APP,以及uni-app、vue、react-native等目前常见的各种前端框架的客户端SDK,这样不论公司的各个项目使用什么样的前端技术,都能够快速的集成websocket服务。

3.断网自动重连和消息补发机制
移动互联网时代,终端用户所处的网络环境多样且复杂,如用户进出电梯,出入地下室或地铁等网络不稳定的场所,或其他原因导致的网络不稳定都是很常见的场景。因此,一个可靠的websocket服务必须具备完善的断网自动重连机制。确保断网后,网络一旦恢复,能第一时间自动重新建立长连接,并且能够立即补发在网络不稳定期间发送的消息。

4.离线消息
基础的Websocket通讯从技术上来说,消息送达的前提条件就是建立起一个长连接,没有建立网络连接就来讨论通讯那是耍流氓。但是从使用者的角度上来说,随手关闭浏览器,或者将小程序、APP进程直接杀掉而导致网络连接断开的情况是随时都在发生的。然后我们下意识的期待,就是我下次打开浏览器访问网页,或者打开APP时,能够收到用户离开系统期间的所有信息。从技术上这是一个跟websocket没有多大关系的需求,但实际上却是websocket服务不可或缺的基本特性,也是一个能够极大提升用户体验的功能。

5.上下线提醒,客户端在线列表
掌握当前系统有哪些用户在线,捕捉用户上下线事件,是搭建一个企业级websocket服务,必不可少的特性,尤其是开发IM产品。

6.支持历史消息查询
websocket服务,某种意义也是属于一个消息系统,对于历史消息的查询需求,是无法绕开的话题。比如IM系统中常见的历史消息,因此在websocket服务内部实现一个高速,可靠的消息队列机制来支持websocket服务实现历史消息的查询就是一个必须的工作。

7.消息的压缩机制
不论是为了保证消息通讯的速度和实时性,还是为了节约流量和带宽费用,或者是出于提高网卡的使用效率和增加系统的吞吐量,在通讯过程中对消息进行必要的压缩都是必不可少的。

除了需要考虑以上七点以外,笔者认为,还有几个问题也是很值得初学者积极关注的:

1.缓存和持久化
选择合适的消息缓存机制,是企业级websocket服务保证性能必须要考虑的问题。

2.异步调用
要支持大量消息通讯的高性能系统,必然推荐异步调用。若设计为同步调用,调用方就需要一直等待被调用方完成。如果一层一层的同步调用下去,所有的调用方需要相同的等待时间,调用方的资源会被大量的浪费。更糟糕的是一旦被调用方出问题,其他调用就会出现多米诺骨牌效应跟着出问题,导致故障蔓延。收到请求立即返回结果,然后再异步执行,不仅可以增加系统的吞吐量,最大的好处是让服务之间的解耦更为彻底。

3.独立于业务和标准化
尽管在一个web项目中可以同时存在常规http服务和websocket服务,尤其对性能要求不高的单应用web系统,这种方式更简单,更便于维护。但对于性能和可用性高的企业级系统或者互联网平台,更好的方式,是将websocket服务作为一个单独的微服务来进行设计,避免和常规的http服务抢占资源,导致系统性能不可控,同时也更便于横向扩展。

一个设计良好的企业级websocket服务应该是一个独立于业务系统、标准化的单独存在的技术性微服务,能够作为公司基础架构的一部分为公司的所有项目提供通讯服务。

4.幂等性和重复消息的过滤
所谓幂等性,就是一次和多次请求一个接口都应该具有同样的后果。为什么需要?对每个接口的调用都会有三种可能的结果:成功,失败和超时。对最后一种的原因很多可能是网络丢包,可能请求没有到达,也有可能返回没有收到。于是在对接口的调用时往往都会有重试机制,但重试机制很容易导致消息的重复发送,从用户层面这往往是不可接受的,因此在接口的设计时,我们就需要考虑接口的幂等性,确保同一条消息发送一次和十次都不回导致消息的重复到达。

5.支持QoS 服务质量分级
其实对于上一点消息重复的问题,行业已经有了解决方案和标准规范,对于消息到达率和重复,常用的手段就是通过消息确认的方式来确保消息到达,要求越高,意味着确认机制越复杂,成本越高。为了在成本和到达率之间有很好的平衡,通常对消息系统的服务质量(QoS)分为以下三个级别 :

  • QoS 0(At most once):“最多发一次”,意味着发送就可以了,不需要确认机制,发送了即可,适用于要求不高的场景,可以接受一定的不到达率,成本最低。

  • QoS 1(At least once):“至少发一次”,意味着发送方必须明确收到接收方的确认信号,否则就会反复发,每条消息至少需要两次通信来确认到达,可以接受一些消息被重发,但成本不高 。

  • QoS 2(Exactly once):“确保只发一次”,意味着每条消息只能到达一次,且不允许重复到达,为了达到这个目标就需要双方至少通讯三次,成本最高。

一个完善的websocket服务面对不同的应用场景,应该能够支持选择不同等级的QoS,在成本和服务质量之间取得平衡。

最后
虽然websocket已经广泛的应用于各种系统和平台,但如果要搭建一个满足企业级或者大型互联网平台的可靠、安全稳定的websocket服务,对于没有经验的同学,在具体的技术实践过程依然是有不少的坑要踩。

对websocket服务有较高要求,选择成熟可靠的第三方websocket服务其实也是一个成本更低和高效的选择。GoEasy作为国内领先的第三方websocket消息平台,已经稳定运行了5年时间,支持千万级消息并发,除了兼容所有常见的浏览器以外,同时也兼容uni-app,各种小程序,以及vue、react-native等常见的前端框架。

希望本文能为初次搭建websocket服务的同学在思路上有所帮助和参考,也欢迎各位前辈多多批评指正,同时也希望未来有机会就更多的技术与大家进行交流。

GoEasy官网:https://www.goeasy.io/

GoEasy系列教程:

收起阅读 »