首页 文章 精选 留言 我的

精选列表

搜索[初体验],共236篇文章
优秀的个人博客,低调大师

SpringBoot2初体验,简单认识spring boot2并且搭建基础工程

前言 发展史 Spring boot 1.1(2014 年 6 月) - 改进的模板支持,gemfire 支持,elasticsearch 和 apache solr 的自动配置。 Spring Boot 1.2(2015 年 3 月) - 升级到 servlet 3.1 / tomcat 8 / jetty 9,spring 4.1 升级,支持 banner / jms / SpringBootApplication 注解。 Spring Boot 1.3(2016 年 12 月) - Spring 4.2 升级,新的 spring-boot-devtools,用于缓存技术(ehcache,hazelcast,redis 和 infinispan)的自动配置以及完全可执行的 jar 支持。 Spring boot 1.4(2017年1月) - spring 4.3 升级,支持 couchbase / neo4j,分析启动失败和RestTemplateBuilder。 Spring boot 1.5(2017年2月) - 支持 kafka / ldap,第三方库升级,弃用 CRaSH 支持和执行器记录器端点以动态修改应用程序日志级别。 Spring boot 2.0(2018 年 03 月)-基于 Java 8,支持 Java 9,支持 Quartz ,调度程序大大简化了安全自动配置,支持嵌入式 Netty。 SpringBoot 1.x再见,拥抱SpringBoot 2.x 2019年8月6日,Spring 官方在其博客宣布,Spring Boot 1.x 停止维护,Spring Boot 1.x 生命周期正式结束。 如今 Spring Boot 1.X 版本最后将永久的定格在 v1.5.22.RELEASE,其实回想起来自从 Spring Boot 1.0 发布已经过去了 5 年多,相当于 Spring Boot 一年发布一个大的子版本,然后到了现在的 1.5.x。 正在使用 Spring Boot 1.x 需要立刻升级吗? spring Boot 1.x 只是停止维护,并不是不能用了,如果你不是特别着急想用 Spring Boot 2.x 的新特性,再用个几年也是没有任何问题的。 Spring Boot 1.x 到 2.x 中间更新了很多 API 以及依赖组件。升级时需要先将 Spring Boot 1.X 系列升级到 1.5.x,再从 1.5.x 升级到 2.x 最新稳定版本。 Spring Boot 2.x 对 Java 环境的要求最低为 JDK 8,可能还有很多的公司服务器还在 1.6 或者 1.7 的环境中跑着,升级的时候也需要先升级服务器的基础环境。 如果项目中使用了微服务架构,建议可以一个一个子服务进行升级,不要一次全部升级完成,保障整个服务在升级过程的稳定性。 建立Springboot工程有两种方式 1.通过官网快速构建 2.手动构建Springboot工程 注:这两种方式在本文都会提到,本文仅构建简单的Springboot工程,如果需要构建Maven聚合的Springboot工程请期待后续文章 一.官网快速构建 1.Springboot官网提供了一个快速构建工程的方案 网址:https://start.spring.io/ 填写一些基本信息 2.下载并解压工程(工程目录如下,是一个标准的Maven工程) 3.Eclipse导入Maven工程 注:请升级至Eclipse_v2019.09,否则导入代码会报错。并且该代码依赖于Maven,所以需要提前准备Maven环境,并且导入的时候需要下载Jar请耐心等待。导入完成如图所示 4.Springboot工程目录结构介绍 5.运行项目 打开SpringbootApplication.java类运行其main方法即可 注:此时项目运行在8080默认端口,用浏览器访问即可。由于我们没写任何代码仅仅测试项目所以访问会报404错误,如下图所示 二.手动构建Springboot工程 1.创建一个Maven工程 创建出来的工程如下 注:第一次创建Maven工程,需要提前准备Maven环境、并且初次创建Maven项目需要去中央仓库下载Jar包,请耐心等待。 2.修改Maven的配置文件pom.xml 添加spring-boot-starter-parent xml 复制代码 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> 添加web依赖 xml 复制代码 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> 添加springboot打包插件 xml 复制代码 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> 注:由于修改了maven配置文件,工程会报错。所以需要刷新工程。 3.创建启动类 java 复制代码 package org.xujun; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringbootMyApplication { public static void main(String[] args) { SpringApplication.run(SpringbootMyApplication.class, args); } } 4.运行项目 参考[通过官网快速构建]的启动方式即可 总结:如果你看到了以上的404报错恭喜你,项目已经构建成功了。

优秀的个人博客,低调大师

SwiftUI 初体验

前言 苹果在 WWDC 2019 的开幕式中给我们来带了超多的惊喜,全新的iPad OS, 给生产力和商业带来了新领域,iOS 项目可以通过简单的修改移植到 Mac OS 上,全新的 Mac Pro 高清的显示器等新硬件,但对于在苹果平台的开发者们,最重要的莫过于 Swift UI。 曾几何时,iOS 开发者的 UI 开发体验一直是大前端中体验最差的,粗矿原始的 Frame 布局系统, API 冗长难用的 Autoalyout, 都是把开发者按在地上使劲的摩擦。 毫无开发体验可言。 去年大火的 Flutter 给客户端上开发带来了全新的体验,声明式的 UI 语法,亚秒级别的实时刷新, 都极大的提升了开发效率,但现在这些都在 SwiftUI.Framework上得以实现,并且是官方原生的支持。 昨天一起观看WWDC的小伙伴们都戏称 SW

优秀的个人博客,低调大师

Kotlin初体验

距离kotlin成为Android一级开发语言已经一年了,这节将从零开始,实现kotlin的几个小知识点。 以下操作是在AndroidStudio3.0.1下完成的,如果是3.0以下版本的AndroidStudio,需要安装好kotlin插件才能使用Kotlin进行开发 Kotlin教程推荐 官网文档,黑马教程,《Kotlin for android developers》中文版翻译 相关代码已经上传到Github的KotlinPractice文件夹下了 创建项目: 在创建项目中勾选kolin支持后直接下一步即可 勾选kotlin支持 创建完项目后我们发现主类已经是使用了kotlin代码了,我们去project的build.gradle目录下可以看到 ext.kotlin_version = '1.1.51' kotlin //版本号 dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"//依赖 } 以及module下build.gradle的: apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' 实际上这就是我们刚才勾选kotlin后给我们自动生成的,如果我们想要在现有Java代码项目中使用kotlin,那么我们就需要手动的去添加这些依赖,或者使用方便的java代码转kotlin来进行配置,我们看到MainActivity默认已经是kotlin类了 /** * : AppCompatActivity() * :类名,表示继承于AppCompatActivity类 */ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } 为TextView控件赋值: 我们在布局从添加一个TextView,id命名为text_01 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.administrator.kotlin.MainActivity"> <TextView android:id="@+id/txt_01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试文本" /> </LinearLayout> 回到Activity,在activity的导包中添加这一行 import kotlinx.android.synthetic.main.activity_main.* 它的作用现在只要知道添加了这一行后我们就不需要再findViewById就可以了,activity_main的意思是Activity布局的包名。 import kotlinx.android.synthetic.main.activity_main.* /** * : AppCompatActivity() :类名,表示继承与什么类 */ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 为TextView设置值,koltin的语句后面是不需要;的,但是如果你加上也不会报错的 txt_01.setText("你好,世界") } } 运行程序,确实和我们想要的是一样的 设置按钮点击事件 我们再为布局添加3个button按钮 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.administrator.kotlin.MainActivity"> <TextView android:id="@+id/txt_01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试文本" /> <Button android:id="@+id/btn_01" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按钮1" /> <Button android:id="@+id/btn_02" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按钮2" /> <Button android:id="@+id/btn_03" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按钮3" /> </LinearLayout> 回到Activity,让MainActivity类继承于View.OnClickListener接口,我们可以看到koltin继承接口是使用,号进行的,如果我们想要继承多个接口,只需要使用多个,分隔开来就行了 /** * : AppCompatActivity() :类名,表示继承于什么类 * ,View.OnClickListener, View.OnLongClickListener 表示继承于 View.OnClickListener 点击事件接口, View.OnLongClickListener 长按事件接口 */ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 为TextView设置值,koltin的语句后面是不需要;的,但是如果你加上也不会报错的 txt_01.setText("你好,世界") } /** * 重写OnClickListener接口的onClick方法 */ override fun onClick(p0: View?) { } /** * 重写OnLongClickListener接口的onLongClick方法 */ override fun onLongClick(p0: View?): Boolean { return false } } 然后就是我们熟悉的在点击事件里面使用switch找到对应控件的id了,只是在kotlin里面是使用when /** * 重写OnClickListener接口的onClick方法 */ override fun onClick(p0: View?) { when (p0!!.id) { R.id.btn_01 -> { Toast.makeText(this, "按钮1", Toast.LENGTH_LONG).show() } R.id.btn_02 -> { Toast.makeText(this, "按钮2", Toast.LENGTH_LONG).show() } R.id.btn_03 -> { Toast.makeText(this, "按钮3", Toast.LENGTH_LONG).show() } } } 最后要注意,虽然我们通过import kotlinx.android.synthetic.main.activity_main.*使得不需要写控件的fndId,但是我们仍然是要写setOnClickListener(this)控件点击回调监听的 完整代码: import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.view.View import android.widget.Toast import kotlinx.android.synthetic.main.activity_main.* /** * : AppCompatActivity() :类名,表示继承与什么类 */ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 为TextView设置值,koltin的语句后面是不需要;的,但是如果你加上也不会报错的 txt_01.setText("你好,世界") // 设置控件的回调监听 init() } /** * 设置控件的回调监听 */ private fun init() { btn_01.setOnClickListener(this) btn_02.setOnClickListener(this) btn_03.setOnClickListener(this) } /** * 重写OnClickListener接口的onClick方法 */ override fun onClick(p0: View?) { when (p0!!.id) { R.id.btn_01 -> { Toast.makeText(this, "按钮1", Toast.LENGTH_LONG).show() } R.id.btn_02 -> { Toast.makeText(this, "按钮2", Toast.LENGTH_LONG).show() } R.id.btn_03 -> { Toast.makeText(this, "按钮3", Toast.LENGTH_LONG).show() } } } /** * 重写OnLongClickListener接口的onLongClick方法 */ override fun onLongClick(p0: View?): Boolean { return false } } 运行程序点击按钮后,出现了我们想要的吐司 实现网络请求 开始之前我们先在module下配置anko依赖,这是为了等下线程调度用的 module: dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' // kotlin插件anko implementation "org.jetbrains.anko:anko-common:0.10.4" } 然后实现我们的network()请求网络方法,最后不要忘了在AndroidManifest.xml下添加网络权限 <uses-permission android:name="android.permission.INTERNET" /> /** * 重写OnClickListener接口的onClick方法 */ override fun onClick(p0: View?) { when (p0!!.id) { R.id.btn_01 -> { // 只进行网络请求 network(); } R.id.btn_02 -> { // 进行网路请求,并且进行json数据解析 Toast.makeText(this, "按钮2", Toast.LENGTH_LONG).show() } R.id.btn_03 -> { Toast.makeText(this, "按钮3", Toast.LENGTH_LONG).show() } } } /** * 网络请求 */ private fun network() { // 声明要请求的网址,在kotlin中,var能声明一切类型,而后面的:String其实是可以不加的 var string: String = "http://www.wanandroid.com/tools/mockapi/2872/k1" // 作用类似java在子线程中进行网络请求,但是async在kotlin中叫做协程 async() { val fore = URL(string).readText() Log.d("tonjies", fore) } } 观察日志,我们可以看到确实已经获取了Json数据 这里写图片描述 Json数据解析 接下来我们进行json数据的解析,首先添加gson解析依赖 // goso解析 compile "com.google.code.gson:gson:2.4" 我们创建bean实体类包,在包下创建解析实体类Student,我们不需要像Java那样去生成get和set对象,因为这kotlin已经帮我们生成好啦! /** * Created by 舍长 on 2018/4/28. * 描述: 数据请求实体类 */ class Student { var name:String?=null var age:String?=null } 在按钮2的点击事件里面写一个gson方法,在gson方法里将网络请求到的数据转换成实体类,并且回到UI线程,显示在文字控件上 /** * 重写OnClickListener接口的onClick方法 */ override fun onClick(p0: View?) { when (p0!!.id) { R.id.btn_01 -> { // 只进行网络请求 network(); } R.id.btn_02 -> { // 进行网路请求,并且进行json数据解析 Toast.makeText(this, "按钮2", Toast.LENGTH_LONG).show() gson(); } R.id.btn_03 -> { Toast.makeText(this, "按钮3", Toast.LENGTH_LONG).show() } } } /** * 网络请求并使用gson解析数据 */ private fun gson() { // 声明要请求的网址,在kotlin中,var能声明一切类型,而后面的:String其实是可以不加的 var string: String = "http://www.wanandroid.com/tools/mockapi/2872/student" // 作用类似java在子线程中进行网络请求,但是async在kotlin中叫做协程 async() { val fore = URL(string).readText() var fromJson: Student = Gson().fromJson(fore, Student::class.java) // 回到UI线程中去更新数据,这里的${属性名}是字符串模板,作用于Java的+属性名+相同 uiThread { txt_01.setText("学生的名字是${fromJson.name},年龄是${fromJson.age}") } } } 运行程序,点击按钮二,可以看到文字确实变成了我们想要的json数据:学习的名字是tonjies,年龄是18 这一小节的完整代码: 布局文件activity_main: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.administrator.kotlin.MainActivity"> <TextView android:id="@+id/txt_01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试文本" /> <Button android:id="@+id/btn_01" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按钮1" /> <Button android:id="@+id/btn_02" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按钮2" /> <Button android:id="@+id/btn_03" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按钮3" /> </LinearLayout> MainActivity package com.example.administrator.kotlin import android.icu.lang.UCharacter.GraphemeClusterBreak.L import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.view.View import android.widget.Toast import com.example.administrator.kotlin.R.id.async import com.example.administrator.kotlin.bean.Student import com.google.gson.Gson import kotlinx.android.synthetic.main.activity_main.* import java.net.URL import org.jetbrains.anko.custom.async import org.jetbrains.anko.uiThread /** * : AppCompatActivity() :类名,表示继承与什么类 */ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 为TextView设置值,koltin的语句后面是不需要;的,但是如果你加上也不会报错的 txt_01.setText("你好,世界") // 设置控件的回调监听 init() } /** * 设置控件的回调监听 */ private fun init() { btn_01.setOnClickListener(this) btn_02.setOnClickListener(this) btn_03.setOnClickListener(this) } /** * 重写OnClickListener接口的onClick方法 */ override fun onClick(p0: View?) { when (p0!!.id) { R.id.btn_01 -> { // 只进行网络请求 network(); } R.id.btn_02 -> { // 进行网路请求,并且进行json数据解析 Toast.makeText(this, "按钮2", Toast.LENGTH_LONG).show() gson(); } R.id.btn_03 -> { Toast.makeText(this, "按钮3", Toast.LENGTH_LONG).show() } } } /** * 网络请求并使用gson解析数据 */ private fun gson() { // 声明要请求的网址,在kotlin中,var能声明一切类型,而后面的:String其实是可以不加的 var string: String = "http://www.wanandroid.com/tools/mockapi/2872/student" // 作用类似java在子线程中进行网络请求,但是async在kotlin中叫做协程 async() { val fore = URL(string).readText() var fromJson: Student = Gson().fromJson(fore, Student::class.java) // 回到UI线程中去更新数据,这里的${属性名}是字符串模板,作用于Java的+属性名+相同 uiThread { txt_01.setText("学生的名字是${fromJson.name},年龄是${fromJson.age}") } } } /** * 网络请求 */ private fun network() { // 声明要请求的网址,在kotlin中,var能声明一切类型,而后面的:String其实是可以不加的 var string: String = "http://www.wanandroid.com/tools/mockapi/2872/student" // 作用类似java在子线程中进行网络请求,但是async在kotlin中叫做协程 async() { val fore = URL(string).readText() Log.d("tonjies", fore) } } /** * 重写OnLongClickListener接口的onLongClick方法 */ override fun onLongClick(p0: View?): Boolean { return false } } 好了,这一小节就到这里啦!在学习的过程中,参考开源项目和巧妙使用Java代码转kotlin的快捷键是挺有用的

优秀的个人博客,低调大师

Weex初体验

今年莫名其妙的被前端炒的特别火,混合式的开发让我们这些只会android native开发的人怎么办哟,要不,我们也学一点,跟着潮流走呗,看着工作室的人写的H5界面用的是bootstrap,咦,感觉很不错的样子,界面排版挺好的,但是感觉玩起来还是个网页,在移动端加载并没有体验特别棒的感觉,然后,就看了下vue.js的mvvm,确实挺强大的数据绑定,不过都是在摸索,之前玩过react native,把环境搭了一下,示例demo跑起来了,但是这个过程感觉好麻烦啊,然后搁置了一段时间,看见了阿里的weex,豪言要“Write once, run everywhere”,我这惊讶呀,看来我得玩玩你到底是个什么东西:weex首页 那接下来,先把环境搭建一下呗: 首先,安装个node环境,下图中箭头的版本 然后下载安装,解压后是这样的 看到node.exe没,以后学node.js开发的时候可以用到,最近也玩了一点node,感觉还不错,npm看到没,所有关于node模块开发的,都可以用这个npm去下载moudle,将这个路径配置到环境变量中去,以后就可以在任意环境下去node或npm了。 安装weex环境 我们在cmd窗口中输入npm install -g weex-toolkit安装weex环境,-g是全局,以后创建weex demo的时候可以在任意环境下面使用weex来创建 安装完成后,我们来看看安装的是啥东西,你一定找不到安装在哪了是吧,别急,在安装成功的上面会有很多的信息,会提示安装路径,跟着上面找就是了,如果没找到,那就是隐藏文件夹,把他显示出来就行了 安装的解压目录 好了,环境起来了,我们看看版本了,weex –version 接下来,我们来玩demo了,创建个demo,随便找个路径执行weex init 执行的时候他会让你输入项目的名称prompt: Project Name: ,我输入的是weexDemo,然后等他加载文件,加载完成后就是这样的一个目录文件 目录结构我还是小白,等会给你们参考资料,大家看看,我们进入src文件里面看看,里面是一个weex-bootstrap.we的文件,这个就是我们要运行的文件,ok,现在基本就是跑demo的时候了 我们在src目录下执行weex weex-bootstrap.we,等待他响应,他会打开你的浏览器显示页面效果 下面一大串是服务器的监听,是node这个服务支撑起来的。 好了,浏览器的效果看见了,现在我们想在手机端看效果怎么办呢,这时候,我们需要一个playground,类似于一个手机端的运行环境,这个环境是一个apk,我们在官网上面下载下来安装到手机就好了链接,android和ios版本的都有。 打开app是这样的: 下面都是demo示例,我们没事的时候可以看看他能做出什么效果出来,现在我们先不管,我们看看右上角的扫一扫图标,首先我们在控制台这样来运行demo 把原先的代码运行关掉,重心执行命令:weex weex-bootstrap.we –qr 他会生成一个二维码,然后用这个扫一扫功能,扫一下这个二维码就可以跑起来了 对了,几个细节要说,你这个手机端网络段必须是和电脑是同一网段,不然你是访问不到的,这个仅局域网可以访问,我是电脑开wifi给手机的。 最后,给大家一些参考资料: weex的中文文档 weex的几个小视频 weex android环境的playground源码 weex-pack初始化android和ios工程 weex-pack打包android和iso工程

优秀的个人博客,低调大师

MAUI初体验:爽

只是记录,只是Hello World体验,别期望太高。 1. 前言 经过几个小时折腾,Maui环境终于安装好了,先上Hello World截图: 1.1 MAUI Windows上 1.2 MAUI Android上 2. 今早看到一个群聊推送 点击链接可以看推送:爱奇艺 Preview 上架微软 Win11 应用商店:全新 WinUI 3 版本,软件界面大变(附下载地址) 爱奇艺使用的WinUI 3可能和MAUI Windows开发使用的WinUI不同?请知道的留言解惑: 3. MAUI环境安装前介绍 MAUI-Check:这是安装MAUI的检测工具,在安装的过程中可能会出现一些问题 这是安装过程: 可能会出现如下异常: 不要担心,下面是安装介绍文章(不翻译了,累),只要你有科学上网工具,多来几次,有可能会成功的,哈哈 3. 正式安装MAUI环境 Mac 参考链接:https://subscribe.packtpub.com/getting-started-with-microsoft-net-maui/ 站长卡在工作负载安装失败,多次都没成功,mac暂时放弃了,后面再研究 有兴趣的看看这些视频学习下: https://www.youtube.com/watch?v=eF5YcpyzQIo&t=350s https://xam.com.au/installing-net-maui-preview/ https://www.banditoth.hu/2021/12/29/setup-net-maui-project-on-macos/ Windows 站长主要看的这个链接安装环境,折腾了几个小时成功了 https://subscribe.packtpub.com/getting-started-with-microsoft-net-maui/ 4. 结束 上面的链接如果不行,你也可以多搜索谷歌其他文章,安装教程网上很多,最好用谷歌,看看油管视频。 昨天和今天只是体验下MAUI开发体验,热重载加载很快,MAUI大有可为,私下站长可能开发桌面应用就上MAUI了(当然移动APP也是),不过暂时就不研究MAUI了,下半年或者明年再研究MAUI。 今天发文只是报告一下体验MAUI hello world,完结。

优秀的个人博客,低调大师

Service Mesh 初体验

前言 计算机软件技术发展到现在,软件架构的演进无不朝着让开发者能够更加轻松快捷地构建大型复杂应用的方向发展。容器技术最初是为了解决运行环境的不一致问题而产生的,随着不断地发展,围绕容器技术衍生出来越来越多的新方向。 最近几年,云计算领域不断地出现很多新的软件架构模式,其中有一些很热门的概念名词如:云原生、函数计算、Serverless、ServiceMesh等等,而本文将初窥一下ServiceMesh的面纱。下面结合自己的理解尽量以通俗的话进行叙述。 背景和定义 微服务及服务治理 在微服务之前的软件开发中,往往通过一个应用的方式将所有的模块都包括进去,并一起编译、打包、部署、运维。这种方式存在很多问题,由于单个应用包含的东西太多,其中某个模块出现问题或者需要更新那么整个应用就需要重新部署。这种方式给开发和运维带来了很大的麻烦。随着应用的逐渐

优秀的个人博客,低调大师

MaxCompute用户初体验

作为一名初次使用MaxCompute的用户,我体会颇深。MaxCompute 开箱即用,拥有集成化的操作界面,你不必关心集群搭建、配置和运维工作。仅需简单的点击鼠标,几步操作,就可以在MaxCompute中上传数据,分析数据并得到分析结果。 作为一种快速、完全托管的 TB/PB 级数据仓库解决方案,MaxCompute不仅为我们提供了传统的命令行操作,而且提供了丰富的web端操作界面。对于数据开发,测试,发布,数据流,数据权限管理都非常好用,支持python,java的udf,对于复杂的逻辑查询也支持传统的MapReduce,同时也支持多种机器学习算法,非常好用。 MaxCompute为我们提供了统一的项目管理。实际开发中各个团队都有自己的项目,自己管理自己的项目, 通过项目隔离,有效的防止数据和任务被其他团队修改和删除等问题。除非是pro项目任务出错,否则不会影响到其他业务线的任务,最大程度降低各业务间的影响。 同时,大数据开发套件和 MaxCompute关系紧密,大数据开发套件为 MaxCompute 提供了一站式的数据同步,任务开发,数据工作流开发,数据管理和数据运维等功能。 当需要处理的数据变得非常多,并且数据发展到足够复杂的时候,这些数据往往需要用不同的模式进行处理,除此之外,当作业出现问题的时候也需要有足够好的工具进行分析和管理。这些其实都是对于开发者而言需要面对的问题,与此同时阿里云为我们提供了DATA IDE环境来解决上述的问题。 在进行数据分析的过程中DATA IDE可以支持SQL的语法以及语法的高亮,同时实现代码补全等功能。开发者即使对于SQL的语法并不熟悉,但是根据DATA IDE给出的关键词提示也可以轻松的进行操作。另外在DATA IDE中可以集成元数据,从而在开发人员进行SQL语句编写时,给予表格、字段以及函数的提示。 在任务执行过程中,我们通过IDE可以看到作业的执行状态以及详细信息,即便是作业出现问题IDE也可以很方便地对于问题进行定位,帮助开发者发现作业中哪一个是瓶颈。 Max Compute还为我们提供了一个基于云计算的灵活的轻量级的自助 BI 工具服务平台-----Quick BI。 Quick BI可以支持多种数据源的添加,以便于我们对不同的数据源进行实时在线分析。 Quick BI为我们提供了丰富的可视化图表,根据行业的不同需求我们可以随意选取,以便于给决策曾提供更清晰准确的报表。而其基于 WEB 页面的工作环境,拖拽式、类似于 EXCEL 的操作方式,一键导入、实时分析,可以灵活切换数据分析的视角,更是锦上添花。 对于完成后的报表图表,可以以接口方式将制作的报表嵌入到第三方软件,从而方便自己的分析查询。 总之,无论是用来学习,还是用来工作MaxCompute都是一个不错的选择。时代在进步,科技在发展,大数据的脚步已经离我们越来越近,更多是知识需要我们去学习,去实践,赶快行动起来加入其中吧。 作者:王艾军 北京青苔数据科技有限公司 大数据研发经理

优秀的个人博客,低调大师

JAVA 11 初体验

随着JAVA每半年发布一次新版本,前几天JAVA 11隆重登场。在JAVA 11中,增加了一些新的特性和api,同时也删除了一些特性和api,还有一些性能和垃圾回收的改进。 作为一名一线的开发人员,JAVA 11给我们带来哪些便利之处呢?下面我们来体验一下。 在Lambda表达式中使用var 本地变量类型var是java 10提出的新概念,它可以从上下文中推断出本地变量的类型,从而提高代码可读性。我们看看下面的例子: public class Main { public static void main(String[] args) throws Exception { URL url = new URL("http://www.oracle.com/"); URLConnection conn = url.openConnection(); Reader reader = new BufferedReader( new InputStreamReader(conn.getInputStream())); } } 使用var声明后,上面的代码可以改写成: public class Main { public static void main(String[] args) throws Exception { var url = new URL("http://www.oracle.com/"); var conn = url.openConnection(); var reader = new BufferedReader( new InputStreamReader(conn.getInputStream())); } } 我们使用var代替了URL、URLConnection、Reader,提高了代码的可读性,也方便了开发。但是在JAVA 10中,var变量不能在lambda表达式中声明,在JAVA 11中,解决了这个问题。我们可以在lambda表达式中使用var,如下: (var x, var y) -> x.process(y) 上面的例子等同于 (x, y) -> x.process(y) 但是我们不能混合使用,下面的两个例子都是错误的: //含蓄型的lambda表达式中,要么全使用var,要么全不使用var (var x, y) -> x.process(y) //在lambda表达式中,不能即使用含蓄型,又使用明确型 (var x, int y) -> x.process(y) 标准化HTTP Client API 以前我们在程序中使用HttpClient时,通常会引入apache的HttpClient工具包。在JAVA 11中,我们可以使用JDK原生的HttpClient了。 public class HttpTest { public static void main(String[] args) throws Exception { String uri = "http://www.baidu.com"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } 上面的例子是同步的get请求,还有其他的方法HttpClient也是提供的,例如: 异步get post提交 并发请求 Get Json Post Json 这些例子这里不做详细介绍了,如有需要请参考官方例子。功能很强大吧,我们不用再引入其他的HttpClient的jar包了。 总结 对于一线开发者而言,JAVA 11的体验就这么多,如有遗漏,会在以后补充。

优秀的个人博客,低调大师

Julia语言初体验

最近MIT发布的julia 1.0.0版,据传整合了C、Python、R等诸多语言特色,是数据科学领域又一把顶级利器。 周末心血来潮赶快体验了一把,因为用习惯了jupyter notebook,安装完julia 1.0.0之后就配置好了jupyter notebook。 在安装配置环境阶段就遭遇了不少坑,吃了不少苦头,这里不得不吐槽级距,julia的安装配置一点儿也不比python简单,自己配置原生环境,结果下载包各种不兼容,想要导入本地数据,需要解决CSV包、xlsx包的接口问题,总之一路坎坷。 这里把自己走过的弯路总结一下,方便后来者学习! 1、环境选择: 强烈建议选择JuliaPro来安装,这里稍稍说明一下,julia虽然在8月8日更新了Julia 1.0.0版本,但是作为一门新兴语言,它的版本后向兼容实在是不敢恭维,原生环境里面一个包都不给配置,需要自己一个一个下载。所以选择了JuliaPro这个集成环境(主要集成了Atom+juno【julia的第三方IDE】、jupyter notebook【浏览器端的编辑器】)。主要是JuliaPro初始化就配置了好几十个常用的包,省的自己一个个下载还不一定能搞定各种路径配置。 下载JuliaPro并安装之后,会有三个主要入口: ●Juno for JuliaPro 0.6.4.1 ●JuliaPro - Command Prompt 0.6.4.1 ●Jupyter for JuliaPro 0.6.4.1 第一个是Atom+juno的环境,相当于PyCharm之于Python,第二个是julia的命令行,第三个是Jupyter notebook编辑环境。如果是要单独下载原生环境并手动配置的话,需要摸索各种难题(还不一定能在网上找到解决方案)。 2、常用文件管理工具: julia的包管理工具类似Pyrhon中的conda,叫做Pkg: Pkg.add("packages") #安装包 Pkg.build("packages") #配置包 Pkg.rm("packages") #卸载包 using packages #加载包 import PyCall #与using功能一样(和Python的导入相同) using IJulia #IJulia是julia与jupyter notebook之间的连接器 notebook() #启动jupyter环境 Pkg.status() #查看当前环境中的包列表 Pkg.installed() #查看已经安装的包信息 homedir() #获取当前用户目录 pwd() #获取当前工作目录 cd("D:/") #设定当前工作目录 cd("C:/Users/RAINDU/Desktop/") include("hello.jl") #脚本执行(julia环境中) julia script.jl arg1 arg2... #终端命令行执行 3、文件I/O常用环境: 3.1 TXT文件导入导出: Pkg.add("CSV") #如果没有安装需先安装 using CSV mydata = CSV.read("EyesAsia.txt") 可恶的中文乱码。最简单的办法就是手动修改编码为UTF-8然后再次导入。 CSV.write("out.csv", mydata) #数据导出 3.2 xlsx文件导入导出 Pkg.add("XLSXReader") Pkg.add("XLSX") using XLSXReader using XLSX cd("C:/Users/RAINDU/Desktop/") mydata = readxlsx("data.xlsx","Sheet1") #读入 XLSX.writetable("mydata.xlsx", DataFrames.columns(mydata), DataFrames.names(mydata)) 4、基本数据类型 4.1 字符串 char = "hello,world!" julia> char = "hello,world!" "hello,world!" julia> print(char) hello,world! julia> length(char) 12 julia> char[1] 'h': ASCII/Unicode U+0068 (category Ll: Letter, lowercase) julia> char[end] '!': ASCII/Unicode U+0021 (category Po: Punctuation, other) julia中字符串可以继续遍历(区别于R,与Python相同) Julia中区别标量和向量(区别于R,同Python) 4.2 列表 mylist = ["A"] 1-element Array{String,1}: "A" julia> mylist = ["A","B","C","D","E"] 5-element Array{String,1}: "A" "B" "C" "D" "E" julia> myarray = [["A","B","C","D","e"],[3,4,9,6,7]] 2-element Array{Array{Any,1},1}: Any["A", "B", "C", "D", "e"] Any[3, 4, 9, 6, 7] julia> getindex(myarray,1) 5-element Array{Any,1}: "A" "B" "C" "D" "e" julia> getindex(myarray,2) 5-element Array{Any,1}: 3 4 9 6 julia中不区分向量和数组,一维数组便是向量。 myarray[1,2] 4.3 元组 julia> my_tuple = ("hello","world") ("hello", "world") julia> typeof(my_tuple) Tuple{String,String} julia> getindex(my_tuple,2) "world" typeof()函数可以用于检查数据对象的类型结构(同R中的typeof,区别于Python中的type()) julia中的索引从1开始,区别于Python中的从0开始,与R相同。 4.4 字典 julia> dict = Dict("a" => 1, "b" => 2, "c" => 3) Dict{String,Int64} with 3 entries: "c" => 3 "b" => 2 "a" => 1 julia> dict Dict{String,Int64} with 3 entries: "c" => 3 "b" => 2 "a" => 1 julia> dict["a"] #字段索引 1 4.5 数据框 using DataFrames #julia的数据框并非内置类型,而是需要额外加载包 julia> DataFrame(A = 1:4, B = ["M", "F", "F", "M"]) 4×2 DataFrames.DataFrame │ Row │ A │ B │ ├-──┼─-┼─-┤ │ 1 │ 1 │ M │ │ 2 │ 2 │ F │ │ 3 │ 3 │ F │ │ 4 │ 4 │ M │ df = DataFrame() df[:A] = 1:8 df[:B] = ["M", "F", "F", "M", "F", "M", "M", "F"] 8×2 DataFrames.DataFrame │ Row │ A │ B │ ├-──┼-─┼-─┤ │ 1 │ 1 │ M │ │ 2 │ 2 │ F │ │ 3 │ 3 │ F │ │ 4 │ 4 │ M │ │ 5 │ 5 │ F │ │ 6 │ 6 │ M │ │ 7 │ 7 │ M │ │ 8 │ 8 │ F │ julia> size(df, 1) #数据框行数 8 julia> size(df, 2) #数据框列数 2 head(df) #预览指定行 tail(df) #预览指定列 julia> size(df) #数据框维度(包含行列) (8, 2) julia> df[1:3, :] #索引行列 3×2 DataFrames.DataFrame │ Row │ A │ B │ ├─-─┼-─┼-─┤ │ 1 │ 1 │ M │ │ 2 │ 2 │ F │ │ 3 │ 3 │ F │ julia> df[1:3, 2] 3-element Array{String,1}: "M" "F" "F" 在数据框索引这一点儿上,julia是吸收了R和Python的特点,即允许直接基于数据框 本身索引行列,使用 范围符号numA:numB,同时默认取所有列或行时用:。 当取单列时,自动降维为一维数组。 julia> describe(df) #描述性统计 A Summary Stats: Mean: 4.500000 Minimum: 1.000000 1st Quartile: 2.750000 Median: 4.500000 3rd Quartile: 6.250000 Maximum: 8.000000 Length: 8 Type: Int64 B Summary Stats: Length: 8 Type: String Number Unique: 2 关于数据合并: names = DataFrame(ID = [20, 40], Name = ["John Doe", "Jane Doe"]) jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"]) join(names, jobs, on = :ID) 现实中数据合并的多种情况,julia中的DataFrames中的dataframe都能够很好地满足。 jobs = DataFrame(ID = [20, 60], Job = [“Lawyer”, “Astronaut”]) join(names, jobs, on = :ID, kind = :inner) join(names, jobs, on = :ID, kind = :left) join(names, jobs, on = :ID, kind = :right) join(names, jobs, on = :ID, kind = :outer) join(names, jobs, on = :ID, kind = :semi) join(names, jobs, on = :ID, kind = :anti) join(names, jobs, kind = :cross) rename!(b, :IDNew => :ID) #修改数据框指定列字段名称 5 简单的聚合运算 using DataFrames, CSV iris = CSV.read(joinpath(Pkg.dir("DataFrames"), "test/data/iris.csv")); julia> by(iris, :Species, size) #分类计数运算 3×2 DataFrames.DataFrame │ Row │ Species │ x1 │ ├─-─┼──────┼──-──┤ │ 1 │ setosa │ (50, 5) │ │ 2 │ versicolor │ (50, 5) │ │ 3 │ virginica │ (50, 5) │ by(iris, :Species, df -> mean(df[:PetalLength])) julia> by(iris, :Species, df -> mean(df[:PetalLength])) 3×2 DataFrames.DataFrame │ Row │ Species │ x1 │ ├──-┼──────┼-───┤ │ 1 │ setosa │ 1.462 │ │ 2 │ versicolor │ 4.26 │ │ 3 │ virginica │ 5.552 │ 这里:Species代表列引用,df -> mean(df[:PetalLength])这一句中的df并无实际意义,仅仅是julia中的匿名函数。所以df写成什么并无所谓。 julia> by(iris, :Species, x -> mean(x[:PetalLength])) 3×2 DataFrames.DataFrame │ Row │ Species │ x1 │ ├──-┼──────┼-───┤ │ 1 │ setosa │ 1.462 │ │ 2 │ versicolor │ 4.26 │ │ 3 │ virginica │ 5.552 │ 可以看到结果一模一样。 by(iris, :Species, df -> DataFrame(N = size(df, 1))) julia> by(iris, :Species, df -> DataFrame(N = size(df, 1))) 3×2 DataFrames.DataFrame │ Row │ Species │ N │ ├─-─┼──--───┼──┤ │ 1 │ setosa │ 50 │ │ 2 │ versicolor │ 50 │ │ 3 │ virginica │ 50 │ 分类计数的另一种写法。 by(iris, :Species,df -> DataFrame(m = mean(df[:PetalLength]), s² = var(df[:PetalLength]))) julia> by(iris, :Species,df -> DataFrame(m = mean(df[:PetalLength]), s² = var(df[:PetalLength]))) 3×3 DataFrames.DataFrame │ Row │ Species │ m │ s² │ ├──-┼──────┼─-----────────┤ │ 1 │ setosa │ 1.462 │ 0.0301592 │ │ 2 │ versicolor │ 4.26 │ 0.220816 │ │ 3 │ virginica │ 5.552 │ 0.304588 │ aggregate(iris, :Species, length) #聚合每一个类别的长度 aggregate(iris, :Species, [sum, mean]) #同时聚合汇总、均值 #长宽转换操作-由宽转长 d = melt(iris, :Species); 6. 日期&时间处理 Pkg.add("Dates") using Dates Date(2013) 2013-01-01 Date(2013,7) 2013-07-01 Date(2013,7,5) 2013-07-05 DateTime(2013) DateTime(2013,7) DateTime(2013,7,5) DateTime(2013,7,1,12) DateTime(2013,7,1,12,30) DateTime(2013,7,1,12,30,59) DateTime(2013,7,1,12,30,59,1) 取日期对应元素 t = Date(2014,1,31) Dates.year(t) Dates.month(t) Dates.week(t) Dates.day(t) 6.一些好用的魔法工具 6.1 函数 julia中的函数定义很有意思,如果是尊重语法规范,应该是这样定义 function f_jisaun(x) result = x^2 + 2x - 1 return result end julia> f_jisaun(5) 34 以上函数定义可简化为: f_jisaun(x) = x^2 + 2x - 1 34 几乎接近代数运算中对函数的定义。 6.1 匿名函数 匿名函数配合map高阶函数可以实现快速计算 n_fun = x -> x^2 + 2x - 1 #匿名函数 map(round, [1.2,3.5,1.7]) julia> map(round, [1.2,3.5,1.7]) 3-element Array{Float64,1}: 1.0 4.0 2.0 map(n_fun, [1.2,3.5,1.7]) julia> map(n_fun, [1.2,3.5,1.7]) 3-element Array{Float64,1}: 2.84 18.25 5.29 6.2 列表表达式 [i for i in 1:10] julia> [i for i in 1:10] 10-element Array{Int64,1}: 1 2 3 4 5 6 7 8 9 10 [(i^2,sqrt(i)) for i = 30:-2:10] julia> [(i^2,sqrt(i)) for i = 30:-2:10] 11-element Array{Tuple{Int64,Float64},1}: (900, 5.47723) (784, 5.2915) (676, 5.09902) (576, 4.89898) (484, 4.69042) (400, 4.47214) (324, 4.24264) (256, 4.0) (196, 3.74166) (144, 3.4641) (100, 3.16228) 7 控制流与逻辑判断 7.1 for 循环 function my_fun(h) i = [] for c in 1:h if c%3 == 0 push!(i,c) end end return i end julia> dd = my_fun(100) 33-element Array{Any,1}: 3 6 9 12 15 18 21 24 27 30 ⋮ 72 75 78 81 84 87 90 93 96 99 7.2 while 循环 function my_fun(h) i = [] range = 1 while range <= 100 if range%3 == 0 push!(i,range) end range += 1 end return i end julia> my_fun(100) 33-element Array{Any,1}: 3 6 9 12 15 18 21 24 27 30 33 36 39 ⋮ 66 69 72 75 78 81 84 87 90 93 96 99 以上便是初次体验julia学会的一些简单知识点,以后有时间还会继续学习julia~ 原文发布时间为:2018-08-26 本文作者:杜雨 本文来自云栖社区合作伙伴“数据小魔方”,了解相关信息可以关注“数据小魔方”。

优秀的个人博客,低调大师

Android Studio 初体验

Google在I/O2013大会上发布了Android新的开发工具Android Studio,趁周末时间做了一下尝试。有需要的可以 在http://developer.android.com/sdk/installing/studio.html下载,当前版本是V0.1。官方解释:Android Studio is a new Android development environment based on IntelliJ IDEA. Similar to Eclipse with the ADT Plugin。 Android Studio 和Eclipse+ADT还是很相似的,所以eclipse转移过去是没有难度的。 Android Studio是基于IntelliJ IDEA,除了IntelliJ功能,Android Studio还提供: 基于Gradle的构建支持; Android特定重构和快速修复; 提示工具更好地对程序性能、可用性、版本兼容和其他问题进行控制捕捉; 支持ProGuard和应用签名功能; 自带布局编辑器,可以让你拖放UI组件,并在多个屏幕配置上预览布局,等等。 Android Studio提供了Windows、Mac OS X、Linux版本,用户根据自己的系统选择下载。 下载完安装请参考:http://developer.android.com/sdk/installing/studio.html#Installing 安装完成,打开Android Studio,如图: 第一次使用,New Project时可能会报: Your Android SDK is out of date or is missing templates. Please ensure you are using SDK version 22 or later. 这需要你使用新的SDK,以前在Eclipse使用习惯了android SDK、ADT等,现在只需进入到eclipse更新SDK即可。或者找到SDK目录下的SDK Manager.exe升级。 升级完成,通过Android Studio的如下路径设置SDK,Configure--> Project Defaults --> Project Structure,如下图 设置完成就可以新建项目了.新建项目: 设置图标: 选择不同的Activity,上面的步骤和Eclipse一样。 新建的工程结构如下,这样就简单的创建了工程。 参考: http://developer.android.com/sdk/installing/studio.html; /** * @author 张兴业 * http://blog.csdn.net/xyz_lmn * 我的新浪微博: @张兴业TBOW * android开发进阶群: 241395671 */ 本文转自xyz_lmn51CTO博客,原文链接:http://blog.51cto.com/xyzlmn/1230742 ,如需转载请自行联系原作者

优秀的个人博客,低调大师

Excel for IPAD 初体验

3月28日微软正式发布了Office for IPAD套件,强大的办公软件三剑客Word、Excel、PowerPoint和更新后的OneNote。原本IPAD比较倾向于娱乐方面,如今有了Office的这三个产品,使得在IPAD上移动办公变得更加容易,毕竟对文档、表格、PPT的兼容性是其他办公类app无法比拟的。我们可以通过App Store搜索下载它们,刚发布这几天你可以通过精品推荐找到它们。三个产品容量每个都有400M,让我16G的IPAD捉襟见肘。 本篇博文为大家预览一下Excel for IPAD的外观界面。 第一次进入Excel,就会发现此次微软将主推Office 365产品。对于Office大家肯定不陌生,但Office 365似乎很多人还不认识,或者接受不了。我们可以通过Office365订阅帐户登陆Excel。 通过微软帐户登陆,并可直接连接到OneDrive,也就是之前的SkyDrive云存储,文件可以保存在微软服务器中,对于我的16G的IPAD再好不过。 登陆成功后,发现还是需要购买订阅才可以,否则只能选择第二个“免费查看”,无法编辑表格。 关于价格,令人遗憾。怎么说呢,不知道微软是如何调查国内市场得出此价格的,还是根本不调查直接照搬国外的付费模式。其实我们国内的消费习惯还应该不适应,有些付费游戏做得不错。在国外付费了才能玩,在国内免费玩,但如需提升游戏可玩性就需要买道具。这就是消费习惯,期待未来免费的内容可以增加部分编辑功能。 进入Office直接连接OneDrive最近操作过的文档。 左边新建按键,可以看到Excel 2013风格的各类报表范例。 打开任意文件,选项卡下提示为“只读”文件。 Excel for IPAD选项卡功能区的功能还是非常多的,单元格样式、筛选、排序、冻结窗格,还有各类公式和Excel 2013新功能推荐图表,也可以在各个选项卡中找到。相比桌面版本,少了部分平时使用较少的功能。毕竟输入对于移动平板来说还是个软肋,复杂的操作没有鼠标键盘的帮助,还是有些困难。我认为移动办公主要还是用以展示和一般的数据数据修改,如需海量的操作PC端暂时无法被取代。 右上角是共享按钮,可以发电子邮件及OneDrive云存储链接地址。 右下角与PC端Excel类似,可以选择默认显示在底部状态栏的计算方式。 点击右上方的放大镜可以进入查找替换状态,功能如桌面版强大。 左上角保存,可以将表格保存在本地或者云端OneDrive中。 在编辑栏输入数据与公式时将会弹出公式键盘,方便对复杂公式的输入。针对平板的公式输入也有了很多提升,诸如单元格的选择也更加方便准确。之后的博文会逐一聊到。 Office for IPAD非常强大,终于可以让喊了好久的移动办公脚踏实地,与PC端无差异操作令人兴奋。当然除了价格。 本文转自kirin51CTO博客,原文链接:http://blog.51cto.com/kirin/1386782,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android之初体验

没进实验室之前,听说过Android,但是不知道它是干什么的,进实验室之后,开始接触Android,然后做了一个HelloWorld程序,觉得原来Android是这样的,也不是很难嘛。但是是在后来的学习过程中,越来越发现知识是无穷的,自己学到的真的是九牛一毛都不到! 我遇到第一个令我头大的问题是在学习Activity的时候。刚开始学习这些前台进程,可见进程,服务进程,后台进程和空进程的时候,我有点疑惑,于是我就联系打开多个网页的情形来理解了这些进程。可是在后来学习Activity生命周期的时候,我把这两个内容糅合在了一起,我彻底凌乱了,,,,,,对于书上说的onCreate,onStart,onStop,onPause等等方法,我在想如果我把一个页面最小化了,这时我该调用哪些方法呢?如果我把页面还原了又要调用哪些方法呢?各种调用,各种乱!!!遇到这个问题的时候,我一方面看参考书,另一方面我也上网查资料,逛论坛,发帖子,但是仍然没有找到好的答案。于是我就不停地在想这个问题,这个Activity生命周期方法的调用一直困扰我了两天,到第三天的时候,学长问我这几天的学习有没有遇到困难,我就把这个困惑给他说了一下,结果他一句话就把我点醒了。他说:“Android页面和web网页不一样,它没有最小化,还原等功能!”我突然恍然大悟,,,原来我把两者弄混了,由此让我懂得在遇到困难的时候,先要学会自己思考,但是借助外力也是必须的!比如上网查找资料,让别人求助等。或许苦苦困惑你的问题别人也遇到过,这样自己也可以节省些时间,少走些弯路!聪明的人从别人的错误中吸取经验! 经过这段时间的学习,让我深有感触的是:切勿眼高手低!!!有时看资料书上的代码觉得看懂了,在看视频的时候也觉得当时听懂了,可是自己不去动手实践的话就只能是左耳朵进右耳朵出!当我写scheduler项目的时候,我想了好久,最后把大致要实现的思路,方法都确立了,就觉得这个不是也很随意的嘛!思路有了,用不了多长时间就可以完成了!于是就开始松懈了,但是当真正敲代码的时候才知道每一个方法,每一个细节不注意都回出错,后来再下一次开会的时候有同学都已经写出来了,而我的还没有完成,,,以后在写程序的时候,一定不会再眼高手低了。而且在思考的时候一定把要实现的项目的逻辑搞清楚,把每一个页面的跳转,传参都要考虑好,精确到每个方法中,这样在动手写程序的时候就思路清晰,不会被这些跳转,传参搞得头晕! 每一次犯错都是在为成功铺路,每一次反思都是在不断地进步,在以后的学习过程中要时时提醒自己不要犯同样的错误! 本文转自HDDevTeam 51CTO博客,原文链接:http://blog.51cto.com/hddev/1218179,如需转载请自行联系原作者

优秀的个人博客,低调大师

Swift初体验(三)

var sex: String? var identify: PersonIdentify? // 构造器中,第一个參数依旧会创建一个和局部參数名称同样的外部參数名称 init(name: String, sex: String, identify: PersonIdentify){ self.name = name; self.sex = sex; self.identify = identify } func description() -> String{ // 调用枚举值的toRaw()方法能够获取到枚举值的初值 return "name = \(name) sex = \(sex) course = \(identify!.toRaw())" } // 在类中假设实现协议中定义的mutating方法,那么不须要加"mutating"keyword func aMutatingMethod(){ } class func aTypeMethod(){ } } var person1 = Person(name:"humingtao",sex:"m",identify:.Student) var person2 = Person(name:"kebi",sex:"m",identify:.Teacher) switch(person1.identify!){ case .Teacher: println("I am a teacher") case .Student: println("student") } class Student:Person{ var number: String? // 在类中,类型属性仅仅能够是计算属性,可是在枚举和结构体中,既能够是计算属性,也能够是存值属性 // 在类中,声明一个类型属性的keyword是class,在结构体和枚举中是static class var attendClassTime: String{ return "9:30" } init(name: String, sex: String, number: String){ super.init(name: name, sex: sex, identify: .Student) self.number = number } } var student1 = Student(name: "hmt",sex: "m",number: "12345678") class Teacher:Person{ var course: String? init(name: String, sex: String, course: String){ super.init(name: name, sex: sex, identify: .Teacher) self.course = course } override func description() -> String{ return super.description() + "" + "course = \(course)" } } // 思考为什么加"?" class course{ var name: String? init(name: String){ self.name = name } } 本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5209449.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Chromium OS 初体验

Chromium OS可是早有耳闻,但是一直没有尝试,最近很多评论甚至认为会对Windows和Mac都能够造成压力,于是迫不及待的想尝试一下了,百度下了官网,官网很贴心,不光给了用于写入U盘的镜像文件(最初是针对上网本的,所以自然不是刻录成光盘)还给了Vmware和Vitual Box的文件,因此只需要下载虚拟机文件就能够直接在虚拟机运行了。我下载了VMware的虚拟机文件,因此运行就比较简单了,直接双击就可以打开虚拟机,由于我上网需要认证,因此我将网络改为NAT的方式这样就可以直接共享主机的网络了。 运行后发现网络仍然不可选,自己查找了下资料,解决方法也很简单,在VMX文件中添加一句话就OK。下图的最后一句:ethernet0.virtualDev = "e1000" 关闭虚拟机重新开启即可运行了,简单设置就进入系统了。 简单体验了下,果然够简洁,鼠标有点迟缓的不习惯,习惯了雅黑的字体再看里面的字体也很别扭,设置啥的都是基于浏览器,尝尝鲜还可以,真正用一时半会估计很难习惯。 虎嗅网:对2014年:Chrome OS会成PC产业Android吗?一文的评论 废话不多说,自己去体验一把再来下结论,Chrome OS官网上就有Vmware和VirtualBox的文件,下载下来自己用虚拟机先体验一把在来看看这些结论。 我认为不会的原因有这几点: 1、Android 1.5产品出来的时候,在当时相对于普通手机来说哪些操作可以说是非常惊艳,界面华丽,操作流畅,更重要的是除了iPhone没有其他类似操作的机型,和同时期的诺基亚S60系统比一下就知道它的优势。 2、相比之下看下Chrome OS,一是概念有点超前,所有操作都在浏览器中完成,而且重度依赖网络,Win8的界面尚带来如此大的反弹,这样的一个操作和变化你觉得用户能够很快接受并适应么? 3、从产品体验本身来看,Android 1.5/1.6相对当时的塞班S40/S60,Chrome OS相对Windows,在竞争形势上可能有点近似,但是从产品本身的颠覆性来说远远不够。 4、Chrome OS不是一两年了,2008年就开始了,只是Chrome OS和上网本来说更加适合一些,上网本本身性能比较弱,多数用来只是访问网络和简单娱乐,定位上比较接近,而现在超极本的定位和上网本有本质区别,加上平板电脑的流行更没有Chrome OS的空间了。 5、相对Chrome OS我更推崇摩托罗拉和华硕类似的将手机与笔记本结合的产品,基于安卓平台实现手机到桌面的扩展,这个我觉得来的更为实际。 本文转自emouse博客园博客,原文链接:http://www.cnblogs.com/emouse/p/3493176.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

RocketMQ 事务消息初体验

事务消息是 RocketMQ 的高级特性之一 。这篇文章,笔者会从应用场景、功能原理、实战例子三个模块慢慢为你揭开事务消息的神秘面纱。 1 应用场景 举一个电商场景的例子:用户购物车结算时,系统会创建支付订单。 用户支付成功后支付订单的状态会由未支付修改为支付成功,然后系统给用户增加积分。 通常我们会使用普通消费方案,该方案能够发挥 MQ 的优势:异步和解耦 , 同时架构设计非常简单。 用户购物车结算时,系统创建支付订单; 支付成功后,更新订单的状态从未支付修改为支付成功; 发送一条普通消息到消息队列服务端; 积分服务消费消息,添加积分记录。 但该方案有个非常直观的缺点:容易出现不一致的现象。 假如先发送消息,后修改订单状态,消息发送成功,订单没有执行成功,需要回滚整个事务(订单数据事务回滚,积分服务消费时,需要先反查事务状态,若事务提交,才插入积分记录)。 假如先修改订单状态,后发送消息,订单状态修改成功,但消息发送失败,需要补偿操作才能保持最终一致。 假如先修改订单,后发送消息,订单状态修改成功,但消息发送超时,此时无法判断需要回滚订单还是提交订单变更。 我们看到,为了完善普通消费方案,业务层还需要做到两点:补偿机制和提供事务状态查询接口。 要做到这两点,难不难呢? 不难,但是业务层代码会比较混乱,更优的方案还是得从中间件层面解决。 2 功能原理 RocketMQ 事务消息是支持在分布式场景下保障消息生产和本地事务的最终一致性。交互流程如下图所示: 1、生产者将消息发送至 Broker 。 2、Broker 将消息持久化成功之后,向生产者返回 Ack 确认消息已经发送成功,此时消息被标记为"暂不能投递",这种状态下的消息即为半事务消息。 3、生产者开始执行本地事务逻辑。 4、生产者根据本地事务执行结果向服务端提交二次确认结果( Commit 或是 Rollback ),Broker 收到确认结果后处理逻辑如下: 二次确认结果为 Commit :Broker 将半事务消息标记为可投递,并投递给消费者。 二次确认结果为 Rollback :Broker 将回滚事务,不会将半事务消息投递给消费者。 5、在断网或者是生产者应用重启的特殊情况下,若 Broker 未收到发送者提交的二次确认结果,或 Broker 收到的二次确认结果为 Unknown 未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。 生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。 生产者根据检查到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行处理。 笔者认为事务消息的精髓在于: 本地事务执行成功,消费者才能消费事务消息; 消息回查本身就是补偿机制的实现,事务生产者需提供了事务状态查询接口。 3 实战例子 为了便于大家理解事务消息 ,笔者新建一个工程用于模拟支付订单创建、支付成功、赠送积分的流程。 首先,我们创建一个真实的订单主题:order-topic 。 然后在数据库中创建三张表 订单表、事务日志表、积分表。 最后我们创建一个 Demo 工程,生产者模块用于创建支付订单、修改支付订单成功,消费者模块用于新增积分记录。 接下来,我们展示事务消息的实现流程。 <strong style="font-size: 15px;line-height: inherit;color: black;">1、创建支付订单</strong> 调用订单生产者服务创建订单接口 ,在 t_order 表中插入一条支付订单记录。 <strong style="font-size: 15px;line-height: inherit;color: black;">2、调用生产者服务修改订单状态接口</strong> 接口的逻辑就是执行事务生产者的 sendMessageInTransaction 方法。 生产者端需要配置事务生产者和事务监听器。 发送事务消息的方法内部包含三个步骤 : 事务生产者首先发送半事务消息,发送成功后,生产者才开始执行本地事务逻辑。 事务监听器实现了两个功能:执行本地事务和供 Broker 回查事务状态 。 执行本地事务的逻辑内部就是执行 orderService.updateOrder 方法。 方法执行成功则返回 LocalTransactionState.COMMIT_MESSAGE , 若执行失败则返回 LocalTransactionState.ROLLBACK_MESSAGE 。 需要注意的是: orderService.updateOrder 方法添加了事务注解,并将修改订单状态和插入事务日志表放进一个事务内,避免订单状态和事务日志表的数据不一致。 最后,生产者根据本地事务执行结果向 Broker 提交二次确认结果。 Broker 收到生产者确认结果后处理逻辑如下: 二次确认结果为 Commit :Broker 将半事务消息标记为可投递,并投递给消费者。 二次确认结果为 Rollback :Broker 将回滚事务,不会将半事务消息投递给消费者。 <strong style="font-size: 15px;line-height: inherit;color: black;">3、积分消费者消费消息,添加积分记录</strong > 当 Broker 将半事务消息标记为可投递时,积分消费者就可以开始消费主题 order-topic 的消息了。 积分消费者服务,我们定义了消费者组名,以及订阅主题和消费监听器。 在消费监听器逻辑里,幂等非常重要 。当收到订单信息后,首先判断该订单是否有积分记录,若没有记录,才插入积分记录。 而且我们在创建积分表时,订单编号也是唯一键,数据库中也必然不会存在相同订单的多条积分记录。 4 总结 RocketMQ 事务消息是支持在分布式场景下保障消息生产和本地事务的最终一致性。 编写一个实战例子并不复杂,但使用事务消息时需要注意如下三点: 1、事务生产者和消费者共同协作才能保证业务数据的最终一致性; 2、事务生产者需要实现事务监听器,并且保存事务的执行结果(比如事务日志表) ; 3、消费者要保证幂等。消费失败时,通过重试、告警+人工介入等手段保证消费结果正确。 本文涉及到的工程源码,笔者已上传到 Github ,感兴趣的同学可以了解一下,若有疑问直接加笔者好友,一起交流技术,一起成长。 笔者会在后续的文章里,详细解析事务消息的实现原理,敬请期待。 实战代码地址: https://github.com/makemyownlife/rocketmq4-learning 如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

优秀的个人博客,低调大师

docker微服务初体验

1. 什么是微服务 在介绍微服务时,首先得先理解什么是微服务, 顾名思义,微服务得从两个方面去理解,什么是"微"、什么是"服务", 微 狭义来讲就是体积小、单个服务的设计。 而所谓服务,一定要区别于系统, 服务一个或者一组相对较小且独立的功能单元,是用户可以感知最小功能集。 微服务,关键其实不仅仅是微服务本身,而是系统要提供一套基础的架构,这种架构使得微服务可以独立的部署、运行、升级, 不仅如此,这个系统架构还让微服务与微服务之间在结构上“松耦合”, 而在功能上则表现为一个统一的整体。这种所谓的“统一的整体”表现出来的是统一风格的界面,统一的权限管理, 统一的安全策略,统一的上线过程,统一的日志和审计方法,统一的调度方式,统一的访问入口等等。 2. 微服务由来 微服务最早由Martin Fowler与James Lewis于2014年共同提出,微服务架构风格是一种使用一套小服务来开发单个应用的方式途径,每个服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API,这些服务基于业务能力构建,并能够通过自动化部署机制来独立部署,这些服务使用不同的编程语言实现,以及不同数据存储技术,并保持最低限度的集中式管理。 3. 微服务的优势 IT架构一直从all in one到近两年热门的微服务架构,技术不断进步,微服务架构模式(Microservice Architect Pattern)开始被越来越多的企业所接受,那么究竟什么是微服务架构?微服务架构模式有什么优点呢? 从整个IT技术发展趋势来看,我们可以看到无论是硬件、还是软件、还是基础架构都在朝着轻量化的方向发展。云计算的发展更让资源的调控灵活性和部署速度都有所提高,微服务就是一项在云中部署应用和服务的技术。采用化整为零的概念,将复杂的IT部署,通过功能化、原子化分解,形成一种松散耦合的组件,让其更容易升级和扩展。 ThoughtWorks的首席科学家,马丁·福勒先生对微服务做出了这样的定义:“微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP协议的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外,应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。” 微服务架构是一项在云中部署应用和服务的技术 总的来说,可以将微服务架构的优势归结为以下几点: 1、复杂度可控 在all in one的状态下,容易造成盲人摸象的状态,造成不必要的数据孤岛。而微服务架构通过分解单体式应用为多个服务方法,让复杂性可控。为了实现同一功能,应用被分解为多个可管理的分支或服务,通过微服务架构模式,让复杂的功能,通过模块化的方式呈现出来,让单个服务更容易开发和维护。 避免“盲人摸象” 2、灵活可扩展 灵活性是基于微服务架构模式使得每个服务独立扩展。微服务架构下,技术选型是去中心化的。在这种模式下,每个团队都可以根据自身服务的需求和行业发展状况做出自己的判断,选择适合的技术栈。 3、独立部署 由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。这样,当某个微服务发生变更时无需编译、部署整个应用,让发布更高效,右下缩短应用交付周期。UI团队可以采用AB测试,快速的部署变化。微服务架构模式使得持续化部署成为可能。 4、开发针对性更强 众所周知,在单块架构系统下,新人的培养周期很长,需要花费大量时间了解本地开发环境。而微服务架构模式使得每个服务独立扩展,开发运维人员也不需要在花费一个月的时间去熟悉本地环境,而只需要了解自己所处的模块状态即可。 5、降低TCO 在传统IT架构中,即单块架构系统中,是以技术分层,譬如逻辑层、数据层等。但随着市场需求的不断变化,用户需求住家个性化,开发周期需要越来越短,产品的生命周期也开始变短,单块架构系统开始面临挑战。无论是开发还是维护成本太高。 相较而言,微服务架构模式下,当某一组件发生故障时,不会发现单块架构系统的进程内扩散等弊端,故障会被隔离在单个服务中。 Docker微服务 Docker 是一个容器工具,提供虚拟环境。docker改变了我们对软件的认识。 站在 Docker 的角度,软件就是容器的组合:业务逻辑容器、数据库容器、储存容器、队列容器......Docker 使得软件可以拆分成若干个标准化容器,然后像搭积木一样组合起来。 这正是微服务(microservices)的思想:软件把任务外包出去,让各种外部服务完成这些任务,软件本身只是底层服务的调度中心和组装层。 微服务很适合用 Docker 容器实现,每个容器承载一个服务。一台计算机同时运行多个容器,从而就能很轻松地模拟出复杂的微服务架构。 配置文件 - Dockerfile DockerFile分为四部分组成:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。例如: #第一行必须指令基于的基础镜像 From ubutu #维护者信息 MAINTAINER docker_user docker_user@mail.com #镜像的操作指令 RUN apt-get update && apt-get install -y ngnix RUN echo "\ndaemon off;">>/etc/ngnix/nignix.conf #容器启动时执行指令 CMD /usr/sbin/ngnix 介绍一下一些常用的命令: 1、From指令 From 或者From : DockerFile第一条必须为From指令。如果同一个DockerFile创建多个镜像时,可使用多个From指令(每个镜像一次) 2、MAINTAINER 格式为maintainer ,指定维护者的信息 3、RUN 格式为Run 或者Run [“executable” ,”Param1”, “param2”] 前者在shell终端上运行,即/bin/sh -C,后者使用exec运行。例如:RUN [“/bin/bash”, “-c”,”echo hello”] 每条run指令在当前基础镜像执行,并且提交新镜像。当命令比较长时,可以使用“/”换行。 4、CMD指令 支持三种格式: CMD [“executable” ,”Param1”, “param2”]使用exec执行,推荐 CMD command param1 param2,在/bin/sh上执行 CMD [“Param1”, “param2”] 提供给ENTRYPOINT做默认参数。 每个容器只能执行一条CMD命令,多个CMD命令时,只最后一条被执行。 5、EXPOSE 格式为 EXPOSE […] 。 告诉Docker服务端容器暴露的端口号,供互联系统使用。在启动Docker时,可以通过-P,主机会自动分配一个端口号转发到指定的端口。使用-P,则可以具体指定哪个本地端口映射过来 例如: EXPOSE 22 80 8443 6、ENV 格式为 ENV 。 指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。 ENV PG_MAJOR 9.3 ENV PG_VERSION 9.3.4 RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && … ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH1234 7、ADD 格式为 ADD 。 该命令将复制指定的 到容器中的 。 其中 可以是Dockerfile所在目录的一个相对路径;也可以是一个URL;还可以是一个tar文件(自动解压为目录)。则。 8、COPY 格式为 COPY 。 复制本地主机的 (为Dockerfile所在目录的相对路径)到容器中的 。 当使用本地目录为源目录时,推荐使用 COPY 。 9、ENTRYPOINT 两种格式: ENTRYPOINT [“executable”, “param1”, “param2”] ENTRYPOINT command param1 param2 (shell中执行)。 配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。 每个Dockerfile中只能有一个 ENTRYPOINT ,当指定多个时,只有最后一个起效。 10、VOLUME 格式为 VOLUME [“/data”] 。 创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。 11、USER 格式为 USER daemon 。 指定运行容器时的用户名或UID,后续的 RUN 也会使用指定用户。 当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如: RUN groupadd -r postgres && useradd -r -g postgres postgres 。要临时获取管理员权限可以使用 gosu ,而不推荐 sudo 。 12、WORKDIR 格式为 WORKDIR /path/to/workdir 。 为后续的 RUN 、 CMD 、 ENTRYPOINT 指令配置工作目录。 可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如 WORKDIR /a WORKDIR b WORKDIR c RUN pwd 则最终路径为 /a/b/c 。 13、ONBUILD 格式为 ONBUILD [INSTRUCTION] 。 配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。 例如,Dockerfile使用如下的内容创建了镜像 image-A 。 […] ONBUILD ADD . /app/src ONBUILD RUN /usr/local/bin/python-build –dir /app/src […] 如果基于A创建新的镜像时,新的Dockerfile中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。 FROM image-A #Automatically run the following ADD . /app/src RUN /usr/local/bin/python-build --dir /app/src 使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild 。 1234567 compose 编排 编排(orchestration),指自动配置、协作和管理服务的过程,在 Docker 中,编排用来描述一组实践过程,这个过程会管理运行在多个 Docker 里的应用,这些 Docker 容器也可能运行在不同的宿主机上。 docker-compose Docker 编排工具 Docker Compose ,由 Python 编写。使用 Docker Compose ,可以用一个 YAML 文件定义一组要启动的容器,以及容器运行时的属性。Docker Compose 称这些容器为“服务”: 容器通过某些方法并制定一些运行时的属性来和其他容器产生交互。 默认的模板文件是 docker-compose.yml,其中定义的每个服务都必须通过 image 指令指定镜像或 build 指令(需要 Dockerfile)来自动构建。 其它大部分指令都跟 docker run 中的类似。 如果使用 build 指令,在 Dockerfile 中设置的选项(例如:CMD, EXPOSE, VOLUME, ENV 等) 将会自动被获取,无需在 docker-compose.yml 中再次设置。 image 指定为镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉去这个镜像。 docker-compose.yml配置文件 先看例子: version: '2' services: web: image: dockercloud/hello-world ports: - 8080 networks: - front-tier - back-tier redis: image: redis links: - web networks: - back-tier lb: image: dockercloud/haproxy ports: - 80:80 links: - web networks: - front-tier - back-tier volumes: - /var/run/docker.sock:/var/run/docker.sock networks: front-tier: driver: bridge back-tier: driver: bridge versionn, services、networks 三大部分,其中最关键的就是 services 和 networks 两个部分,下面先来看 services 的书写规则。 1. image services: web: image: hello-world 在 services 标签下的第二级标签是 web,这个名字是用户自己自定义,它就是服务名称。 image 则是指定服务的镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉取这个镜像。 例如下面这些格式都是可以的: image: redis image: ubuntu:14.04 image: tutum/influxdb image: example-registry.com:4000/postgresql image: a4bc65fd 2. build 服务除了可以基于指定的镜像,还可以基于一份 Dockerfile,在使用 up 启动之时执行构建任务,这个构建标签就是 build,它可以指定 Dockerfile 所在文件夹的路径。Compose 将会利用它自动构建这个镜像,然后使用这个镜像启动服务容器。 build: /path/to/build/dir 也可以是相对路径,只要上下文确定就可以读取到 Dockerfile。 build: ./dir 设定上下文根目录,然后以该目录为准指定 Dockerfile。 build: context: ../ dockerfile: path/of/Dockerfile 注意 build 都是一个目录,如果你要指定 Dockerfile 文件需要在 build 标签的子级标签中使用 dockerfile 标签指定,如上面的例子。 如果你同时指定了 image 和 build 两个标签,那么 Compose 会构建镜像并且把镜像命名为 image 后面的那个名字。 build: ./dir image: webapp:tag 既然可以在 docker-compose.yml 中定义构建任务,那么一定少不了 arg 这个标签,就像 Dockerfile 中的 ARG 指令,它可以在构建过程中指定环境变量,但是在构建成功后取消,在 docker-compose.yml 文件中也支持这样的写法: build: context: . args: buildno: 1 password: secret 下面这种写法也是支持的,一般来说下面的写法更适合阅读。 build: context: . args: - buildno=1 - password=secret 与 ENV 不同的是,ARG 是允许空值的。例如: args: - buildno - password 这样构建过程可以向它们赋值。 注意:YAML 的布尔值(true, false, yes, no, on, off)必须要使用引号引起来(单引号、双引号均可),否则会当成字符串解析。 3. command 使用 command 可以覆盖容器启动后默认执行的命令。 command: bundle exec thin -p 3000 也可以写成类似 Dockerfile 中的格式: command: [bundle, exec, thin, -p, 3000] 4.container_name 前面说过 Compose 的容器名称格式是:<项目名称><服务名称><序号> 虽然可以自定义项目名称、服务名称,但是如果你想完全控制容器的命名,可以使用这个标签指定: container_name: app 这样容器的名字就指定为 app 了。 5.depends_on 在使用 Compose 时,最大的好处就是少打启动命令,但是一般项目容器启动的顺序是有要求的,如果直接从上到下启动容器,必然会因为容器依赖问题而启动失败。 例如在没启动数据库容器的时候启动了应用容器,这时候应用容器会因为找不到数据库而退出,为了避免这种情况我们需要加入一个标签,就是 depends_on,这个标签解决了容器的依赖、启动先后的问题。 例如下面容器会先启动 redis 和 db 两个服务,最后才启动 web 服务: version: '2' services: web: build: . depends_on: - db - redis redis: image: redis db: image: postgres 注意的是,默认情况下使用 docker-compose up web 这样的方式启动 web 服务时,也会启动 redis 和 db 两个服务,因为在配置文件中定义了依赖关系。 6.dns 和 --dns 参数一样用途,格式如下: dns: 8.8.8.8 也可以是一个列表: dns: - 8.8.8.8 - 9.9.9.9 此外 dns_search 的配置也类似: dns_search: example.com dns_search: - dc1.example.com - dc2.example.com 7. tmpfs 挂载临时目录到容器内部,与 run 的参数一样效果: tmpfs: /run tmpfs: - /run - /tmp 8. entrypoint 在 Dockerfile 中有一个指令叫做 ENTRYPOINT 指令,用于指定接入点,第四章有对比过与 CMD 的区别。 在 docker-compose.yml 中可以定义接入点,覆盖 Dockerfile 中的定义: entrypoint: /code/entrypoint.sh 格式和 Docker 类似,不过还可以写成这样: entrypoint: - php - -d - zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so - -d - memory_limit=-1 - vendor/bin/phpunit 9.env_file 还记得前面提到的 .env 文件吧,这个文件可以设置 Compose 的变量。而在 docker-compose.yml 中可以定义一个专门存放变量的文件。 如果通过 docker-compose -f FILE 指定了配置文件,则 env_file 中路径会使用配置文件路径。 如果有变量名称与 environment 指令冲突,则以后者为准。格式如下: env_file: .env 或者根据 docker-compose.yml 设置多个: env_file: - ./common.env - ./apps/web.env - /opt/secrets.env 注意的是这里所说的环境变量是对宿主机的 Compose 而言的,如果在配置文件中有 build 操作,这些变量并不会进入构建过程中,如果要在构建中使用变量还是首选前面刚讲的 arg 标签。 10. environment 与上面的 env_file 标签完全不同,反而和 arg 有几分类似,这个标签的作用是设置镜像变量,它可以保存变量到镜像里面,也就是说启动的容器也会包含这些变量设置,这是与 arg 最大的不同。 一般 arg 标签的变量仅用在构建过程中。而 environment 和 Dockerfile 中的 ENV 指令一样会把变量一直保存在镜像、容器中,类似 docker run -e 的效果。 environment: RACK_ENV: development SHOW: 'true' SESSION_SECRET: environment: - RACK_ENV=development - SHOW=true - SESSION_SECRET 11. expose 这个标签与Dockerfile中的EXPOSE指令一样,用于指定暴露的端口,但是只是作为一种参考,实际上docker-compose.yml的端口映射还得ports这样的标签。 expose: - "3000" - "8000" 12. external_links 在使用Docker过程中,我们会有许多单独使用docker run启动的容器,为了使Compose能够连接这些不在docker-compose.yml中定义的容器,我们需要一个特殊的标签,就是external_links,它可以让Compose项目里面的容器连接到那些项目配置外部的容器(前提是外部容器中必须至少有一个容器是连接到与项目内的服务的同一个网络里面)。 格式如下: external_links: - redis_1 - project_db_1:mysql - project_db_1:postgresql 13. extra_hosts 添加主机名的标签,就是往/etc/hosts文件中添加一些记录,与Docker client的--add-host类似: extra_hosts: - "somehost:162.242.195.82" - "otherhost:50.31.209.229" 启动之后查看容器内部hosts: 162.242.195.82 somehost 50.31.209.229 otherhost 14. labels 向容器添加元数据,和Dockerfile的LABEL指令一个意思,格式如下: labels: com.example.description: "Accounting webapp" com.example.department: "Finance" com.example.label-with-empty-value: "" labels: - "com.example.description=Accounting webapp" - "com.example.department=Finance" - "com.example.label-with-empty-value" 15. links 还记得上面的depends_on吧,那个标签解决的是启动顺序问题,这个标签解决的是容器连接问题,与Docker client的--link一样效果,会连接到其它服务中的容器。 格式如下: links: - db - db:database - redis 使用的别名将会自动在服务容器中的/etc/hosts里创建。例如: 172.12.2.186 db 172.12.2.186 database 172.12.2.187 redis 相应的环境变量也将被创建。 16. logging 这个标签用于配置日志服务。格式如下: logging: driver: syslog options: syslog-address: "tcp://192.168.0.42:123" 默认的driver是json-file。只有json-file和journald可以通过docker-compose logs显示日志,其他方式有其他日志查看方式,但目前Compose不支持。对于可选值可以使用options指定。 有关更多这方面的信息可以阅读官方文档:https://docs.docker.com/engine/admin/logging/overview/ 17. pid pid: "host" 将PID模式设置为主机PID模式,跟主机系统共享进程命名空间。容器使用这个标签将能够访问和操纵其他容器和宿主机的名称空间。 18. ports 映射端口的标签。 使用HOST:CONTAINER格式或者只是指定容器的端口,宿主机会随机映射端口。 ports: - "3000" - "8000:8000" - "49100:22" - "127.0.0.1:8001:8001" 注意:当使用HOST:CONTAINER格式来映射端口时,如果你使用的容器端口小于60你可能会得到错误得结果,因为YAML将会解析xx:yy这种数字格式为60进制。所以建议采用字符串格式。 19. security_opt 为每个容器覆盖默认的标签。简单说来就是管理全部服务的标签。比如设置全部服务的user标签值为USER。 security_opt: - label:user:USER - label:role:ROLE 20. stop_signal 设置另一个信号来停止容器。在默认情况下使用的是SIGTERM停止容器。设置另一个信号可以使用stop_signal标签。 stop_signal: SIGUSR1 21. volumes 挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER] 这样的格式,或者使用 [HOST:CONTAINER:ro] 这样的格式,后者对于容器来说,数据卷是只读的,这样可以有效保护宿主机的文件系统。 Compose的数据卷指定路径可以是相对路径,使用 . 或者 .. 来指定相对目录。 数据卷的格式可以是下面多种形式: volumes: // 只是指定一个路径,Docker 会自动在创建一个数据卷(这个路径是容器内部的)。 - /var/lib/mysql // 使用绝对路径挂载数据卷 - /opt/data:/var/lib/mysql // 以 Compose 配置文件为中心的相对路径作为数据卷挂载到容器。 - ./cache:/tmp/cache // 使用用户的相对路径(~/ 表示的目录是 /home/<用户目录>/ 或者 /root/)。 - ~/configs:/etc/configs/:ro // 已经存在的命名的数据卷。 - datavolume:/var/lib/mysql 如果你不使用宿主机的路径,你可以指定一个volume_driver。 volume_driver: mydriver 22. volumes_from 从其它容器或者服务挂载数据卷,可选的参数是 :ro或者 :rw,前者表示容器只读,后者表示容器对数据卷是可读可写的。默认情况下是可读可写的。 volumes_from: - service_name - service_name:ro - container:container_name - container:container_name:rw 23. cap_add, cap_drop 添加或删除容器的内核功能。详细信息在前面容器章节有讲解,此处不再赘述。 cap_add: - ALL cap_drop: - NET_ADMIN - SYS_ADMIN 24. cgroup_parent 指定一个容器的父级cgroup。 cgroup_parent: m-executor-abcd 25. devices 设备映射列表。与Docker client的--device参数类似。 devices: - "/dev/ttyUSB0:/dev/ttyUSB0" 26. extends 这个标签可以扩展另一个服务,扩展内容可以是来自在当前文件,也可以是来自其他文件,相同服务的情况下,后来者会有选择地覆盖原有配置。 extends: file: common.yml service: webapp 用户可以在任何地方使用这个标签,只要标签内容包含file和service两个值就可以了。file的值可以是相对或者绝对路径,如果不指定file的值,那么Compose会读取当前YML文件的信息。 更多的操作细节在后面的12.3.4小节有介绍。 27. network_mode 网络模式,与Docker client的--net参数类似,只是相对多了一个service:[service name] 的格式。 例如: network_mode: "bridge" network_mode: "host" network_mode: "none" network_mode: "service:[service name]" network_mode: "container:[container name/id]" 可以指定使用服务或者容器的网络。 28. networks 加入指定网络,格式如下: services: some-service: networks: - some-network - other-network 关于这个标签还有一个特别的子标签aliases,这是一个用来设置服务别名的标签,例如: services: some-service: networks: some-network: aliases: - alias1 - alias3 other-network: aliases: - alias2 相同的服务可以在不同的网络有不同的别名。 29. 其它 还有这些标签:cpu_shares, cpu_quota, cpuset, domainname, hostname, ipc, mac_address, mem_limit, memswap_limit, privileged, read_only, restart, shm_size, stdin_open, tty, user, working_dir 上面这些都是一个单值的标签,类似于使用docker run的效果。 cpu_shares: 73 cpu_quota: 50000 cpuset: 0,1 user: postgresql working_dir: /code domainname: foo.com hostname: foo ipc: host mac_address: 02:42:ac:11:65:43 mem_limit: 1000000000 memswap_limit: 2000000000 privileged: true restart: always read_only: true shm_size: 64M stdin_open: true tty: true docker compose使用 Define and run multi-container applications with Docker. Usage: docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...] docker-compose -h|--help Options: -f, --file FILE Specify an alternate compose file (default: docker-compose.yml) -p, --project-name NAME Specify an alternate project name (default: directory name) --verbose Show more output --log-level LEVEL Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) --no-ansi Do not print ANSI control characters -v, --version Print version and exit -H, --host HOST Daemon socket to connect to --tls Use TLS; implied by --tlsverify --tlscacert CA_PATH Trust certs signed only by this CA --tlscert CLIENT_CERT_PATH Path to TLS certificate file --tlskey TLS_KEY_PATH Path to TLS key file --tlsverify Use TLS and verify the remote --skip-hostname-check Don't check the daemon's hostname against the name specified in the client certificate --project-directory PATH Specify an alternate working directory (default: the path of the Compose file) --compatibility If set, Compose will attempt to convert deploy keys in v3 files to their non-Swarm equivalent Commands: build Build or rebuild services bundle Generate a Docker bundle from the Compose file config Validate and view the Compose file create Create services down Stop and remove containers, networks, images, and volumes events Receive real time events from containers exec Execute a command in a running container help Get help on a command images List images kill Kill containers logs View output from containers pause Pause services port Print the public port for a port binding ps List containers pull Pull service images push Push service images restart Restart services rm Remove stopped containers run Run a one-off command scale Set number of containers for a service start Start services stop Stop services top Display the running processes unpause Unpause services up Create and start containers version Show the Docker-Compose version information docker-compose ps 列出本地 docker-compose.yml 文件定义的正在运行的所有服务,查看服务运行状态 docker-compose logs docker-compose stop

优秀的个人博客,低调大师

使用Docker for Windows初体验

这是第二次使用Docker for Windows了。 最近准备研究一下Docker的一些高级特性如Swarm Clusters,需要用到docker-machine,docker-machine目前仅支持Mac 或 Windows,由于没有Mac所以需要在Windows上运行Docker。官方声称Docker for Windows是一个在Windows系统中创建容器化App的完整开发平台。看完这篇文章,或许你会对Docker和Windows有重新的认识,一改之前对Windows的那些“不好感”。 先放几个截图供查阅: 1.docker 引擎信息 2.容器基本操作 3.容器镜像基本操作: Docker for Windows运行环境要求: 1.当前Docker for Windows版本需要64位Windows 10 Pro、Enterprise或Education(1511 November update, Build 10586 or later)系统,后续版本可能会支持更多Windows 10,Windows Server 2016同样被支持 2.必须启用CPU虚拟化和Hyper-V功能,Hyper-V角色可以在Docker for Windows安装过程中自动安装,可能会重启Windows,一旦安装Docker for Windows,将无法再使用VMware虚拟化产品以及其他虚拟化产品,如无法再使用VMware Workstation和Virtualbox等 Docker for Windows一些基本知识: Docker for Windows运行原理远比现有了解的复杂的多得多,只是简单描述一些已经获得的知识: 1.Docker for Windows的组成部分有多个,不仅包含Windows平台上的一些bin程序供用户使用,也包含了一个基于Hyper-V的虚拟机,虚拟机采用Alpine Linux v3.5操作系统 2.docker volume create指令创建出的数据卷存在在虚拟机中,不易与主机进行交互(Hyper-V虚拟机运行期间无法将磁盘中的数据暴露到主机上),因此数据卷这个功能或许会被-v选项所替代 3.Docker for Windows与PowerShell联用,通过PowerShell来操作docker行为,当然cmd也可以 4.Docker for Windows支持两种容器,Linux container和Windows Container,默认是Linux container,依赖于运行在Hyper-V中的虚拟机。Windows Container并不依赖于虚拟机,但也同样依赖于Hyper-V。两种模式的切换会导致重启Windows,而且显而易见的两种模式下的数据并不共享,它们的配置和数据都是独立存在的。令人意外的是Windows container无法运行依赖Linux环境的容器,如nginx等。 Docker for Windows使用小技巧: 与Linux平台上安装的docker环境基本一样,Docker for Windows同样支持一些共有的特性: 1.配置不安全的registry地址和registry镜像(加速)地址 2.支持数据卷和主机存储路径映射(-v选项),数据卷的支持在Docker for Windows中用起来不方便(参考上文的基本知识),推荐使用-v选项 3.在使用-v选项之前,个人建议在磁盘管理中创建一个vhd虚拟磁盘挂载到主机,比如标记成E盘,然后将这个虚拟磁盘共享给Docker for Windows: 需要注意的是,重启后vhd虚拟磁盘将会不再挂载,需要手动"附加vhd"。 借助Docker for Windows做几件有意思的事儿: 1.重新定义app,将运行在Linux上的app,原生的“放到”Windows中,轻松获得心理上的“原生感” 2.操作容器简单化,不再需要打开VMware等虚拟化产品也不需要再使用端口映射,启动Linux再启动容器这样麻烦,只需要双击运行Docker for Windows,即可使用,外部访问轻松配置 3.开始玩转docker-machine和Swarm Clusters等 开始安装吧!因为一点也不难! 参考链接: 开始使用Docker for Windowshttps://docs.docker.com/docker-for-windows/ 安装Docker for Windowshttps://docs.docker.com/docker-for-windows/install/ tag:Docker for Windows --end-- 本文转自 urey_pp 51CTO博客,原文链接:http://blog.51cto.com/dgd2010/1914864,如需转载请自行联系原作者

优秀的个人博客,低调大师

Objective-C 初体验

因为要接SDK的原因,现在搞搞OC,本人是以控制台程序入手学的。 本片主要知识点: 一:创建控制台项目 二:创建类(h文件与m文件分开) 三:类成员的编写,坑啊 1创建控制台项目: 1,打开XCode , File -》 New -》 Project... 2,在打开的界面中如下操作: 3,选择项目的保存位置。。。 2,新建类(h文件和m文件) 1,File-》New -》File... 2,进入创建界面后如下操作(这样会生成h文件和m文件): 3,选择文件保存的位置。。。。。 代码: Aonaufly.h如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // //Aonaufly.h //Ainy_Console // //CreatedbyAppleon2017/9/7. //Copyright2017年Apple.Allrightsreserved. // #import<Foundation/Foundation.h> @interfaceAonaufly:NSObject @property int _a,_b; -( int )sum_one:( int )csum_b:( int )d; //带参数名的方法 -( int )sum:( int )i:( int )j; //不带参数名的方法 @end Aonaufly.m代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // //Aonaufly.m //Ainy_Console // //CreatedbyAppleon2017/9/7. //Copyright2017年Apple.Allrightsreserved. // #import"Aonaufly.h" @implementationAonaufly @synthesize_a,_b; -( int )sum_one:( int )csum_b:( int )d { return [selfsum:c:d]; //调用本类的方法sum } -( int )sum:( int )i:( int )j { return i+j; } @end 入口main调用如下: 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 // //main.m //Ainy_Console // //CreatedbyAppleon2017/9/6. //Copyright2017年Apple.Allrightsreserved. // #import"Aonaufly.h" int main( int argc, const char *argv[]){ @autoreleasepool{ Aonaufly*myAonaufly; myAonaufly=[[Aonauflyalloc]init]; int sum=[myAonauflysum_one:1sum_b:2]; //调用方法(带参数) NSLog(@ "thisis1+2SUM:%i" ,sum); //为属性_a,_b赋值 myAonaufly._a=3; myAonaufly._b=5; //调用不带参数名的sum方法如下 sum=[myAonauflysum:myAonaufly._a:myAonaufly._b]; NSLog(@ "this%i+%ivalueis:%i" ,myAonaufly._a,myAonaufly._b,sum); } return 0; } 结果: 解析如下: 1,头文件 @property 实际声明的是seter 和 geter , 在m文件中直接用@synthesize直接实现 2,关于方法-》 -(int)定义的是返回值类型 sum_one : ( int) c sum_b : (int) d;的调用方式[ myAonaufly sum_one:1 sum_b:2] sum :(int) i : (int) j;的调用方式[myAonaufly sum:myAonaufly._a :myAonaufly._b] 很坑 ,独树一帜和很多主流编程语言都不一样。。。。 本文转自Aonaufly51CTO博客,原文链接:http://blog.51cto.com/aonaufly/1963502,如需转载请自行联系原作者

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册