• Category Archives: 未分类

java使用jedis执行lua脚本

1、引入依赖

		<!-- Jedis -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>

2、编写配置文件

#Redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=3

spring.redis.password=
spring.redis.timeout=2000

#Jedis
#最大空闲数
spring.redis.jedis.pool.max-idle=6
#最大连接数
spring.redis.jedis.pool.max-active=10
#最小空闲数
spring.redis.jedis.pool.min-idle=2
#连接超时
spring.redis.jedis.pool.timeout=2000

3、编写配置类

package com.zero4j.config;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class JedisConfig {

    private Logger logger = LoggerFactory.getLogger(JedisConfig.class);

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;

    @Bean
    public JedisPool jedisPool(){

        JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxTotal(maxActive);

        JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port,timeout,password);

        logger.info("JedisPool连接成功:"+host+"\t"+port);

        return jedisPool;
    }

}

4、实现业务逻辑

	@Autowired
	private JedisPool jedisPool;

 

		Jedis jedis = null;

		try {
			jedis = jedisPool.getResource();
			String poolId = "pool1";
			String poolLuaRedisKey = "cardLottery." + poolId + "-lua-shaKey";
			String poolLuaRedisValue = jedis.get(poolLuaRedisKey);
			byte[] shaKey = null;
			if(poolLuaRedisValue!=null){
				shaKey = poolLuaRedisValue.getBytes();
			}
			if (shaKey == null) {
				ClassPathResource cps = new ClassPathResource("lua/test.lua");
				InputStream in = cps.getInputStream();
				byte[] data = new byte[in.available()];
				in.read(data);
				shaKey = jedis.scriptLoad(data);
				jedis.set("cardLottery." + poolId + "-lua-shaKey", new String(shaKey));
			}

			System.out.println("本次执行的lua脚本的sha为:" + new String(shaKey));

			List<String> keys = new ArrayList<>();
			keys.add(poolId);
			keys.add("100");
			keys.add("0.5");
			List<String> vals = new ArrayList<>();
			for (int i = 0; i < 10; i++) {
				Date _startTime = new Date();
				Object result = jedis.evalsha(new String(shaKey), keys, vals);
				Date _endTime = new Date();
				long _timeCross = _endTime.getTime() - _startTime.getTime();
				System.out.println("lua的第" + (i + 1) + "次执行花了" + _timeCross + "毫秒,执行结果:" + result);
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally {
			if(jedis!=null){
				jedis.close();
			}
		}

5、Lua脚本

local key = 'cardPool.'..KEYS[1]..'-bucket' -- redis中令牌桶对象的key
local initTokens = tonumber(KEYS[2]) -- {初始化时的令牌数}
local probability = tonumber(KEYS[3]) -- {中奖概率:小于0的数字(百分比概念)}

print(probability)

local bucket = redis.call('hgetall', key) -- 当前 key 的令牌桶对象
local currentTokens -- 当前token数量
local runCount -- 累计执行次数统计

-- 若当前桶未初始化,先初始化令牌桶
if table.maxn(bucket) == 0 then
    -- 初始桶内令牌
    currentTokens = initTokens
    runCount = 0
    redis.call('hset', key, 'stock', currentTokens)
    redis.call('hset', key, 'probability', probability)
    redis.call('hset', key, 'runCount', 0)
elseif table.maxn(bucket) == 6 then
    currentTokens = tonumber(bucket[2])
    probability = tonumber(bucket[4])
    runCount = tonumber(bucket[6])
end

-- 如果当前桶内令牌小于 0,抛出异常
assert(currentTokens >= 0)

redis.call('hset', key, 'runCount', runCount + 1)

if currentTokens == 0 then  -- 如果当前令牌 == 0, 返回false
    return -1
else    -- 如果当前令牌 大于 0, 则执行抽奖逻辑
    if probability<> nil or probability >= 1 or math.random() <= probability then -- 如果中奖概率大于或等于1,生成随机的小数如果在probability内,则中奖
        -- 更新当前桶内的令牌 -1, 返回true
        redis.call('hset', key, 'stock', currentTokens - 1)
        return 1
    else
        return 0
    end
end

注意:如果Lua脚本返回是true和false的类型,则jedis实际获得的值为1和null。

 

java使用lua脚本操作redis的相关笔记

Lua脚本

local key = 'cardPool.'..KEYS[1]..'-stock' -- redis中令牌桶对象的key
local initTokens = tonumber(KEYS[2]) -- {初始化时的库存数量}

local bucket = redis.call('hgetall', key) -- 当前 key 的令牌桶对象
local currentTokens -- 当前token数量
local runCount -- 累计运行次数统计

-- 若当前桶未初始化,先初始化令牌桶
if table.maxn(bucket) == 0 then
    -- 初始桶内令牌
    currentTokens = initTokens
    runCount = 0
elseif table.maxn(bucket) == 4 then
    currentTokens = tonumber(bucket[2])
    runCount = tonumber(bucket[4])
end

-- 如果当前桶内令牌小于 0,抛出异常
assert(currentTokens >= 0)

-- 如果当前令牌 == 0, 返回false
if currentTokens == 0 then
    redis.call('hset', key, 'tokensRemaining', currentTokens)
    redis.call('hset', key, 'runCount', runCount + 1)
    return false
else
    -- 如果当前令牌 大于 0, 更新当前桶内的令牌 -1, 返回true
    redis.call('hset', key, 'tokensRemaining', currentTokens - 1)
    redis.call('hset', key, 'runCount', runCount + 1)
    return true
end

JAVA运行Lua脚本:

		DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
		redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/test.lua")));
		redisScript.setResultType(Boolean.class);

		Boolean result = true;
		long count = 0;
		do{
			Date _startTime = new Date();
			List<String> keys = Arrays.asList("pool1", "10000");
			try{
				result = redisTemplate.execute(redisScript, keys);
			}catch(Exception e){
				e.printStackTrace();
			}
			Date _endTime = new Date();
			long _timeCross = _endTime.getTime()- _startTime.getTime();
			count++;
			System.out.println("lua的第"+count+"次执行花了"+_timeCross+"毫秒");
		}while(result==true);

注意hgetall返回的redis变量读取方式

Restful 返回数据格式

返回数据的一般格式:

{
“code”: 1,
“mssage”: “返回的信息”,
“data”: {
“list”:[
{
“title”:”xxx”,
“contents”:”xxx”,
},{
“title”:”xxx”,
“contents”:”xxx”,
}
]
“total”:21,
},
“extra”: {},
“pageNum”: 3,
“pageSize”: 20,
“totalSize”: 235
}

code的一般含义

public enum ResultCode {

  /* 成功状态码 */
  SUCCESS(0, "成功"),

  /* 令牌失效 */
  TOKEN_INVALID(401,"令牌失效"),

  SERVER_ERROR(400, "服务器错误"),

  /*参数错误 10001-19999 */

  PARAM_IS_INVALID(10001, "参数无效"),
  PARAM_IS_BLANK(10002, "参数为空"),
  PARAM_TYPE_BIND_ERROR(10003, "参数类型错误"),
  PARAM_NOT_COMPLETE(10004, "参数缺失"),

  /* 用户错误:20001-29999*/
  USER_NOT_LOGGED_IN(20001, "用户未登录"),
  USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
  USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"),
  USER_NOT_EXIST(20004, "用户不存在"),
  USER_HAS_EXISTED(20005, "用户已存在"),
  Cert_HAS_EXISTED(20006, "认证已存在"),


  /* 业务错误:30001-39999 */
  CREATE_FAIL(30001, "创建失败"),

  /* 系统错误:40001-49999 */
  SYSTEM_INNER_ERROR(40001, "系统繁忙,请稍后重试"),

  /* 数据错误:50001-599999 */
  RESULE_DATA_NONE(50001, "数据未找到"),
  DATA_IS_WRONG(50002, "数据有误"),
  DATA_ALREADY_EXISTED(50003, "数据已存在"),

  /* 接口错误:60001-69999 */
  INTERFACE_INNER_INVOKE_ERROR(60001, "内部系统接口调用异常"),
  INTERFACE_OUTTER_INVOKE_ERROR(60002, "外部系统接口调用异常"),
  INTERFACE_FORBID_VISIT(60003, "该接口禁止访问"),
  INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"),
  INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"),
  INTERFACE_EXCEED_LOAD(60006, "接口负载过高"),

  /* 权限错误:70001-79999 */
  PERMISSION_NO_ACCESS(70001, "只有标签 Owner ,才具备删除权限"),
  PERMISSION_NO_PHONE_ACCESS(70002,"此认证标签已有员工认证,不可以进行删除");
  
}

 

使用uniapp的安心打包制作ios安装文件时出现PKCS12 import (wrong password?)

假如你确定你的证书密码是对的,就是死活不成功,出现如下提示:

[Info] begin copyFile files to ipa...
[Info] begin replace files to ipa...
[Info] begin update mobileprovision to ipa...
[Info] begin outPut t_entitlements_full plist file...
[Info] begin outPut outPutEntitlements plist file...
[Info] begin verifyPlistFiles
[Info] begin update files to ipa...
[Info] begin createKeychain...
[Info] begin unlockKeychain...
[Info] begin setListKeychains...
[Info] begin importAppleCerts...
[Info] 1 certificate imported.
[Info] 1 certificate imported.
[Info] 1 certificate imported.
[Info] begin importSecurity...
[Error] security: SecKeychainItemImport: MAC verification failed during PKCS12 import (wrong password?)
[Error] importSecurity failed
[Info] Package make result:Failed.    Reason: 

那有可能是因为导出p12证书的时候密码过于简单,不符合要求,导致后续证书使用时的安检失败引起的。请重新导出新的、且符合密码强度要求的p12证书。

uniapp自定义入口Activity并进行交互

第一步:首先新建自己的MainActivity

package cn.com.sway.app

import android.os.Bundle;

import io.dcloud.WebAppActivity;

public class MainActivity extends WebAppActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    public boolean isStreamAppMode() {
        return false;
    }

}

注意:

1、务必集成io.dcloud.WebAppActivity这个类

2、必须要有isStreamAppMode方法,如果没有这个方法,会无法正常加载页面

第二部:在AndroidManifest.xml里修改入口Activity

    <activity
            android:name="cn.com.sway.app.MainActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|navigation"
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:hardwareAccelerated="true"
            android:theme="@style/TranslucentTheme"
            android:screenOrientation="user"
            android:windowSoftInputMode="adjustResize" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

其他:在前端与MainActivity进行交互

前端这么写:

var main = plus.android.runtimeMainActivity();//获取当前的Activity
var res = main.getResData();//直接调用自定义Activity中的getResData方法
console.log(res);

在MainActivity补充getResData方法:

public String getResData(){
    return data;
}

 

使用chrome进行跨域请求调试

赠送不同版本chrome的下载地址:

https://www.chromedownloads.net/chrome64win-stable/

问题背景

在跨平台开发IDE常遇到调试问题,在浏览器进行异步请求时出现如下提示:

Access to XMLHttpRequest at 'https://***********' from origin 'http://www.sway.com.cn' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这是因为跨域请求导致的,考虑到安全问题,浏览器默认是不允许进行跨域名访问的。

333

有时候我们在web开发时需要进行跨域调试,会出现上图错误提示。

49版本以前的解决方案

使用chrome的用户,可以找到chrome.exe的程序创建一个快捷方式,并在上图标识的位置加入如下参数:

 --args --disable-web-security --user-data-dir

49版本以后的解决方案

 

111

使用chrome的用户,可以找到chrome.exe的程序创建一个快捷方式,并在上图标识的位置加入如下参数:

 --args --disable-web-security --user-data-dir=C:\Users\****\MyChromeDevUserData

注意:****为您的winows账号目录名,另外还需要在该账号目录侠创建MyChromeDevUserData目录以保存相关的chrome信息。

222启动刚刚配置号的快捷方式,出现如上图标示的提示,即可开启跨域模式。

91版本以前的解决方案(未验证)

有的用户还是不能访问,此时在console控制台里会看到一段背景是黄颜色的字

...Specify SameSite=None and Secure if the cookie should be sent in cross-site...'

Chrome中访问地址chrome://flags/ 搜索samesite 将same-site-by-default-cookies,和SameSite by default cookies这两项设置为Disabled后重启浏览器再运行项目即可解决。该设置默认情况下会将未指定SameSite属性的请求看做SameSite=Lax来处理。

91版本-94版本以前的解决方案(未验证)

在chrome的快捷方式后面加上以下运行参数:

--disable-features=SameSiteByDefaultCookies

或者

--flag-switches-begin --disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure --flag-switches-end

94版本以后的解决方案(未验证)

Chromium项目官网提到在94版本通过命令行禁用设置SameSite默认值的方式会被移除,到时方案1和方案2的方式都将无法使用,后续可通过nginx等代理工具或软件将跨域请求转为非跨域请求来解决改问题。以后如果要在开发环境作业, 老老实实上https也许是最简单的一种办法。

使用Impactor进行unc0ver重签时提示line:81错误的解决方法

折腾了好久,在网上找倒以下几种解决办法:

1、使用管理员模式来运行Impactor

2、使用impactor的revoke certificates清除证书后尝试

3、关闭appleid的双重验证

4、使用新的appleid

以上说的,第二种apple已经不允许关闭了,第三种是不现实的,所以我使用第一种和第二种都死活不成功。

原来,使用impactor的revoke certificates清除证书后,需要先重启你的iphone,然后再重签,否则死活不成功。

close