widget has an unresolved type,and thus it was upcasted to android.view.View

使用 KotlinView Binding 来绑定 View, 对于框架提供的控件没有任何问题,但是对自定义控件会出现如下错误:

比如,有自定义控件 com.example.customText

xx.xml

1
2
3
4
<com.example.customText
adnroid:id='@+id/custom_text'
...
/>

如果直接使用

1
custom_text.text = 'Hello'

会有如下错误

1
widget has an unresolved type 'com.example.customText',and thus it was upcasted to 'android.view.View'

需要先做类型转换:

1
2
val customtext as customText
customtext.text = 'Hello'

微服务

微服务定义

微服务将功能分解为由RESTful API松散耦合的独立应用程序。例如,eBay在2006年开发的独立Java servlet应用程序,用于处理用户、项目、账户、反馈、交易和70多项其他要素,其中每一个逻辑功能应用程序都是一个微服务。

这些微服务都是独立的,并且不共享数据层,每一个都有自己的数据库和负载均衡器,隔离是微服务架构的关键要素。不同的微服务需要不同的扩展技术,例如,有些微服务可能使用关系型数据库,而其他的可能使用NoSQL数据库。

微服务的好处

微服务架构可以将内部架构非常复杂的大型单体应用程序,分解成小型的可独立扩展的应用程序。每个微服务都很小,开发、更新和部署也不太复杂。

在考虑微服务时,为什么首先要将这些功能都构建到单个应用程序中呢?至少在理论上,你可以想象为:它们可以存在于单独的应用程序和数据孤岛中,这不会有什么大问题。例如,如果在拍卖中收到两份投标,但只有四分之一的销售收到反馈,那么在一天中的任何时间里,投标服务的活跃程度至少是反馈应用程序的八倍。如果将这些组合到一个应用程序中,你最终运行并更新的代码将比经常需要的代码更多。在本质上,将不同的功能组分隔成不同的应用程序,自有其道理。

而且,围绕微服务架构进行开发可获得一些隐性优势,例如可与PaaS、Docker和Linux容器等新技术紧密结合。

以微服务方式构建应用程序不仅可使应用程序更加灵活,更具可扩展性,它们还增加了构建应用程序的团队的可伸缩性。使用单一代码,你可以建立一支大型团队,虽然团队成员能够处理大段代码,但是他们始终彼此掣肘,随着代码整体数量的增长,开发速度会呈指数级下降。

不过,借助微服务架构,应用程序可由小型的、分散的开发团队进行构建,他们可以独立地工作和修改微服务。这样做的好处是升级服务和添加功能更加容易,软件和开发流程也将变得更加灵活。

微服务的挑战

但每种架构都有优点和缺点。虽然优点明显,但是微服务架构也带来了一系列难以解决的新问题–特别是记录、监控、测试和调试去中心化且松耦合的新应用程序。

如果有一个漏洞,那么哪个微服务应当对此负责呢?微服务之间的相互依存关系使得这个问题很难回答。微服务通常通过轻量级JSON REST API相互交换数据。与其前身XML-RPC和SOAP不同的地方是,REST接口的定义正变得越来越松散。虽然这些轻量级API更灵活,更容易扩展,但是它们增加了需要监控的新接口,这可能会产生中断或导致出现漏洞。

在单体应用程序中,你可以在代码中添加调试钩子,并在逻辑上逐步执行每个执行层,以发现问题区域。如果当数十个甚至数百个独立的微服务使用松散定义的API相互交换数据,那么在处理由这些微服务组成的网格时,你就不能再这么做了。

尽管如此,只要精心安排,这些困难是可以克服的。一些调试工具可以提供帮助,不过你可能需要根据其他部分的情况整合自己的解决方案。

微服务与容器和PaaS的关系

有一种常见的误解是,如果要使用微服务,那么你就需要使用PaaS或Linux容器。其实事实根本不是这么回事。你可以在没有微服务的情况下使用PaaS和Linux容器,也可以在没有PaaS或Linux容器的情况下使用微服务。它们彼此并不需要对方。

不过,它们之间确实能够很好地相互补充。无论是Heroku等公有云,还是Cloud Foundry或者OpenShift等私有云,PaaS环境都可以优化运行许多小型应用程序。像将330万行C ++应用程序移植到PaaS平台的事情永远都不会发生。

如果将应用程序分解为小的、可独立扩展的自足型应用程序,那么这些小型应用程序通常都非常适合在PaaS环境中运行。

同样,Linux容器更适合如微服务这样小型的无状态应用程序,而不是大型的单体应用程序。

毕竟,虚拟机和Linux容器之间最大和最明显的区别之一是缺少状态。虚拟机可通过配置保持其状态,而Linux容器的架构在本质上已经不再与基础映像有任何差异。你可以在Linux容器中安装状态文件夹,但是除非你提交更改,否则容器本身不会进行更改。

微服务架构的横向扩展理念促进了无共享、无状态应用程序的观念。它们不存储或修改底层文件系统。这就是为什么人们容易将微服务与Linux容器混为一谈,原因在于两者都不保留状态。

微服务与SOA的关系

微服务与SOA(面向服务的架构)关系密切,但又存在明显差异。从表面上看,SOA与SOAP和XML-RPC相关联,而微服务则与JSON相关联。但在某些方面,相关的API格式有着明显的外观差异。

同样,SOA使用企业服务总线,而微服务使用更轻量级的发布-订阅服务总线。尽管后者更为轻便,但原理是相似的。

微服务架构和SOA之间最大的区别在于微服务必须是可独立部署的,而SOA服务通常在部署整体中实现。

http://www.ccw.com.cn/intelumbrellacampaign/163yun/index.php/Home/Index/wenzhang/id/5818/lanid/8

1
In microservice architecture, multiple loosely coupled services work together. Each service focuses on a single purpose and has a high cohesion of related behaviors and data.

This definition includes three microservice design principles:

  • Single purpose — each service should focus on one single purpose and do it well.
  • Loose coupling — services know little about each other. A change to one service should not require changing the others. Communication between services should happen only through public service interfaces.
  • High cohesion — each service encapsulates all related behaviors and datatogether. If we need to build a new feature, all the changes should be localized to just one single service.

https://medium.engineering/microservice-architecture-at-medium-9c33805eb74f

Laravel Mail 配置

邮件相关的默认配置,打开 .env :

1
2
3
4
5
6
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

config/mail.php 中有相关说明:

MAIL_DRIVER: 一般保持默认的 smtp 即可。
MAIL_HOST :邮箱的主机,常用的:QQ邮箱(smtp.qq.com)、163 邮箱(smtp.163.com)等。
MAIL_PORT:用于配置邮箱发送服务端口号,一般为 25, 但如果设置SMTP使用SSL加密,该值为465。
MAIL_USERNAME:邮箱的登录名,即为邮箱账号。
MAIL_PASSWORD:邮箱登录密码,QQ 邮箱有例外,后面说明。
MAIL_ENCRYPTION:加密类型,默认为 null,如果使用 SSL,则为 ssl 。

mail.php 中,还有 from 的配置:

MAIL_FROM_ADDRESS : 发送邮件的账号,一般设置和 MAIL_USERNAME 相同。
MAIL_FROM_NAME:发送邮件的用户名,一般设为应用的名称。

QQ 邮箱的设置:

开启 smtp:

如下图,复制左侧生成的授权码作为密码:

如果要保存已发送的邮箱到服务器,在收取选项中勾选 SMTP 发信后保存到服务器

配置示例:

1
2
3
4
5
6
7
8
MAIL_DRIVER=smtp
MAIL_HOST=smtp.qq.com
MAIL_PORT=465
MAIL_USERNAME=mailteam@qq.com
MAIL_PASSWORD=11111
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS =mailteam@qq.com
MAIL_FROM_NAME=App

参考:
https://www.jianshu.com/p/8ccb2820df23
https://blog.csdn.net/wulove52/article/details/71172842

Laravel 备份

laravel 版本: 5.6.*

安装 spatie/laravel-backup:

1
composer require spatie/laravel-backup

发布配置:

1
php artisan vendor:publish --provider="Spatie\Backup\BackupServiceProvider"

生成配置文件 config/backup.php.

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
'source' => [
/*
*设置要备份的文件
*/
'files' => [

/*
* 默认是备份整个项目文件,实际上是不必要的,一般只备份上传的文件、图片等。
*/
'include' => [
base_path(),
],

/*
* 配合上面的设置,去除不需要备份的目录或文件
*/
'exclude' => [
base_path('vendor'),
base_path('node_modules'),
],
],

/*
*要备份的数据库
*/
'databases' => [
'mysql',
],
],
1
2
3
'mail' => [
'to' => 'example@example.com',
],

这里配置接受备份通知的邮箱地址,可以参考Laravel Mail 配置 配置邮箱。

接下来,在 config/database.php 配置数据库备份的相关信息,文档 https://docs.spatie.be/laravel-backup/v5/installation-and-setup#dumping-the-database

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//config/database.php

'connections' => [
'mysql' => [
'driver' => 'mysql'
...,
'dump' => [
'dump_binary_path' => '/path/to/the/binary', // only the path, so without `mysqldump` or `pg_dump`
'use_single_transaction',
'timeout' => 60 * 5, // 5 minute timeout
'exclude_tables' => ['table1', 'table2'],
'add_extra_option' => '--optionname=optionvalue',
]
],

其中 dump_binary_pathmysqldump 等所在的目录,在 Linux 系统中,可以通过 which mysqldump 命令查看。

示例:

1
2
3
4
5
6
7
8
9
10
'connections' => [
'mysql' => [
'driver' => 'mysql'
...,
'dump' => [
'dump_binary_path' => '/usr/bin',
'use_single_transaction' => true,
'timeout' => 60 * 5,
]
],

运行备份:

1
php artisan backup:run

生成的备份文件位于 storage/app/Laravel 目录下。

参考:
https://docs.spatie.be/laravel-backup/v5/introduction
https://www.jianshu.com/p/39457bc6ca22

WAMPServer 之 Apache 日志分割

WAMPServer 之 Apache 日志分割

WAMPServer 服务器访问出错,想看看访问日志,但是累积的日志文件过大(1.4G),文本编辑器没法打开,便打算做日志分割,在网上找到了办法,并能正确设置,现在记录一下:

httpd.conf

打开 httpd.conf 文件,查找 ErrorLog ,作如下修改:

1
ErrorLog "|${INSTALL_DIR}/bin/apache/apache2.4.23/bin/rotatelogs.exe ${INSTALL_DIR}/logs/error-%y%m%d.log 86400 480"

查找 CustomLog 作如下修改:

1
CustomLog "|${INSTALL_DIR}/bin/apache/apache2.4.23/bin/rotatelogs.exe ${INSTALL_DIR}/logs/access-%y%m%d.log 86400 480" common

重启所有服务。

参考:
window apache 日誌分割

Ubuntu redis 多实例安装

安装 redis:

1
apt-get install redis-server

安装成功后,默认端口为 6379.

接下来完成一个端口为 6380 的服务端:

修改 redis 配置文件:

1
2
3
4
5
cd /etc/redis

cp redis.conf redis-6380.conf

vi redis-6380.conf

作如下修改:

1
2
3
4
5
6
7
8
port 6380

pidfile /var/run/redis/redis-server-6380.pid

logfile /var/log/redis/redis-server-6380.log

dbfilename dump-6380.rdb

修改 redis 启动文件:

1
2
3
4
5
cd /etc/init.d/

cp redis-server redis-server-6380

vi redis-server-6380

作如下修改:

1
2
3
4
5
6
DAEMON_ARGS=/etc/redis/redis-6380.conf

DESC=redis-server-6380

PIDFILE=$RUNDIR/redis-server-6380.pid

添加 service:

1
2
3
cp /lib/systemd/system/redis-server.service /lib/systemd/system/redis-server-6380.service

vi /lib/systemd/system/redis-server-6380.service

作如下修改:

1
2
3
ExecStart=/usr/bin/redis-server /etc/redis/redis-6380.conf

PIDFile=/var/run/redis/redis-server-6380.pid
1
2
cd /etc/systemd/system
ln -s /lib/systemd/system/redis-server-6380.service redis-6380.service

启动 redis-server-6380:

1
2
3
service redis-server-6380 start 

ps -ef | grep redis
1
2
redis     5885     1  0 Jul20 ?        00:00:40 /usr/bin/redis-server 127.0.0.1:6379
redis 9339 1 0 09:20 ? 00:00:00 /usr/bin/redis-server 127.0.0.1:6380

Laravel 用 UUID 做主键

Laravel version 5.6.*

Laravel 项目用 UUID 做主键, 可以用 Str::uuidStr::orderedUuid 方法生成,但是当前版本直接使用 Str::orderedUuid 方法会报错,需要安装 moontoast/math 扩展包:

1
composer require moontoast/math

我遇到的另一个问题是,查询数据时, 结果中 ID (主键) 会自动转化为整数,发生的原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace Illuminate\Database\Eloquent;

abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable{

...

/**
* The "type" of the auto-incrementing ID.
*
* @var string
*/
protected $keyType = 'int';

...
}

由于主键默认设置为 int 类型,所以会自动转换。

解决办法,在我们的 模型 文件中,重写该属性:

1
protected $keyType = 'string';

Android获取签名证书的sha1值

开发环境: Windows 10

一般用 keytool 命令获取。

打开 PowerShell:

1
cd  .android

输入如下命令,回车:

1
keytool

如果没有错误,万事大吉;如果出现 keytool 不是内部或外部命令,也不是可运行的程序或批处理文件, 则说明 java 环境没有配置好:

1、安装 JDK

2、新建系统变量 JAVA_HOME, CLASSPATH, 如下所示(注意自己 JDK 安装目录)
JAVA_HOME D:\Java\jdk
CLASSPATH %JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar

3、然后在 path 中分别添加:
%JAVA_HOMW%\bin
%JAVA_HOME%\jre\bin

生成 sha1:

网上教程一般都用如下命令:

1
keytool -list -keystore debug.keystore

秘钥: android

会正确生成 sha1 ,但也会有如下提示:

1
2
Warning:
JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore debug.keystore -destkeystore debug.keystore -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。

按提示修改。

RecyclerView 滚动监听

偶然在在网上看到的,先抄下来~

1
2
3
4
5
6
7
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
manager = new LinearLayoutManager(this);
adapter = new RecyclerAdapter();
//设置布局管理器
recyclerView.setLayoutManager(manager);
//给recyclerview设置适配器
recyclerView.setAdapter(adapter);

如果是想每个item横向滑动的话,只需要使用LinearLayoutManager的三个参数的构造方法就行了,new LInearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true)就可以实现横向滑动的效果了。

给RecyclerView添加滚动监听。与listView和gridView有写不同,recyclerView判断滚动到哪一个位置的时候,需要使用到使用到布局管理器(前面设置的recyclerview.setLayoutManager())。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
recyclerView_follow.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//滚动的状态改变时,调用此方法。
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//屏幕中最下面一个item的所在数据源的位置(postion)。
int lastVisiableItem = manager.findLastVisibleItemPosition();
//一共有多少个
int totalItemCount = manager.getItemCount();
//当滑动到倒数第二个item时,即联网获取下一页的数据
if (lastVisiableItem >= totalItemCount - 2 && dy > 0) {
page++;//第二页
reloadData(page);
}
}
});